aboutsummaryrefslogtreecommitdiff
path: root/gas/config
diff options
context:
space:
mode:
authorRichard Henderson <rth@redhat.com>1999-05-03 07:29:11 +0000
committerRichard Henderson <rth@redhat.com>1999-05-03 07:29:11 +0000
commit252b5132c753830d5fd56823373aed85f2a0db63 (patch)
tree1af963bfd8d3e55167b81def4207f175eaff3a56 /gas/config
downloadgdb-252b5132c753830d5fd56823373aed85f2a0db63.zip
gdb-252b5132c753830d5fd56823373aed85f2a0db63.tar.gz
gdb-252b5132c753830d5fd56823373aed85f2a0db63.tar.bz2
19990502 sourceware importbinu_ss_19990502
Diffstat (limited to 'gas/config')
-rw-r--r--gas/config/aout_gnu.h455
-rw-r--r--gas/config/atof-ieee.c696
-rw-r--r--gas/config/atof-tahoe.c431
-rw-r--r--gas/config/atof-vax.c517
-rw-r--r--gas/config/e-i386coff.c17
-rw-r--r--gas/config/e-i386elf.c17
-rw-r--r--gas/config/e-mipsecoff.c37
-rw-r--r--gas/config/e-mipself.c37
-rw-r--r--gas/config/go32.cfg93
-rw-r--r--gas/config/itbl-mips.h47
-rw-r--r--gas/config/m68k-parse.h283
-rw-r--r--gas/config/m68k-parse.y1061
-rw-r--r--gas/config/m88k-opcode.h559
-rw-r--r--gas/config/obj-aout.c629
-rw-r--r--gas/config/obj-aout.h239
-rw-r--r--gas/config/obj-bout.c348
-rw-r--r--gas/config/obj-bout.h316
-rw-r--r--gas/config/obj-coff.c4482
-rw-r--r--gas/config/obj-coff.h841
-rw-r--r--gas/config/obj-ecoff.c309
-rw-r--r--gas/config/obj-ecoff.h67
-rw-r--r--gas/config/obj-elf.c1764
-rw-r--r--gas/config/obj-elf.h194
-rw-r--r--gas/config/obj-evax.c83
-rw-r--r--gas/config/obj-evax.h95
-rw-r--r--gas/config/obj-generic.c41
-rw-r--r--gas/config/obj-generic.h80
-rw-r--r--gas/config/obj-hp300.c52
-rw-r--r--gas/config/obj-hp300.h71
-rw-r--r--gas/config/obj-ieee.c627
-rw-r--r--gas/config/obj-ieee.h50
-rw-r--r--gas/config/obj-multi.c4
-rw-r--r--gas/config/obj-multi.h50
-rw-r--r--gas/config/obj-som.c307
-rw-r--r--gas/config/obj-som.h73
-rw-r--r--gas/config/obj-vms.c5549
-rw-r--r--gas/config/obj-vms.h552
-rw-r--r--gas/config/tc-a29k.c1296
-rw-r--r--gas/config/tc-a29k.h54
-rw-r--r--gas/config/tc-alpha.c4762
-rw-r--r--gas/config/tc-alpha.h104
-rw-r--r--gas/config/tc-arc.c1481
-rw-r--r--gas/config/tc-arc.h71
-rw-r--r--gas/config/tc-arm.c6857
-rw-r--r--gas/config/tc-arm.h204
-rw-r--r--gas/config/tc-d10v.c1636
-rw-r--r--gas/config/tc-d10v.h62
-rw-r--r--gas/config/tc-d30v.c2218
-rw-r--r--gas/config/tc-d30v.h59
-rw-r--r--gas/config/tc-fr30.c664
-rw-r--r--gas/config/tc-fr30.h81
-rw-r--r--gas/config/tc-generic.c0
-rw-r--r--gas/config/tc-generic.h39
-rw-r--r--gas/config/tc-h8300.c1595
-rw-r--r--gas/config/tc-h8300.h58
-rw-r--r--gas/config/tc-h8500.c1633
-rw-r--r--gas/config/tc-h8500.h57
-rw-r--r--gas/config/tc-hppa.c6716
-rw-r--r--gas/config/tc-hppa.h162
-rw-r--r--gas/config/tc-i386.c4475
-rw-r--r--gas/config/tc-i386.h471
-rw-r--r--gas/config/tc-i860.c1263
-rw-r--r--gas/config/tc-i860.h39
-rw-r--r--gas/config/tc-i960.c3231
-rw-r--r--gas/config/tc-i960.h171
-rw-r--r--gas/config/tc-m32r.c1300
-rw-r--r--gas/config/tc-m32r.h102
-rw-r--r--gas/config/tc-m68851.h304
-rw-r--r--gas/config/tc-m68k.c7009
-rw-r--r--gas/config/tc-m68k.h216
-rw-r--r--gas/config/tc-m88k.c1452
-rw-r--r--gas/config/tc-m88k.h108
-rw-r--r--gas/config/tc-mcore.c2183
-rw-r--r--gas/config/tc-mcore.h119
-rw-r--r--gas/config/tc-mips.c11783
-rw-r--r--gas/config/tc-mips.h153
-rw-r--r--gas/config/tc-mn10200.c1414
-rw-r--r--gas/config/tc-mn10200.h51
-rw-r--r--gas/config/tc-mn10300.c1649
-rw-r--r--gas/config/tc-mn10300.h50
-rw-r--r--gas/config/tc-ns32k.c2328
-rw-r--r--gas/config/tc-ns32k.h155
-rw-r--r--gas/config/tc-ppc.c5003
-rw-r--r--gas/config/tc-ppc.h276
-rw-r--r--gas/config/tc-sh.c2425
-rw-r--r--gas/config/tc-sh.h152
-rw-r--r--gas/config/tc-sparc.c3551
-rw-r--r--gas/config/tc-sparc.h164
-rw-r--r--gas/config/tc-tahoe.c2027
-rw-r--r--gas/config/tc-tahoe.h43
-rw-r--r--gas/config/tc-tic30.c1887
-rw-r--r--gas/config/tc-tic30.h55
-rw-r--r--gas/config/tc-tic80.c1061
-rw-r--r--gas/config/tc-tic80.h63
-rw-r--r--gas/config/tc-v850.c2478
-rw-r--r--gas/config/tc-v850.h85
-rw-r--r--gas/config/tc-vax.c3242
-rw-r--r--gas/config/tc-vax.h45
-rw-r--r--gas/config/tc-w65.c1219
-rw-r--r--gas/config/tc-w65.h61
-rw-r--r--gas/config/tc-z8k.c1589
-rw-r--r--gas/config/tc-z8k.h54
-rw-r--r--gas/config/te-386bsd.h31
-rw-r--r--gas/config/te-aux.h17
-rw-r--r--gas/config/te-delt88.h13
-rw-r--r--gas/config/te-delta.h14
-rw-r--r--gas/config/te-dpx2.h12
-rw-r--r--gas/config/te-dynix.h7
-rw-r--r--gas/config/te-epoc-pe.h8
-rw-r--r--gas/config/te-generic.h22
-rw-r--r--gas/config/te-go32.h16
-rw-r--r--gas/config/te-hp300.h25
-rw-r--r--gas/config/te-hppa.h26
-rw-r--r--gas/config/te-i386aix.h29
-rw-r--r--gas/config/te-ic960.h38
-rw-r--r--gas/config/te-linux.h4
-rw-r--r--gas/config/te-lnews.h5
-rw-r--r--gas/config/te-lynx.h7
-rw-r--r--gas/config/te-mach.h2
-rw-r--r--gas/config/te-macos.h11
-rw-r--r--gas/config/te-multi.h22
-rw-r--r--gas/config/te-nbsd.h23
-rw-r--r--gas/config/te-nbsd532.h19
-rw-r--r--gas/config/te-pc532mach.h19
-rw-r--r--gas/config/te-pe.h7
-rw-r--r--gas/config/te-ppcnw.h31
-rw-r--r--gas/config/te-psos.h22
-rw-r--r--gas/config/te-riscix.h6
-rw-r--r--gas/config/te-sparcaout.h21
-rw-r--r--gas/config/te-sun3.h48
-rw-r--r--gas/config/te-svr4.h4
-rw-r--r--gas/config/te-sysv32.h6
-rw-r--r--gas/config/vax-inst.h77
-rw-r--r--gas/config/vms-a-conf.h129
-rw-r--r--gas/config/vms-conf.h179
135 files changed, 117288 insertions, 0 deletions
diff --git a/gas/config/aout_gnu.h b/gas/config/aout_gnu.h
new file mode 100644
index 0000000..badf9cb
--- /dev/null
+++ b/gas/config/aout_gnu.h
@@ -0,0 +1,455 @@
+/* This file is aout_gnu.h
+
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef __A_OUT_GNU_H__
+#define __A_OUT_GNU_H__
+
+/* There are two main flavours of a.out, one which uses the standard
+ relocations, and one which uses extended relocations.
+
+ Today, the extended reloc uses are
+ TC_SPARC, TC_A29K
+
+ each must define the enum reloc_type
+
+*/
+
+#define USE_EXTENDED_RELOC (defined(TC_SPARC) || defined(TC_A29K))
+
+#if defined(TC_SPARC) || defined(TC_A29K)
+enum reloc_type
+ {
+ RELOC_8, RELOC_16, RELOC_32,/* simple relocations */
+ RELOC_DISP8, RELOC_DISP16, RELOC_DISP32, /* pc-rel displacement */
+ RELOC_WDISP30, RELOC_WDISP22,
+ RELOC_HI22, RELOC_22,
+ RELOC_13, RELOC_LO10,
+ RELOC_SFA_BASE, RELOC_SFA_OFF13,
+ RELOC_BASE10, RELOC_BASE13, RELOC_BASE22, /* P.I.C. (base-relative) */
+ RELOC_PC10, RELOC_PC22, /* for some sort of pc-rel P.I.C. (?) */
+ RELOC_JMP_TBL, /* P.I.C. jump table */
+ RELOC_SEGOFF16, /* reputedly for shared libraries somehow */
+ RELOC_GLOB_DAT, RELOC_JMP_SLOT, RELOC_RELATIVE,
+ RELOC_10, RELOC_11,
+ RELOC_WDISP2_14,
+ RELOC_WDISP19,
+ RELOC_HHI22,
+ RELOC_HLO10,
+
+ /* 29K relocation types */
+ RELOC_JUMPTARG, RELOC_CONST, RELOC_CONSTH,
+
+ RELOC_WDISP14, RELOC_WDISP21,
+
+ NO_RELOC
+ };
+
+#endif /* TC_SPARC or TC_A29K */
+
+
+#define __GNU_EXEC_MACROS__
+
+#ifndef __STRUCT_EXEC_OVERRIDE__
+
+/* This is the layout on disk of a Unix V7, Berkeley, SunOS, Vax Ultrix
+ "struct exec". Don't assume that on this machine, the "struct exec"
+ will lay out the same sizes or alignments. */
+
+struct exec_bytes
+ {
+ unsigned char a_info[4];
+ unsigned char a_text[4];
+ unsigned char a_data[4];
+ unsigned char a_bss[4];
+ unsigned char a_syms[4];
+ unsigned char a_entry[4];
+ unsigned char a_trsize[4];
+ unsigned char a_drsize[4];
+ };
+
+/* How big the "struct exec" is on disk */
+#define EXEC_BYTES_SIZE (8 * 4)
+
+/* This is the layout in memory of a "struct exec" while we process it. */
+
+struct exec
+{
+ unsigned long a_info; /* Use macros N_MAGIC, etc for access */
+ unsigned a_text; /* length of text, in bytes */
+ unsigned a_data; /* length of data, in bytes */
+ unsigned a_bss; /* length of uninitialized data area for file, in bytes */
+ unsigned a_syms; /* length of symbol table data in file, in bytes */
+ unsigned a_entry; /* start address */
+ unsigned a_trsize; /* length of relocation info for text, in bytes */
+ unsigned a_drsize; /* length of relocation info for data, in bytes */
+};
+
+#endif /* __STRUCT_EXEC_OVERRIDE__ */
+
+/* these go in the N_MACHTYPE field */
+/* These symbols could be defined by code from Suns...punt 'em */
+#undef M_UNKNOWN
+#undef M_68010
+#undef M_68020
+#undef M_SPARC
+enum machine_type
+ {
+ M_UNKNOWN = 0,
+ M_68010 = 1,
+ M_68020 = 2,
+ M_SPARC = 3,
+ /* skip a bunch so we don't run into any of sun's numbers */
+ M_386 = 100,
+ M_29K = 101,
+ M_RS6000 = 102, /* IBM RS/6000 */
+ /* HP/BSD formats */
+ M_HP200 = 200, /* hp200 (68010) BSD binary */
+ M_HP300 = 300, /* hp300 (68020+68881) BSD binary */
+ M_HPUX23 = 0x020C /* hp200/300 HPUX binary */
+ };
+
+#define N_MAGIC(exec) ((exec).a_info & 0xffff)
+#define N_MACHTYPE(exec) ((enum machine_type)(((exec).a_info >> 16) & 0xff))
+#define N_FLAGS(exec) (((exec).a_info >> 24) & 0xff)
+#define N_SET_INFO(exec, magic, type, flags) \
+ ((exec).a_info = ((magic) & 0xffff) \
+ | (((int)(type) & 0xff) << 16) \
+ | (((flags) & 0xff) << 24))
+#define N_SET_MAGIC(exec, magic) \
+ ((exec).a_info = (((exec).a_info & 0xffff0000) | ((magic) & 0xffff)))
+
+#define N_SET_MACHTYPE(exec, machtype) \
+ ((exec).a_info = \
+ ((exec).a_info&0xff00ffff) | ((((int)(machtype))&0xff) << 16))
+
+#define N_SET_FLAGS(exec, flags) \
+ ((exec).a_info = \
+ ((exec).a_info&0x00ffffff) | (((flags) & 0xff) << 24))
+
+/* Code indicating object file or impure executable. */
+#ifndef OMAGIC
+#define OMAGIC 0407
+#endif
+/* Code indicating pure executable. */
+#define NMAGIC 0410
+/* Code indicating demand-paged executable. */
+#define ZMAGIC 0413
+
+/* Virtual Address of text segment from the a.out file. For OMAGIC,
+ (almost always "unlinked .o's" these days), should be zero.
+ For linked files, should reflect reality if we know it. */
+
+#ifndef N_TXTADDR
+#define N_TXTADDR(x) (N_MAGIC(x)==OMAGIC? 0 : TEXT_START_ADDR)
+#endif
+
+#ifndef N_BADMAG
+#define N_BADMAG(x) (N_MAGIC(x) != OMAGIC \
+ && N_MAGIC(x) != NMAGIC \
+ && N_MAGIC(x) != ZMAGIC)
+#endif
+
+/* By default, segment size is constant. But on some machines, it can
+ be a function of the a.out header (e.g. machine type). */
+#ifndef N_SEGSIZE
+#define N_SEGSIZE(x) SEGMENT_SIZE
+#endif
+
+/* This complexity is for encapsulated COFF support */
+#ifndef _N_HDROFF
+#define _N_HDROFF(x) (N_SEGSIZE(x) - sizeof (struct exec))
+#endif
+
+#ifndef N_TXTOFF
+#define N_TXTOFF(x) (N_MAGIC(x) == ZMAGIC ? \
+ _N_HDROFF((x)) + sizeof (struct exec) : \
+ sizeof (struct exec))
+#endif
+
+
+#ifndef N_DATOFF
+#define N_DATOFF(x) ( N_TXTOFF(x) + (x).a_text )
+#endif
+
+#ifndef N_TRELOFF
+#define N_TRELOFF(x) ( N_DATOFF(x) + (x).a_data )
+#endif
+
+#ifndef N_DRELOFF
+#define N_DRELOFF(x) ( N_TRELOFF(x) + (x).a_trsize )
+#endif
+
+#ifndef N_SYMOFF
+#define N_SYMOFF(x) ( N_DRELOFF(x) + (x).a_drsize )
+#endif
+
+#ifndef N_STROFF
+#define N_STROFF(x) ( N_SYMOFF(x) + (x).a_syms )
+#endif
+
+/* Address of text segment in memory after it is loaded. */
+#ifndef N_TXTADDR
+#define N_TXTADDR(x) 0
+#endif
+
+#ifndef N_DATADDR
+#define N_DATADDR(x) \
+ (N_MAGIC(x)==OMAGIC? (N_TXTADDR(x)+(x).a_text) \
+ : (N_SEGSIZE(x) + ((N_TXTADDR(x)+(x).a_text-1) & ~(N_SEGSIZE(x)-1))))
+#endif
+
+/* Address of bss segment in memory after it is loaded. */
+#define N_BSSADDR(x) (N_DATADDR(x) + (x).a_data)
+
+struct nlist
+ {
+ union
+ {
+ char *n_name;
+ struct nlist *n_next;
+ long n_strx;
+ }
+ n_un;
+ unsigned char n_type;
+ char n_other;
+ short n_desc;
+ unsigned long n_value;
+ };
+
+#define N_UNDF 0
+#define N_ABS 2
+#define N_TEXT 4
+#define N_DATA 6
+#define N_BSS 8
+#define N_COMM 0x12 /* common (visible in shared lib commons) */
+#define N_FN 0x1F /* File name of a .o file */
+
+/* Note: N_EXT can only usefully be OR-ed with N_UNDF, N_ABS, N_TEXT,
+ N_DATA, or N_BSS. When the low-order bit of other types is set,
+ (e.g. N_WARNING versus N_FN), they are two different types. */
+#define N_EXT 1
+#define N_TYPE 036
+#define N_STAB 0340
+
+/* The following type indicates the definition of a symbol as being
+ an indirect reference to another symbol. The other symbol
+ appears as an undefined reference, immediately following this symbol.
+
+ Indirection is asymmetrical. The other symbol's value will be used
+ to satisfy requests for the indirect symbol, but not vice versa.
+ If the other symbol does not have a definition, libraries will
+ be searched to find a definition. */
+
+#define N_INDR 0xa
+
+/* The following symbols refer to set elements.
+ All the N_SET[ATDB] symbols with the same name form one set.
+ Space is allocated for the set in the text section, and each set
+ element's value is stored into one word of the space.
+ The first word of the space is the length of the set (number of elements).
+
+ The address of the set is made into an N_SETV symbol
+ whose name is the same as the name of the set.
+ This symbol acts like a N_DATA global symbol
+ in that it can satisfy undefined external references. */
+
+/* These appear as input to LD, in a .o file. */
+#define N_SETA 0x14 /* Absolute set element symbol */
+#define N_SETT 0x16 /* Text set element symbol */
+#define N_SETD 0x18 /* Data set element symbol */
+#define N_SETB 0x1A /* Bss set element symbol */
+
+/* This is output from LD. */
+#define N_SETV 0x1C /* Pointer to set vector in data area. */
+
+/* Warning symbol. The text gives a warning message, the next symbol
+ in the table will be undefined. When the symbol is referenced, the
+ message is printed. */
+
+#define N_WARNING 0x1e
+
+/* Weak symbols. These are a GNU extension to the a.out format. The
+ semantics are those of ELF weak symbols. Weak symbols are always
+ externally visible. The N_WEAK? values are squeezed into the
+ available slots. The value of a N_WEAKU symbol is 0. The values
+ of the other types are the definitions. */
+#define N_WEAKU 0x0d /* Weak undefined symbol. */
+#define N_WEAKA 0x0e /* Weak absolute symbol. */
+#define N_WEAKT 0x0f /* Weak text symbol. */
+#define N_WEAKD 0x10 /* Weak data symbol. */
+#define N_WEAKB 0x11 /* Weak bss symbol. */
+
+/* This structure describes a single relocation to be performed.
+ The text-relocation section of the file is a vector of these structures,
+ all of which apply to the text section.
+ Likewise, the data-relocation section applies to the data section. */
+
+/* The following enum and struct were borrowed from SunOS's
+ /usr/include/sun4/a.out.h and extended to handle
+ other machines. It is currently used on SPARC and AMD 29000.
+
+ reloc_ext_bytes is how it looks on disk. reloc_info_extended is
+ how we might process it on a native host. */
+#if USE_EXTENDED_RELOC
+
+struct reloc_ext_bytes
+ {
+ unsigned char r_address[4];
+ unsigned char r_index[3];
+ unsigned char r_bits[1];
+ unsigned char r_addend[4];
+ };
+
+
+#define RELOC_EXT_BITS_EXTERN_BIG 0x80
+#define RELOC_EXT_BITS_EXTERN_LITTLE 0x01
+
+#define RELOC_EXT_BITS_TYPE_BIG 0x1F
+#define RELOC_EXT_BITS_TYPE_SH_BIG 0
+#define RELOC_EXT_BITS_TYPE_LITTLE 0xF8
+#define RELOC_EXT_BITS_TYPE_SH_LITTLE 3
+
+#define RELOC_EXT_SIZE 12 /* Bytes per relocation entry */
+
+struct reloc_info_extended
+{
+ unsigned long r_address;
+ unsigned int r_index:24;
+# define r_symbolnum r_index
+ unsigned r_extern:1;
+ unsigned:2;
+ /* RS/6000 compiler does not support enum bitfield
+ enum reloc_type r_type:5; */
+ enum reloc_type r_type;
+ long int r_addend;
+};
+
+#else
+
+/* The standard, old-fashioned, Berkeley compatible relocation struct */
+
+
+
+#ifdef TC_I860
+/* NOTE: three bits max, see struct reloc_info_i860.r_type */
+enum i860_reloc_type
+ {
+ NO_RELOC = 0, BRADDR, LOW0, LOW1, LOW2, LOW3, LOW4, SPLIT0, SPLIT1, SPLIT2, RELOC_32,
+ };
+
+typedef enum i860_reloc_type reloc_type;
+
+/* NOTE: two bits max, see reloc_info_i860.r_type */
+enum highlow_type
+ {
+ NO_SPEC = 0, PAIR, HIGH, HIGHADJ,
+ };
+
+
+struct reloc_info_i860
+{
+ unsigned long r_address;
+ /*
+ * Using bit fields here is a bad idea because the order is not portable. :-(
+ */
+ unsigned int r_symbolnum:24;
+ unsigned int r_pcrel:1;
+ unsigned int r_extern:1;
+ /* combining the two field simplifies the argument passing in "new_fix()" */
+ /* and is compatible with the existing Sparc #ifdef's */
+ /* r_type: highlow_type - bits 5,4; reloc_type - bits 3-0 */
+ unsigned int r_type:6;
+ long r_addend;
+};
+
+#endif /* TC_I860 */
+
+
+struct reloc_std_bytes
+ {
+ unsigned char r_address[4];
+ unsigned char r_index[3];
+ unsigned char r_bits[1];
+ };
+
+#define RELOC_STD_BITS_PCREL_BIG 0x80
+#define RELOC_STD_BITS_PCREL_LITTLE 0x01
+
+#define RELOC_STD_BITS_LENGTH_BIG 0x60
+#define RELOC_STD_BITS_LENGTH_SH_BIG 5 /* To shift to units place */
+#define RELOC_STD_BITS_LENGTH_LITTLE 0x06
+#define RELOC_STD_BITS_LENGTH_SH_LITTLE 1
+
+#define RELOC_STD_BITS_EXTERN_BIG 0x10
+#define RELOC_STD_BITS_EXTERN_LITTLE 0x08
+
+#define RELOC_STD_BITS_BASEREL_BIG 0x08
+#define RELOC_STD_BITS_BASEREL_LITTLE 0x08
+
+#define RELOC_STD_BITS_JMPTABLE_BIG 0x04
+#define RELOC_STD_BITS_JMPTABLE_LITTLE 0x04
+
+#define RELOC_STD_BITS_RELATIVE_BIG 0x02
+#define RELOC_STD_BITS_RELATIVE_LITTLE 0x02
+
+#define RELOC_STD_SIZE 8 /* Bytes per relocation entry */
+
+#endif /* USE_EXTENDED_RELOC */
+
+#ifndef CUSTOM_RELOC_FORMAT
+struct relocation_info
+{
+ /* Address (within segment) to be relocated. */
+ int r_address;
+ /* The meaning of r_symbolnum depends on r_extern. */
+ unsigned int r_symbolnum:24;
+ /* Nonzero means value is a pc-relative offset
+ and it should be relocated for changes in its own address
+ as well as for changes in the symbol or section specified. */
+ unsigned int r_pcrel:1;
+ /* Length (as exponent of 2) of the field to be relocated.
+ Thus, a value of 2 indicates 1<<2 bytes. */
+ unsigned int r_length:2;
+ /* 1 => relocate with value of symbol.
+ r_symbolnum is the index of the symbol
+ in file's the symbol table.
+ 0 => relocate with the address of a segment.
+ r_symbolnum is N_TEXT, N_DATA, N_BSS or N_ABS
+ (the N_EXT bit may be set also, but signifies nothing). */
+ unsigned int r_extern:1;
+ /* The next three bits are for SunOS shared libraries, and seem to
+ be undocumented. */
+#ifdef TC_NS32K
+ unsigned int r_bsr:1;
+ unsigned int r_disp:2;
+#else
+ unsigned int r_baserel:1; /* Linkage table relative */
+ unsigned int r_jmptable:1; /* pc-relative to jump table */
+ unsigned int r_relative:1; /* "relative relocation" */
+#endif /* TC_NS32K */
+ /* unused */
+ unsigned int r_pad:1; /* Padding -- set to zero */
+};
+
+#endif /* CUSTOM_RELOC_FORMAT */
+
+#endif /* __A_OUT_GNU_H__ */
+
+/* end of aout_gnu.h */
diff --git a/gas/config/atof-ieee.c b/gas/config/atof-ieee.c
new file mode 100644
index 0000000..d586e3a
--- /dev/null
+++ b/gas/config/atof-ieee.c
@@ -0,0 +1,696 @@
+/* atof_ieee.c - turn a Flonum into an IEEE floating point number
+ Copyright (C) 1987, 92, 93, 94, 95, 96, 97, 1998
+ Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#include "as.h"
+
+/* Flonums returned here. */
+extern FLONUM_TYPE generic_floating_point_number;
+
+static int next_bits PARAMS ((int));
+static void unget_bits PARAMS ((int));
+static void make_invalid_floating_point_number PARAMS ((LITTLENUM_TYPE *));
+
+extern const char EXP_CHARS[];
+/* Precision in LittleNums. */
+/* Don't count the gap in the m68k extended precision format. */
+#define MAX_PRECISION (5)
+#define F_PRECISION (2)
+#define D_PRECISION (4)
+#define X_PRECISION (5)
+#define P_PRECISION (5)
+
+/* Length in LittleNums of guard bits. */
+#define GUARD (2)
+
+static const unsigned long mask[] =
+{
+ 0x00000000,
+ 0x00000001,
+ 0x00000003,
+ 0x00000007,
+ 0x0000000f,
+ 0x0000001f,
+ 0x0000003f,
+ 0x0000007f,
+ 0x000000ff,
+ 0x000001ff,
+ 0x000003ff,
+ 0x000007ff,
+ 0x00000fff,
+ 0x00001fff,
+ 0x00003fff,
+ 0x00007fff,
+ 0x0000ffff,
+ 0x0001ffff,
+ 0x0003ffff,
+ 0x0007ffff,
+ 0x000fffff,
+ 0x001fffff,
+ 0x003fffff,
+ 0x007fffff,
+ 0x00ffffff,
+ 0x01ffffff,
+ 0x03ffffff,
+ 0x07ffffff,
+ 0x0fffffff,
+ 0x1fffffff,
+ 0x3fffffff,
+ 0x7fffffff,
+ 0xffffffff,
+};
+
+
+static int bits_left_in_littlenum;
+static int littlenums_left;
+static LITTLENUM_TYPE *littlenum_pointer;
+
+static int
+next_bits (number_of_bits)
+ int number_of_bits;
+{
+ int return_value;
+
+ if (!littlenums_left)
+ return (0);
+ if (number_of_bits >= bits_left_in_littlenum)
+ {
+ return_value = mask[bits_left_in_littlenum] & *littlenum_pointer;
+ number_of_bits -= bits_left_in_littlenum;
+ return_value <<= number_of_bits;
+
+ if (--littlenums_left)
+ {
+ bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS - number_of_bits;
+ --littlenum_pointer;
+ return_value |= (*littlenum_pointer >> bits_left_in_littlenum) & mask[number_of_bits];
+ }
+ }
+ else
+ {
+ bits_left_in_littlenum -= number_of_bits;
+ return_value = mask[number_of_bits] & (*littlenum_pointer >> bits_left_in_littlenum);
+ }
+ return (return_value);
+}
+
+/* Num had better be less than LITTLENUM_NUMBER_OF_BITS */
+static void
+unget_bits (num)
+ int num;
+{
+ if (!littlenums_left)
+ {
+ ++littlenum_pointer;
+ ++littlenums_left;
+ bits_left_in_littlenum = num;
+ }
+ else if (bits_left_in_littlenum + num > LITTLENUM_NUMBER_OF_BITS)
+ {
+ bits_left_in_littlenum = num - (LITTLENUM_NUMBER_OF_BITS - bits_left_in_littlenum);
+ ++littlenum_pointer;
+ ++littlenums_left;
+ }
+ else
+ bits_left_in_littlenum += num;
+}
+
+static void
+make_invalid_floating_point_number (words)
+ LITTLENUM_TYPE *words;
+{
+ as_bad (_("cannot create floating-point number"));
+ words[0] = (LITTLENUM_TYPE) ((unsigned) -1) >> 1; /* Zero the leftmost bit */
+ words[1] = (LITTLENUM_TYPE) -1;
+ words[2] = (LITTLENUM_TYPE) -1;
+ words[3] = (LITTLENUM_TYPE) -1;
+ words[4] = (LITTLENUM_TYPE) -1;
+ words[5] = (LITTLENUM_TYPE) -1;
+}
+
+/************************************************************************\
+ * Warning: this returns 16-bit LITTLENUMs. It is up to the caller *
+ * to figure out any alignment problems and to conspire for the *
+ * bytes/word to be emitted in the right order. Bigendians beware! *
+ * *
+\************************************************************************/
+
+/* Note that atof-ieee always has X and P precisions enabled. it is up
+ to md_atof to filter them out if the target machine does not support
+ them. */
+
+/* Returns pointer past text consumed. */
+char *
+atof_ieee (str, what_kind, words)
+ char *str; /* Text to convert to binary. */
+ char what_kind; /* 'd', 'f', 'g', 'h' */
+ LITTLENUM_TYPE *words; /* Build the binary here. */
+{
+ /* Extra bits for zeroed low-order bits. The 1st MAX_PRECISION are
+ zeroed, the last contain flonum bits. */
+ static LITTLENUM_TYPE bits[MAX_PRECISION + MAX_PRECISION + GUARD];
+ char *return_value;
+ /* Number of 16-bit words in the format. */
+ int precision;
+ long exponent_bits;
+ FLONUM_TYPE save_gen_flonum;
+
+ /* We have to save the generic_floating_point_number because it
+ contains storage allocation about the array of LITTLENUMs where
+ the value is actually stored. We will allocate our own array of
+ littlenums below, but have to restore the global one on exit. */
+ save_gen_flonum = generic_floating_point_number;
+
+ return_value = str;
+ generic_floating_point_number.low = bits + MAX_PRECISION;
+ generic_floating_point_number.high = NULL;
+ generic_floating_point_number.leader = NULL;
+ generic_floating_point_number.exponent = 0;
+ generic_floating_point_number.sign = '\0';
+
+ /* Use more LittleNums than seems necessary: the highest flonum may
+ have 15 leading 0 bits, so could be useless. */
+
+ memset (bits, '\0', sizeof (LITTLENUM_TYPE) * MAX_PRECISION);
+
+ switch (what_kind)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ precision = F_PRECISION;
+ exponent_bits = 8;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ precision = D_PRECISION;
+ exponent_bits = 11;
+ break;
+
+ case 'x':
+ case 'X':
+ case 'e':
+ case 'E':
+ precision = X_PRECISION;
+ exponent_bits = 15;
+ break;
+
+ case 'p':
+ case 'P':
+
+ precision = P_PRECISION;
+ exponent_bits = -1;
+ break;
+
+ default:
+ make_invalid_floating_point_number (words);
+ return (NULL);
+ }
+
+ generic_floating_point_number.high
+ = generic_floating_point_number.low + precision - 1 + GUARD;
+
+ if (atof_generic (&return_value, ".", EXP_CHARS,
+ &generic_floating_point_number))
+ {
+ make_invalid_floating_point_number (words);
+ return (NULL);
+ }
+ gen_to_words (words, precision, exponent_bits);
+
+ /* Restore the generic_floating_point_number's storage alloc (and
+ everything else). */
+ generic_floating_point_number = save_gen_flonum;
+
+ return return_value;
+}
+
+/* Turn generic_floating_point_number into a real float/double/extended. */
+int
+gen_to_words (words, precision, exponent_bits)
+ LITTLENUM_TYPE *words;
+ int precision;
+ long exponent_bits;
+{
+ int return_value = 0;
+
+ long exponent_1;
+ long exponent_2;
+ long exponent_3;
+ long exponent_4;
+ int exponent_skippage;
+ LITTLENUM_TYPE word1;
+ LITTLENUM_TYPE *lp;
+ LITTLENUM_TYPE *words_end;
+
+ words_end = words + precision;
+#ifdef TC_M68K
+ if (precision == X_PRECISION)
+ /* On the m68k the extended precision format has a gap of 16 bits
+ between the exponent and the mantissa. */
+ words_end++;
+#endif
+
+ if (generic_floating_point_number.low > generic_floating_point_number.leader)
+ {
+ /* 0.0e0 seen. */
+ if (generic_floating_point_number.sign == '+')
+ words[0] = 0x0000;
+ else
+ words[0] = 0x8000;
+ memset (&words[1], '\0',
+ (words_end - words - 1) * sizeof (LITTLENUM_TYPE));
+ return (return_value);
+ }
+
+ /* NaN: Do the right thing */
+ if (generic_floating_point_number.sign == 0)
+ {
+ if (precision == F_PRECISION)
+ {
+ words[0] = 0x7fff;
+ words[1] = 0xffff;
+ }
+ else if (precision == X_PRECISION)
+ {
+#ifdef TC_M68K
+ words[0] = 0x7fff;
+ words[1] = 0;
+ words[2] = 0xffff;
+ words[3] = 0xffff;
+ words[4] = 0xffff;
+ words[5] = 0xffff;
+#else /* ! TC_M68K */
+#ifdef TC_I386
+ words[0] = 0xffff;
+ words[1] = 0xc000;
+ words[2] = 0;
+ words[3] = 0;
+ words[4] = 0;
+#else /* ! TC_I386 */
+ abort ();
+#endif /* ! TC_I386 */
+#endif /* ! TC_M68K */
+ }
+ else
+ {
+ words[0] = 0x7fff;
+ words[1] = 0xffff;
+ words[2] = 0xffff;
+ words[3] = 0xffff;
+ }
+ return return_value;
+ }
+ else if (generic_floating_point_number.sign == 'P')
+ {
+ /* +INF: Do the right thing */
+ if (precision == F_PRECISION)
+ {
+ words[0] = 0x7f80;
+ words[1] = 0;
+ }
+ else if (precision == X_PRECISION)
+ {
+#ifdef TC_M68K
+ words[0] = 0x7fff;
+ words[1] = 0;
+ words[2] = 0;
+ words[3] = 0;
+ words[4] = 0;
+ words[5] = 0;
+#else /* ! TC_M68K */
+#ifdef TC_I386
+ words[0] = 0x7fff;
+ words[1] = 0x8000;
+ words[2] = 0;
+ words[3] = 0;
+ words[4] = 0;
+#else /* ! TC_I386 */
+ abort ();
+#endif /* ! TC_I386 */
+#endif /* ! TC_M68K */
+ }
+ else
+ {
+ words[0] = 0x7ff0;
+ words[1] = 0;
+ words[2] = 0;
+ words[3] = 0;
+ }
+ return (return_value);
+ }
+ else if (generic_floating_point_number.sign == 'N')
+ {
+ /* Negative INF */
+ if (precision == F_PRECISION)
+ {
+ words[0] = 0xff80;
+ words[1] = 0x0;
+ }
+ else if (precision == X_PRECISION)
+ {
+#ifdef TC_M68K
+ words[0] = 0xffff;
+ words[1] = 0;
+ words[2] = 0;
+ words[3] = 0;
+ words[4] = 0;
+ words[5] = 0;
+#else /* ! TC_M68K */
+#ifdef TC_I386
+ words[0] = 0xffff;
+ words[1] = 0x8000;
+ words[2] = 0;
+ words[3] = 0;
+ words[4] = 0;
+#else /* ! TC_I386 */
+ abort ();
+#endif /* ! TC_I386 */
+#endif /* ! TC_M68K */
+ }
+ else
+ {
+ words[0] = 0xfff0;
+ words[1] = 0x0;
+ words[2] = 0x0;
+ words[3] = 0x0;
+ }
+ return (return_value);
+ }
+ /*
+ * The floating point formats we support have:
+ * Bit 15 is sign bit.
+ * Bits 14:n are excess-whatever exponent.
+ * Bits n-1:0 (if any) are most significant bits of fraction.
+ * Bits 15:0 of the next word(s) are the next most significant bits.
+ *
+ * So we need: number of bits of exponent, number of bits of
+ * mantissa.
+ */
+ bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS;
+ littlenum_pointer = generic_floating_point_number.leader;
+ littlenums_left = (1
+ + generic_floating_point_number.leader
+ - generic_floating_point_number.low);
+ /* Seek (and forget) 1st significant bit */
+ for (exponent_skippage = 0; !next_bits (1); ++exponent_skippage);;
+ exponent_1 = (generic_floating_point_number.exponent
+ + generic_floating_point_number.leader
+ + 1
+ - generic_floating_point_number.low);
+ /* Radix LITTLENUM_RADIX, point just higher than
+ generic_floating_point_number.leader. */
+ exponent_2 = exponent_1 * LITTLENUM_NUMBER_OF_BITS;
+ /* Radix 2. */
+ exponent_3 = exponent_2 - exponent_skippage;
+ /* Forget leading zeros, forget 1st bit. */
+ exponent_4 = exponent_3 + ((1 << (exponent_bits - 1)) - 2);
+ /* Offset exponent. */
+
+ lp = words;
+
+ /* Word 1. Sign, exponent and perhaps high bits. */
+ word1 = ((generic_floating_point_number.sign == '+')
+ ? 0
+ : (1 << (LITTLENUM_NUMBER_OF_BITS - 1)));
+
+ /* Assume 2's complement integers. */
+ if (exponent_4 <= 0)
+ {
+ int prec_bits;
+ int num_bits;
+
+ unget_bits (1);
+ num_bits = -exponent_4;
+ prec_bits = LITTLENUM_NUMBER_OF_BITS * precision - (exponent_bits + 1 + num_bits);
+#ifdef TC_I386
+ if (precision == X_PRECISION && exponent_bits == 15)
+ {
+ /* On the i386 a denormalized extended precision float is
+ shifted down by one, effectively decreasing the exponent
+ bias by one. */
+ prec_bits -= 1;
+ num_bits += 1;
+ }
+#endif
+
+ if (num_bits >= LITTLENUM_NUMBER_OF_BITS - exponent_bits)
+ {
+ /* Bigger than one littlenum */
+ num_bits -= (LITTLENUM_NUMBER_OF_BITS - 1) - exponent_bits;
+ *lp++ = word1;
+ if (num_bits + exponent_bits + 1 >= precision * LITTLENUM_NUMBER_OF_BITS)
+ {
+ /* Exponent overflow */
+ make_invalid_floating_point_number (words);
+ return (return_value);
+ }
+#ifdef TC_M68K
+ if (precision == X_PRECISION && exponent_bits == 15)
+ *lp++ = 0;
+#endif
+ while (num_bits >= LITTLENUM_NUMBER_OF_BITS)
+ {
+ num_bits -= LITTLENUM_NUMBER_OF_BITS;
+ *lp++ = 0;
+ }
+ if (num_bits)
+ *lp++ = next_bits (LITTLENUM_NUMBER_OF_BITS - (num_bits));
+ }
+ else
+ {
+ if (precision == X_PRECISION && exponent_bits == 15)
+ {
+ *lp++ = word1;
+#ifdef TC_M68K
+ *lp++ = 0;
+#endif
+ *lp++ = next_bits (LITTLENUM_NUMBER_OF_BITS - num_bits);
+ }
+ else
+ {
+ word1 |= next_bits ((LITTLENUM_NUMBER_OF_BITS - 1) - (exponent_bits + num_bits));
+ *lp++ = word1;
+ }
+ }
+ while (lp < words_end)
+ *lp++ = next_bits (LITTLENUM_NUMBER_OF_BITS);
+
+ /* Round the mantissa up, but don't change the number */
+ if (next_bits (1))
+ {
+ --lp;
+ if (prec_bits > LITTLENUM_NUMBER_OF_BITS)
+ {
+ int n = 0;
+ int tmp_bits;
+
+ n = 0;
+ tmp_bits = prec_bits;
+ while (tmp_bits > LITTLENUM_NUMBER_OF_BITS)
+ {
+ if (lp[n] != (LITTLENUM_TYPE) - 1)
+ break;
+ --n;
+ tmp_bits -= LITTLENUM_NUMBER_OF_BITS;
+ }
+ if (tmp_bits > LITTLENUM_NUMBER_OF_BITS || (lp[n] & mask[tmp_bits]) != mask[tmp_bits])
+ {
+ unsigned long carry;
+
+ for (carry = 1; carry && (lp >= words); lp--)
+ {
+ carry = *lp + carry;
+ *lp = carry;
+ carry >>= LITTLENUM_NUMBER_OF_BITS;
+ }
+ }
+ else
+ {
+ /* This is an overflow of the denormal numbers. We
+ need to forget what we have produced, and instead
+ generate the smallest normalized number. */
+ lp = words;
+ word1 = ((generic_floating_point_number.sign == '+')
+ ? 0
+ : (1 << (LITTLENUM_NUMBER_OF_BITS - 1)));
+ word1 |= (1
+ << ((LITTLENUM_NUMBER_OF_BITS - 1)
+ - exponent_bits));
+ *lp++ = word1;
+ while (lp < words_end)
+ *lp++ = 0;
+ }
+ }
+ else if ((*lp & mask[prec_bits]) != mask[prec_bits])
+ *lp += 1;
+ }
+
+ return return_value;
+ }
+ else if ((unsigned long) exponent_4 >= mask[exponent_bits])
+ {
+ /*
+ * Exponent overflow. Lose immediately.
+ */
+
+ /*
+ * We leave return_value alone: admit we read the
+ * number, but return a floating exception
+ * because we can't encode the number.
+ */
+ make_invalid_floating_point_number (words);
+ return return_value;
+ }
+ else
+ {
+ word1 |= (exponent_4 << ((LITTLENUM_NUMBER_OF_BITS - 1) - exponent_bits))
+ | next_bits ((LITTLENUM_NUMBER_OF_BITS - 1) - exponent_bits);
+ }
+
+ *lp++ = word1;
+
+ /* X_PRECISION is special: on the 68k, it has 16 bits of zero in the
+ middle. Either way, it is then followed by a 1 bit. */
+ if (exponent_bits == 15 && precision == X_PRECISION)
+ {
+#ifdef TC_M68K
+ *lp++ = 0;
+#endif
+ *lp++ = (1 << (LITTLENUM_NUMBER_OF_BITS - 1)
+ | next_bits (LITTLENUM_NUMBER_OF_BITS - 1));
+ }
+
+ /* The rest of the words are just mantissa bits. */
+ while (lp < words_end)
+ *lp++ = next_bits (LITTLENUM_NUMBER_OF_BITS);
+
+ if (next_bits (1))
+ {
+ unsigned long carry;
+ /*
+ * Since the NEXT bit is a 1, round UP the mantissa.
+ * The cunning design of these hidden-1 floats permits
+ * us to let the mantissa overflow into the exponent, and
+ * it 'does the right thing'. However, we lose if the
+ * highest-order bit of the lowest-order word flips.
+ * Is that clear?
+ */
+
+ /* #if (sizeof(carry)) < ((sizeof(bits[0]) * BITS_PER_CHAR) + 2)
+ Please allow at least 1 more bit in carry than is in a LITTLENUM.
+ We need that extra bit to hold a carry during a LITTLENUM carry
+ propagation. Another extra bit (kept 0) will assure us that we
+ don't get a sticky sign bit after shifting right, and that
+ permits us to propagate the carry without any masking of bits.
+ #endif */
+ for (carry = 1, lp--; carry && (lp >= words); lp--)
+ {
+ carry = *lp + carry;
+ *lp = carry;
+ carry >>= LITTLENUM_NUMBER_OF_BITS;
+ }
+ if (precision == X_PRECISION && exponent_bits == 15)
+ {
+ /* Extended precision numbers have an explicit integer bit
+ that we may have to restore. */
+ if (lp == words)
+ {
+#ifdef TC_M68K
+ /* On the m68k there is a gap of 16 bits. We must
+ explicitly propagate the carry into the exponent. */
+ words[0] += words[1];
+ words[1] = 0;
+ lp++;
+#endif
+ /* Put back the integer bit. */
+ lp[1] |= 1 << (LITTLENUM_NUMBER_OF_BITS - 1);
+ }
+ }
+ if ((word1 ^ *words) & (1 << (LITTLENUM_NUMBER_OF_BITS - 1)))
+ {
+ /* We leave return_value alone: admit we read the
+ * number, but return a floating exception
+ * because we can't encode the number.
+ */
+ *words &= ~(1 << (LITTLENUM_NUMBER_OF_BITS - 1));
+ /* make_invalid_floating_point_number (words); */
+ /* return return_value; */
+ }
+ }
+ return (return_value);
+}
+
+#if 0 /* unused */
+/* This routine is a real kludge. Someone really should do it better,
+ but I'm too lazy, and I don't understand this stuff all too well
+ anyway. (JF) */
+static void
+int_to_gen (x)
+ long x;
+{
+ char buf[20];
+ char *bufp;
+
+ sprintf (buf, "%ld", x);
+ bufp = &buf[0];
+ if (atof_generic (&bufp, ".", EXP_CHARS, &generic_floating_point_number))
+ as_bad (_("Error converting number to floating point (Exponent overflow?)"));
+}
+#endif
+
+#ifdef TEST
+char *
+print_gen (gen)
+ FLONUM_TYPE *gen;
+{
+ FLONUM_TYPE f;
+ LITTLENUM_TYPE arr[10];
+ double dv;
+ float fv;
+ static char sbuf[40];
+
+ if (gen)
+ {
+ f = generic_floating_point_number;
+ generic_floating_point_number = *gen;
+ }
+ gen_to_words (&arr[0], 4, 11);
+ memcpy (&dv, &arr[0], sizeof (double));
+ sprintf (sbuf, "%x %x %x %x %.14G ", arr[0], arr[1], arr[2], arr[3], dv);
+ gen_to_words (&arr[0], 2, 8);
+ memcpy (&fv, &arr[0], sizeof (float));
+ sprintf (sbuf + strlen (sbuf), "%x %x %.12g\n", arr[0], arr[1], fv);
+
+ if (gen)
+ {
+ generic_floating_point_number = f;
+ }
+
+ return (sbuf);
+}
+
+#endif
+
+/* end of atof-ieee.c */
diff --git a/gas/config/atof-tahoe.c b/gas/config/atof-tahoe.c
new file mode 100644
index 0000000..760485c
--- /dev/null
+++ b/gas/config/atof-tahoe.c
@@ -0,0 +1,431 @@
+
+/* atof_tahoe.c - turn a string into a Tahoe floating point number
+ Copyright (C) 1987, 1998 Free Software Foundation, Inc.
+ */
+
+/* This is really a simplified version of atof_vax.c. I glommed it wholesale
+ and then shaved it down. I don't even know how it works. (Don't you find
+ my honesty refreshing? bowen@cs.Buffalo.EDU (Devon E Bowen)
+
+ I don't allow uppercase letters in the precision descrpitors. Ie 'f' and
+ 'd' are allowed but 'F' and 'D' aren't */
+
+#include "as.h"
+
+/* Precision in LittleNums. */
+#define MAX_PRECISION (4)
+#define D_PRECISION (4)
+#define F_PRECISION (2)
+
+/* Precision in chars. */
+#define D_PRECISION_CHARS (8)
+#define F_PRECISION_CHARS (4)
+
+/* Length in LittleNums of guard bits. */
+#define GUARD (2)
+
+static const long int mask[] =
+{
+ 0x00000000,
+ 0x00000001,
+ 0x00000003,
+ 0x00000007,
+ 0x0000000f,
+ 0x0000001f,
+ 0x0000003f,
+ 0x0000007f,
+ 0x000000ff,
+ 0x000001ff,
+ 0x000003ff,
+ 0x000007ff,
+ 0x00000fff,
+ 0x00001fff,
+ 0x00003fff,
+ 0x00007fff,
+ 0x0000ffff,
+ 0x0001ffff,
+ 0x0003ffff,
+ 0x0007ffff,
+ 0x000fffff,
+ 0x001fffff,
+ 0x003fffff,
+ 0x007fffff,
+ 0x00ffffff,
+ 0x01ffffff,
+ 0x03ffffff,
+ 0x07ffffff,
+ 0x0fffffff,
+ 0x1fffffff,
+ 0x3fffffff,
+ 0x7fffffff,
+ 0xffffffff
+};
+
+
+/* Shared between flonum_gen2tahoe and next_bits */
+static int bits_left_in_littlenum;
+static LITTLENUM_TYPE *littlenum_pointer;
+static LITTLENUM_TYPE *littlenum_end;
+
+#if __STDC__ == 1
+
+int flonum_gen2tahoe (int format_letter, FLONUM_TYPE * f, LITTLENUM_TYPE * words);
+
+#else /* not __STDC__ */
+
+int flonum_gen2tahoe ();
+
+#endif /* not __STDC__ */
+
+
+static int
+next_bits (number_of_bits)
+ int number_of_bits;
+{
+ int return_value;
+
+ if (littlenum_pointer < littlenum_end)
+ return 0;
+ if (number_of_bits >= bits_left_in_littlenum)
+ {
+ return_value = mask[bits_left_in_littlenum] & *littlenum_pointer;
+ number_of_bits -= bits_left_in_littlenum;
+ return_value <<= number_of_bits;
+ bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS - number_of_bits;
+ littlenum_pointer--;
+ if (littlenum_pointer >= littlenum_end)
+ return_value |= ((*littlenum_pointer) >> (bits_left_in_littlenum)) &
+ mask[number_of_bits];
+ }
+ else
+ {
+ bits_left_in_littlenum -= number_of_bits;
+ return_value = mask[number_of_bits] &
+ ((*littlenum_pointer) >> bits_left_in_littlenum);
+ }
+ return (return_value);
+}
+
+static void
+make_invalid_floating_point_number (words)
+ LITTLENUM_TYPE *words;
+{
+ *words = 0x8000; /* Floating Reserved Operand Code */
+}
+
+static int /* 0 means letter is OK. */
+what_kind_of_float (letter, precisionP, exponent_bitsP)
+ char letter; /* In: lowercase please. What kind of float? */
+ int *precisionP; /* Number of 16-bit words in the float. */
+ long int *exponent_bitsP; /* Number of exponent bits. */
+{
+ int retval; /* 0: OK. */
+
+ retval = 0;
+ switch (letter)
+ {
+ case 'f':
+ *precisionP = F_PRECISION;
+ *exponent_bitsP = 8;
+ break;
+
+ case 'd':
+ *precisionP = D_PRECISION;
+ *exponent_bitsP = 8;
+ break;
+
+ default:
+ retval = 69;
+ break;
+ }
+ return (retval);
+}
+
+/***********************************************************************\
+* *
+* Warning: this returns 16-bit LITTLENUMs, because that is *
+* what the VAX thinks in. It is up to the caller to figure *
+* out any alignment problems and to conspire for the bytes/word *
+* to be emitted in the right order. Bigendians beware! *
+* *
+\***********************************************************************/
+
+char * /* Return pointer past text consumed. */
+atof_tahoe (str, what_kind, words)
+ char *str; /* Text to convert to binary. */
+ char what_kind; /* 'd', 'f', 'g', 'h' */
+ LITTLENUM_TYPE *words; /* Build the binary here. */
+{
+ FLONUM_TYPE f;
+ LITTLENUM_TYPE bits[MAX_PRECISION + MAX_PRECISION + GUARD];
+ /* Extra bits for zeroed low-order bits. */
+ /* The 1st MAX_PRECISION are zeroed, */
+ /* the last contain flonum bits. */
+ char *return_value;
+ int precision; /* Number of 16-bit words in the format. */
+ long int exponent_bits;
+
+ return_value = str;
+ f.low = bits + MAX_PRECISION;
+ f.high = NULL;
+ f.leader = NULL;
+ f.exponent = NULL;
+ f.sign = '\0';
+
+ if (what_kind_of_float (what_kind, &precision, &exponent_bits))
+ {
+ return_value = NULL; /* We lost. */
+ make_invalid_floating_point_number (words);
+ }
+ if (return_value)
+ {
+ memset (bits, '\0', sizeof (LITTLENUM_TYPE) * MAX_PRECISION);
+
+ /* Use more LittleNums than seems */
+ /* necessary: the highest flonum may have */
+ /* 15 leading 0 bits, so could be useless. */
+ f.high = f.low + precision - 1 + GUARD;
+
+ if (atof_generic (&return_value, ".", "eE", &f))
+ {
+ make_invalid_floating_point_number (words);
+ return_value = NULL; /* we lost */
+ }
+ else
+ {
+ if (flonum_gen2tahoe (what_kind, &f, words))
+ {
+ return_value = NULL;
+ }
+ }
+ }
+ return (return_value);
+}
+
+/*
+ * In: a flonum, a Tahoe floating point format.
+ * Out: a Tahoe floating-point bit pattern.
+ */
+
+int /* 0: OK. */
+flonum_gen2tahoe (format_letter, f, words)
+ char format_letter; /* One of 'd' 'f'. */
+ FLONUM_TYPE *f;
+ LITTLENUM_TYPE *words; /* Deliver answer here. */
+{
+ LITTLENUM_TYPE *lp;
+ int precision;
+ long int exponent_bits;
+ int return_value; /* 0 == OK. */
+
+ return_value = what_kind_of_float (format_letter, &precision, &exponent_bits);
+ if (return_value != 0)
+ {
+ make_invalid_floating_point_number (words);
+ }
+ else
+ {
+ if (f->low > f->leader)
+ {
+ /* 0.0e0 seen. */
+ memset (words, '\0', sizeof (LITTLENUM_TYPE) * precision);
+ }
+ else
+ {
+ long int exponent_1;
+ long int exponent_2;
+ long int exponent_3;
+ long int exponent_4;
+ int exponent_skippage;
+ LITTLENUM_TYPE word1;
+
+ /* JF: Deal with new Nan, +Inf and -Inf codes */
+ if (f->sign != '-' && f->sign != '+')
+ {
+ make_invalid_floating_point_number (words);
+ return return_value;
+ }
+ /*
+ * All tahoe floating_point formats have:
+ * Bit 15 is sign bit.
+ * Bits 14:n are excess-whatever exponent.
+ * Bits n-1:0 (if any) are most significant bits of fraction.
+ * Bits 15:0 of the next word are the next most significant bits.
+ * And so on for each other word.
+ *
+ * So we need: number of bits of exponent, number of bits of
+ * mantissa.
+ */
+
+ bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS;
+ littlenum_pointer = f->leader;
+ littlenum_end = f->low;
+ /* Seek (and forget) 1st significant bit */
+ for (exponent_skippage = 0;
+ !next_bits (1);
+ exponent_skippage++)
+ {
+ }
+ exponent_1 = f->exponent + f->leader + 1 - f->low;
+ /* Radix LITTLENUM_RADIX, point just higher than f -> leader. */
+ exponent_2 = exponent_1 * LITTLENUM_NUMBER_OF_BITS;
+ /* Radix 2. */
+ exponent_3 = exponent_2 - exponent_skippage;
+ /* Forget leading zeros, forget 1st bit. */
+ exponent_4 = exponent_3 + (1 << (exponent_bits - 1));
+ /* Offset exponent. */
+
+ if (exponent_4 & ~mask[exponent_bits])
+ {
+ /*
+ * Exponent overflow. Lose immediately.
+ */
+
+ make_invalid_floating_point_number (words);
+
+ /*
+ * We leave return_value alone: admit we read the
+ * number, but return a floating exception
+ * because we can't encode the number.
+ */
+ }
+ else
+ {
+ lp = words;
+
+ /* Word 1. Sign, exponent and perhaps high bits. */
+ /* Assume 2's complement integers. */
+ word1 = ((exponent_4 & mask[exponent_bits]) << (15 - exponent_bits))
+ | ((f->sign == '+') ? 0 : 0x8000)
+ | next_bits (15 - exponent_bits);
+ *lp++ = word1;
+
+ /* The rest of the words are just mantissa bits. */
+ for (; lp < words + precision; lp++)
+ {
+ *lp = next_bits (LITTLENUM_NUMBER_OF_BITS);
+ }
+
+ if (next_bits (1))
+ {
+ /*
+ * Since the NEXT bit is a 1, round UP the mantissa.
+ * The cunning design of these hidden-1 floats permits
+ * us to let the mantissa overflow into the exponent, and
+ * it 'does the right thing'. However, we lose if the
+ * highest-order bit of the lowest-order word flips.
+ * Is that clear?
+ */
+
+ unsigned long int carry;
+
+ /*
+ #if (sizeof(carry)) < ((sizeof(bits[0]) * BITS_PER_CHAR) + 2)
+ Please allow at least 1 more bit in carry than is in a LITTLENUM.
+ We need that extra bit to hold a carry during a LITTLENUM carry
+ propagation. Another extra bit (kept 0) will assure us that we
+ don't get a sticky sign bit after shifting right, and that
+ permits us to propagate the carry without any masking of bits.
+ #endif
+ */
+ for (carry = 1, lp--;
+ carry && (lp >= words);
+ lp--)
+ {
+ carry = *lp + carry;
+ *lp = carry;
+ carry >>= LITTLENUM_NUMBER_OF_BITS;
+ }
+
+ if ((word1 ^ *words) & (1 << (LITTLENUM_NUMBER_OF_BITS - 1)))
+ {
+ make_invalid_floating_point_number (words);
+ /*
+ * We leave return_value alone: admit we read the
+ * number, but return a floating exception
+ * because we can't encode the number.
+ */
+ }
+ } /* if (we needed to round up) */
+ } /* if (exponent overflow) */
+ } /* if (0.0e0) */
+ } /* if (float_type was OK) */
+ return (return_value);
+}
+
+/*
+ * md_atof()
+ *
+ * In: input_line_pointer -> the 1st character of a floating-point
+ * number.
+ * 1 letter denoting the type of statement that wants a
+ * binary floating point number returned.
+ * Address of where to build floating point literal.
+ * Assumed to be 'big enough'.
+ * Address of where to return size of literal (in chars).
+ *
+ * Out: Input_line_pointer -> of next char after floating number.
+ * Error message, or 0.
+ * Floating point literal.
+ * Number of chars we used for the literal.
+ */
+
+char *
+md_atof (what_statement_type, literalP, sizeP)
+ char what_statement_type;
+ char *literalP;
+ int *sizeP;
+{
+ LITTLENUM_TYPE words[MAX_PRECISION];
+ register char kind_of_float;
+ register int number_of_chars;
+ register LITTLENUM_TYPE *littlenum_pointer;
+
+ switch (what_statement_type)
+ {
+ case 'f': /* .ffloat */
+ case 'd': /* .dfloat */
+ kind_of_float = what_statement_type;
+ break;
+
+ default:
+ kind_of_float = 0;
+ break;
+ };
+
+ if (kind_of_float)
+ {
+ register LITTLENUM_TYPE *limit;
+
+ input_line_pointer = atof_tahoe (input_line_pointer,
+ kind_of_float,
+ words);
+ /*
+ * The atof_tahoe() builds up 16-bit numbers.
+ * Since the assembler may not be running on
+ * a different-endian machine, be very careful about
+ * converting words to chars.
+ */
+ number_of_chars = (kind_of_float == 'f' ? F_PRECISION_CHARS :
+ (kind_of_float == 'd' ? D_PRECISION_CHARS : 0));
+ know (number_of_chars <= MAX_PRECISION * sizeof (LITTLENUM_TYPE));
+ limit = words + (number_of_chars / sizeof (LITTLENUM_TYPE));
+ for (littlenum_pointer = words;
+ littlenum_pointer < limit;
+ littlenum_pointer++)
+ {
+ md_number_to_chars (literalP, *littlenum_pointer,
+ sizeof (LITTLENUM_TYPE));
+ literalP += sizeof (LITTLENUM_TYPE);
+ };
+ }
+ else
+ {
+ number_of_chars = 0;
+ };
+
+ *sizeP = number_of_chars;
+ return kind_of_float ? 0 : _("Bad call to md_atof()");
+}
+
+/* atof_tahoe.c */
diff --git a/gas/config/atof-vax.c b/gas/config/atof-vax.c
new file mode 100644
index 0000000..45b90a8
--- /dev/null
+++ b/gas/config/atof-vax.c
@@ -0,0 +1,517 @@
+/* atof_vax.c - turn a Flonum into a VAX floating point number
+ Copyright (C) 1987, 1992, 93, 95, 1997, 1998 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#include "as.h"
+
+static int atof_vax_sizeof PARAMS ((int));
+static int next_bits PARAMS ((int));
+static void make_invalid_floating_point_number PARAMS ((LITTLENUM_TYPE *));
+static int what_kind_of_float PARAMS ((int, int *, long *));
+static char *atof_vax PARAMS ((char *, int, LITTLENUM_TYPE *));
+
+/* Precision in LittleNums. */
+#define MAX_PRECISION (8)
+#define H_PRECISION (8)
+#define G_PRECISION (4)
+#define D_PRECISION (4)
+#define F_PRECISION (2)
+
+/* Length in LittleNums of guard bits. */
+#define GUARD (2)
+
+int flonum_gen2vax PARAMS ((int format_letter, FLONUM_TYPE * f,
+ LITTLENUM_TYPE * words));
+
+/* Number of chars in flonum type 'letter'. */
+static int
+atof_vax_sizeof (letter)
+ int letter;
+{
+ int return_value;
+
+ /*
+ * Permitting uppercase letters is probably a bad idea.
+ * Please use only lower-cased letters in case the upper-cased
+ * ones become unsupported!
+ */
+ switch (letter)
+ {
+ case 'f':
+ case 'F':
+ return_value = 4;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'g':
+ case 'G':
+ return_value = 8;
+ break;
+
+ case 'h':
+ case 'H':
+ return_value = 16;
+ break;
+
+ default:
+ return_value = 0;
+ break;
+ }
+ return (return_value);
+} /* atof_vax_sizeof */
+
+static const long mask[] =
+{
+ 0x00000000,
+ 0x00000001,
+ 0x00000003,
+ 0x00000007,
+ 0x0000000f,
+ 0x0000001f,
+ 0x0000003f,
+ 0x0000007f,
+ 0x000000ff,
+ 0x000001ff,
+ 0x000003ff,
+ 0x000007ff,
+ 0x00000fff,
+ 0x00001fff,
+ 0x00003fff,
+ 0x00007fff,
+ 0x0000ffff,
+ 0x0001ffff,
+ 0x0003ffff,
+ 0x0007ffff,
+ 0x000fffff,
+ 0x001fffff,
+ 0x003fffff,
+ 0x007fffff,
+ 0x00ffffff,
+ 0x01ffffff,
+ 0x03ffffff,
+ 0x07ffffff,
+ 0x0fffffff,
+ 0x1fffffff,
+ 0x3fffffff,
+ 0x7fffffff,
+ 0xffffffff
+};
+
+
+/* Shared between flonum_gen2vax and next_bits */
+static int bits_left_in_littlenum;
+static LITTLENUM_TYPE *littlenum_pointer;
+static LITTLENUM_TYPE *littlenum_end;
+
+static int
+next_bits (number_of_bits)
+ int number_of_bits;
+{
+ int return_value;
+
+ if (littlenum_pointer < littlenum_end)
+ return 0;
+ if (number_of_bits >= bits_left_in_littlenum)
+ {
+ return_value = mask[bits_left_in_littlenum] & *littlenum_pointer;
+ number_of_bits -= bits_left_in_littlenum;
+ return_value <<= number_of_bits;
+ bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS - number_of_bits;
+ littlenum_pointer--;
+ if (littlenum_pointer >= littlenum_end)
+ return_value |= ((*littlenum_pointer) >> (bits_left_in_littlenum)) & mask[number_of_bits];
+ }
+ else
+ {
+ bits_left_in_littlenum -= number_of_bits;
+ return_value = mask[number_of_bits] & ((*littlenum_pointer) >> bits_left_in_littlenum);
+ }
+ return (return_value);
+}
+
+static void
+make_invalid_floating_point_number (words)
+ LITTLENUM_TYPE *words;
+{
+ *words = 0x8000; /* Floating Reserved Operand Code */
+}
+
+static int /* 0 means letter is OK. */
+what_kind_of_float (letter, precisionP, exponent_bitsP)
+ int letter; /* In: lowercase please. What kind of float? */
+ int *precisionP; /* Number of 16-bit words in the float. */
+ long *exponent_bitsP; /* Number of exponent bits. */
+{
+ int retval; /* 0: OK. */
+
+ retval = 0;
+ switch (letter)
+ {
+ case 'f':
+ *precisionP = F_PRECISION;
+ *exponent_bitsP = 8;
+ break;
+
+ case 'd':
+ *precisionP = D_PRECISION;
+ *exponent_bitsP = 8;
+ break;
+
+ case 'g':
+ *precisionP = G_PRECISION;
+ *exponent_bitsP = 11;
+ break;
+
+ case 'h':
+ *precisionP = H_PRECISION;
+ *exponent_bitsP = 15;
+ break;
+
+ default:
+ retval = 69;
+ break;
+ }
+ return (retval);
+}
+
+/***********************************************************************\
+ * *
+ * Warning: this returns 16-bit LITTLENUMs, because that is *
+ * what the VAX thinks in. It is up to the caller to figure *
+ * out any alignment problems and to conspire for the bytes/word *
+ * to be emitted in the right order. Bigendians beware! *
+ * *
+ \***********************************************************************/
+
+static char * /* Return pointer past text consumed. */
+atof_vax (str, what_kind, words)
+ char *str; /* Text to convert to binary. */
+ int what_kind; /* 'd', 'f', 'g', 'h' */
+ LITTLENUM_TYPE *words; /* Build the binary here. */
+{
+ FLONUM_TYPE f;
+ LITTLENUM_TYPE bits[MAX_PRECISION + MAX_PRECISION + GUARD];
+ /* Extra bits for zeroed low-order bits. */
+ /* The 1st MAX_PRECISION are zeroed, */
+ /* the last contain flonum bits. */
+ char *return_value;
+ int precision; /* Number of 16-bit words in the format. */
+ long exponent_bits;
+
+ return_value = str;
+ f.low = bits + MAX_PRECISION;
+ f.high = NULL;
+ f.leader = NULL;
+ f.exponent = 0;
+ f.sign = '\0';
+
+ if (what_kind_of_float (what_kind, &precision, &exponent_bits))
+ {
+ return_value = NULL; /* We lost. */
+ make_invalid_floating_point_number (words);
+ }
+
+ if (return_value)
+ {
+ memset (bits, '\0', sizeof (LITTLENUM_TYPE) * MAX_PRECISION);
+
+ /* Use more LittleNums than seems */
+ /* necessary: the highest flonum may have */
+ /* 15 leading 0 bits, so could be useless. */
+ f.high = f.low + precision - 1 + GUARD;
+
+ if (atof_generic (&return_value, ".", "eE", &f))
+ {
+ make_invalid_floating_point_number (words);
+ return_value = NULL; /* we lost */
+ }
+ else
+ {
+ if (flonum_gen2vax (what_kind, &f, words))
+ {
+ return_value = NULL;
+ }
+ }
+ }
+ return (return_value);
+} /* atof_vax() */
+
+/*
+ * In: a flonum, a vax floating point format.
+ * Out: a vax floating-point bit pattern.
+ */
+
+int /* 0: OK. */
+flonum_gen2vax (format_letter, f, words)
+ char format_letter; /* One of 'd' 'f' 'g' 'h'. */
+ FLONUM_TYPE *f;
+ LITTLENUM_TYPE *words; /* Deliver answer here. */
+{
+ LITTLENUM_TYPE *lp;
+ int precision;
+ long exponent_bits;
+ int return_value; /* 0 == OK. */
+
+ return_value = what_kind_of_float (format_letter, &precision, &exponent_bits);
+
+ if (return_value != 0)
+ {
+ make_invalid_floating_point_number (words);
+ }
+ else
+ {
+ if (f->low > f->leader)
+ {
+ /* 0.0e0 seen. */
+ memset (words, '\0', sizeof (LITTLENUM_TYPE) * precision);
+ }
+ else
+ {
+ long exponent_1;
+ long exponent_2;
+ long exponent_3;
+ long exponent_4;
+ int exponent_skippage;
+ LITTLENUM_TYPE word1;
+
+ /* JF: Deal with new Nan, +Inf and -Inf codes */
+ if (f->sign != '-' && f->sign != '+')
+ {
+ make_invalid_floating_point_number (words);
+ return return_value;
+ }
+ /*
+ * All vaxen floating_point formats (so far) have:
+ * Bit 15 is sign bit.
+ * Bits 14:n are excess-whatever exponent.
+ * Bits n-1:0 (if any) are most significant bits of fraction.
+ * Bits 15:0 of the next word are the next most significant bits.
+ * And so on for each other word.
+ *
+ * All this to be compatible with a KF11?? (Which is still faster
+ * than lots of vaxen I can think of, but it also has higher
+ * maintenance costs ... sigh).
+ *
+ * So we need: number of bits of exponent, number of bits of
+ * mantissa.
+ */
+
+#ifdef NEVER /******* This zeroing seems redundant - Dean 3may86 **********/
+ /*
+ * No matter how few bits we got back from the atof()
+ * routine, add enough zero littlenums so the rest of the
+ * code won't run out of "significant" bits in the mantissa.
+ */
+ {
+ LITTLENUM_TYPE *ltp;
+ for (ltp = f->leader + 1;
+ ltp <= f->low + precision;
+ ltp++)
+ {
+ *ltp = 0;
+ }
+ }
+#endif
+
+ bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS;
+ littlenum_pointer = f->leader;
+ littlenum_end = f->low;
+ /* Seek (and forget) 1st significant bit */
+ for (exponent_skippage = 0;
+ !next_bits (1);
+ exponent_skippage++);;
+
+ exponent_1 = f->exponent + f->leader + 1 - f->low;
+ /* Radix LITTLENUM_RADIX, point just higher than f->leader. */
+ exponent_2 = exponent_1 * LITTLENUM_NUMBER_OF_BITS;
+ /* Radix 2. */
+ exponent_3 = exponent_2 - exponent_skippage;
+ /* Forget leading zeros, forget 1st bit. */
+ exponent_4 = exponent_3 + (1 << (exponent_bits - 1));
+ /* Offset exponent. */
+
+ if (exponent_4 & ~mask[exponent_bits])
+ {
+ /*
+ * Exponent overflow. Lose immediately.
+ */
+
+ make_invalid_floating_point_number (words);
+
+ /*
+ * We leave return_value alone: admit we read the
+ * number, but return a floating exception
+ * because we can't encode the number.
+ */
+ }
+ else
+ {
+ lp = words;
+
+ /* Word 1. Sign, exponent and perhaps high bits. */
+ /* Assume 2's complement integers. */
+ word1 = (((exponent_4 & mask[exponent_bits]) << (15 - exponent_bits))
+ | ((f->sign == '+') ? 0 : 0x8000)
+ | next_bits (15 - exponent_bits));
+ *lp++ = word1;
+
+ /* The rest of the words are just mantissa bits. */
+ for (; lp < words + precision; lp++)
+ {
+ *lp = next_bits (LITTLENUM_NUMBER_OF_BITS);
+ }
+
+ if (next_bits (1))
+ {
+ /*
+ * Since the NEXT bit is a 1, round UP the mantissa.
+ * The cunning design of these hidden-1 floats permits
+ * us to let the mantissa overflow into the exponent, and
+ * it 'does the right thing'. However, we lose if the
+ * highest-order bit of the lowest-order word flips.
+ * Is that clear?
+ */
+
+ unsigned long carry;
+
+ /*
+ #if (sizeof(carry)) < ((sizeof(bits[0]) * BITS_PER_CHAR) + 2)
+ Please allow at least 1 more bit in carry than is in a LITTLENUM.
+ We need that extra bit to hold a carry during a LITTLENUM carry
+ propagation. Another extra bit (kept 0) will assure us that we
+ don't get a sticky sign bit after shifting right, and that
+ permits us to propagate the carry without any masking of bits.
+ #endif
+ */
+ for (carry = 1, lp--;
+ carry && (lp >= words);
+ lp--)
+ {
+ carry = *lp + carry;
+ *lp = carry;
+ carry >>= LITTLENUM_NUMBER_OF_BITS;
+ }
+
+ if ((word1 ^ *words) & (1 << (LITTLENUM_NUMBER_OF_BITS - 1)))
+ {
+ make_invalid_floating_point_number (words);
+ /*
+ * We leave return_value alone: admit we read the
+ * number, but return a floating exception
+ * because we can't encode the number.
+ */
+ }
+ } /* if (we needed to round up) */
+ } /* if (exponent overflow) */
+ } /* if (0.0e0) */
+ } /* if (float_type was OK) */
+ return (return_value);
+} /* flonum_gen2vax() */
+
+
+/* JF this used to be in vax.c but this looks like a better place for it */
+
+/*
+ * md_atof()
+ *
+ * In: input_line_pointer->the 1st character of a floating-point
+ * number.
+ * 1 letter denoting the type of statement that wants a
+ * binary floating point number returned.
+ * Address of where to build floating point literal.
+ * Assumed to be 'big enough'.
+ * Address of where to return size of literal (in chars).
+ *
+ * Out: Input_line_pointer->of next char after floating number.
+ * Error message, or 0.
+ * Floating point literal.
+ * Number of chars we used for the literal.
+ */
+
+#define MAXIMUM_NUMBER_OF_LITTLENUMS (8) /* For .hfloats. */
+
+char *
+md_atof (what_statement_type, literalP, sizeP)
+ int what_statement_type;
+ char *literalP;
+ int *sizeP;
+{
+ LITTLENUM_TYPE words[MAXIMUM_NUMBER_OF_LITTLENUMS];
+ register char kind_of_float;
+ register int number_of_chars;
+ register LITTLENUM_TYPE *littlenumP;
+
+ switch (what_statement_type)
+ {
+ case 'F': /* .float */
+ case 'f': /* .ffloat */
+ kind_of_float = 'f';
+ break;
+
+ case 'D': /* .double */
+ case 'd': /* .dfloat */
+ kind_of_float = 'd';
+ break;
+
+ case 'g': /* .gfloat */
+ kind_of_float = 'g';
+ break;
+
+ case 'h': /* .hfloat */
+ kind_of_float = 'h';
+ break;
+
+ default:
+ kind_of_float = 0;
+ break;
+ };
+
+ if (kind_of_float)
+ {
+ register LITTLENUM_TYPE *limit;
+
+ input_line_pointer = atof_vax (input_line_pointer,
+ kind_of_float,
+ words);
+ /*
+ * The atof_vax() builds up 16-bit numbers.
+ * Since the assembler may not be running on
+ * a little-endian machine, be very careful about
+ * converting words to chars.
+ */
+ number_of_chars = atof_vax_sizeof (kind_of_float);
+ know (number_of_chars <= MAXIMUM_NUMBER_OF_LITTLENUMS * sizeof (LITTLENUM_TYPE));
+ limit = words + (number_of_chars / sizeof (LITTLENUM_TYPE));
+ for (littlenumP = words; littlenumP < limit; littlenumP++)
+ {
+ md_number_to_chars (literalP, *littlenumP, sizeof (LITTLENUM_TYPE));
+ literalP += sizeof (LITTLENUM_TYPE);
+ };
+ }
+ else
+ {
+ number_of_chars = 0;
+ };
+
+ *sizeP = number_of_chars;
+ return kind_of_float ? 0 : _("Bad call to md_atof()");
+}
+
+/* end of atof-vax.c */
diff --git a/gas/config/e-i386coff.c b/gas/config/e-i386coff.c
new file mode 100644
index 0000000..afed728
--- /dev/null
+++ b/gas/config/e-i386coff.c
@@ -0,0 +1,17 @@
+#include "as.h"
+#include "emul.h"
+
+static const char *
+i386coff_bfd_name ()
+{
+ abort ();
+ return NULL;
+}
+
+#define emul_bfd_name i386coff_bfd_name
+#define emul_format &coff_format_ops
+
+#define emul_name "i386coff"
+#define emul_struct_name i386coff
+#define emul_default_endian 0
+#include "emul-target.h"
diff --git a/gas/config/e-i386elf.c b/gas/config/e-i386elf.c
new file mode 100644
index 0000000..a16701e
--- /dev/null
+++ b/gas/config/e-i386elf.c
@@ -0,0 +1,17 @@
+#include "as.h"
+#include "emul.h"
+
+static const char *
+i386elf_bfd_name ()
+{
+ abort ();
+ return NULL;
+}
+
+#define emul_bfd_name i386elf_bfd_name
+#define emul_format &elf_format_ops
+
+#define emul_name "i386elf"
+#define emul_struct_name i386elf
+#define emul_default_endian 0
+#include "emul-target.h"
diff --git a/gas/config/e-mipsecoff.c b/gas/config/e-mipsecoff.c
new file mode 100644
index 0000000..be2f71b
--- /dev/null
+++ b/gas/config/e-mipsecoff.c
@@ -0,0 +1,37 @@
+#include "as.h"
+#include "emul.h"
+
+static const char *mipsecoff_bfd_name PARAMS ((void));
+
+static const char *
+mipsecoff_bfd_name ()
+{
+ abort ();
+ return NULL;
+}
+
+#define emul_bfd_name mipsecoff_bfd_name
+#define emul_format &ecoff_format_ops
+
+#define emul_name "mipsbecoff"
+#define emul_struct_name mipsbecoff
+#define emul_default_endian 1
+#include "emul-target.h"
+
+#undef emul_name
+#undef emul_struct_name
+#undef emul_default_endian
+
+#define emul_name "mipslecoff"
+#define emul_struct_name mipslecoff
+#define emul_default_endian 0
+#include "emul-target.h"
+
+#undef emul_name
+#undef emul_struct_name
+#undef emul_default_endian
+
+#define emul_name "mipsecoff"
+#define emul_struct_name mipsecoff
+#define emul_default_endian 2
+#include "emul-target.h"
diff --git a/gas/config/e-mipself.c b/gas/config/e-mipself.c
new file mode 100644
index 0000000..eea72f5
--- /dev/null
+++ b/gas/config/e-mipself.c
@@ -0,0 +1,37 @@
+#include "as.h"
+#include "emul.h"
+
+static const char *mipself_bfd_name PARAMS ((void));
+
+static const char *
+mipself_bfd_name ()
+{
+ abort ();
+ return NULL;
+}
+
+#define emul_bfd_name mipself_bfd_name
+#define emul_format &elf_format_ops
+
+#define emul_name "mipsbelf"
+#define emul_struct_name mipsbelf
+#define emul_default_endian 1
+#include "emul-target.h"
+
+#undef emul_name
+#undef emul_struct_name
+#undef emul_default_endian
+
+#define emul_name "mipslelf"
+#define emul_struct_name mipslelf
+#define emul_default_endian 0
+#include "emul-target.h"
+
+#undef emul_name
+#undef emul_struct_name
+#undef emul_default_endian
+
+#define emul_name "mipself"
+#define emul_struct_name mipself
+#define emul_default_endian 2
+#include "emul-target.h"
diff --git a/gas/config/go32.cfg b/gas/config/go32.cfg
new file mode 100644
index 0000000..4059395
--- /dev/null
+++ b/gas/config/go32.cfg
@@ -0,0 +1,93 @@
+/* config.h for go32 */
+
+#define I386COFF 1
+
+/* Define if using alloca.c. */
+#undef C_ALLOCA
+
+/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems.
+ This function is required for alloca.c support on those systems. */
+#undef CRAY_STACKSEG_END
+
+/* Define if you have <alloca.h> and it should be used (not on Ultrix). */
+#undef HAVE_ALLOCA_H
+
+/* Define as __inline if that's what the C compiler calls it. */
+#undef inline
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at run-time.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown
+ */
+#undef STACK_DIRECTION
+
+/* Should gas use high-level BFD interfaces? */
+#undef BFD_ASSEMBLER
+
+/* If we aren't doing cross-assembling, some operations can be optimized,
+ since byte orders and value sizes don't need to be adjusted. */
+#undef CROSS_COMPILE
+
+/* Some gas code wants to know these parameters. */
+#define TARGET_ALIAS "i386"
+#define TARGET_CPU "i386"
+#define TARGET_CANONICAL "i386-i386"
+#define TARGET_OS "djgpp"
+#define TARGET_VENDOR "djgpp"
+
+/* Some operating systems, for example DOS, require the use of "wb" mode when
+ opening a binary file for writing. If only "w" is used, the file will not
+ be correct. However, some other systems reject such a mode. This indicates
+ which ../include/fopen-*.h header file we want to include, so that we can
+ get macros that'll do the right thing for this system. */
+#define WANT_FOPEN_BIN 1
+
+/* Sometimes the system header files don't declare malloc and realloc. */
+#undef NEED_DECLARATION_MALLOC
+
+/* Sometimes the system header files don't declare free. */
+#undef NEED_DECLARATION_FREE
+
+/* Sometimes errno.h doesn't declare errno itself. */
+#undef NEED_DECLARATION_ERRNO
+
+#define MANY_SEGMENTS 1
+
+/* Needed only for sparc configuration */
+#undef sparcv9
+
+/* Define if you have the remove function. */
+#define HAVE_REMOVE 1
+
+/* Define if you have the unlink function. */
+#define HAVE_UNLINK 1
+
+/* Define if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define if you have the <stdarg.h> header file. */
+#define HAVE_STDARG_H 1
+
+/* Define if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define if you have the <varargs.h> header file. */
+#undef HAVE_VARARGS_H
diff --git a/gas/config/itbl-mips.h b/gas/config/itbl-mips.h
new file mode 100644
index 0000000..f6482bd
--- /dev/null
+++ b/gas/config/itbl-mips.h
@@ -0,0 +1,47 @@
+
+/* itbl-mips.h
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+/* Defines for Mips itbl cop support */
+
+#include "opcode/mips.h"
+
+/* Values for processors will be from 0 to NUMBER_OF_PROCESSORS-1 */
+#define NUMBER_OF_PROCESSORS 4
+#define MAX_BITPOS 31
+
+/* Mips specifics */
+#define MIPS_OPCODE_COP0 (0x21) /* COPz+CO, bits 31-25: 0100zz1 */
+#define MIPS_ENCODE_COP_NUM(z) ((MIPS_OPCODE_COP0|z<<1)<<25)
+#define MIPS_IS_COP_INSN(insn) ((MIPS_OPCODE_COP0&(insn>>25)) \
+ == MIPS_OPCODE_COP0)
+#define MIPS_DECODE_COP_NUM(insn) ((~MIPS_OPCODE_COP0&(insn>>25))>>1)
+#define MIPS_DECODE_COP_COFUN(insn) ((~MIPS_ENCODE_COP_NUM(3))&(insn))
+
+/* definitions required by generic code */
+#define ITBL_IS_INSN(insn) MIPS_IS_COP_INSN(insn)
+#define ITBL_DECODE_PNUM(insn) MIPS_DECODE_COP_NUM(insn)
+#define ITBL_ENCODE_PNUM(pnum) MIPS_ENCODE_COP_NUM(pnum)
+
+#define ITBL_OPCODE_STRUCT mips_opcode
+#define ITBL_OPCODES mips_opcodes
+#define ITBL_NUM_OPCODES NUMOPCODES
+#define ITBL_NUM_MACROS M_NUM_MACROS
diff --git a/gas/config/m68k-parse.h b/gas/config/m68k-parse.h
new file mode 100644
index 0000000..e131342
--- /dev/null
+++ b/gas/config/m68k-parse.h
@@ -0,0 +1,283 @@
+/* m68k-parse.h -- header file for m68k assembler
+ Copyright (C) 1987, 91, 92, 93, 94, 1995 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifndef M68K_PARSE_H
+#define M68K_PARSE_H
+
+/* This header file defines things which are shared between the
+ operand parser in m68k.y and the m68k assembler proper in
+ tc-m68k.c. */
+
+/* The various m68k registers. */
+
+/* DATA and ADDR have to be contiguous, so that reg-DATA gives
+ 0-7==data reg, 8-15==addr reg for operands that take both types.
+
+ We don't use forms like "ADDR0 = ADDR" here because this file is
+ likely to be used on an Apollo, and the broken Apollo compiler
+ gives an `undefined variable' error if we do that, according to
+ troy@cbme.unsw.edu.au. */
+
+#define DATA DATA0
+#define ADDR ADDR0
+#define SP ADDR7
+#define BAD BAD0
+#define BAC BAC0
+
+enum m68k_register
+{
+ DATA0 = 1, /* 1- 8 == data registers 0-7 */
+ DATA1,
+ DATA2,
+ DATA3,
+ DATA4,
+ DATA5,
+ DATA6,
+ DATA7,
+
+ ADDR0,
+ ADDR1,
+ ADDR2,
+ ADDR3,
+ ADDR4,
+ ADDR5,
+ ADDR6,
+ ADDR7,
+
+ FP0, /* Eight FP registers */
+ FP1,
+ FP2,
+ FP3,
+ FP4,
+ FP5,
+ FP6,
+ FP7,
+
+ COP0, /* Co-processor #0-#7 */
+ COP1,
+ COP2,
+ COP3,
+ COP4,
+ COP5,
+ COP6,
+ COP7,
+
+ PC, /* Program counter */
+ ZPC, /* Hack for Program space, but 0 addressing */
+ SR, /* Status Reg */
+ CCR, /* Condition code Reg */
+
+ /* These have to be grouped together for the movec instruction to work. */
+ USP, /* User Stack Pointer */
+ ISP, /* Interrupt stack pointer */
+ SFC,
+ DFC,
+ CACR,
+ VBR,
+ CAAR,
+ MSP,
+ ITT0,
+ ITT1,
+ DTT0,
+ DTT1,
+ MMUSR,
+ TC,
+ SRP,
+ URP,
+ BUSCR, /* 68060 added these */
+ PCR,
+ ROMBAR, /* mcf5200 added these */
+ RAMBAR0,
+ RAMBAR1,
+ MBAR,
+#define last_movec_reg MBAR
+ /* end of movec ordering constraints */
+
+ FPI,
+ FPS,
+ FPC,
+
+ DRP, /* 68851 or 68030 MMU regs */
+ CRP,
+ CAL,
+ VAL,
+ SCC,
+ AC,
+ BAD0,
+ BAD1,
+ BAD2,
+ BAD3,
+ BAD4,
+ BAD5,
+ BAD6,
+ BAD7,
+ BAC0,
+ BAC1,
+ BAC2,
+ BAC3,
+ BAC4,
+ BAC5,
+ BAC6,
+ BAC7,
+ PSR, /* aka MMUSR on 68030 (but not MMUSR on 68040)
+ and ACUSR on 68ec030 */
+ PCSR,
+
+ IC, /* instruction cache token */
+ DC, /* data cache token */
+ NC, /* no cache token */
+ BC, /* both caches token */
+
+ TT0, /* 68030 access control unit regs */
+ TT1,
+
+ ZDATA0, /* suppressed data registers. */
+ ZDATA1,
+ ZDATA2,
+ ZDATA3,
+ ZDATA4,
+ ZDATA5,
+ ZDATA6,
+ ZDATA7,
+
+ ZADDR0, /* suppressed address registers. */
+ ZADDR1,
+ ZADDR2,
+ ZADDR3,
+ ZADDR4,
+ ZADDR5,
+ ZADDR6,
+ ZADDR7,
+};
+
+/* Size information. */
+
+enum m68k_size
+{
+ /* Unspecified. */
+ SIZE_UNSPEC,
+
+ /* Byte. */
+ SIZE_BYTE,
+
+ /* Word (2 bytes). */
+ SIZE_WORD,
+
+ /* Longword (4 bytes). */
+ SIZE_LONG
+};
+
+/* The structure used to hold information about an index register. */
+
+struct m68k_indexreg
+{
+ /* The index register itself. */
+ enum m68k_register reg;
+
+ /* The size to use. */
+ enum m68k_size size;
+
+ /* The value to scale by. */
+ int scale;
+};
+
+#ifdef OBJ_ELF
+/* The type of a PIC expression. */
+
+enum pic_relocation
+{
+ pic_none, /* not pic */
+ pic_plt_pcrel, /* @PLTPC */
+ pic_got_pcrel, /* @GOTPC */
+ pic_plt_off, /* @PLT */
+ pic_got_off /* @GOT */
+};
+#endif
+
+/* The structure used to hold information about an expression. */
+
+struct m68k_exp
+{
+ /* The size to use. */
+ enum m68k_size size;
+
+#ifdef OBJ_ELF
+ /* The type of pic relocation if any. */
+ enum pic_relocation pic_reloc;
+#endif
+
+ /* The expression itself. */
+ expressionS exp;
+};
+
+/* The operand modes. */
+
+enum m68k_operand_type
+{
+ IMMED = 1,
+ ABSL,
+ DREG,
+ AREG,
+ FPREG,
+ CONTROL,
+ AINDR,
+ AINC,
+ ADEC,
+ DISP,
+ BASE,
+ POST,
+ PRE,
+ REGLST
+};
+
+/* The structure used to hold a parsed operand. */
+
+struct m68k_op
+{
+ /* The type of operand. */
+ enum m68k_operand_type mode;
+
+ /* The main register. */
+ enum m68k_register reg;
+
+ /* The register mask for mode REGLST. */
+ unsigned long mask;
+
+ /* An error message. */
+ const char *error;
+
+ /* The index register. */
+ struct m68k_indexreg index;
+
+ /* The displacement. */
+ struct m68k_exp disp;
+
+ /* The outer displacement. */
+ struct m68k_exp odisp;
+};
+
+#endif /* ! defined (M68K_PARSE_H) */
+
+/* The parsing function. */
+
+extern int m68k_ip_op PARAMS ((char *, struct m68k_op *));
+
+/* Whether register prefixes are optional. */
+extern int flag_reg_prefix_optional;
diff --git a/gas/config/m68k-parse.y b/gas/config/m68k-parse.y
new file mode 100644
index 0000000..70a4e4f
--- /dev/null
+++ b/gas/config/m68k-parse.y
@@ -0,0 +1,1061 @@
+/* m68k.y -- bison grammar for m68k operand parsing
+ Copyright (C) 1995, 96, 1997, 1998 Free Software Foundation, Inc.
+ Written by Ken Raeburn and Ian Lance Taylor, Cygnus Support
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+/* This file holds a bison grammar to parse m68k operands. The m68k
+ has a complicated operand syntax, and gas supports two main
+ variations of it. Using a grammar is probably overkill, but at
+ least it makes clear exactly what we do support. */
+
+%{
+
+#include "as.h"
+#include "tc-m68k.h"
+#include "m68k-parse.h"
+
+/* Remap normal yacc parser interface names (yyparse, yylex, yyerror,
+ etc), as well as gratuitiously global symbol names If other parser
+ generators (bison, byacc, etc) produce additional global names that
+ conflict at link time, then those parser generators need to be
+ fixed instead of adding those names to this list. */
+
+#define yymaxdepth m68k_maxdepth
+#define yyparse m68k_parse
+#define yylex m68k_lex
+#define yyerror m68k_error
+#define yylval m68k_lval
+#define yychar m68k_char
+#define yydebug m68k_debug
+#define yypact m68k_pact
+#define yyr1 m68k_r1
+#define yyr2 m68k_r2
+#define yydef m68k_def
+#define yychk m68k_chk
+#define yypgo m68k_pgo
+#define yyact m68k_act
+#define yyexca m68k_exca
+#define yyerrflag m68k_errflag
+#define yynerrs m68k_nerrs
+#define yyps m68k_ps
+#define yypv m68k_pv
+#define yys m68k_s
+#define yy_yys m68k_yys
+#define yystate m68k_state
+#define yytmp m68k_tmp
+#define yyv m68k_v
+#define yy_yyv m68k_yyv
+#define yyval m68k_val
+#define yylloc m68k_lloc
+#define yyreds m68k_reds /* With YYDEBUG defined */
+#define yytoks m68k_toks /* With YYDEBUG defined */
+#define yylhs m68k_yylhs
+#define yylen m68k_yylen
+#define yydefred m68k_yydefred
+#define yydgoto m68k_yydgoto
+#define yysindex m68k_yysindex
+#define yyrindex m68k_yyrindex
+#define yygindex m68k_yygindex
+#define yytable m68k_yytable
+#define yycheck m68k_yycheck
+
+#ifndef YYDEBUG
+#define YYDEBUG 1
+#endif
+
+/* Internal functions. */
+
+static enum m68k_register m68k_reg_parse PARAMS ((char **));
+static int yylex PARAMS ((void));
+static void yyerror PARAMS ((const char *));
+
+/* The parser sets fields pointed to by this global variable. */
+static struct m68k_op *op;
+
+%}
+
+%union
+{
+ struct m68k_indexreg indexreg;
+ enum m68k_register reg;
+ struct m68k_exp exp;
+ unsigned long mask;
+ int onereg;
+}
+
+%token <reg> DR AR FPR FPCR LPC ZAR ZDR LZPC CREG
+%token <indexreg> INDEXREG
+%token <exp> EXPR
+
+%type <indexreg> zireg zdireg
+%type <reg> zadr zdr apc zapc zpc optzapc optczapc
+%type <exp> optcexpr optexprc
+%type <mask> reglist ireglist reglistpair
+%type <onereg> reglistreg
+
+%%
+
+/* An operand. */
+
+operand:
+ generic_operand
+ | motorola_operand
+ | mit_operand
+ ;
+
+/* A generic operand. */
+
+generic_operand:
+ DR
+ {
+ op->mode = DREG;
+ op->reg = $1;
+ }
+ | AR
+ {
+ op->mode = AREG;
+ op->reg = $1;
+ }
+ | FPR
+ {
+ op->mode = FPREG;
+ op->reg = $1;
+ }
+ | FPCR
+ {
+ op->mode = CONTROL;
+ op->reg = $1;
+ }
+ | CREG
+ {
+ op->mode = CONTROL;
+ op->reg = $1;
+ }
+ | EXPR
+ {
+ op->mode = ABSL;
+ op->disp = $1;
+ }
+ | '#' EXPR
+ {
+ op->mode = IMMED;
+ op->disp = $2;
+ }
+ | '&' EXPR
+ {
+ op->mode = IMMED;
+ op->disp = $2;
+ }
+ | reglist
+ {
+ op->mode = REGLST;
+ op->mask = $1;
+ }
+ ;
+
+/* An operand in Motorola syntax. This includes MRI syntax as well,
+ which may or may not be different in that it permits commutativity
+ of index and base registers, and permits an offset expression to
+ appear inside or outside of the parentheses. */
+
+motorola_operand:
+ '(' AR ')'
+ {
+ op->mode = AINDR;
+ op->reg = $2;
+ }
+ | '(' AR ')' '+'
+ {
+ op->mode = AINC;
+ op->reg = $2;
+ }
+ | '-' '(' AR ')'
+ {
+ op->mode = ADEC;
+ op->reg = $3;
+ }
+ | '(' EXPR ',' zapc ')'
+ {
+ op->reg = $4;
+ op->disp = $2;
+ if (($4 >= ZADDR0 && $4 <= ZADDR7)
+ || $4 == ZPC)
+ op->mode = BASE;
+ else
+ op->mode = DISP;
+ }
+ | '(' zapc ',' EXPR ')'
+ {
+ op->reg = $2;
+ op->disp = $4;
+ if (($2 >= ZADDR0 && $2 <= ZADDR7)
+ || $2 == ZPC)
+ op->mode = BASE;
+ else
+ op->mode = DISP;
+ }
+ | EXPR '(' zapc ')'
+ {
+ op->reg = $3;
+ op->disp = $1;
+ if (($3 >= ZADDR0 && $3 <= ZADDR7)
+ || $3 == ZPC)
+ op->mode = BASE;
+ else
+ op->mode = DISP;
+ }
+ | '(' LPC ')'
+ {
+ op->mode = DISP;
+ op->reg = $2;
+ }
+ | '(' ZAR ')'
+ {
+ op->mode = BASE;
+ op->reg = $2;
+ }
+ | '(' LZPC ')'
+ {
+ op->mode = BASE;
+ op->reg = $2;
+ }
+ | '(' EXPR ',' zapc ',' zireg ')'
+ {
+ op->mode = BASE;
+ op->reg = $4;
+ op->disp = $2;
+ op->index = $6;
+ }
+ | '(' EXPR ',' zapc ',' zpc ')'
+ {
+ if ($4 == PC || $4 == ZPC)
+ yyerror (_("syntax error"));
+ op->mode = BASE;
+ op->reg = $6;
+ op->disp = $2;
+ op->index.reg = $4;
+ op->index.size = SIZE_UNSPEC;
+ op->index.scale = 1;
+ }
+ | '(' EXPR ',' zdireg optczapc ')'
+ {
+ op->mode = BASE;
+ op->reg = $5;
+ op->disp = $2;
+ op->index = $4;
+ }
+ | '(' zdireg ',' EXPR ')'
+ {
+ op->mode = BASE;
+ op->disp = $4;
+ op->index = $2;
+ }
+ | EXPR '(' zapc ',' zireg ')'
+ {
+ op->mode = BASE;
+ op->reg = $3;
+ op->disp = $1;
+ op->index = $5;
+ }
+ | '(' zapc ',' zireg ')'
+ {
+ op->mode = BASE;
+ op->reg = $2;
+ op->index = $4;
+ }
+ | EXPR '(' zapc ',' zpc ')'
+ {
+ if ($3 == PC || $3 == ZPC)
+ yyerror (_("syntax error"));
+ op->mode = BASE;
+ op->reg = $5;
+ op->disp = $1;
+ op->index.reg = $3;
+ op->index.size = SIZE_UNSPEC;
+ op->index.scale = 1;
+ }
+ | '(' zapc ',' zpc ')'
+ {
+ if ($2 == PC || $2 == ZPC)
+ yyerror (_("syntax error"));
+ op->mode = BASE;
+ op->reg = $4;
+ op->index.reg = $2;
+ op->index.size = SIZE_UNSPEC;
+ op->index.scale = 1;
+ }
+ | EXPR '(' zdireg optczapc ')'
+ {
+ op->mode = BASE;
+ op->reg = $4;
+ op->disp = $1;
+ op->index = $3;
+ }
+ | '(' zdireg optczapc ')'
+ {
+ op->mode = BASE;
+ op->reg = $3;
+ op->index = $2;
+ }
+ | '(' '[' EXPR optczapc ']' ',' zireg optcexpr ')'
+ {
+ op->mode = POST;
+ op->reg = $4;
+ op->disp = $3;
+ op->index = $7;
+ op->odisp = $8;
+ }
+ | '(' '[' EXPR optczapc ']' optcexpr ')'
+ {
+ op->mode = POST;
+ op->reg = $4;
+ op->disp = $3;
+ op->odisp = $6;
+ }
+ | '(' '[' zapc ']' ',' zireg optcexpr ')'
+ {
+ op->mode = POST;
+ op->reg = $3;
+ op->index = $6;
+ op->odisp = $7;
+ }
+ | '(' '[' zapc ']' optcexpr ')'
+ {
+ op->mode = POST;
+ op->reg = $3;
+ op->odisp = $5;
+ }
+ | '(' '[' EXPR ',' zapc ',' zireg ']' optcexpr ')'
+ {
+ op->mode = PRE;
+ op->reg = $5;
+ op->disp = $3;
+ op->index = $7;
+ op->odisp = $9;
+ }
+ | '(' '[' zapc ',' zireg ']' optcexpr ')'
+ {
+ op->mode = PRE;
+ op->reg = $3;
+ op->index = $5;
+ op->odisp = $7;
+ }
+ | '(' '[' EXPR ',' zapc ',' zpc ']' optcexpr ')'
+ {
+ if ($5 == PC || $5 == ZPC)
+ yyerror (_("syntax error"));
+ op->mode = PRE;
+ op->reg = $7;
+ op->disp = $3;
+ op->index.reg = $5;
+ op->index.size = SIZE_UNSPEC;
+ op->index.scale = 1;
+ op->odisp = $9;
+ }
+ | '(' '[' zapc ',' zpc ']' optcexpr ')'
+ {
+ if ($3 == PC || $3 == ZPC)
+ yyerror (_("syntax error"));
+ op->mode = PRE;
+ op->reg = $5;
+ op->index.reg = $3;
+ op->index.size = SIZE_UNSPEC;
+ op->index.scale = 1;
+ op->odisp = $7;
+ }
+ | '(' '[' optexprc zdireg optczapc ']' optcexpr ')'
+ {
+ op->mode = PRE;
+ op->reg = $5;
+ op->disp = $3;
+ op->index = $4;
+ op->odisp = $7;
+ }
+ ;
+
+/* An operand in MIT syntax. */
+
+mit_operand:
+ optzapc '@'
+ {
+ /* We use optzapc to avoid a shift/reduce conflict. */
+ if ($1 < ADDR0 || $1 > ADDR7)
+ yyerror (_("syntax error"));
+ op->mode = AINDR;
+ op->reg = $1;
+ }
+ | optzapc '@' '+'
+ {
+ /* We use optzapc to avoid a shift/reduce conflict. */
+ if ($1 < ADDR0 || $1 > ADDR7)
+ yyerror (_("syntax error"));
+ op->mode = AINC;
+ op->reg = $1;
+ }
+ | optzapc '@' '-'
+ {
+ /* We use optzapc to avoid a shift/reduce conflict. */
+ if ($1 < ADDR0 || $1 > ADDR7)
+ yyerror (_("syntax error"));
+ op->mode = ADEC;
+ op->reg = $1;
+ }
+ | optzapc '@' '(' EXPR ')'
+ {
+ op->reg = $1;
+ op->disp = $4;
+ if (($1 >= ZADDR0 && $1 <= ZADDR7)
+ || $1 == ZPC)
+ op->mode = BASE;
+ else
+ op->mode = DISP;
+ }
+ | optzapc '@' '(' optexprc zireg ')'
+ {
+ op->mode = BASE;
+ op->reg = $1;
+ op->disp = $4;
+ op->index = $5;
+ }
+ | optzapc '@' '(' EXPR ')' '@' '(' optexprc zireg ')'
+ {
+ op->mode = POST;
+ op->reg = $1;
+ op->disp = $4;
+ op->index = $9;
+ op->odisp = $8;
+ }
+ | optzapc '@' '(' EXPR ')' '@' '(' EXPR ')'
+ {
+ op->mode = POST;
+ op->reg = $1;
+ op->disp = $4;
+ op->odisp = $8;
+ }
+ | optzapc '@' '(' optexprc zireg ')' '@' '(' EXPR ')'
+ {
+ op->mode = PRE;
+ op->reg = $1;
+ op->disp = $4;
+ op->index = $5;
+ op->odisp = $9;
+ }
+ ;
+
+/* An index register, possibly suppressed, which need not have a size
+ or scale. */
+
+zireg:
+ INDEXREG
+ | zadr
+ {
+ $$.reg = $1;
+ $$.size = SIZE_UNSPEC;
+ $$.scale = 1;
+ }
+ ;
+
+/* A register which may be an index register, but which may not be an
+ address register. This nonterminal is used to avoid ambiguity when
+ trying to parse something like (0,d5,a6) as compared to (0,a6,d5). */
+
+zdireg:
+ INDEXREG
+ | zdr
+ {
+ $$.reg = $1;
+ $$.size = SIZE_UNSPEC;
+ $$.scale = 1;
+ }
+ ;
+
+/* An address or data register, or a suppressed address or data
+ register. */
+
+zadr:
+ zdr
+ | AR
+ | ZAR
+ ;
+
+/* A data register which may be suppressed. */
+
+zdr:
+ DR
+ | ZDR
+ ;
+
+/* Either an address register or the PC. */
+
+apc:
+ AR
+ | LPC
+ ;
+
+/* Either an address register, or the PC, or a suppressed address
+ register, or a suppressed PC. */
+
+zapc:
+ apc
+ | LZPC
+ | ZAR
+ ;
+
+/* An optional zapc. */
+
+optzapc:
+ /* empty */
+ {
+ $$ = ZADDR0;
+ }
+ | zapc
+ ;
+
+/* The PC, optionally suppressed. */
+
+zpc:
+ LPC
+ | LZPC
+ ;
+
+/* ',' zapc when it may be omitted. */
+
+optczapc:
+ /* empty */
+ {
+ $$ = ZADDR0;
+ }
+ | ',' zapc
+ {
+ $$ = $2;
+ }
+ ;
+
+/* ',' EXPR when it may be omitted. */
+
+optcexpr:
+ /* empty */
+ {
+ $$.exp.X_op = O_absent;
+ $$.size = SIZE_UNSPEC;
+ }
+ | ',' EXPR
+ {
+ $$ = $2;
+ }
+ ;
+
+/* EXPR ',' when it may be omitted. */
+
+optexprc:
+ /* empty */
+ {
+ $$.exp.X_op = O_absent;
+ $$.size = SIZE_UNSPEC;
+ }
+ | EXPR ','
+ {
+ $$ = $1;
+ }
+ ;
+
+/* A register list for the movem instruction. */
+
+reglist:
+ reglistpair
+ | reglistpair '/' ireglist
+ {
+ $$ = $1 | $3;
+ }
+ | reglistreg '/' ireglist
+ {
+ $$ = (1 << $1) | $3;
+ }
+ ;
+
+/* We use ireglist when we know we are looking at a reglist, and we
+ can safely reduce a simple register to reglistreg. If we permitted
+ reglist to reduce to reglistreg, it would be ambiguous whether a
+ plain register were a DREG/AREG/FPREG or a REGLST. */
+
+ireglist:
+ reglistreg
+ {
+ $$ = 1 << $1;
+ }
+ | reglistpair
+ | reglistpair '/' ireglist
+ {
+ $$ = $1 | $3;
+ }
+ | reglistreg '/' ireglist
+ {
+ $$ = (1 << $1) | $3;
+ }
+ ;
+
+reglistpair:
+ reglistreg '-' reglistreg
+ {
+ if ($1 <= $3)
+ $$ = (1 << ($3 + 1)) - 1 - ((1 << $1) - 1);
+ else
+ $$ = (1 << ($1 + 1)) - 1 - ((1 << $3) - 1);
+ }
+ ;
+
+reglistreg:
+ DR
+ {
+ $$ = $1 - DATA0;
+ }
+ | AR
+ {
+ $$ = $1 - ADDR0 + 8;
+ }
+ | FPR
+ {
+ $$ = $1 - FP0 + 16;
+ }
+ | FPCR
+ {
+ if ($1 == FPI)
+ $$ = 24;
+ else if ($1 == FPS)
+ $$ = 25;
+ else
+ $$ = 26;
+ }
+ ;
+
+%%
+
+/* The string to parse is stored here, and modified by yylex. */
+
+static char *str;
+
+/* The original string pointer. */
+
+static char *strorig;
+
+/* If *CCP could be a register, return the register number and advance
+ *CCP. Otherwise don't change *CCP, and return 0. */
+
+static enum m68k_register
+m68k_reg_parse (ccp)
+ register char **ccp;
+{
+ char *start = *ccp;
+ char c;
+ char *p;
+ symbolS *symbolp;
+
+ if (flag_reg_prefix_optional)
+ {
+ if (*start == REGISTER_PREFIX)
+ start++;
+ p = start;
+ }
+ else
+ {
+ if (*start != REGISTER_PREFIX)
+ return 0;
+ p = start + 1;
+ }
+
+ if (! is_name_beginner (*p))
+ return 0;
+
+ p++;
+ while (is_part_of_name (*p) && *p != '.' && *p != ':' && *p != '*')
+ p++;
+
+ c = *p;
+ *p = 0;
+ symbolp = symbol_find (start);
+ *p = c;
+
+ if (symbolp != NULL && S_GET_SEGMENT (symbolp) == reg_section)
+ {
+ *ccp = p;
+ return S_GET_VALUE (symbolp);
+ }
+
+ /* In MRI mode, something like foo.bar can be equated to a register
+ name. */
+ while (flag_mri && c == '.')
+ {
+ ++p;
+ while (is_part_of_name (*p) && *p != '.' && *p != ':' && *p != '*')
+ p++;
+ c = *p;
+ *p = '\0';
+ symbolp = symbol_find (start);
+ *p = c;
+ if (symbolp != NULL && S_GET_SEGMENT (symbolp) == reg_section)
+ {
+ *ccp = p;
+ return S_GET_VALUE (symbolp);
+ }
+ }
+
+ return 0;
+}
+
+/* The lexer. */
+
+static int
+yylex ()
+{
+ enum m68k_register reg;
+ char *s;
+ int parens;
+ int c = 0;
+ int tail = 0;
+ char *hold;
+
+ if (*str == ' ')
+ ++str;
+
+ if (*str == '\0')
+ return 0;
+
+ /* Various special characters are just returned directly. */
+ switch (*str)
+ {
+ case '@':
+ /* In MRI mode, this can be the start of an octal number. */
+ if (flag_mri)
+ {
+ if (isdigit (str[1])
+ || ((str[1] == '+' || str[1] == '-')
+ && isdigit (str[2])))
+ break;
+ }
+ /* Fall through. */
+ case '#':
+ case '&':
+ case ',':
+ case ')':
+ case '/':
+ case '[':
+ case ']':
+ return *str++;
+ case '+':
+ /* It so happens that a '+' can only appear at the end of an
+ operand. If it appears anywhere else, it must be a unary
+ plus on an expression. */
+ if (str[1] == '\0')
+ return *str++;
+ break;
+ case '-':
+ /* A '-' can only appear in -(ar), rn-rn, or ar@-. If it
+ appears anywhere else, it must be a unary minus on an
+ expression. */
+ if (str[1] == '\0')
+ return *str++;
+ s = str + 1;
+ if (*s == '(')
+ ++s;
+ if (m68k_reg_parse (&s) != 0)
+ return *str++;
+ break;
+ case '(':
+ /* A '(' can only appear in `(reg)', `(expr,...', `([', `@(', or
+ `)('. If it appears anywhere else, it must be starting an
+ expression. */
+ if (str[1] == '['
+ || (str > strorig
+ && (str[-1] == '@'
+ || str[-1] == ')')))
+ return *str++;
+ s = str + 1;
+ if (m68k_reg_parse (&s) != 0)
+ return *str++;
+ /* Check for the case of '(expr,...' by scanning ahead. If we
+ find a comma outside of balanced parentheses, we return '('.
+ If we find an unbalanced right parenthesis, then presumably
+ the '(' really starts an expression. */
+ parens = 0;
+ for (s = str + 1; *s != '\0'; s++)
+ {
+ if (*s == '(')
+ ++parens;
+ else if (*s == ')')
+ {
+ if (parens == 0)
+ break;
+ --parens;
+ }
+ else if (*s == ',' && parens == 0)
+ {
+ /* A comma can not normally appear in an expression, so
+ this is a case of '(expr,...'. */
+ return *str++;
+ }
+ }
+ }
+
+ /* See if it's a register. */
+
+ reg = m68k_reg_parse (&str);
+ if (reg != 0)
+ {
+ int ret;
+
+ yylval.reg = reg;
+
+ if (reg >= DATA0 && reg <= DATA7)
+ ret = DR;
+ else if (reg >= ADDR0 && reg <= ADDR7)
+ ret = AR;
+ else if (reg >= FP0 && reg <= FP7)
+ return FPR;
+ else if (reg == FPI
+ || reg == FPS
+ || reg == FPC)
+ return FPCR;
+ else if (reg == PC)
+ return LPC;
+ else if (reg >= ZDATA0 && reg <= ZDATA7)
+ ret = ZDR;
+ else if (reg >= ZADDR0 && reg <= ZADDR7)
+ ret = ZAR;
+ else if (reg == ZPC)
+ return LZPC;
+ else
+ return CREG;
+
+ /* If we get here, we have a data or address register. We
+ must check for a size or scale; if we find one, we must
+ return INDEXREG. */
+
+ s = str;
+
+ if (*s != '.' && *s != ':' && *s != '*')
+ return ret;
+
+ yylval.indexreg.reg = reg;
+
+ if (*s != '.' && *s != ':')
+ yylval.indexreg.size = SIZE_UNSPEC;
+ else
+ {
+ ++s;
+ switch (*s)
+ {
+ case 'w':
+ case 'W':
+ yylval.indexreg.size = SIZE_WORD;
+ ++s;
+ break;
+ case 'l':
+ case 'L':
+ yylval.indexreg.size = SIZE_LONG;
+ ++s;
+ break;
+ default:
+ yyerror (_("illegal size specification"));
+ yylval.indexreg.size = SIZE_UNSPEC;
+ break;
+ }
+ }
+
+ yylval.indexreg.scale = 1;
+
+ if (*s == '*' || *s == ':')
+ {
+ expressionS scale;
+
+ ++s;
+
+ hold = input_line_pointer;
+ input_line_pointer = s;
+ expression (&scale);
+ s = input_line_pointer;
+ input_line_pointer = hold;
+
+ if (scale.X_op != O_constant)
+ yyerror (_("scale specification must resolve to a number"));
+ else
+ {
+ switch (scale.X_add_number)
+ {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ yylval.indexreg.scale = scale.X_add_number;
+ break;
+ default:
+ yyerror (_("invalid scale value"));
+ break;
+ }
+ }
+ }
+
+ str = s;
+
+ return INDEXREG;
+ }
+
+ /* It must be an expression. Before we call expression, we need to
+ look ahead to see if there is a size specification. We must do
+ that first, because otherwise foo.l will be treated as the symbol
+ foo.l, rather than as the symbol foo with a long size
+ specification. The grammar requires that all expressions end at
+ the end of the operand, or with ',', '(', ']', ')'. */
+
+ parens = 0;
+ for (s = str; *s != '\0'; s++)
+ {
+ if (*s == '(')
+ {
+ if (parens == 0
+ && s > str
+ && (s[-1] == ')' || isalnum ((unsigned char) s[-1])))
+ break;
+ ++parens;
+ }
+ else if (*s == ')')
+ {
+ if (parens == 0)
+ break;
+ --parens;
+ }
+ else if (parens == 0
+ && (*s == ',' || *s == ']'))
+ break;
+ }
+
+ yylval.exp.size = SIZE_UNSPEC;
+ if (s <= str + 2
+ || (s[-2] != '.' && s[-2] != ':'))
+ tail = 0;
+ else
+ {
+ switch (s[-1])
+ {
+ case 's':
+ case 'S':
+ case 'b':
+ case 'B':
+ yylval.exp.size = SIZE_BYTE;
+ break;
+ case 'w':
+ case 'W':
+ yylval.exp.size = SIZE_WORD;
+ break;
+ case 'l':
+ case 'L':
+ yylval.exp.size = SIZE_LONG;
+ break;
+ default:
+ break;
+ }
+ if (yylval.exp.size != SIZE_UNSPEC)
+ tail = 2;
+ }
+
+#ifdef OBJ_ELF
+ {
+ /* Look for @PLTPC, etc. */
+ char *cp;
+
+ yylval.exp.pic_reloc = pic_none;
+ cp = s - tail;
+ if (cp - 6 > str && cp[-6] == '@')
+ {
+ if (strncmp (cp - 6, "@PLTPC", 6) == 0)
+ {
+ yylval.exp.pic_reloc = pic_plt_pcrel;
+ tail += 6;
+ }
+ else if (strncmp (cp - 6, "@GOTPC", 6) == 0)
+ {
+ yylval.exp.pic_reloc = pic_got_pcrel;
+ tail += 6;
+ }
+ }
+ else if (cp - 4 > str && cp[-4] == '@')
+ {
+ if (strncmp (cp - 4, "@PLT", 4) == 0)
+ {
+ yylval.exp.pic_reloc = pic_plt_off;
+ tail += 4;
+ }
+ else if (strncmp (cp - 4, "@GOT", 4) == 0)
+ {
+ yylval.exp.pic_reloc = pic_got_off;
+ tail += 4;
+ }
+ }
+ }
+#endif
+
+ if (tail != 0)
+ {
+ c = s[-tail];
+ s[-tail] = 0;
+ }
+
+ hold = input_line_pointer;
+ input_line_pointer = str;
+ expression (&yylval.exp.exp);
+ str = input_line_pointer;
+ input_line_pointer = hold;
+
+ if (tail != 0)
+ {
+ s[-tail] = c;
+ str = s;
+ }
+
+ return EXPR;
+}
+
+/* Parse an m68k operand. This is the only function which is called
+ from outside this file. */
+
+int
+m68k_ip_op (s, oparg)
+ char *s;
+ struct m68k_op *oparg;
+{
+ memset (oparg, 0, sizeof *oparg);
+ oparg->error = NULL;
+ oparg->index.reg = ZDATA0;
+ oparg->index.scale = 1;
+ oparg->disp.exp.X_op = O_absent;
+ oparg->odisp.exp.X_op = O_absent;
+
+ str = strorig = s;
+ op = oparg;
+
+ return yyparse ();
+}
+
+/* The error handler. */
+
+static void
+yyerror (s)
+ const char *s;
+{
+ op->error = s;
+}
diff --git a/gas/config/m88k-opcode.h b/gas/config/m88k-opcode.h
new file mode 100644
index 0000000..27464bc
--- /dev/null
+++ b/gas/config/m88k-opcode.h
@@ -0,0 +1,559 @@
+/* m88k-opcode.h -- Instruction information for the Motorola 88000
+ Contributed by Devon Bowen of Buffalo University
+ and Torbjorn Granlund of the Swedish Institute of Computer Science.
+ Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING. If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#if !defined(__STDC__) && !defined(const)
+#define const
+#endif
+
+/*
+ Character codes for op_spec field below.
+ Reserved for self-matching: [ ] ,
+
+ d = GRF Destination register (21:5)
+ x = XRF register prefix. Makes next d, 1, or 2, match an extended register.
+ 1 = Source register 1 (16:5)
+ 2 = Source register 2 (0:5)
+ 3 = Both source registers (same value) (0:5 and 16:5)
+ I = IMM16 (0:16)
+ b = bit field spec. (0:10)
+ p = 16 bit pc displ. (0:16)
+ P = 26 bit pc displ. (0:26)
+ B = bb0/bb1 condition (21:5)
+ M = bcnd condition (21:5)
+ f = fcr (5:6)
+ c = cr (5:6)
+ V = VEC9 (0:9)
+ o = O6 field of "prot" insn (10:7)
+ ? = Give warning for this insn/operand combination
+ */
+
+/* instruction descriptor structure */
+
+struct m88k_opcode
+{
+ unsigned int opcode;
+ char *name;
+ char *op_spec;
+};
+
+/* and introducing... the Motorola 88100 and 88110 instruction sets... */
+
+/* By default, include the 88110 instructions. */
+#define MC88110
+
+#if defined (MC88110)
+#define _MC88100(OPCODE,MNEM,OP_SPEC)
+#define _MC88110(OPCODE,MNEM,OP_SPEC) {OPCODE,MNEM,OP_SPEC},
+#else
+#define _MC88100(OPCODE,MNEM,OP_SPEC) {OPCODE,MNEM,OP_SPEC},
+#define _MC88110(OPCODE,MNEM,OP_SPEC)
+#endif
+
+#define _MC88xxx(OPCODE,MNEM,OP_SPEC) {OPCODE,MNEM,OP_SPEC},
+
+/* Equal mnemonics must be adjacent.
+ More specific operand specification must go before more general.
+ For example, "d,1,2" must go before "d,1,I" as a register for s2
+ would otherwise be considered a variable name. */
+
+static struct m88k_opcode m88k_opcodes[] =
+{
+ /* Opcode Mnemonic Opspec */
+
+ _MC88xxx (0xf4007000, "add", "d,1,2")
+ _MC88xxx (0x70000000, "add", "d,1,I")
+ _MC88xxx (0xf4007200, "add.ci", "d,1,2")
+ _MC88xxx (0xf4007300, "add.cio", "d,1,2")
+ _MC88xxx (0xf4007100, "add.co", "d,1,2")
+ _MC88xxx (0xf4006000, "addu", "d,1,2")
+ _MC88xxx (0x60000000, "addu", "d,1,I")
+ _MC88xxx (0xf4006200, "addu.ci", "d,1,2")
+ _MC88xxx (0xf4006300, "addu.cio", "d,1,2")
+ _MC88xxx (0xf4006100, "addu.co", "d,1,2")
+ _MC88xxx (0xf4004000, "and", "d,1,2")
+ _MC88xxx (0x40000000, "and", "d,1,I")
+ _MC88xxx (0xf4004400, "and.c", "d,1,2")
+ _MC88xxx (0x44000000, "and.u", "d,1,I")
+ _MC88xxx (0xd0000000, "bb0", "B,1,p")
+ _MC88xxx (0xd4000000, "bb0.n", "B,1,p")
+ _MC88xxx (0xd8000000, "bb1", "B,1,p")
+ _MC88xxx (0xdc000000, "bb1.n", "B,1,p")
+ _MC88xxx (0xe8000000, "bcnd", "M,1,p")
+ _MC88xxx (0xec000000, "bcnd.n", "M,1,p")
+ _MC88xxx (0xc0000000, "br", "P")
+ _MC88xxx (0xc4000000, "br.n", "P")
+ _MC88xxx (0xc8000000, "bsr", "P")
+ _MC88xxx (0xcc000000, "bsr.n", "P")
+ _MC88xxx (0xf4008000, "clr", "d,1,2")
+ _MC88xxx (0xf0008000, "clr", "d,1,b")
+ _MC88xxx (0xf4007c00, "cmp", "d,1,2")
+ _MC88xxx (0x7c000000, "cmp", "d,1,I")
+ _MC88xxx (0xf4007800, "div", "d,1,2")
+ _MC88xxx (0x78000000, "div", "d,1,I")
+ _MC88xxx (0xf4007800, "divs", "d,1,2")
+ _MC88xxx (0x78000000, "divs", "d,1,I")
+ _MC88110 (0xf4006900, "divu.d", "d,1,2")
+ _MC88xxx (0xf4006800, "divu", "d,1,2")
+ _MC88xxx (0x68000000, "divu", "d,1,I")
+ _MC88xxx (0xf4009000, "ext", "d,1,2")
+ _MC88xxx (0xf0009000, "ext", "d,1,b")
+ _MC88xxx (0xf4009800, "extu", "d,1,2")
+ _MC88xxx (0xf0009800, "extu", "d,1,b")
+ _MC88xxx (0x84002800, "fadd.sss", "d,1,2")
+ _MC88110 (0x8400a800, "fadd.sss", "xd,x1,x2")
+ _MC88xxx (0x84002880, "fadd.ssd", "d,1,2")
+ _MC88110 (0x8400a820, "fadd.ssd", "xd,x1,x2")
+ _MC88110 (0x8400a840, "fadd.ssx", "xd,x1,x2")
+ _MC88xxx (0x84002a00, "fadd.sds", "d,1,2")
+ _MC88110 (0x8400a880, "fadd.sds", "xd,x1,x2")
+ _MC88xxx (0x84002a80, "fadd.sdd", "d,1,2")
+ _MC88110 (0x8400a8a0, "fadd.sdd", "xd,x1,x2")
+ _MC88110 (0x8400a8c0, "fadd.sdx", "xd,x1,x2")
+ _MC88110 (0x8400a900, "fadd.sxs", "xd,x1,x2")
+ _MC88110 (0x8400a920, "fadd.sxd", "xd,x1,x2")
+ _MC88110 (0x8400a940, "fadd.sxx", "xd,x1,x2")
+ _MC88xxx (0x84002820, "fadd.dss", "d,1,2")
+ _MC88110 (0x8400aa00, "fadd.dss", "xd,x1,x2")
+ _MC88xxx (0x840028a0, "fadd.dsd", "d,1,2")
+ _MC88110 (0x8400aa20, "fadd.dsd", "xd,x1,x2")
+ _MC88110 (0x8400aa40, "fadd.dsx", "xd,x1,x2")
+ _MC88xxx (0x84002a20, "fadd.dds", "d,1,2")
+ _MC88110 (0x8400aa80, "fadd.dds", "xd,x1,x2")
+ _MC88xxx (0x84002aa0, "fadd.ddd", "d,1,2")
+ _MC88110 (0x8400aaa0, "fadd.ddd", "xd,x1,x2")
+ _MC88110 (0x8400aac0, "fadd.ddx", "xd,x1,x2")
+ _MC88110 (0x8400ab00, "fadd.dxs", "xd,x1,x2")
+ _MC88110 (0x8400ab20, "fadd.dxd", "xd,x1,x2")
+ _MC88110 (0x8400ab40, "fadd.dxx", "xd,x1,x2")
+ _MC88110 (0x8400ac00, "fadd.xss", "xd,x1,x2")
+ _MC88110 (0x8400ac20, "fadd.xsd", "xd,x1,x2")
+ _MC88110 (0x8400ac40, "fadd.xsx", "xd,x1,x2")
+ _MC88110 (0x8400ac80, "fadd.xds", "xd,x1,x2")
+ _MC88110 (0x8400aca0, "fadd.xdd", "xd,x1,x2")
+ _MC88110 (0x8400acc0, "fadd.xdx", "xd,x1,x2")
+ _MC88110 (0x8400ad00, "fadd.xxs", "xd,x1,x2")
+ _MC88110 (0x8400ad20, "fadd.xxd", "xd,x1,x2")
+ _MC88110 (0x8400ad40, "fadd.xxx", "xd,x1,x2")
+ _MC88xxx (0x84003a80, "fcmp.sdd", "d,1,2")
+ _MC88110 (0x8400ba80, "fcmp.sdd", "d,x1,x2")
+ _MC88xxx (0x84003a00, "fcmp.sds", "d,1,2")
+ _MC88110 (0x8400ba00, "fcmp.sds", "d,x1,x2")
+ _MC88110 (0x8400bb00, "fcmp.sdx", "d,x1,x2")
+ _MC88xxx (0x84003880, "fcmp.ssd", "d,1,2")
+ _MC88110 (0x8400b880, "fcmp.ssd", "d,x1,x2")
+ _MC88xxx (0x84003800, "fcmp.sss", "d,1,2")
+ _MC88110 (0x8400b800, "fcmp.sss", "d,x1,x2")
+ _MC88110 (0x8400b900, "fcmp.ssx", "d,x1,x2")
+ _MC88110 (0x8400bc80, "fcmp.sxd", "d,x1,x2")
+ _MC88110 (0x8400bc00, "fcmp.sxs", "d,x1,x2")
+ _MC88110 (0x8400bd00, "fcmp.sxx", "d,x1,x2")
+ _MC88110 (0x84003aa0, "fcmpu.sdd", "d,1,2")
+ _MC88110 (0x8400baa0, "fcmpu.sdd", "d,x1,x2")
+ _MC88110 (0x84003a20, "fcmpu.sds", "d,1,2")
+ _MC88110 (0x8400ba20, "fcmpu.sds", "d,x1,x2")
+ _MC88110 (0x8400bb20, "fcmpu.sdx", "d,x1,x2")
+ _MC88110 (0x840038a0, "fcmpu.ssd", "d,1,2")
+ _MC88110 (0x8400b8a0, "fcmpu.ssd", "d,x1,x2")
+ _MC88110 (0x84003820, "fcmpu.sss", "d,1,2")
+ _MC88110 (0x8400b820, "fcmpu.sss", "d,x1,x2")
+ _MC88110 (0x8400b920, "fcmpu.ssx", "d,x1,x2")
+ _MC88110 (0x8400bca0, "fcmpu.sxd", "d,x1,x2")
+ _MC88110 (0x8400bc20, "fcmpu.sxs", "d,x1,x2")
+ _MC88110 (0x8400bd20, "fcmpu.sxx", "d,x1,x2")
+ _MC88110 (0x84000880, "fcvt.ds", "d,2")
+ _MC88110 (0x84008880, "fcvt.ds", "xd,x2")
+ _MC88110 (0x840088c0, "fcvt.dx", "xd,x2")
+ _MC88110 (0x84000820, "fcvt.sd", "d,2")
+ _MC88110 (0x84008820, "fcvt.sd", "xd,x2")
+ _MC88110 (0x84008840, "fcvt.sx", "xd,x2")
+ _MC88110 (0x84008920, "fcvt.xd", "xd,x2")
+ _MC88110 (0x84008900, "fcvt.xs", "xd,x2")
+ _MC88xxx (0x84007000, "fdiv.sss", "d,1,2")
+ _MC88110 (0x8400f000, "fdiv.sss", "xd,x1,x2")
+ _MC88xxx (0x84007080, "fdiv.ssd", "d,1,2")
+ _MC88110 (0x8400f020, "fdiv.ssd", "xd,x1,x2")
+ _MC88110 (0x8400f040, "fdiv.ssx", "xd,x1,x2")
+ _MC88xxx (0x84007200, "fdiv.sds", "d,1,2")
+ _MC88110 (0x8400f080, "fdiv.sds", "xd,x1,x2")
+ _MC88xxx (0x84007280, "fdiv.sdd", "d,1,2")
+ _MC88110 (0x8400f0a0, "fdiv.sdd", "xd,x1,x2")
+ _MC88110 (0x8400f0c0, "fdiv.sdx", "xd,x1,x2")
+ _MC88110 (0x8400f100, "fdiv.sxs", "xd,x1,x2")
+ _MC88110 (0x8400f120, "fdiv.sxd", "xd,x1,x2")
+ _MC88110 (0x8400f140, "fdiv.sxx", "xd,x1,x2")
+ _MC88xxx (0x84007020, "fdiv.dss", "d,1,2")
+ _MC88110 (0x8400f200, "fdiv.dss", "xd,x1,x2")
+ _MC88xxx (0x840070a0, "fdiv.dsd", "d,1,2")
+ _MC88110 (0x8400f220, "fdiv.dsd", "xd,x1,x2")
+ _MC88110 (0x8400f240, "fdiv.dsx", "xd,x1,x2")
+ _MC88xxx (0x84007220, "fdiv.dds", "d,1,2")
+ _MC88110 (0x8400f280, "fdiv.dds", "xd,x1,x2")
+ _MC88xxx (0x840072a0, "fdiv.ddd", "d,1,2")
+ _MC88110 (0x8400f2a0, "fdiv.ddd", "xd,x1,x2")
+ _MC88110 (0x8400f2c0, "fdiv.ddx", "xd,x1,x2")
+ _MC88110 (0x8400f300, "fdiv.dxs", "xd,x1,x2")
+ _MC88110 (0x8400f320, "fdiv.dxd", "xd,x1,x2")
+ _MC88110 (0x8400f340, "fdiv.dxx", "xd,x1,x2")
+ _MC88110 (0x8400f400, "fdiv.xss", "xd,x1,x2")
+ _MC88110 (0x8400f420, "fdiv.xsd", "xd,x1,x2")
+ _MC88110 (0x8400f440, "fdiv.xsx", "xd,x1,x2")
+ _MC88110 (0x8400f480, "fdiv.xds", "xd,x1,x2")
+ _MC88110 (0x8400f4a0, "fdiv.xdd", "xd,x1,x2")
+ _MC88110 (0x8400f4c0, "fdiv.xdx", "xd,x1,x2")
+ _MC88110 (0x8400f500, "fdiv.xxs", "xd,x1,x2")
+ _MC88110 (0x8400f520, "fdiv.xxd", "xd,x1,x2")
+ _MC88110 (0x8400f540, "fdiv.xxx", "xd,x1,x2")
+ _MC88xxx (0xf400ec00, "ff0", "d,2")
+ _MC88xxx (0xf400e800, "ff1", "d,2")
+ _MC88xxx (0x80004800, "fldcr", "d,f")
+ _MC88xxx (0x84002020, "flt.ds", "d,2")
+ _MC88110 (0x84002220, "flt.ds", "xd,2")
+ _MC88xxx (0x84002000, "flt.ss", "d,2")
+ _MC88110 (0x84002200, "flt.ss", "xd,2")
+ _MC88110 (0x84002240, "flt.xs", "xd,2")
+ _MC88xxx (0x84000000, "fmul.sss", "d,1,2")
+ _MC88110 (0x84008000, "fmul.sss", "xd,x1,x2")
+ _MC88xxx (0x84000080, "fmul.ssd", "d,1,2")
+ _MC88110 (0x84008020, "fmul.ssd", "xd,x1,x2")
+ _MC88110 (0x84008040, "fmul.ssx", "xd,x1,x2")
+ _MC88xxx (0x84000200, "fmul.sds", "d,1,2")
+ _MC88110 (0x84008080, "fmul.sds", "xd,x1,x2")
+ _MC88xxx (0x84000280, "fmul.sdd", "d,1,2")
+ _MC88110 (0x840080a0, "fmul.sdd", "xd,x1,x2")
+ _MC88110 (0x840080c0, "fmul.sdx", "xd,x1,x2")
+ _MC88110 (0x84008100, "fmul.sxs", "xd,x1,x2")
+ _MC88110 (0x84008120, "fmul.sxd", "xd,x1,x2")
+ _MC88110 (0x84008140, "fmul.sxx", "xd,x1,x2")
+ _MC88xxx (0x84000020, "fmul.dss", "d,1,2")
+ _MC88110 (0x84008200, "fmul.dss", "xd,x1,x2")
+ _MC88xxx (0x840000a0, "fmul.dsd", "d,1,2")
+ _MC88110 (0x84008220, "fmul.dsd", "xd,x1,x2")
+ _MC88110 (0x84008240, "fmul.dsx", "xd,x1,x2")
+ _MC88xxx (0x84000220, "fmul.dds", "d,1,2")
+ _MC88110 (0x84008280, "fmul.dds", "xd,x1,x2")
+ _MC88xxx (0x840002a0, "fmul.ddd", "d,1,2")
+ _MC88110 (0x840082a0, "fmul.ddd", "xd,x1,x2")
+ _MC88110 (0x840082c0, "fmul.ddx", "xd,x1,x2")
+ _MC88110 (0x84008300, "fmul.dxs", "xd,x1,x2")
+ _MC88110 (0x84008320, "fmul.dxd", "xd,x1,x2")
+ _MC88110 (0x84008340, "fmul.dxx", "xd,x1,x2")
+ _MC88110 (0x84008400, "fmul.xss", "xd,x1,x2")
+ _MC88110 (0x84008420, "fmul.xsd", "xd,x1,x2")
+ _MC88110 (0x84008440, "fmul.xsx", "xd,x1,x2")
+ _MC88110 (0x84008480, "fmul.xds", "xd,x1,x2")
+ _MC88110 (0x840084a0, "fmul.xdd", "xd,x1,x2")
+ _MC88110 (0x840084c0, "fmul.xdx", "xd,x1,x2")
+ _MC88110 (0x84008500, "fmul.xxs", "xd,x1,x2")
+ _MC88110 (0x84008520, "fmul.xxd", "xd,x1,x2")
+ _MC88110 (0x84008540, "fmul.xxx", "xd,x1,x2")
+ _MC88110 (0x840078a0, "fsqrt.dd", "d,2")
+ _MC88110 (0x8400f8a0, "fsqrt.dd", "xd,x2")
+ _MC88110 (0x84007880, "fsqrt.ds", "d,2")
+ _MC88110 (0x8400f880, "fsqrt.ds", "xd,x2")
+ _MC88110 (0x8400f8c0, "fsqrt.dx", "xd,x2")
+ _MC88110 (0x84007820, "fsqrt.sd", "d,2")
+ _MC88110 (0x8400f820, "fsqrt.sd", "xd,x2")
+ _MC88110 (0x84007800, "fsqrt.ss", "d,2")
+ _MC88110 (0x8400f800, "fsqrt.ss", "xd,x2")
+ _MC88110 (0x8400f840, "fsqrt.sx", "xd,x2")
+ _MC88110 (0x8400f920, "fsqrt.xd", "xd,x2")
+ _MC88110 (0x8400f900, "fsqrt.xs", "xd,x2")
+ _MC88110 (0x8400f940, "fsqrt.xx", "xd,x2")
+ _MC88xxx (0x80008800, "fstcr", "3,f")
+ _MC88xxx (0x84003000, "fsub.sss", "d,1,2")
+ _MC88110 (0x8400b000, "fsub.sss", "xd,x1,x2")
+ _MC88xxx (0x84003080, "fsub.ssd", "d,1,2")
+ _MC88110 (0x8400b020, "fsub.ssd", "xd,x1,x2")
+ _MC88110 (0x8400b040, "fsub.ssx", "xd,x1,x2")
+ _MC88xxx (0x84003200, "fsub.sds", "d,1,2")
+ _MC88110 (0x8400b080, "fsub.sds", "xd,x1,x2")
+ _MC88xxx (0x84003280, "fsub.sdd", "d,1,2")
+ _MC88110 (0x8400b0a0, "fsub.sdd", "xd,x1,x2")
+ _MC88110 (0x8400b0c0, "fsub.sdx", "xd,x1,x2")
+ _MC88110 (0x8400b100, "fsub.sxs", "xd,x1,x2")
+ _MC88110 (0x8400b120, "fsub.sxd", "xd,x1,x2")
+ _MC88110 (0x8400b140, "fsub.sxx", "xd,x1,x2")
+ _MC88xxx (0x84003020, "fsub.dss", "d,1,2")
+ _MC88110 (0x8400b200, "fsub.dss", "xd,x1,x2")
+ _MC88xxx (0x840030a0, "fsub.dsd", "d,1,2")
+ _MC88110 (0x8400b220, "fsub.dsd", "xd,x1,x2")
+ _MC88110 (0x8400b240, "fsub.dsx", "xd,x1,x2")
+ _MC88xxx (0x84003220, "fsub.dds", "d,1,2")
+ _MC88110 (0x8400b280, "fsub.dds", "xd,x1,x2")
+ _MC88xxx (0x840032a0, "fsub.ddd", "d,1,2")
+ _MC88110 (0x8400b2a0, "fsub.ddd", "xd,x1,x2")
+ _MC88110 (0x8400b2c0, "fsub.ddx", "xd,x1,x2")
+ _MC88110 (0x8400b300, "fsub.dxs", "xd,x1,x2")
+ _MC88110 (0x8400b320, "fsub.dxd", "xd,x1,x2")
+ _MC88110 (0x8400b340, "fsub.dxx", "xd,x1,x2")
+ _MC88110 (0x8400b400, "fsub.xss", "xd,x1,x2")
+ _MC88110 (0x8400b420, "fsub.xsd", "xd,x1,x2")
+ _MC88110 (0x8400b440, "fsub.xsx", "xd,x1,x2")
+ _MC88110 (0x8400b480, "fsub.xds", "xd,x1,x2")
+ _MC88110 (0x8400b4a0, "fsub.xdd", "xd,x1,x2")
+ _MC88110 (0x8400b4c0, "fsub.xdx", "xd,x1,x2")
+ _MC88110 (0x8400b500, "fsub.xxs", "xd,x1,x2")
+ _MC88110 (0x8400b520, "fsub.xxd", "xd,x1,x2")
+ _MC88110 (0x8400b540, "fsub.xxx", "xd,x1,x2")
+ _MC88xxx (0x8000c800, "fxcr", "d,3,f")
+ _MC88xxx (0x8400fc01, "illop1", "")
+ _MC88xxx (0x8400fc02, "illop2", "")
+ _MC88xxx (0x8400fc03, "illop3", "")
+ _MC88xxx (0x84004880, "int.sd", "d,2")
+ _MC88110 (0x8400c880, "int.sd", "d,x2")
+ _MC88xxx (0x84004800, "int.ss", "d,2")
+ _MC88110 (0x8400c800, "int.ss", "d,x2")
+ _MC88110 (0x8400c900, "int.sx", "d,x2")
+ _MC88xxx (0xf400c000, "jmp", "2")
+ _MC88xxx (0xf400c400, "jmp.n", "2")
+ _MC88xxx (0xf400c800, "jsr", "2")
+ _MC88xxx (0xf400cc00, "jsr.n", "2")
+ _MC88xxx (0xf4001400, "ld", "d,1,2")
+ _MC88xxx (0xf4001600, "ld", "d,1[2]")
+ _MC88xxx (0x14000000, "ld", "d,1,I")
+ _MC88110 (0xf0001600, "ld", "xd,1[2]")
+ _MC88110 (0xf0001400, "ld", "xd,1,2")
+ _MC88110 (0x04000000, "ld", "xd,1,I")
+ _MC88xxx (0xf4001e00, "ld.b", "d,1[2]")
+ _MC88xxx (0xf4001c00, "ld.b", "d,1,2")
+ _MC88xxx (0x1c000000, "ld.b", "d,1,I")
+ _MC88xxx (0xf4001d00, "ld.b.usr", "d,1,2")
+ _MC88xxx (0xf4001f00, "ld.b.usr", "d,1[2]")
+ _MC88xxx (0xf4000e00, "ld.bu", "d,1[2]")
+ _MC88xxx (0xf4000c00, "ld.bu", "d,1,2")
+ _MC88xxx (0x0c000000, "ld.bu", "d,1,I")
+ _MC88xxx (0xf4000d00, "ld.bu.usr", "d,1,2")
+ _MC88xxx (0xf4000f00, "ld.bu.usr", "d,1[2]")
+ _MC88xxx (0xf4001200, "ld.d", "d,1[2]")
+ _MC88xxx (0xf4001000, "ld.d", "d,1,2")
+ _MC88xxx (0x10000000, "ld.d", "d,1,I")
+ _MC88110 (0xf0001200, "ld.d", "xd,1[2]")
+ _MC88110 (0xf0001000, "ld.d", "xd,1,2")
+ _MC88110 (0x00000000, "ld.d", "xd,1,I")
+ _MC88xxx (0xf4001100, "ld.d.usr", "d,1,2")
+ _MC88xxx (0xf4001300, "ld.d.usr", "d,1[2]")
+ _MC88110 (0xf0001100, "ld.d.usr", "xd,1,2")
+ _MC88110 (0xf0001300, "ld.d.usr", "xd,1[2]")
+ _MC88xxx (0xf4001a00, "ld.h", "d,1[2]")
+ _MC88xxx (0xf4001800, "ld.h", "d,1,2")
+ _MC88xxx (0x18000000, "ld.h", "d,1,I")
+ _MC88xxx (0xf4001900, "ld.h.usr", "d,1,2")
+ _MC88xxx (0xf4001b00, "ld.h.usr", "d,1[2]")
+ _MC88xxx (0xf4000a00, "ld.hu", "d,1[2]")
+ _MC88xxx (0xf4000800, "ld.hu", "d,1,2")
+ _MC88xxx (0x08000000, "ld.hu", "d,1,I")
+ _MC88xxx (0xf4000900, "ld.hu.usr", "d,1,2")
+ _MC88xxx (0xf4000b00, "ld.hu.usr", "d,1[2]")
+ _MC88xxx (0xf4001500, "ld.usr", "d,1,2")
+ _MC88xxx (0xf4001700, "ld.usr", "d,1[2]")
+ _MC88110 (0xf0001500, "ld.usr", "xd,1,2")
+ _MC88110 (0xf0001700, "ld.usr", "xd,1[2]")
+ _MC88110 (0xf0001a00, "ld.x", "xd,1[2]")
+ _MC88110 (0xf0001800, "ld.x", "xd,1,2")
+ _MC88110 (0x3c000000, "ld.x", "xd,1,I")
+ _MC88110 (0xf0001900, "ld.x.usr", "xd,1,2")
+ _MC88110 (0xf0001b00, "ld.x.usr", "xd,1[2]")
+ _MC88xxx (0xf4003600, "lda", "d,1[2]")
+ _MC88xxx (0xf4006000, "lda", "?d,1,2") /* Output addu */
+ _MC88xxx (0x60000000, "lda", "?d,1,I") /* Output addu */
+ _MC88xxx (0xf4006000, "lda.b", "?d,1[2]") /* Output addu */
+ _MC88xxx (0xf4006000, "lda.b", "?d,1,2") /* Output addu */
+ _MC88xxx (0x60000000, "lda.b", "?d,1,I") /* Output addu */
+ _MC88xxx (0xf4003200, "lda.d", "d,1[2]")
+ _MC88xxx (0xf4006000, "lda.d", "?d,1,2") /* Output addu */
+ _MC88xxx (0x60000000, "lda.d", "?d,1,I") /* Output addu */
+ _MC88110 (0xf4003e00, "lda.x", "d,1[2]")
+ _MC88xxx (0xf4003a00, "lda.h", "d,1[2]")
+ _MC88xxx (0xf4006000, "lda.h", "?d,1,2") /* Output addu */
+ _MC88xxx (0x60000000, "lda.h", "?d,1,I") /* Output addu */
+ _MC88xxx (0x80004000, "ldcr", "d,c")
+ _MC88xxx (0xf400a000, "mak", "d,1,2")
+ _MC88xxx (0xf000a000, "mak", "d,1,b")
+ _MC88xxx (0x48000000, "mask", "d,1,I")
+ _MC88xxx (0x4c000000, "mask.u", "d,1,I")
+ _MC88110 (0x8400c000, "mov.s", "d,x2")
+ _MC88110 (0x84004200, "mov.s", "xd,2")
+ _MC88110 (0x8400c080, "mov.d", "d,x2")
+ _MC88110 (0x84004280, "mov.d", "xd,2")
+ _MC88110 (0x8400c300, "mov", "xd,x2")
+ _MC88xxx (0xf4006c00, "mul", "d,1,2")
+ _MC88xxx (0x6c000000, "mul", "d,1,I")
+ _MC88xxx (0xf4006e00, "muls", "d,1,2")
+ _MC88xxx (0x6c000000, "muls", "d,1,I")
+ _MC88xxx (0xf4006c00, "mulu", "d,1,2") /* synonym for mul */
+ _MC88xxx (0x6c000000, "mulu", "d,1,I") /* synonym for mul */
+ _MC88110 (0xf4006d00, "mulu.d", "d,1,2")
+ _MC88xxx (0x84005080, "nint.sd", "d,2")
+ _MC88110 (0x8400d080, "nint.sd", "d,x2")
+ _MC88xxx (0x84005000, "nint.ss", "d,2")
+ _MC88110 (0x8400d000, "nint.ss", "d,x2")
+ _MC88110 (0x8400d100, "nint.sx", "d,x2")
+ _MC88xxx (0xf4005800, "or", "d,1,2")
+ _MC88xxx (0x58000000, "or", "d,1,I")
+ _MC88xxx (0xf4005c00, "or.c", "d,1,2")
+ _MC88xxx (0x5c000000, "or.u", "d,1,I")
+ _MC88110 (0x88002020, "padd.b", "d,1,2")
+ _MC88110 (0x88002040, "padd.h", "d,1,2")
+ _MC88110 (0x88002060, "padd", "d,1,2")
+ _MC88110 (0x880020a0, "padds.u.b", "d,1,2")
+ _MC88110 (0x880020c0, "padds.u.h", "d,1,2")
+ _MC88110 (0x880020e0, "padds.u", "d,1,2")
+ _MC88110 (0x88002120, "padds.us.b", "d,1,2")
+ _MC88110 (0x88002140, "padds.us.h", "d,1,2")
+ _MC88110 (0x88002160, "padds.us", "d,1,2")
+ _MC88110 (0x880021a0, "padds.s.b", "d,1,2")
+ _MC88110 (0x880021c0, "padds.s.h", "d,1,2")
+ _MC88110 (0x880021e0, "padds.s", "d,1,2")
+ _MC88110 (0x88003860, "pcmp", "d,1,2")
+ _MC88110 (0x88000000, "pmul", "d,1,2")
+ _MC88110 (0x88006420, "ppack.32.b", "d,1,2")
+ _MC88110 (0x88006240, "ppack.16.h", "d,1,2")
+ _MC88110 (0x88006440, "ppack.32.h", "d,1,2")
+ _MC88110 (0x88006160, "ppack.8", "d,1,2")
+ _MC88110 (0x88006260, "ppack.16", "d,1,2")
+ _MC88110 (0x88006460, "ppack.32", "d,1,2")
+ _MC88110 (0x88007800, "prot", "d,1,2")
+ _MC88110 (0x88007000, "prot", "d,1,o")
+ _MC88110 (0x88003020, "psub.b", "d,1,2")
+ _MC88110 (0x88003040, "psub.h", "d,1,2")
+ _MC88110 (0x88003060, "psub", "d,1,2")
+ _MC88110 (0x880030a0, "psubs.u.b", "d,1,2")
+ _MC88110 (0x880030c0, "psubs.u.h", "d,1,2")
+ _MC88110 (0x880030e0, "psubs.u", "d,1,2")
+ _MC88110 (0x88003120, "psubs.us.b", "d,1,2")
+ _MC88110 (0x88003140, "psubs.us.h", "d,1,2")
+ _MC88110 (0x88003160, "psubs.us", "d,1,2")
+ _MC88110 (0x880031a0, "psubs.s.b", "d,1,2")
+ _MC88110 (0x880031c0, "psubs.s.h", "d,1,2")
+ _MC88110 (0x880031e0, "psubs.s", "d,1,2")
+ _MC88110 (0x88006800, "punpk.n", "d,1")
+ _MC88110 (0x88006820, "punpk.b", "d,1")
+ _MC88110 (0x88006840, "punpk.h", "d,1")
+ _MC88xxx (0xf400a800, "rot", "d,1,2")
+ _MC88xxx (0xf000a800, "rot", "d,1,b")
+ _MC88xxx (0xf400fc00, "rte", "")
+ _MC88xxx (0xf4008800, "set", "d,1,2")
+ _MC88xxx (0xf0008800, "set", "d,1,b")
+ _MC88xxx (0xf4002600, "st", "d,1[2]")
+ _MC88xxx (0xf4002400, "st", "d,1,2")
+ _MC88xxx (0x24000000, "st", "d,1,I")
+ _MC88110 (0xf0002600, "st", "xd,1[2]")
+ _MC88110 (0xf0002400, "st", "xd,1,2")
+ _MC88110 (0x34000000, "st", "xd,1,I")
+ _MC88xxx (0xf4002e00, "st.b", "d,1[2]")
+ _MC88xxx (0xf4002c00, "st.b", "d,1,2")
+ _MC88xxx (0x2c000000, "st.b", "d,1,I")
+ _MC88xxx (0xf4002d00, "st.b.usr", "d,1,2")
+ _MC88xxx (0xf4002f00, "st.b.usr", "d,1[2]")
+ _MC88110 (0xf4002d80, "st.b.usr.wt", "d,1,2")
+ _MC88110 (0xf4002f80, "st.b.usr.wt", "d,1[2]")
+ _MC88110 (0xf4002c80, "st.b.wt", "d,1,2")
+ _MC88110 (0xf4002e80, "st.b.wt", "d,1[2]")
+ _MC88xxx (0xf4002200, "st.d", "d,1[2]")
+ _MC88xxx (0xf4002000, "st.d", "d,1,2")
+ _MC88xxx (0x20000000, "st.d", "d,1,I")
+ _MC88110 (0xf0002200, "st.d", "xd,1[2]")
+ _MC88110 (0xf0002000, "st.d", "xd,1,2")
+ _MC88110 (0x30000000, "st.d", "xd,1,I")
+ _MC88xxx (0xf4002100, "st.d.usr", "d,1,2")
+ _MC88xxx (0xf4002300, "st.d.usr", "d,1[2]")
+ _MC88110 (0xf0002100, "st.d.usr", "xd,1,2")
+ _MC88110 (0xf0002300, "st.d.usr", "xd,1[2]")
+ _MC88110 (0xf4002180, "st.d.usr.wt", "d,1,2")
+ _MC88110 (0xf4002380, "st.d.usr.wt", "d,1[2]")
+ _MC88110 (0xf0002180, "st.d.usr.wt", "xd,1,2")
+ _MC88110 (0xf0002380, "st.d.usr.wt", "xd,1[2]")
+ _MC88110 (0xf4002080, "st.d.wt", "d,1,2")
+ _MC88110 (0xf4002280, "st.d.wt", "d,1[2]")
+ _MC88110 (0xf0002080, "st.d.wt", "xd,1,2")
+ _MC88110 (0xf0002280, "st.d.wt", "xd,1[2]")
+ _MC88xxx (0xf4002a00, "st.h", "d,1[2]")
+ _MC88xxx (0xf4002800, "st.h", "d,1,2")
+ _MC88xxx (0x28000000, "st.h", "d,1,I")
+ _MC88xxx (0xf4002900, "st.h.usr", "d,1,2")
+ _MC88xxx (0xf4002b00, "st.h.usr", "d,1[2]")
+ _MC88110 (0xf4002980, "st.h.usr.wt", "d,1,2")
+ _MC88110 (0xf4002b80, "st.h.usr.wt", "d,1[2]")
+ _MC88110 (0xf4002880, "st.h.wt", "d,1,2")
+ _MC88110 (0xf4002a80, "st.h.wt", "d,1[2]")
+ _MC88xxx (0xf4002500, "st.usr", "d,1,2")
+ _MC88xxx (0xf4002700, "st.usr", "d,1[2]")
+ _MC88110 (0xf0002500, "st.usr", "xd,1,2")
+ _MC88110 (0xf0002700, "st.usr", "xd,1[2]")
+ _MC88110 (0xf4002580, "st.usr.wt", "d,1,2")
+ _MC88110 (0xf4002780, "st.usr.wt", "d,1[2]")
+ _MC88110 (0xf0002580, "st.usr.wt", "xd,1,2")
+ _MC88110 (0xf0002780, "st.usr.wt", "xd,1[2]")
+ _MC88110 (0xf4002480, "st.wt", "d,1,2")
+ _MC88110 (0xf4002680, "st.wt", "d,1[2]")
+ _MC88110 (0xf0002480, "st.wt", "xd,1,2")
+ _MC88110 (0xf0002680, "st.wt", "xd,1[2]")
+ _MC88110 (0xf0002a00, "st.x", "xd,1[2]")
+ _MC88110 (0xf0002800, "st.x", "xd,1,2")
+ _MC88110 (0x38000000, "st.x", "xd,1,I")
+ _MC88110 (0xf0002900, "st.x.usr", "xd,1,2")
+ _MC88110 (0xf0002b00, "st.x.usr", "xd,1[2]")
+ _MC88110 (0xf0002980, "st.x.usr.wt", "xd,1,2")
+ _MC88110 (0xf0002b80, "st.x.usr.wt", "xd,1[2]")
+ _MC88110 (0xf0002880, "st.x.wt", "xd,1,2")
+ _MC88110 (0xf0002a80, "st.x.wt", "xd,1[2]")
+ _MC88xxx (0x80008000, "stcr", "3,c")
+ _MC88xxx (0xf4007400, "sub", "d,1,2")
+ _MC88xxx (0x74000000, "sub", "d,1,I")
+ _MC88xxx (0xf4007600, "sub.ci", "d,1,2")
+ _MC88xxx (0xf4007700, "sub.cio", "d,1,2")
+ _MC88xxx (0xf4007500, "sub.co", "d,1,2")
+ _MC88xxx (0xf4006400, "subu", "d,1,2")
+ _MC88xxx (0x64000000, "subu", "d,1,I")
+ _MC88xxx (0xf4006600, "subu.ci", "d,1,2")
+ _MC88xxx (0xf4006700, "subu.cio", "d,1,2")
+ _MC88xxx (0xf4006500, "subu.co", "d,1,2")
+ _MC88xxx (0xf000d000, "tb0", "B,1,V")
+ _MC88xxx (0xf000d800, "tb1", "B,1,V")
+ _MC88xxx (0xf400f800, "tbnd", "1,2")
+ _MC88xxx (0xf8000000, "tbnd", "1,I")
+ _MC88xxx (0xf000e800, "tcnd", "M,1,V")
+ _MC88xxx (0x84005880, "trnc.sd", "d,2")
+ _MC88110 (0x8400d880, "trnc.sd", "d,x2")
+ _MC88xxx (0x84005800, "trnc.ss", "d,2")
+ _MC88110 (0x8400d800, "trnc.ss", "d,x2")
+ _MC88110 (0x8400d900, "trnc.sx", "d,x2")
+ _MC88xxx (0x8000c000, "xcr", "d,3,c")
+ _MC88xxx (0xf4000600, "xmem", "d,1[2]")
+ _MC88xxx (0xf4000400, "xmem", "d,1,2")
+ _MC88100 (0x04000000, "xmem", "?d,1,I")
+ _MC88xxx (0xf4000200, "xmem.bu", "d,1[2]")
+ _MC88xxx (0xf4000000, "xmem.bu", "d,1,2")
+ _MC88100 (0x00000000, "xmem.bu", "?d,1,I")
+ _MC88xxx (0xf4000300, "xmem.bu.usr", "d,1[2]")
+ _MC88xxx (0xf4000100, "xmem.bu.usr", "d,1,2")
+ _MC88100 (0x00000100, "xmem.bu.usr", "?d,1,I")
+ _MC88xxx (0xf4000700, "xmem.usr", "d,1[2]")
+ _MC88xxx (0xf4000500, "xmem.usr", "d,1,2")
+ _MC88100 (0x04000100, "xmem.usr", "?d,1,I")
+ _MC88xxx (0xf4005000, "xor", "d,1,2")
+ _MC88xxx (0x50000000, "xor", "d,1,I")
+ _MC88xxx (0xf4005400, "xor.c", "d,1,2")
+ _MC88xxx (0x54000000, "xor.u", "d,1,I")
+ _MC88xxx (0x00000000, "", 0)
+};
+
+#define NUMOPCODES ((sizeof m88k_opcodes)/(sizeof m88k_opcodes[0]))
diff --git a/gas/config/obj-aout.c b/gas/config/obj-aout.c
new file mode 100644
index 0000000..b519347
--- /dev/null
+++ b/gas/config/obj-aout.c
@@ -0,0 +1,629 @@
+/* a.out object file format
+ Copyright (C) 1989, 90, 91, 92, 93, 94, 95, 1996
+ Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2,
+or (at your option) any later version.
+
+GAS is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public
+License along with GAS; see the file COPYING. If not, write
+to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "as.h"
+#ifdef BFD_ASSEMBLER
+#undef NO_RELOC
+#include "aout/aout64.h"
+#endif
+#include "obstack.h"
+
+#ifndef BFD_ASSEMBLER
+/* in: segT out: N_TYPE bits */
+const short seg_N_TYPE[] =
+{
+ N_ABS,
+ N_TEXT,
+ N_DATA,
+ N_BSS,
+ N_UNDF, /* unknown */
+ N_UNDF, /* error */
+ N_UNDF, /* expression */
+ N_UNDF, /* debug */
+ N_UNDF, /* ntv */
+ N_UNDF, /* ptv */
+ N_REGISTER, /* register */
+};
+
+const segT N_TYPE_seg[N_TYPE + 2] =
+{ /* N_TYPE == 0x1E = 32-2 */
+ SEG_UNKNOWN, /* N_UNDF == 0 */
+ SEG_GOOF,
+ SEG_ABSOLUTE, /* N_ABS == 2 */
+ SEG_GOOF,
+ SEG_TEXT, /* N_TEXT == 4 */
+ SEG_GOOF,
+ SEG_DATA, /* N_DATA == 6 */
+ SEG_GOOF,
+ SEG_BSS, /* N_BSS == 8 */
+ SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_REGISTER, /* dummy N_REGISTER for regs = 30 */
+ SEG_GOOF,
+};
+#endif
+
+static void obj_aout_line PARAMS ((int));
+static void obj_aout_weak PARAMS ((int));
+static void obj_aout_type PARAMS ((int));
+
+const pseudo_typeS obj_pseudo_table[] =
+{
+ {"line", obj_aout_line, 0}, /* source code line number */
+ {"ln", obj_aout_line, 0}, /* coff line number that we use anyway */
+
+ {"weak", obj_aout_weak, 0}, /* mark symbol as weak. */
+
+ {"type", obj_aout_type, 0},
+
+ /* coff debug pseudos (ignored) */
+ {"def", s_ignore, 0},
+ {"dim", s_ignore, 0},
+ {"endef", s_ignore, 0},
+ {"ident", s_ignore, 0},
+ {"line", s_ignore, 0},
+ {"ln", s_ignore, 0},
+ {"scl", s_ignore, 0},
+ {"size", s_ignore, 0},
+ {"tag", s_ignore, 0},
+ {"val", s_ignore, 0},
+ {"version", s_ignore, 0},
+
+ {"optim", s_ignore, 0}, /* For sun386i cc (?) */
+
+ /* other stuff */
+ {"ABORT", s_abort, 0},
+
+ {NULL} /* end sentinel */
+}; /* obj_pseudo_table */
+
+
+#ifdef BFD_ASSEMBLER
+
+void
+obj_aout_frob_symbol (sym, punt)
+ symbolS *sym;
+ int *punt;
+{
+ flagword flags;
+ asection *sec;
+ int desc, type, other;
+
+ flags = sym->bsym->flags;
+ desc = S_GET_DESC (sym);
+ type = S_GET_TYPE (sym);
+ other = S_GET_OTHER (sym);
+ sec = sym->bsym->section;
+
+ /* Only frob simple symbols this way right now. */
+ if (! (type & ~ (N_TYPE | N_EXT)))
+ {
+ if (type == (N_UNDF | N_EXT)
+ && sec == &bfd_abs_section)
+ sym->bsym->section = sec = bfd_und_section_ptr;
+
+ if ((type & N_TYPE) != N_INDR
+ && (type & N_TYPE) != N_SETA
+ && (type & N_TYPE) != N_SETT
+ && (type & N_TYPE) != N_SETD
+ && (type & N_TYPE) != N_SETB
+ && type != N_WARNING
+ && (sec == &bfd_abs_section
+ || sec == &bfd_und_section))
+ return;
+ if (flags & BSF_EXPORT)
+ type |= N_EXT;
+
+ switch (type & N_TYPE)
+ {
+ case N_SETA:
+ case N_SETT:
+ case N_SETD:
+ case N_SETB:
+ /* Set the debugging flag for constructor symbols so that
+ BFD leaves them alone. */
+ sym->bsym->flags |= BSF_DEBUGGING;
+
+ /* You can't put a common symbol in a set. The way a set
+ element works is that the symbol has a definition and a
+ name, and the linker adds the definition to the set of
+ that name. That does not work for a common symbol,
+ because the linker can't tell which common symbol the
+ user means. FIXME: Using as_bad here may be
+ inappropriate, since the user may want to force a
+ particular type without regard to the semantics of sets;
+ on the other hand, we certainly don't want anybody to be
+ mislead into thinking that their code will work. */
+ if (S_IS_COMMON (sym))
+ as_bad (_("Attempt to put a common symbol into set %s"),
+ S_GET_NAME (sym));
+ /* Similarly, you can't put an undefined symbol in a set. */
+ else if (! S_IS_DEFINED (sym))
+ as_bad (_("Attempt to put an undefined symbol into set %s"),
+ S_GET_NAME (sym));
+
+ break;
+ case N_INDR:
+ /* Put indirect symbols in the indirect section. */
+ sym->bsym->section = bfd_ind_section_ptr;
+ sym->bsym->flags |= BSF_INDIRECT;
+ if (type & N_EXT)
+ {
+ sym->bsym->flags |= BSF_EXPORT;
+ sym->bsym->flags &=~ BSF_LOCAL;
+ }
+ break;
+ case N_WARNING:
+ /* Mark warning symbols. */
+ sym->bsym->flags |= BSF_WARNING;
+ break;
+ }
+ }
+ else
+ {
+ sym->bsym->flags |= BSF_DEBUGGING;
+ }
+
+ S_SET_TYPE (sym, type);
+
+ /* Double check weak symbols. */
+ if (sym->bsym->flags & BSF_WEAK)
+ {
+ if (S_IS_COMMON (sym))
+ as_bad (_("Symbol `%s' can not be both weak and common"),
+ S_GET_NAME (sym));
+ }
+}
+
+void
+obj_aout_frob_file ()
+{
+ /* Relocation processing may require knowing the VMAs of the sections.
+ Since writing to a section will cause the BFD back end to compute the
+ VMAs, fake it out here.... */
+ bfd_byte b = 0;
+ boolean x = true;
+ if (bfd_section_size (stdoutput, text_section) != 0)
+ {
+ x = bfd_set_section_contents (stdoutput, text_section, &b, (file_ptr) 0,
+ (bfd_size_type) 1);
+ }
+ else if (bfd_section_size (stdoutput, data_section) != 0)
+ {
+ x = bfd_set_section_contents (stdoutput, data_section, &b, (file_ptr) 0,
+ (bfd_size_type) 1);
+ }
+ assert (x == true);
+}
+
+#else
+
+/* Relocation. */
+
+/*
+ * emit_relocations()
+ *
+ * Crawl along a fixS chain. Emit the segment's relocations.
+ */
+void
+obj_emit_relocations (where, fixP, segment_address_in_file)
+ char **where;
+ fixS *fixP; /* Fixup chain for this segment. */
+ relax_addressT segment_address_in_file;
+{
+ for (; fixP; fixP = fixP->fx_next)
+ if (fixP->fx_done == 0)
+ {
+ symbolS *sym;
+
+ sym = fixP->fx_addsy;
+ while (sym->sy_value.X_op == O_symbol
+ && (! S_IS_DEFINED (sym) || S_IS_COMMON (sym)))
+ sym = sym->sy_value.X_add_symbol;
+ fixP->fx_addsy = sym;
+
+ if (! sym->sy_resolved && ! S_IS_DEFINED (sym))
+ {
+ char *file;
+ unsigned int line;
+
+ if (expr_symbol_where (sym, &file, &line))
+ as_bad_where (file, line, _("unresolved relocation"));
+ else
+ as_bad (_("bad relocation: symbol `%s' not in symbol table"),
+ S_GET_NAME (sym));
+ }
+
+ tc_aout_fix_to_chars (*where, fixP, segment_address_in_file);
+ *where += md_reloc_size;
+ }
+}
+
+#ifndef obj_header_append
+/* Aout file generation & utilities */
+void
+obj_header_append (where, headers)
+ char **where;
+ object_headers *headers;
+{
+ tc_headers_hook (headers);
+
+#ifdef CROSS_COMPILE
+ md_number_to_chars (*where, headers->header.a_info, sizeof (headers->header.a_info));
+ *where += sizeof (headers->header.a_info);
+ md_number_to_chars (*where, headers->header.a_text, sizeof (headers->header.a_text));
+ *where += sizeof (headers->header.a_text);
+ md_number_to_chars (*where, headers->header.a_data, sizeof (headers->header.a_data));
+ *where += sizeof (headers->header.a_data);
+ md_number_to_chars (*where, headers->header.a_bss, sizeof (headers->header.a_bss));
+ *where += sizeof (headers->header.a_bss);
+ md_number_to_chars (*where, headers->header.a_syms, sizeof (headers->header.a_syms));
+ *where += sizeof (headers->header.a_syms);
+ md_number_to_chars (*where, headers->header.a_entry, sizeof (headers->header.a_entry));
+ *where += sizeof (headers->header.a_entry);
+ md_number_to_chars (*where, headers->header.a_trsize, sizeof (headers->header.a_trsize));
+ *where += sizeof (headers->header.a_trsize);
+ md_number_to_chars (*where, headers->header.a_drsize, sizeof (headers->header.a_drsize));
+ *where += sizeof (headers->header.a_drsize);
+
+#else /* CROSS_COMPILE */
+
+ append (where, (char *) &headers->header, sizeof (headers->header));
+#endif /* CROSS_COMPILE */
+
+}
+#endif
+
+void
+obj_symbol_to_chars (where, symbolP)
+ char **where;
+ symbolS *symbolP;
+{
+ md_number_to_chars ((char *) &(S_GET_OFFSET (symbolP)), S_GET_OFFSET (symbolP), sizeof (S_GET_OFFSET (symbolP)));
+ md_number_to_chars ((char *) &(S_GET_DESC (symbolP)), S_GET_DESC (symbolP), sizeof (S_GET_DESC (symbolP)));
+ md_number_to_chars ((char *) &(symbolP->sy_symbol.n_value), S_GET_VALUE (symbolP), sizeof (symbolP->sy_symbol.n_value));
+
+ append (where, (char *) &symbolP->sy_symbol, sizeof (obj_symbol_type));
+}
+
+void
+obj_emit_symbols (where, symbol_rootP)
+ char **where;
+ symbolS *symbol_rootP;
+{
+ symbolS *symbolP;
+
+ /* Emit all symbols left in the symbol chain. */
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next (symbolP))
+ {
+ /* Used to save the offset of the name. It is used to point
+ to the string in memory but must be a file offset. */
+ register char *temp;
+
+ temp = S_GET_NAME (symbolP);
+ S_SET_OFFSET (symbolP, symbolP->sy_name_offset);
+
+ /* Any symbol still undefined and is not a dbg symbol is made N_EXT. */
+ if (!S_IS_DEBUG (symbolP) && !S_IS_DEFINED (symbolP))
+ S_SET_EXTERNAL (symbolP);
+
+ /* Adjust the type of a weak symbol. */
+ if (S_GET_WEAK (symbolP))
+ {
+ switch (S_GET_TYPE (symbolP))
+ {
+ case N_UNDF: S_SET_TYPE (symbolP, N_WEAKU); break;
+ case N_ABS: S_SET_TYPE (symbolP, N_WEAKA); break;
+ case N_TEXT: S_SET_TYPE (symbolP, N_WEAKT); break;
+ case N_DATA: S_SET_TYPE (symbolP, N_WEAKD); break;
+ case N_BSS: S_SET_TYPE (symbolP, N_WEAKB); break;
+ default: as_bad (_("%s: bad type for weak symbol"), temp); break;
+ }
+ }
+
+ obj_symbol_to_chars (where, symbolP);
+ S_SET_NAME (symbolP, temp);
+ }
+}
+
+#endif /* ! BFD_ASSEMBLER */
+
+static void
+obj_aout_line (ignore)
+ int ignore;
+{
+ /* Assume delimiter is part of expression.
+ BSD4.2 as fails with delightful bug, so we
+ are not being incompatible here. */
+ new_logical_line ((char *) NULL, (int) (get_absolute_expression ()));
+ demand_empty_rest_of_line ();
+} /* obj_aout_line() */
+
+/* Handle .weak. This is a GNU extension. */
+
+static void
+obj_aout_weak (ignore)
+ int ignore;
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+
+ do
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+ S_SET_WEAK (symbolP);
+ if (c == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\n')
+ c = '\n';
+ }
+ }
+ while (c == ',');
+ demand_empty_rest_of_line ();
+}
+
+/* Handle .type. On {Net,Open}BSD, this is used to set the n_other field,
+ which is then apparently used when doing dynamic linking. Older
+ versions ogas ignored the .type pseudo-op, so we also ignore it if
+ we can't parse it. */
+
+static void
+obj_aout_type (ignore)
+ int ignore;
+{
+ char *name;
+ int c;
+ symbolS *sym;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ sym = symbol_find (name);
+ *input_line_pointer = c;
+ if (sym != NULL)
+ {
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '@')
+ {
+ ++input_line_pointer;
+ if (strncmp (input_line_pointer, "object", 6) == 0)
+ S_SET_OTHER (sym, 1);
+ else if (strncmp (input_line_pointer, "function", 8) == 0)
+ S_SET_OTHER (sym, 2);
+ }
+ }
+ }
+
+ /* Ignore everything else on the line. */
+ s_ignore (0);
+}
+
+void
+obj_read_begin_hook ()
+{
+}
+
+#ifndef BFD_ASSEMBLER
+
+void
+obj_crawl_symbol_chain (headers)
+ object_headers *headers;
+{
+ symbolS *symbolP;
+ symbolS **symbolPP;
+ int symbol_number = 0;
+
+ tc_crawl_symbol_chain (headers);
+
+ symbolPP = &symbol_rootP; /*->last symbol chain link. */
+ while ((symbolP = *symbolPP) != NULL)
+ {
+ if (symbolP->sy_mri_common)
+ {
+ if (S_IS_EXTERNAL (symbolP))
+ as_bad (_("%s: global symbols not supported in common sections"),
+ S_GET_NAME (symbolP));
+ *symbolPP = symbol_next (symbolP);
+ continue;
+ }
+
+ if (flag_readonly_data_in_text && (S_GET_SEGMENT (symbolP) == SEG_DATA))
+ {
+ S_SET_SEGMENT (symbolP, SEG_TEXT);
+ } /* if pusing data into text */
+
+ resolve_symbol_value (symbolP, 1);
+
+ /* Skip symbols which were equated to undefined or common
+ symbols. */
+ if (symbolP->sy_value.X_op == O_symbol
+ && (! S_IS_DEFINED (symbolP) || S_IS_COMMON (symbolP)))
+ {
+ *symbolPP = symbol_next (symbolP);
+ continue;
+ }
+
+ /* OK, here is how we decide which symbols go out into the brave
+ new symtab. Symbols that do are:
+
+ * symbols with no name (stabd's?)
+ * symbols with debug info in their N_TYPE
+
+ Symbols that don't are:
+ * symbols that are registers
+ * symbols with \1 as their 3rd character (numeric labels)
+ * "local labels" as defined by S_LOCAL_NAME(name) if the -L
+ switch was passed to gas.
+
+ All other symbols are output. We complain if a deleted
+ symbol was marked external. */
+
+
+ if (!S_IS_REGISTER (symbolP)
+ && (!S_GET_NAME (symbolP)
+ || S_IS_DEBUG (symbolP)
+ || !S_IS_DEFINED (symbolP)
+ || S_IS_EXTERNAL (symbolP)
+ || (S_GET_NAME (symbolP)[0] != '\001'
+ && (flag_keep_locals || !S_LOCAL_NAME (symbolP)))))
+ {
+ symbolP->sy_number = symbol_number++;
+
+ /* The + 1 after strlen account for the \0 at the
+ end of each string */
+ if (!S_IS_STABD (symbolP))
+ {
+ /* Ordinary case. */
+ symbolP->sy_name_offset = string_byte_count;
+ string_byte_count += strlen (S_GET_NAME (symbolP)) + 1;
+ }
+ else /* .Stabd case. */
+ symbolP->sy_name_offset = 0;
+ symbolPP = &(symbol_next (symbolP));
+ }
+ else
+ {
+ if (S_IS_EXTERNAL (symbolP) || !S_IS_DEFINED (symbolP))
+ /* This warning should never get triggered any more.
+ Well, maybe if you're doing twisted things with
+ register names... */
+ {
+ as_bad (_("Local symbol %s never defined."), decode_local_label_name (S_GET_NAME (symbolP)));
+ } /* oops. */
+
+ /* Unhook it from the chain */
+ *symbolPP = symbol_next (symbolP);
+ } /* if this symbol should be in the output */
+ } /* for each symbol */
+
+ H_SET_SYMBOL_TABLE_SIZE (headers, symbol_number);
+}
+
+/*
+ * Find strings by crawling along symbol table chain.
+ */
+
+void
+obj_emit_strings (where)
+ char **where;
+{
+ symbolS *symbolP;
+
+#ifdef CROSS_COMPILE
+ /* Gotta do md_ byte-ordering stuff for string_byte_count first - KWK */
+ md_number_to_chars (*where, string_byte_count, sizeof (string_byte_count));
+ *where += sizeof (string_byte_count);
+#else /* CROSS_COMPILE */
+ append (where, (char *) &string_byte_count, (unsigned long) sizeof (string_byte_count));
+#endif /* CROSS_COMPILE */
+
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next (symbolP))
+ {
+ if (S_GET_NAME (symbolP))
+ append (&next_object_file_charP, S_GET_NAME (symbolP),
+ (unsigned long) (strlen (S_GET_NAME (symbolP)) + 1));
+ } /* walk symbol chain */
+}
+
+#ifndef AOUT_VERSION
+#define AOUT_VERSION 0
+#endif
+
+void
+obj_pre_write_hook (headers)
+ object_headers *headers;
+{
+ H_SET_DYNAMIC (headers, 0);
+ H_SET_VERSION (headers, AOUT_VERSION);
+ H_SET_MACHTYPE (headers, AOUT_MACHTYPE);
+ tc_aout_pre_write_hook (headers);
+}
+
+void
+DEFUN_VOID (s_sect)
+{
+ /* Strip out the section name */
+ char *section_name;
+ char *section_name_end;
+ char c;
+
+ unsigned int len;
+ unsigned int exp;
+ char *save;
+
+ section_name = input_line_pointer;
+ c = get_symbol_end ();
+ section_name_end = input_line_pointer;
+
+ len = section_name_end - section_name;
+ input_line_pointer++;
+ save = input_line_pointer;
+
+ SKIP_WHITESPACE ();
+ if (c == ',')
+ {
+ exp = get_absolute_expression ();
+ }
+ else if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ exp = get_absolute_expression ();
+ }
+ else
+ {
+ input_line_pointer = save;
+ exp = 0;
+ }
+ if (exp >= 1000)
+ {
+ as_bad (_("subsegment index too high"));
+ }
+
+ if (strcmp (section_name, ".text") == 0)
+ {
+ subseg_set (SEG_TEXT, (subsegT) exp);
+ }
+
+ if (strcmp (section_name, ".data") == 0)
+ {
+ if (flag_readonly_data_in_text)
+ subseg_set (SEG_TEXT, (subsegT) exp + 1000);
+ else
+ subseg_set (SEG_DATA, (subsegT) exp);
+ }
+
+ *section_name_end = c;
+}
+
+#endif /* ! BFD_ASSEMBLER */
+
+/* end of obj-aout.c */
diff --git a/gas/config/obj-aout.h b/gas/config/obj-aout.h
new file mode 100644
index 0000000..339070e
--- /dev/null
+++ b/gas/config/obj-aout.h
@@ -0,0 +1,239 @@
+/* obj-aout.h, a.out object file format for gas, the assembler.
+ Copyright (C) 1989, 90, 91, 92, 93, 94, 95, 96, 1998
+ Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+/* Tag to validate a.out object file format processing */
+#define OBJ_AOUT 1
+
+#include "targ-cpu.h"
+
+#ifdef BFD_ASSEMBLER
+
+#include "bfd/libaout.h"
+
+#define OUTPUT_FLAVOR bfd_target_aout_flavour
+
+#else /* ! BFD_ASSEMBLER */
+
+#ifndef VMS
+#include "aout_gnu.h" /* Needed to define struct nlist. Sigh. */
+#else
+#include "a_out.h"
+#endif
+
+#ifndef AOUT_MACHTYPE
+#define AOUT_MACHTYPE 0
+#endif /* AOUT_MACHTYPE */
+
+extern const short seg_N_TYPE[];
+extern const segT N_TYPE_seg[];
+
+#ifndef DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE
+#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE (OMAGIC)
+#endif /* DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE */
+
+#endif /* ! BFD_ASSEMBLER */
+
+/* SYMBOL TABLE */
+/* Symbol table entry data type */
+
+typedef struct nlist obj_symbol_type; /* Symbol table entry */
+
+/* Symbol table macros and constants */
+
+#ifdef BFD_ASSEMBLER
+
+#define S_SET_OTHER(S,V) (aout_symbol((S)->bsym)->other = (V))
+#define S_SET_TYPE(S,T) (aout_symbol((S)->bsym)->type = (T))
+#define S_SET_DESC(S,D) (aout_symbol((S)->bsym)->desc = (D))
+#define S_GET_OTHER(S) (aout_symbol((S)->bsym)->other)
+#define S_GET_TYPE(S) (aout_symbol((S)->bsym)->type)
+#define S_GET_DESC(S) (aout_symbol((S)->bsym)->desc)
+
+asection *text_section, *data_section, *bss_section;
+
+#define obj_frob_symbol(S,PUNT) obj_aout_frob_symbol (S, &PUNT)
+#define obj_frob_file() obj_aout_frob_file ()
+extern void obj_aout_frob_symbol PARAMS ((struct symbol *, int *));
+extern void obj_aout_frob_file PARAMS ((void));
+
+#define obj_sec_sym_ok_for_reloc(SEC) (1)
+
+#else
+
+/* We use the sy_obj field to record whether a symbol is weak. */
+#define OBJ_SYMFIELD_TYPE char
+
+/*
+ * Macros to extract information from a symbol table entry.
+ * This syntaxic indirection allows independence regarding a.out or coff.
+ * The argument (s) of all these macros is a pointer to a symbol table entry.
+ */
+
+/* True if the symbol is external */
+#define S_IS_EXTERNAL(s) ((s)->sy_symbol.n_type & N_EXT)
+
+/* True if symbol has been defined, ie is in N_{TEXT,DATA,BSS,ABS} or N_EXT */
+#define S_IS_DEFINED(s) \
+ (S_GET_TYPE (s) != N_UNDF || S_GET_DESC (s) != 0)
+
+#define S_IS_COMMON(s) \
+ (S_GET_TYPE (s) == N_UNDF && S_GET_VALUE (s) != 0)
+
+#define S_IS_REGISTER(s) ((s)->sy_symbol.n_type == N_REGISTER)
+
+/* True if a debug special symbol entry */
+#define S_IS_DEBUG(s) ((s)->sy_symbol.n_type & N_STAB)
+/* True if a symbol is local symbol name */
+#define S_IS_LOCAL(s) \
+ ((S_GET_NAME (s) \
+ && !S_IS_DEBUG (s) \
+ && (strchr (S_GET_NAME (s), '\001') != NULL \
+ || strchr (S_GET_NAME (s), '\002') != NULL \
+ || (S_LOCAL_NAME(s) && !flag_keep_locals))) \
+ || (flag_strip_local_absolute \
+ && ! S_IS_EXTERNAL(s) \
+ && S_GET_SEGMENT (s) == absolute_section))
+/* True if a symbol is not defined in this file */
+#define S_IS_EXTERN(s) ((s)->sy_symbol.n_type & N_EXT)
+/* True if the symbol has been generated because of a .stabd directive */
+#define S_IS_STABD(s) (S_GET_NAME(s) == (char *)0)
+
+/* Accessors */
+/* The name of the symbol */
+#define S_GET_NAME(s) ((s)->sy_symbol.n_un.n_name)
+/* The pointer to the string table */
+#define S_GET_OFFSET(s) ((s)->sy_symbol.n_un.n_strx)
+/* The type of the symbol */
+#define S_GET_TYPE(s) ((s)->sy_symbol.n_type & N_TYPE)
+/* The numeric value of the segment */
+#define S_GET_SEGMENT(s) (N_TYPE_seg[S_GET_TYPE(s)])
+/* The n_other expression value */
+#define S_GET_OTHER(s) ((s)->sy_symbol.n_other)
+/* The n_desc expression value */
+#define S_GET_DESC(s) ((s)->sy_symbol.n_desc)
+/* Whether the symbol is weak. */
+#define S_GET_WEAK(s) ((s)->sy_obj)
+
+/* Modifiers */
+/* Assume that a symbol cannot be simultaneously in more than on segment */
+/* set segment */
+#define S_SET_SEGMENT(s,seg) ((s)->sy_symbol.n_type &= ~N_TYPE,(s)->sy_symbol.n_type|=SEGMENT_TO_SYMBOL_TYPE(seg))
+/* The symbol is external */
+#define S_SET_EXTERNAL(s) ((s)->sy_symbol.n_type |= N_EXT)
+/* The symbol is not external */
+#define S_CLEAR_EXTERNAL(s) ((s)->sy_symbol.n_type &= ~N_EXT)
+/* Set the name of the symbol */
+#define S_SET_NAME(s,v) ((s)->sy_symbol.n_un.n_name = (v))
+/* Set the offset in the string table */
+#define S_SET_OFFSET(s,v) ((s)->sy_symbol.n_un.n_strx = (v))
+/* Set the n_type field */
+#define S_SET_TYPE(s,t) ((s)->sy_symbol.n_type = (t))
+/* Set the n_other expression value */
+#define S_SET_OTHER(s,v) ((s)->sy_symbol.n_other = (v))
+/* Set the n_desc expression value */
+#define S_SET_DESC(s,v) ((s)->sy_symbol.n_desc = (v))
+/* Mark the symbol as weak. This causes n_type to be adjusted when
+ the symbol is written out. */
+#define S_SET_WEAK(s) ((s)->sy_obj = 1)
+
+/* File header macro and type definition */
+
+#define H_GET_FILE_SIZE(h) (H_GET_HEADER_SIZE(h) \
+ + H_GET_TEXT_SIZE(h) \
+ + H_GET_DATA_SIZE(h) \
+ + H_GET_SYMBOL_TABLE_SIZE(h) \
+ + H_GET_TEXT_RELOCATION_SIZE(h) \
+ + H_GET_DATA_RELOCATION_SIZE(h) \
+ + H_GET_STRING_SIZE(h))
+
+#define H_GET_HEADER_SIZE(h) (EXEC_BYTES_SIZE)
+#define H_GET_TEXT_SIZE(h) ((h)->header.a_text)
+#define H_GET_DATA_SIZE(h) ((h)->header.a_data)
+#define H_GET_BSS_SIZE(h) ((h)->header.a_bss)
+#define H_GET_TEXT_RELOCATION_SIZE(h) ((h)->header.a_trsize)
+#define H_GET_DATA_RELOCATION_SIZE(h) ((h)->header.a_drsize)
+#define H_GET_SYMBOL_TABLE_SIZE(h) ((h)->header.a_syms)
+#define H_GET_ENTRY_POINT(h) ((h)->header.a_entry)
+#define H_GET_STRING_SIZE(h) ((h)->string_table_size)
+#define H_GET_LINENO_SIZE(h) (0)
+
+#define H_GET_DYNAMIC(h) ((h)->header.a_info >> 31)
+#define H_GET_VERSION(h) (((h)->header.a_info >> 24) & 0x7f)
+#define H_GET_MACHTYPE(h) (((h)->header.a_info >> 16) & 0xff)
+#define H_GET_MAGIC_NUMBER(h) ((h)->header.a_info & 0xffff)
+
+#define H_SET_DYNAMIC(h,v) ((h)->header.a_info = (((v) << 31) \
+ | (H_GET_VERSION(h) << 24) \
+ | (H_GET_MACHTYPE(h) << 16) \
+ | (H_GET_MAGIC_NUMBER(h))))
+
+#define H_SET_VERSION(h,v) ((h)->header.a_info = ((H_GET_DYNAMIC(h) << 31) \
+ | ((v) << 24) \
+ | (H_GET_MACHTYPE(h) << 16) \
+ | (H_GET_MAGIC_NUMBER(h))))
+
+#define H_SET_MACHTYPE(h,v) ((h)->header.a_info = ((H_GET_DYNAMIC(h) << 31) \
+ | (H_GET_VERSION(h) << 24) \
+ | ((v) << 16) \
+ | (H_GET_MAGIC_NUMBER(h))))
+
+#define H_SET_MAGIC_NUMBER(h,v) ((h)->header.a_info = ((H_GET_DYNAMIC(h) << 31) \
+ | (H_GET_VERSION(h) << 24) \
+ | (H_GET_MACHTYPE(h) << 16) \
+ | ((v))))
+
+#define H_SET_TEXT_SIZE(h,v) ((h)->header.a_text = md_section_align(SEG_TEXT, (v)))
+#define H_SET_DATA_SIZE(h,v) ((h)->header.a_data = md_section_align(SEG_DATA, (v)))
+#define H_SET_BSS_SIZE(h,v) ((h)->header.a_bss = md_section_align(SEG_BSS, (v)))
+
+#define H_SET_RELOCATION_SIZE(h,t,d) (H_SET_TEXT_RELOCATION_SIZE((h),(t)),\
+ H_SET_DATA_RELOCATION_SIZE((h),(d)))
+
+#define H_SET_TEXT_RELOCATION_SIZE(h,v) ((h)->header.a_trsize = (v))
+#define H_SET_DATA_RELOCATION_SIZE(h,v) ((h)->header.a_drsize = (v))
+#define H_SET_SYMBOL_TABLE_SIZE(h,v) ((h)->header.a_syms = (v) * 12)
+
+#define H_SET_ENTRY_POINT(h,v) ((h)->header.a_entry = (v))
+#define H_SET_STRING_SIZE(h,v) ((h)->string_table_size = (v))
+
+typedef struct
+ {
+ struct exec header; /* a.out header */
+ long string_table_size; /* names + '\0' + sizeof(int) */
+ }
+
+object_headers;
+
+/* line numbering stuff. */
+#define OBJ_EMIT_LINENO(a, b, c) {;}
+
+struct fix;
+void tc_aout_fix_to_chars PARAMS ((char *where, struct fix *fixP, relax_addressT segment_address));
+
+#endif
+
+#define obj_symbol_new_hook(s) {;}
+
+#define EMIT_SECTION_SYMBOLS 0
+
+#define AOUT_STABS
+
+/* end of obj-aout.h */
diff --git a/gas/config/obj-bout.c b/gas/config/obj-bout.c
new file mode 100644
index 0000000..c9b80f5
--- /dev/null
+++ b/gas/config/obj-bout.c
@@ -0,0 +1,348 @@
+/* b.out object file format
+ Copyright (C) 1989, 90, 91, 92, 93, 94, 95, 1996
+ Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with GAS; see the file COPYING. If not, write
+ to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "as.h"
+#include "obstack.h"
+const short /* in: segT out: N_TYPE bits */
+ seg_N_TYPE[] =
+{
+ N_ABS,
+ N_TEXT,
+ N_DATA,
+ N_BSS,
+ N_UNDF, /* unknown */
+ N_UNDF, /* error */
+ N_UNDF, /* expression */
+ N_UNDF, /* debug */
+ N_UNDF, /* ntv */
+ N_UNDF, /* ptv */
+ N_REGISTER, /* register */
+};
+
+const segT N_TYPE_seg[N_TYPE + 2] =
+{ /* N_TYPE == 0x1E = 32-2 */
+ SEG_UNKNOWN, /* N_UNDF == 0 */
+ SEG_GOOF,
+ SEG_ABSOLUTE, /* N_ABS == 2 */
+ SEG_GOOF,
+ SEG_TEXT, /* N_TEXT == 4 */
+ SEG_GOOF,
+ SEG_DATA, /* N_DATA == 6 */
+ SEG_GOOF,
+ SEG_BSS, /* N_BSS == 8 */
+ SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_REGISTER, /* dummy N_REGISTER for regs = 30 */
+ SEG_GOOF,
+};
+
+static void obj_bout_line PARAMS ((int));
+
+const pseudo_typeS obj_pseudo_table[] =
+{
+ {"line", obj_bout_line, 0}, /* source code line number */
+
+/* coff debugging directives. Currently ignored silently */
+ {"def", s_ignore, 0},
+ {"dim", s_ignore, 0},
+ {"endef", s_ignore, 0},
+ {"ln", s_ignore, 0},
+ {"scl", s_ignore, 0},
+ {"size", s_ignore, 0},
+ {"tag", s_ignore, 0},
+ {"type", s_ignore, 0},
+ {"val", s_ignore, 0},
+
+/* other stuff we don't handle */
+ {"ABORT", s_ignore, 0},
+ {"ident", s_ignore, 0},
+
+ {NULL} /* end sentinel */
+}; /* obj_pseudo_table */
+
+/* Relocation. */
+
+/*
+ * emit_relocations()
+ *
+ * Crawl along a fixS chain. Emit the segment's relocations.
+ */
+void
+obj_emit_relocations (where, fixP, segment_address_in_file)
+ char **where;
+ fixS *fixP; /* Fixup chain for this segment. */
+ relax_addressT segment_address_in_file;
+{
+ for (; fixP; fixP = fixP->fx_next)
+ {
+ if (fixP->fx_done == 0
+ || fixP->fx_r_type != NO_RELOC)
+ {
+ symbolS *sym;
+
+ sym = fixP->fx_addsy;
+ while (sym->sy_value.X_op == O_symbol
+ && (! S_IS_DEFINED (sym) || S_IS_COMMON (sym)))
+ sym = sym->sy_value.X_add_symbol;
+ fixP->fx_addsy = sym;
+
+ tc_bout_fix_to_chars (*where, fixP, segment_address_in_file);
+ *where += sizeof (struct relocation_info);
+ } /* if there's a symbol */
+ } /* for each fixup */
+
+} /* emit_relocations() */
+
+/* Aout file generation & utilities */
+
+/* Convert a lvalue to machine dependent data */
+void
+obj_header_append (where, headers)
+ char **where;
+ object_headers *headers;
+{
+ /* Always leave in host byte order */
+
+ headers->header.a_talign = section_alignment[SEG_TEXT];
+
+ if (headers->header.a_talign < 2)
+ {
+ headers->header.a_talign = 2;
+ } /* force to at least 2 */
+
+ headers->header.a_dalign = section_alignment[SEG_DATA];
+ headers->header.a_balign = section_alignment[SEG_BSS];
+
+ headers->header.a_tload = 0;
+ headers->header.a_dload = md_section_align (SEG_DATA, H_GET_TEXT_SIZE (headers));
+
+ headers->header.a_relaxable = linkrelax;
+
+#ifdef CROSS_COMPILE
+ md_number_to_chars (*where, headers->header.a_magic, sizeof (headers->header.a_magic));
+ *where += sizeof (headers->header.a_magic);
+ md_number_to_chars (*where, headers->header.a_text, sizeof (headers->header.a_text));
+ *where += sizeof (headers->header.a_text);
+ md_number_to_chars (*where, headers->header.a_data, sizeof (headers->header.a_data));
+ *where += sizeof (headers->header.a_data);
+ md_number_to_chars (*where, headers->header.a_bss, sizeof (headers->header.a_bss));
+ *where += sizeof (headers->header.a_bss);
+ md_number_to_chars (*where, headers->header.a_syms, sizeof (headers->header.a_syms));
+ *where += sizeof (headers->header.a_syms);
+ md_number_to_chars (*where, headers->header.a_entry, sizeof (headers->header.a_entry));
+ *where += sizeof (headers->header.a_entry);
+ md_number_to_chars (*where, headers->header.a_trsize, sizeof (headers->header.a_trsize));
+ *where += sizeof (headers->header.a_trsize);
+ md_number_to_chars (*where, headers->header.a_drsize, sizeof (headers->header.a_drsize));
+ *where += sizeof (headers->header.a_drsize);
+ md_number_to_chars (*where, headers->header.a_tload, sizeof (headers->header.a_tload));
+ *where += sizeof (headers->header.a_tload);
+ md_number_to_chars (*where, headers->header.a_dload, sizeof (headers->header.a_dload));
+ *where += sizeof (headers->header.a_dload);
+ md_number_to_chars (*where, headers->header.a_talign, sizeof (headers->header.a_talign));
+ *where += sizeof (headers->header.a_talign);
+ md_number_to_chars (*where, headers->header.a_dalign, sizeof (headers->header.a_dalign));
+ *where += sizeof (headers->header.a_dalign);
+ md_number_to_chars (*where, headers->header.a_balign, sizeof (headers->header.a_balign));
+ *where += sizeof (headers->header.a_balign);
+ md_number_to_chars (*where, headers->header.a_relaxable, sizeof (headers->header.a_relaxable));
+ *where += sizeof (headers->header.a_relaxable);
+#else /* ! CROSS_COMPILE */
+ append (where, (char *) &headers->header, sizeof (headers->header));
+#endif /* ! CROSS_COMPILE */
+} /* a_header_append() */
+
+void
+obj_symbol_to_chars (where, symbolP)
+ char **where;
+ symbolS *symbolP;
+{
+ md_number_to_chars ((char *) &(S_GET_OFFSET (symbolP)), S_GET_OFFSET (symbolP), sizeof (S_GET_OFFSET (symbolP)));
+ md_number_to_chars ((char *) &(S_GET_DESC (symbolP)), S_GET_DESC (symbolP), sizeof (S_GET_DESC (symbolP)));
+ md_number_to_chars ((char *) &symbolP->sy_symbol.n_value, S_GET_VALUE (symbolP), sizeof (symbolP->sy_symbol.n_value));
+
+ append (where, (char *) &symbolP->sy_symbol, sizeof (obj_symbol_type));
+} /* obj_symbol_to_chars() */
+
+void
+obj_emit_symbols (where, symbol_rootP)
+ char **where;
+ symbolS *symbol_rootP;
+{
+ symbolS *symbolP;
+
+ /*
+ * Emit all symbols left in the symbol chain.
+ */
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next (symbolP))
+ {
+ /* Used to save the offset of the name. It is used to point
+ to the string in memory but must be a file offset. */
+ char *temp;
+
+ temp = S_GET_NAME (symbolP);
+ S_SET_OFFSET (symbolP, symbolP->sy_name_offset);
+
+ /* Any symbol still undefined and is not a dbg symbol is made N_EXT. */
+ if (!S_IS_DEBUG (symbolP) && !S_IS_DEFINED (symbolP))
+ S_SET_EXTERNAL (symbolP);
+
+ obj_symbol_to_chars (where, symbolP);
+ S_SET_NAME (symbolP, temp);
+ }
+} /* emit_symbols() */
+
+void
+obj_symbol_new_hook (symbolP)
+ symbolS *symbolP;
+{
+ S_SET_OTHER (symbolP, 0);
+ S_SET_DESC (symbolP, 0);
+}
+
+static void
+obj_bout_line (ignore)
+ int ignore;
+{
+ /* Assume delimiter is part of expression. */
+ /* BSD4.2 as fails with delightful bug, so we */
+ /* are not being incompatible here. */
+ new_logical_line ((char *) NULL, (int) (get_absolute_expression ()));
+ demand_empty_rest_of_line ();
+} /* obj_bout_line() */
+
+void
+obj_read_begin_hook ()
+{
+}
+
+void
+obj_crawl_symbol_chain (headers)
+ object_headers *headers;
+{
+ symbolS **symbolPP;
+ symbolS *symbolP;
+ int symbol_number = 0;
+
+ tc_crawl_symbol_chain (headers);
+
+ symbolPP = &symbol_rootP; /*->last symbol chain link. */
+ while ((symbolP = *symbolPP) != NULL)
+ {
+ if (flag_readonly_data_in_text && (S_GET_SEGMENT (symbolP) == SEG_DATA))
+ {
+ S_SET_SEGMENT (symbolP, SEG_TEXT);
+ } /* if pusing data into text */
+
+ resolve_symbol_value (symbolP, 1);
+
+ /* Skip symbols which were equated to undefined or common
+ symbols. */
+ if (symbolP->sy_value.X_op == O_symbol
+ && (! S_IS_DEFINED (symbolP) || S_IS_COMMON (symbolP)))
+ {
+ *symbolPP = symbol_next (symbolP);
+ continue;
+ }
+
+ /* OK, here is how we decide which symbols go out into the
+ brave new symtab. Symbols that do are:
+
+ * symbols with no name (stabd's?)
+ * symbols with debug info in their N_TYPE
+
+ Symbols that don't are:
+ * symbols that are registers
+ * symbols with \1 as their 3rd character (numeric labels)
+ * "local labels" as defined by S_LOCAL_NAME(name)
+ if the -L switch was passed to gas.
+
+ All other symbols are output. We complain if a deleted
+ symbol was marked external. */
+
+
+ if (1
+ && !S_IS_REGISTER (symbolP)
+ && (!S_GET_NAME (symbolP)
+ || S_IS_DEBUG (symbolP)
+#ifdef TC_I960
+ /* FIXME-SOON this ifdef seems highly dubious to me. xoxorich. */
+ || !S_IS_DEFINED (symbolP)
+ || S_IS_EXTERNAL (symbolP)
+#endif /* TC_I960 */
+ || (S_GET_NAME (symbolP)[0] != '\001' && (flag_keep_locals || !S_LOCAL_NAME (symbolP)))))
+ {
+ symbolP->sy_number = symbol_number++;
+
+ /* The + 1 after strlen account for the \0 at the
+ end of each string */
+ if (!S_IS_STABD (symbolP))
+ {
+ /* Ordinary case. */
+ symbolP->sy_name_offset = string_byte_count;
+ string_byte_count += strlen (S_GET_NAME (symbolP)) + 1;
+ }
+ else /* .Stabd case. */
+ symbolP->sy_name_offset = 0;
+ symbolPP = &(symbol_next (symbolP));
+ }
+ else
+ {
+ if (S_IS_EXTERNAL (symbolP) || !S_IS_DEFINED (symbolP))
+ {
+ as_bad (_("Local symbol %s never defined"), S_GET_NAME (symbolP));
+ } /* oops. */
+
+ /* Unhook it from the chain */
+ *symbolPP = symbol_next (symbolP);
+ } /* if this symbol should be in the output */
+ } /* for each symbol */
+
+ H_SET_SYMBOL_TABLE_SIZE (headers, symbol_number);
+}
+
+/*
+ * Find strings by crawling along symbol table chain.
+ */
+
+void
+obj_emit_strings (where)
+ char **where;
+{
+ symbolS *symbolP;
+
+#ifdef CROSS_COMPILE
+ /* Gotta do md_ byte-ordering stuff for string_byte_count first - KWK */
+ md_number_to_chars (*where, string_byte_count, sizeof (string_byte_count));
+ *where += sizeof (string_byte_count);
+#else /* CROSS_COMPILE */
+ append (where, (char *) &string_byte_count, (unsigned long) sizeof (string_byte_count));
+#endif /* CROSS_COMPILE */
+
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next (symbolP))
+ {
+ if (S_GET_NAME (symbolP))
+ append (where, S_GET_NAME (symbolP), (unsigned long) (strlen (S_GET_NAME (symbolP)) + 1));
+ } /* walk symbol chain */
+}
+
+/* end of obj-bout.c */
diff --git a/gas/config/obj-bout.h b/gas/config/obj-bout.h
new file mode 100644
index 0000000..ec539a0
--- /dev/null
+++ b/gas/config/obj-bout.h
@@ -0,0 +1,316 @@
+/* b.out object file format
+ Copyright (C) 1989, 90, 91, 92, 93, 94, 95, 1996
+ Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with GAS; see the file COPYING. If not, write
+ to the Free Software Foundation, 59 Temple Place - Suite 330, Cambridge, MA
+ 02139, USA. */
+
+/*
+ * This file is a modified version of 'a.out.h'. It is to be used in all GNU
+ * tools modified to support the i80960 b.out format (or tools that operate on
+ * object files created by such tools).
+ *
+ * All i80960 development is done in a CROSS-DEVELOPMENT environment. I.e.,
+ * object code is generated on, and executed under the direction of a symbolic
+ * debugger running on, a host system. We do not want to be subject to the
+ * vagaries of which host it is or whether it supports COFF or a.out format, or
+ * anything else. We DO want to:
+ *
+ * o always generate the same format object files, regardless of host.
+ *
+ * o have an 'a.out' header that we can modify for our own purposes
+ * (the 80960 is typically an embedded processor and may require
+ * enhanced linker support that the normal a.out.h header can't
+ * accommodate).
+ *
+ * As for byte-ordering, the following rules apply:
+ *
+ * o Text and data that is actually downloaded to the target is always
+ * in i80960 (little-endian) order.
+ *
+ * o All other numbers (in the header, symbols, relocation directives)
+ * are in host byte-order: object files CANNOT be lifted from a
+ * little-end host and used on a big-endian (or vice versa) without
+ * modification.
+ * ==> THIS IS NO LONGER TRUE USING BFD. WE CAN GENERATE ANY BYTE ORDER
+ * FOR THE HEADER, AND READ ANY BYTE ORDER. PREFERENCE WOULD BE TO
+ * USE LITTLE-ENDIAN BYTE ORDER THROUGHOUT, REGARDLESS OF HOST. <==
+ *
+ * o The downloader ('comm960') takes care to generate a pseudo-header
+ * with correct (i80960) byte-ordering before shipping text and data
+ * off to the NINDY monitor in the target systems. Symbols and
+ * relocation info are never sent to the target.
+ */
+
+
+#define OBJ_BOUT 1
+
+#define OUTPUT_FLAVOR bfd_target_aout_flavour
+
+#include "targ-cpu.h"
+
+#define OBJ_DEFAULT_OUTPUT_FILE_NAME "b.out"
+
+extern const short seg_N_TYPE[];
+extern const segT N_TYPE_seg[];
+
+#define BMAGIC 0415
+/* We don't accept the following (see N_BADMAG macro).
+ * They're just here so GNU code will compile.
+ */
+#define OMAGIC 0407 /* old impure format */
+#define NMAGIC 0410 /* read-only text */
+#define ZMAGIC 0413 /* demand load format */
+
+#ifndef DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE
+#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE (BMAGIC)
+#endif /* DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE */
+
+/* FILE HEADER
+ * All 'lengths' are given as a number of bytes.
+ * All 'alignments' are for relinkable files only; an alignment of
+ * 'n' indicates the corresponding segment must begin at an
+ * address that is a multiple of (2**n).
+ */
+struct exec
+ {
+ /* Standard stuff */
+ unsigned long a_magic; /* Identifies this as a b.out file */
+ unsigned long a_text; /* Length of text */
+ unsigned long a_data; /* Length of data */
+ unsigned long a_bss; /* Length of runtime uninitialized data area */
+ unsigned long a_syms; /* Length of symbol table */
+ unsigned long a_entry; /* Runtime start address */
+ unsigned long a_trsize; /* Length of text relocation info */
+ unsigned long a_drsize; /* Length of data relocation info */
+
+ /* Added for i960 */
+ unsigned long a_tload; /* Text runtime load address */
+ unsigned long a_dload; /* Data runtime load address */
+ unsigned char a_talign; /* Alignment of text segment */
+ unsigned char a_dalign; /* Alignment of data segment */
+ unsigned char a_balign; /* Alignment of bss segment */
+ unsigned char a_relaxable; /* Contains enough info to relax */
+ };
+
+#define N_BADMAG(x) (((x).a_magic)!=BMAGIC)
+#define N_TXTOFF(x) ( sizeof(struct exec) )
+#define N_DATOFF(x) ( N_TXTOFF(x) + (x).a_text )
+#define N_TROFF(x) ( N_DATOFF(x) + (x).a_data )
+#define N_DROFF(x) ( N_TROFF(x) + (x).a_trsize )
+#define N_SYMOFF(x) ( N_DROFF(x) + (x).a_drsize )
+#define N_STROFF(x) ( N_SYMOFF(x) + (x).a_syms )
+
+/* A single entry in the symbol table
+ */
+struct nlist
+ {
+ union
+ {
+ char *n_name;
+ struct nlist *n_next;
+ long n_strx; /* Index into string table */
+ }
+ n_un;
+ unsigned char n_type; /* See below */
+ char n_other; /* Used in i80960 support -- see below */
+ short n_desc;
+ unsigned long n_value;
+ };
+
+typedef struct nlist obj_symbol_type;
+
+/* Legal values of n_type
+ */
+#define N_UNDF 0 /* Undefined symbol */
+#define N_ABS 2 /* Absolute symbol */
+#define N_TEXT 4 /* Text symbol */
+#define N_DATA 6 /* Data symbol */
+#define N_BSS 8 /* BSS symbol */
+#define N_FN 31 /* Filename symbol */
+
+#define N_EXT 1 /* External symbol (OR'd in with one of above) */
+#define N_TYPE 036 /* Mask for all the type bits */
+#define N_STAB 0340 /* Mask for all bits used for SDB entries */
+
+#ifndef CUSTOM_RELOC_FORMAT
+struct relocation_info
+ {
+ int r_address; /* File address of item to be relocated */
+ unsigned
+ r_index:24, /* Index of symbol on which relocation is based*/
+ r_pcrel:1, /* 1 => relocate PC-relative; else absolute
+ * On i960, pc-relative implies 24-bit
+ * address, absolute implies 32-bit.
+ */
+ r_length:2, /* Number of bytes to relocate:
+ * 0 => 1 byte
+ * 1 => 2 bytes
+ * 2 => 4 bytes -- only value used for i960
+ */
+ r_extern:1, r_bsr:1, /* Something for the GNU NS32K assembler */
+ r_disp:1, /* Something for the GNU NS32K assembler */
+ r_callj:1, /* 1 if relocation target is an i960 'callj' */
+ nuthin:1; /* Unused */
+ };
+
+#endif /* CUSTOM_RELOC_FORMAT */
+
+/*
+ * Macros to extract information from a symbol table entry.
+ * This syntaxic indirection allows independence regarding a.out or coff.
+ * The argument (s) of all these macros is a pointer to a symbol table entry.
+ */
+
+/* Predicates */
+/* True if the symbol is external */
+#define S_IS_EXTERNAL(s) ((s)->sy_symbol.n_type & N_EXT)
+
+/* True if symbol has been defined, ie is in N_{TEXT,DATA,BSS,ABS} or N_EXT */
+#define S_IS_DEFINED(s) ((S_GET_TYPE(s) != N_UNDF) || (S_GET_DESC(s) != 0))
+
+#define S_IS_COMMON(s) \
+ (S_GET_TYPE (s) == N_UNDF && S_GET_VALUE (s) != 0)
+
+#define S_IS_REGISTER(s) ((s)->sy_symbol.n_type == N_REGISTER)
+
+/* True if a debug special symbol entry */
+#define S_IS_DEBUG(s) ((s)->sy_symbol.n_type & N_STAB)
+/* True if a symbol is local symbol name */
+#define S_IS_LOCAL(s) \
+ ((S_GET_NAME (s) \
+ && !S_IS_DEBUG (s) \
+ && (strchr (S_GET_NAME (s), '\001') != NULL \
+ || strchr (S_GET_NAME (s), '\002') != NULL \
+ || (S_LOCAL_NAME(s) && !flag_keep_locals))) \
+ || (flag_strip_local_absolute \
+ && !S_IS_EXTERNAL(s) \
+ && S_GET_SEGMENT(s) == absolute_section))
+/* True if a symbol is not defined in this file */
+#define S_IS_EXTERN(s) ((s)->sy_symbol.n_type & N_EXT)
+/* True if the symbol has been generated because of a .stabd directive */
+#define S_IS_STABD(s) (S_GET_NAME(s) == NULL)
+
+/* Accessors */
+/* The name of the symbol */
+#define S_GET_NAME(s) ((s)->sy_symbol.n_un.n_name)
+/* The pointer to the string table */
+#define S_GET_OFFSET(s) ((s)->sy_symbol.n_un.n_strx)
+/* The type of the symbol */
+#define S_GET_TYPE(s) ((s)->sy_symbol.n_type & N_TYPE)
+/* The numeric value of the segment */
+#define S_GET_SEGMENT(s) (N_TYPE_seg[S_GET_TYPE(s)])
+/* The n_other expression value */
+#define S_GET_OTHER(s) ((s)->sy_symbol.n_other)
+/* The n_desc expression value */
+#define S_GET_DESC(s) ((s)->sy_symbol.n_desc)
+
+/* Modifiers */
+/* Assume that a symbol cannot be simultaneously in more than on segment */
+/* set segment */
+#define S_SET_SEGMENT(s,seg) ((s)->sy_symbol.n_type &= ~N_TYPE,(s)->sy_symbol.n_type|=SEGMENT_TO_SYMBOL_TYPE(seg))
+/* The symbol is external */
+#define S_SET_EXTERNAL(s) ((s)->sy_symbol.n_type |= N_EXT)
+/* The symbol is not external */
+#define S_CLEAR_EXTERNAL(s) ((s)->sy_symbol.n_type &= ~N_EXT)
+/* Set the name of the symbol */
+#define S_SET_NAME(s,v) ((s)->sy_symbol.n_un.n_name = (v))
+/* Set the offset in the string table */
+#define S_SET_OFFSET(s,v) ((s)->sy_symbol.n_un.n_strx = (v))
+/* Set the n_other expression value */
+#define S_SET_OTHER(s,v) ((s)->sy_symbol.n_other = (v))
+/* Set the n_desc expression value */
+#define S_SET_DESC(s,v) ((s)->sy_symbol.n_desc = (v))
+/* Set the n_type value */
+#define S_SET_TYPE(s,v) ((s)->sy_symbol.n_type = (v))
+
+/* File header macro and type definition */
+
+#define H_GET_FILE_SIZE(h) (sizeof(struct exec) + \
+ H_GET_TEXT_SIZE(h) + H_GET_DATA_SIZE(h) + \
+ H_GET_SYMBOL_TABLE_SIZE(h) + \
+ H_GET_TEXT_RELOCATION_SIZE(h) + \
+ H_GET_DATA_RELOCATION_SIZE(h) + \
+ (h)->string_table_size)
+
+#define H_GET_HEADER_SIZE(h) (sizeof(struct exec))
+#define H_GET_TEXT_SIZE(h) ((h)->header.a_text)
+#define H_GET_DATA_SIZE(h) ((h)->header.a_data)
+#define H_GET_BSS_SIZE(h) ((h)->header.a_bss)
+#define H_GET_TEXT_RELOCATION_SIZE(h) ((h)->header.a_trsize)
+#define H_GET_DATA_RELOCATION_SIZE(h) ((h)->header.a_drsize)
+#define H_GET_SYMBOL_TABLE_SIZE(h) ((h)->header.a_syms)
+#define H_GET_MAGIC_NUMBER(h) ((h)->header.a_info)
+#define H_GET_ENTRY_POINT(h) ((h)->header.a_entry)
+#define H_GET_STRING_SIZE(h) ((h)->string_table_size)
+#define H_GET_LINENO_SIZE(h) (0)
+
+#ifdef EXEC_MACHINE_TYPE
+#define H_GET_MACHINE_TYPE(h) ((h)->header.a_machtype)
+#endif /* EXEC_MACHINE_TYPE */
+#ifdef EXEC_VERSION
+#define H_GET_VERSION(h) ((h)->header.a_version)
+#endif /* EXEC_VERSION */
+
+#define H_SET_TEXT_SIZE(h,v) ((h)->header.a_text = (v))
+#define H_SET_DATA_SIZE(h,v) ((h)->header.a_data = (v))
+#define H_SET_BSS_SIZE(h,v) ((h)->header.a_bss = (v))
+
+#define H_SET_RELOCATION_SIZE(h,t,d) (H_SET_TEXT_RELOCATION_SIZE((h),(t)),\
+ H_SET_DATA_RELOCATION_SIZE((h),(d)))
+
+#define H_SET_TEXT_RELOCATION_SIZE(h,v) ((h)->header.a_trsize = (v))
+#define H_SET_DATA_RELOCATION_SIZE(h,v) ((h)->header.a_drsize = (v))
+#define H_SET_SYMBOL_TABLE_SIZE(h,v) ((h)->header.a_syms = (v) * \
+ sizeof(struct nlist))
+
+#define H_SET_MAGIC_NUMBER(h,v) ((h)->header.a_magic = (v))
+
+#define H_SET_ENTRY_POINT(h,v) ((h)->header.a_entry = (v))
+#define H_SET_STRING_SIZE(h,v) ((h)->string_table_size = (v))
+#ifdef EXEC_MACHINE_TYPE
+#define H_SET_MACHINE_TYPE(h,v) ((h)->header.a_machtype = (v))
+#endif /* EXEC_MACHINE_TYPE */
+#ifdef EXEC_VERSION
+#define H_SET_VERSION(h,v) ((h)->header.a_version = (v))
+#endif /* EXEC_VERSION */
+
+typedef struct
+ {
+ struct exec header; /* a.out header */
+ long string_table_size; /* names + '\0' + sizeof(int) */
+ }
+
+object_headers;
+
+/* unused hooks. */
+#define OBJ_EMIT_LINENO(a, b, c) {;}
+#define obj_pre_write_hook(a) {;}
+
+#if __STDC__
+struct fix;
+#endif
+extern void tc_aout_fix_to_chars PARAMS ((char *where,
+ struct fix *fixP,
+ relax_addressT segment_address));
+extern void tc_bout_fix_to_chars PARAMS ((char *where,
+ struct fix *fixP,
+ relax_addressT segment_address));
+
+#define AOUT_STABS
+
+/* end of obj-bout.h */
diff --git a/gas/config/obj-coff.c b/gas/config/obj-coff.c
new file mode 100644
index 0000000..5639fbe
--- /dev/null
+++ b/gas/config/obj-coff.c
@@ -0,0 +1,4482 @@
+/* coff object file format
+ Copyright (C) 1989, 90, 91, 92, 93, 94, 95, 96, 97, 1998
+ Free Software Foundation, Inc.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#define OBJ_HEADER "obj-coff.h"
+
+#include "as.h"
+#include "obstack.h"
+#include "subsegs.h"
+
+/* I think this is probably always correct. */
+#ifndef KEEP_RELOC_INFO
+#define KEEP_RELOC_INFO
+#endif
+
+static void obj_coff_bss PARAMS ((int));
+const char *s_get_name PARAMS ((symbolS * s));
+static symbolS *def_symbol_in_progress;
+
+
+/* stack stuff */
+typedef struct
+ {
+ unsigned long chunk_size;
+ unsigned long element_size;
+ unsigned long size;
+ char *data;
+ unsigned long pointer;
+ }
+stack;
+
+static stack *
+stack_init (chunk_size, element_size)
+ unsigned long chunk_size;
+ unsigned long element_size;
+{
+ stack *st;
+
+ st = (stack *) malloc (sizeof (stack));
+ if (!st)
+ return 0;
+ st->data = malloc (chunk_size);
+ if (!st->data)
+ {
+ free (st);
+ return 0;
+ }
+ st->pointer = 0;
+ st->size = chunk_size;
+ st->chunk_size = chunk_size;
+ st->element_size = element_size;
+ return st;
+}
+
+#if 0
+/* Not currently used. */
+static void
+stack_delete (st)
+ stack *st;
+{
+ free (st->data);
+ free (st);
+}
+#endif
+
+static char *
+stack_push (st, element)
+ stack *st;
+ char *element;
+{
+ if (st->pointer + st->element_size >= st->size)
+ {
+ st->size += st->chunk_size;
+ if ((st->data = xrealloc (st->data, st->size)) == (char *) 0)
+ return (char *) 0;
+ }
+ memcpy (st->data + st->pointer, element, st->element_size);
+ st->pointer += st->element_size;
+ return st->data + st->pointer;
+}
+
+static char *
+stack_pop (st)
+ stack *st;
+{
+ if (st->pointer < st->element_size)
+ {
+ st->pointer = 0;
+ return (char *) 0;
+ }
+ st->pointer -= st->element_size;
+ return st->data + st->pointer;
+}
+
+/*
+ * Maintain a list of the tagnames of the structres.
+ */
+
+static struct hash_control *tag_hash;
+
+static void
+tag_init ()
+{
+ tag_hash = hash_new ();
+}
+
+static void
+tag_insert (name, symbolP)
+ const char *name;
+ symbolS *symbolP;
+{
+ const char *error_string;
+
+ if ((error_string = hash_jam (tag_hash, name, (char *) symbolP)))
+ {
+ as_fatal (_("Inserting \"%s\" into structure table failed: %s"),
+ name, error_string);
+ }
+}
+
+static symbolS *
+tag_find (name)
+ char *name;
+{
+#ifdef STRIP_UNDERSCORE
+ if (*name == '_')
+ name++;
+#endif /* STRIP_UNDERSCORE */
+ return (symbolS *) hash_find (tag_hash, name);
+}
+
+static symbolS *
+tag_find_or_make (name)
+ char *name;
+{
+ symbolS *symbolP;
+
+ if ((symbolP = tag_find (name)) == NULL)
+ {
+ symbolP = symbol_new (name, undefined_section,
+ 0, &zero_address_frag);
+
+ tag_insert (S_GET_NAME (symbolP), symbolP);
+#ifdef BFD_ASSEMBLER
+ symbol_table_insert (symbolP);
+#endif
+ } /* not found */
+
+ return symbolP;
+}
+
+/* We accept the .bss directive to set the section for backward
+ compatibility with earlier versions of gas. */
+
+static void
+obj_coff_bss (ignore)
+ int ignore;
+{
+ if (*input_line_pointer == '\n')
+ subseg_new (".bss", get_absolute_expression ());
+ else
+ s_lcomm (0);
+}
+
+/* Handle .weak. This is a GNU extension. */
+
+static void
+obj_coff_weak (ignore)
+ int ignore;
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+
+ do
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+
+#ifdef BFD_ASSEMLER
+ S_SET_WEAK (symbolP);
+#endif
+
+#ifdef TE_PE
+ S_SET_STORAGE_CLASS (symbolP, C_NT_WEAK);
+#else
+ S_SET_STORAGE_CLASS (symbolP, C_WEAKEXT);
+#endif
+
+ if (c == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\n')
+ c = '\n';
+ }
+ }
+ while (c == ',');
+
+ demand_empty_rest_of_line ();
+}
+
+#ifdef BFD_ASSEMBLER
+
+static void SA_SET_SYM_TAGNDX PARAMS ((symbolS *, symbolS *));
+
+#define GET_FILENAME_STRING(X) \
+((char*)(&((X)->sy_symbol.ost_auxent->x_file.x_n.x_offset))[1])
+
+/* @@ Ick. */
+static segT
+fetch_coff_debug_section ()
+{
+ static segT debug_section;
+ if (!debug_section)
+ {
+ CONST asymbol *s;
+ s = bfd_make_debug_symbol (stdoutput, (char *) 0, 0);
+ assert (s != 0);
+ debug_section = s->section;
+ }
+ return debug_section;
+}
+
+void
+SA_SET_SYM_ENDNDX (sym, val)
+ symbolS *sym;
+ symbolS *val;
+{
+ combined_entry_type *entry, *p;
+
+ entry = &coffsymbol (sym->bsym)->native[1];
+ p = coffsymbol (val->bsym)->native;
+ entry->u.auxent.x_sym.x_fcnary.x_fcn.x_endndx.p = p;
+ entry->fix_end = 1;
+}
+
+static void
+SA_SET_SYM_TAGNDX (sym, val)
+ symbolS *sym;
+ symbolS *val;
+{
+ combined_entry_type *entry, *p;
+
+ entry = &coffsymbol (sym->bsym)->native[1];
+ p = coffsymbol (val->bsym)->native;
+ entry->u.auxent.x_sym.x_tagndx.p = p;
+ entry->fix_tag = 1;
+}
+
+static int
+S_GET_DATA_TYPE (sym)
+ symbolS *sym;
+{
+ return coffsymbol (sym->bsym)->native->u.syment.n_type;
+}
+
+int
+S_SET_DATA_TYPE (sym, val)
+ symbolS *sym;
+ int val;
+{
+ coffsymbol (sym->bsym)->native->u.syment.n_type = val;
+ return val;
+}
+
+int
+S_GET_STORAGE_CLASS (sym)
+ symbolS *sym;
+{
+ return coffsymbol (sym->bsym)->native->u.syment.n_sclass;
+}
+
+int
+S_SET_STORAGE_CLASS (sym, val)
+ symbolS *sym;
+ int val;
+{
+ coffsymbol (sym->bsym)->native->u.syment.n_sclass = val;
+ return val;
+}
+
+/* Merge a debug symbol containing debug information into a normal symbol. */
+
+void
+c_symbol_merge (debug, normal)
+ symbolS *debug;
+ symbolS *normal;
+{
+ S_SET_DATA_TYPE (normal, S_GET_DATA_TYPE (debug));
+ S_SET_STORAGE_CLASS (normal, S_GET_STORAGE_CLASS (debug));
+
+ if (S_GET_NUMBER_AUXILIARY (debug) > S_GET_NUMBER_AUXILIARY (normal))
+ {
+ /* take the most we have */
+ S_SET_NUMBER_AUXILIARY (normal, S_GET_NUMBER_AUXILIARY (debug));
+ }
+
+ if (S_GET_NUMBER_AUXILIARY (debug) > 0)
+ {
+ /* Move all the auxiliary information. */
+ memcpy (SYM_AUXINFO (normal), SYM_AUXINFO (debug),
+ (S_GET_NUMBER_AUXILIARY (debug)
+ * sizeof (*SYM_AUXINFO (debug))));
+ }
+
+ /* Move the debug flags. */
+ SF_SET_DEBUG_FIELD (normal, SF_GET_DEBUG_FIELD (debug));
+}
+
+void
+c_dot_file_symbol (filename)
+ char *filename;
+{
+ symbolS *symbolP;
+
+ symbolP = symbol_new (filename, bfd_abs_section_ptr, 0, &zero_address_frag);
+
+ S_SET_STORAGE_CLASS (symbolP, C_FILE);
+ S_SET_NUMBER_AUXILIARY (symbolP, 1);
+
+ symbolP->bsym->flags = BSF_DEBUGGING;
+
+#ifndef NO_LISTING
+ {
+ extern int listing;
+ if (listing)
+ {
+ listing_source_file (filename);
+ }
+ }
+#endif
+
+ /* Make sure that the symbol is first on the symbol chain */
+ if (symbol_rootP != symbolP)
+ {
+ symbol_remove (symbolP, &symbol_rootP, &symbol_lastP);
+ symbol_insert (symbolP, symbol_rootP, &symbol_rootP, &symbol_lastP);
+ } /* if not first on the list */
+}
+
+/* Line number handling */
+
+struct line_no {
+ struct line_no *next;
+ fragS *frag;
+ alent l;
+};
+
+int coff_line_base;
+
+/* Symbol of last function, which we should hang line#s off of. */
+static symbolS *line_fsym;
+
+#define in_function() (line_fsym != 0)
+#define clear_function() (line_fsym = 0)
+#define set_function(F) (line_fsym = (F), coff_add_linesym (F))
+
+
+void
+coff_obj_symbol_new_hook (symbolP)
+ symbolS *symbolP;
+{
+ long sz = (OBJ_COFF_MAX_AUXENTRIES + 1) * sizeof (combined_entry_type);
+ char * s = (char *) xmalloc (sz);
+
+ memset (s, 0, sz);
+ coffsymbol (symbolP->bsym)->native = (combined_entry_type *) s;
+
+ S_SET_DATA_TYPE (symbolP, T_NULL);
+ S_SET_STORAGE_CLASS (symbolP, 0);
+ S_SET_NUMBER_AUXILIARY (symbolP, 0);
+
+ if (S_IS_STRING (symbolP))
+ SF_SET_STRING (symbolP);
+
+ if (S_IS_LOCAL (symbolP))
+ SF_SET_LOCAL (symbolP);
+}
+
+
+/*
+ * Handle .ln directives.
+ */
+
+static symbolS *current_lineno_sym;
+static struct line_no *line_nos;
+/* @@ Blindly assume all .ln directives will be in the .text section... */
+int coff_n_line_nos;
+
+static void
+add_lineno (frag, offset, num)
+ fragS *frag;
+ int offset;
+ int num;
+{
+ struct line_no *new_line =
+ (struct line_no *) xmalloc (sizeof (struct line_no));
+ if (!current_lineno_sym)
+ {
+ abort ();
+ }
+ new_line->next = line_nos;
+ new_line->frag = frag;
+ new_line->l.line_number = num;
+ new_line->l.u.offset = offset;
+ line_nos = new_line;
+ coff_n_line_nos++;
+}
+
+void
+coff_add_linesym (sym)
+ symbolS *sym;
+{
+ if (line_nos)
+ {
+ coffsymbol (current_lineno_sym->bsym)->lineno = (alent *) line_nos;
+ coff_n_line_nos++;
+ line_nos = 0;
+ }
+ current_lineno_sym = sym;
+}
+
+static void
+obj_coff_ln (appline)
+ int appline;
+{
+ int l;
+
+ if (! appline && def_symbol_in_progress != NULL)
+ {
+ as_warn (_(".ln pseudo-op inside .def/.endef: ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ l = get_absolute_expression ();
+ if (!appline)
+ {
+ add_lineno (frag_now, frag_now_fix (), l);
+ }
+
+ if (appline)
+ new_logical_line ((char *) NULL, l - 1);
+
+#ifndef NO_LISTING
+ {
+ extern int listing;
+
+ if (listing)
+ {
+ if (! appline)
+ l += coff_line_base - 1;
+ listing_source_line (l);
+ }
+ }
+#endif
+
+ demand_empty_rest_of_line ();
+}
+
+/*
+ * def()
+ *
+ * Handle .def directives.
+ *
+ * One might ask : why can't we symbol_new if the symbol does not
+ * already exist and fill it with debug information. Because of
+ * the C_EFCN special symbol. It would clobber the value of the
+ * function symbol before we have a chance to notice that it is
+ * a C_EFCN. And a second reason is that the code is more clear this
+ * way. (at least I think it is :-).
+ *
+ */
+
+#define SKIP_SEMI_COLON() while (*input_line_pointer++ != ';')
+#define SKIP_WHITESPACES() while (*input_line_pointer == ' ' || \
+ *input_line_pointer == '\t') \
+ input_line_pointer++;
+
+static void
+obj_coff_def (what)
+ int what;
+{
+ char name_end; /* Char after the end of name */
+ char *symbol_name; /* Name of the debug symbol */
+ char *symbol_name_copy; /* Temporary copy of the name */
+ unsigned int symbol_name_length;
+
+ if (def_symbol_in_progress != NULL)
+ {
+ as_warn (_(".def pseudo-op used inside of .def/.endef: ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ } /* if not inside .def/.endef */
+
+ SKIP_WHITESPACES ();
+
+ symbol_name = input_line_pointer;
+#ifdef STRIP_UNDERSCORE
+ if (symbol_name[0] == '_' && symbol_name[1] != 0)
+ symbol_name++;
+#endif /* STRIP_UNDERSCORE */
+
+ name_end = get_symbol_end ();
+ symbol_name_length = strlen (symbol_name);
+ symbol_name_copy = xmalloc (symbol_name_length + 1);
+ strcpy (symbol_name_copy, symbol_name);
+#ifdef tc_canonicalize_symbol_name
+ symbol_name_copy = tc_canonicalize_symbol_name (symbol_name_copy);
+#endif
+
+ /* Initialize the new symbol */
+ def_symbol_in_progress = symbol_make (symbol_name_copy);
+ def_symbol_in_progress->sy_frag = &zero_address_frag;
+ S_SET_VALUE (def_symbol_in_progress, 0);
+
+ if (S_IS_STRING (def_symbol_in_progress))
+ SF_SET_STRING (def_symbol_in_progress);
+
+ *input_line_pointer = name_end;
+
+ demand_empty_rest_of_line ();
+}
+
+unsigned int dim_index;
+
+static void
+obj_coff_endef (ignore)
+ int ignore;
+{
+ symbolS *symbolP;
+
+ /* DIM BUG FIX sac@cygnus.com */
+ dim_index = 0;
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".endef pseudo-op used outside of .def/.endef: ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ } /* if not inside .def/.endef */
+
+ /* Set the section number according to storage class. */
+ switch (S_GET_STORAGE_CLASS (def_symbol_in_progress))
+ {
+ case C_STRTAG:
+ case C_ENTAG:
+ case C_UNTAG:
+ SF_SET_TAG (def_symbol_in_progress);
+ /* intentional fallthrough */
+ case C_FILE:
+ case C_TPDEF:
+ SF_SET_DEBUG (def_symbol_in_progress);
+ S_SET_SEGMENT (def_symbol_in_progress, fetch_coff_debug_section ());
+ break;
+
+ case C_EFCN:
+ SF_SET_LOCAL (def_symbol_in_progress); /* Do not emit this symbol. */
+ /* intentional fallthrough */
+ case C_BLOCK:
+ SF_SET_PROCESS (def_symbol_in_progress); /* Will need processing before writing */
+ /* intentional fallthrough */
+ case C_FCN:
+ {
+ CONST char *name;
+ S_SET_SEGMENT (def_symbol_in_progress, text_section);
+
+ name = bfd_asymbol_name (def_symbol_in_progress->bsym);
+ if (name[1] == 'b' && name[2] == 'f')
+ {
+ if (! in_function ())
+ as_warn (_("`%s' symbol without preceding function"), name);
+/* SA_SET_SYM_LNNO (def_symbol_in_progress, 12345);*/
+ /* Will need relocating */
+ SF_SET_PROCESS (def_symbol_in_progress);
+ clear_function ();
+ }
+ }
+ break;
+
+#ifdef C_AUTOARG
+ case C_AUTOARG:
+#endif /* C_AUTOARG */
+ case C_AUTO:
+ case C_REG:
+ case C_ARG:
+ case C_REGPARM:
+ case C_FIELD:
+ SF_SET_DEBUG (def_symbol_in_progress);
+ S_SET_SEGMENT (def_symbol_in_progress, absolute_section);
+ break;
+
+ case C_MOS:
+ case C_MOE:
+ case C_MOU:
+ case C_EOS:
+ S_SET_SEGMENT (def_symbol_in_progress, absolute_section);
+ break;
+
+ case C_EXT:
+ case C_WEAKEXT:
+#ifdef TE_PE
+ case C_NT_WEAK:
+#endif
+ case C_STAT:
+ case C_LABEL:
+ /* Valid but set somewhere else (s_comm, s_lcomm, colon) */
+ break;
+
+ default:
+ case C_USTATIC:
+ case C_EXTDEF:
+ case C_ULABEL:
+ as_warn (_("unexpected storage class %d"),
+ S_GET_STORAGE_CLASS (def_symbol_in_progress));
+ break;
+ } /* switch on storage class */
+
+ /* Now that we have built a debug symbol, try to find if we should
+ merge with an existing symbol or not. If a symbol is C_EFCN or
+ SEG_ABSOLUTE or untagged SEG_DEBUG it never merges. */
+
+ /* Two cases for functions. Either debug followed by definition or
+ definition followed by debug. For definition first, we will
+ merge the debug symbol into the definition. For debug first, the
+ lineno entry MUST point to the definition function or else it
+ will point off into space when obj_crawl_symbol_chain() merges
+ the debug symbol into the real symbol. Therefor, let's presume
+ the debug symbol is a real function reference. */
+
+ /* FIXME-SOON If for some reason the definition label/symbol is
+ never seen, this will probably leave an undefined symbol at link
+ time. */
+
+ if (S_GET_STORAGE_CLASS (def_symbol_in_progress) == C_EFCN
+ || (!strcmp (bfd_get_section_name (stdoutput,
+ S_GET_SEGMENT (def_symbol_in_progress)),
+ "*DEBUG*")
+ && !SF_GET_TAG (def_symbol_in_progress))
+ || S_GET_SEGMENT (def_symbol_in_progress) == absolute_section
+ || (symbolP = symbol_find_base (S_GET_NAME (def_symbol_in_progress), DO_NOT_STRIP)) == NULL)
+ {
+ if (def_symbol_in_progress != symbol_lastP)
+ symbol_append (def_symbol_in_progress, symbol_lastP, &symbol_rootP,
+ &symbol_lastP);
+ }
+ else
+ {
+ /* This symbol already exists, merge the newly created symbol
+ into the old one. This is not mandatory. The linker can
+ handle duplicate symbols correctly. But I guess that it save
+ a *lot* of space if the assembly file defines a lot of
+ symbols. [loic] */
+
+ /* The debug entry (def_symbol_in_progress) is merged into the
+ previous definition. */
+
+ c_symbol_merge (def_symbol_in_progress, symbolP);
+ symbol_remove (def_symbol_in_progress, &symbol_rootP, &symbol_lastP);
+
+ def_symbol_in_progress = symbolP;
+
+ if (SF_GET_FUNCTION (def_symbol_in_progress)
+ || SF_GET_TAG (def_symbol_in_progress)
+ || S_GET_STORAGE_CLASS (def_symbol_in_progress) == C_STAT)
+ {
+ /* For functions, and tags, and static symbols, the symbol
+ *must* be where the debug symbol appears. Move the
+ existing symbol to the current place. */
+ /* If it already is at the end of the symbol list, do nothing */
+ if (def_symbol_in_progress != symbol_lastP)
+ {
+ symbol_remove (def_symbol_in_progress, &symbol_rootP, &symbol_lastP);
+ symbol_append (def_symbol_in_progress, symbol_lastP, &symbol_rootP, &symbol_lastP);
+ }
+ }
+ }
+
+ if (SF_GET_TAG (def_symbol_in_progress))
+ {
+ symbolS *oldtag;
+
+ oldtag = symbol_find_base (S_GET_NAME (def_symbol_in_progress),
+ DO_NOT_STRIP);
+ if (oldtag == NULL || ! SF_GET_TAG (oldtag))
+ tag_insert (S_GET_NAME (def_symbol_in_progress),
+ def_symbol_in_progress);
+ }
+
+ if (SF_GET_FUNCTION (def_symbol_in_progress))
+ {
+ know (sizeof (def_symbol_in_progress) <= sizeof (long));
+ set_function (def_symbol_in_progress);
+ SF_SET_PROCESS (def_symbol_in_progress);
+
+ if (symbolP == NULL)
+ {
+ /* That is, if this is the first time we've seen the
+ function... */
+ symbol_table_insert (def_symbol_in_progress);
+ } /* definition follows debug */
+ } /* Create the line number entry pointing to the function being defined */
+
+ def_symbol_in_progress = NULL;
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_dim (ignore)
+ int ignore;
+{
+ int dim_index;
+
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".dim pseudo-op used outside of .def/.endef: ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ } /* if not inside .def/.endef */
+
+ S_SET_NUMBER_AUXILIARY (def_symbol_in_progress, 1);
+
+ for (dim_index = 0; dim_index < DIMNUM; dim_index++)
+ {
+ SKIP_WHITESPACES ();
+ SA_SET_SYM_DIMEN (def_symbol_in_progress, dim_index,
+ get_absolute_expression ());
+
+ switch (*input_line_pointer)
+ {
+ case ',':
+ input_line_pointer++;
+ break;
+
+ default:
+ as_warn (_("badly formed .dim directive ignored"));
+ /* intentional fallthrough */
+ case '\n':
+ case ';':
+ dim_index = DIMNUM;
+ break;
+ }
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_line (ignore)
+ int ignore;
+{
+ int this_base;
+
+ if (def_symbol_in_progress == NULL)
+ {
+ /* Probably stabs-style line? */
+ obj_coff_ln (0);
+ return;
+ }
+
+ this_base = get_absolute_expression ();
+ if (!strcmp (".bf", S_GET_NAME (def_symbol_in_progress)))
+ coff_line_base = this_base;
+
+ S_SET_NUMBER_AUXILIARY (def_symbol_in_progress, 1);
+ SA_SET_SYM_LNNO (def_symbol_in_progress, coff_line_base);
+
+ demand_empty_rest_of_line ();
+
+#ifndef NO_LISTING
+ if (strcmp (".bf", S_GET_NAME (def_symbol_in_progress)) == 0)
+ {
+ extern int listing;
+
+ if (listing)
+ listing_source_line ((unsigned int) coff_line_base);
+ }
+#endif
+}
+
+static void
+obj_coff_size (ignore)
+ int ignore;
+{
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".size pseudo-op used outside of .def/.endef ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ } /* if not inside .def/.endef */
+
+ S_SET_NUMBER_AUXILIARY (def_symbol_in_progress, 1);
+ SA_SET_SYM_SIZE (def_symbol_in_progress, get_absolute_expression ());
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_scl (ignore)
+ int ignore;
+{
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".scl pseudo-op used outside of .def/.endef ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ } /* if not inside .def/.endef */
+
+ S_SET_STORAGE_CLASS (def_symbol_in_progress, get_absolute_expression ());
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_tag (ignore)
+ int ignore;
+{
+ char *symbol_name;
+ char name_end;
+
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".tag pseudo-op used outside of .def/.endef ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ S_SET_NUMBER_AUXILIARY (def_symbol_in_progress, 1);
+ symbol_name = input_line_pointer;
+ name_end = get_symbol_end ();
+
+#ifdef tc_canonicalize_symbol_name
+ symbol_name = tc_canonicalize_symbol_name (symbol_name);
+#endif
+
+ /* Assume that the symbol referred to by .tag is always defined.
+ This was a bad assumption. I've added find_or_make. xoxorich. */
+ SA_SET_SYM_TAGNDX (def_symbol_in_progress,
+ tag_find_or_make (symbol_name));
+ if (SA_GET_SYM_TAGNDX (def_symbol_in_progress) == 0L)
+ {
+ as_warn (_("tag not found for .tag %s"), symbol_name);
+ } /* not defined */
+
+ SF_SET_TAGGED (def_symbol_in_progress);
+ *input_line_pointer = name_end;
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_type (ignore)
+ int ignore;
+{
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".type pseudo-op used outside of .def/.endef ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ } /* if not inside .def/.endef */
+
+ S_SET_DATA_TYPE (def_symbol_in_progress, get_absolute_expression ());
+
+ if (ISFCN (S_GET_DATA_TYPE (def_symbol_in_progress)) &&
+ S_GET_STORAGE_CLASS (def_symbol_in_progress) != C_TPDEF)
+ {
+ SF_SET_FUNCTION (def_symbol_in_progress);
+ } /* is a function */
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_val (ignore)
+ int ignore;
+{
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".val pseudo-op used outside of .def/.endef ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ } /* if not inside .def/.endef */
+
+ if (is_name_beginner (*input_line_pointer))
+ {
+ char *symbol_name = input_line_pointer;
+ char name_end = get_symbol_end ();
+
+#ifdef tc_canonicalize_symbol_name
+ symbol_name = tc_canonicalize_symbol_name (symbol_name);
+#endif
+ if (!strcmp (symbol_name, "."))
+ {
+ def_symbol_in_progress->sy_frag = frag_now;
+ S_SET_VALUE (def_symbol_in_progress, (valueT) frag_now_fix ());
+ /* If the .val is != from the .def (e.g. statics) */
+ }
+ else if (strcmp (S_GET_NAME (def_symbol_in_progress), symbol_name))
+ {
+ def_symbol_in_progress->sy_value.X_op = O_symbol;
+ def_symbol_in_progress->sy_value.X_add_symbol =
+ symbol_find_or_make (symbol_name);
+ def_symbol_in_progress->sy_value.X_op_symbol = NULL;
+ def_symbol_in_progress->sy_value.X_add_number = 0;
+
+ /* If the segment is undefined when the forward reference is
+ resolved, then copy the segment id from the forward
+ symbol. */
+ SF_SET_GET_SEGMENT (def_symbol_in_progress);
+ }
+ /* Otherwise, it is the name of a non debug symbol and its value will be calculated later. */
+ *input_line_pointer = name_end;
+ }
+ else
+ {
+ S_SET_VALUE (def_symbol_in_progress, get_absolute_expression ());
+ } /* if symbol based */
+
+ demand_empty_rest_of_line ();
+}
+
+void
+coff_obj_read_begin_hook ()
+{
+ /* These had better be the same. Usually 18 bytes. */
+#ifndef BFD_HEADERS
+ know (sizeof (SYMENT) == sizeof (AUXENT));
+ know (SYMESZ == AUXESZ);
+#endif
+ tag_init ();
+}
+
+
+symbolS *coff_last_function;
+static symbolS *coff_last_bf;
+
+void
+coff_frob_symbol (symp, punt)
+ symbolS *symp;
+ int *punt;
+{
+ static symbolS *last_tagP;
+ static stack *block_stack;
+ static symbolS *set_end;
+ symbolS *next_set_end = NULL;
+
+ if (symp == &abs_symbol)
+ {
+ *punt = 1;
+ return;
+ }
+
+ if (current_lineno_sym)
+ coff_add_linesym ((symbolS *) 0);
+
+ if (!block_stack)
+ block_stack = stack_init (512, sizeof (symbolS*));
+
+ if (S_IS_WEAK (symp))
+ {
+#ifdef TE_PE
+ S_SET_STORAGE_CLASS (symp, C_NT_WEAK);
+#else
+ S_SET_STORAGE_CLASS (symp, C_WEAKEXT);
+#endif
+ }
+
+ if (!S_IS_DEFINED (symp)
+ && !S_IS_WEAK (symp)
+ && S_GET_STORAGE_CLASS (symp) != C_STAT)
+ S_SET_STORAGE_CLASS (symp, C_EXT);
+
+ if (!SF_GET_DEBUG (symp))
+ {
+ symbolS *real;
+ if (!SF_GET_LOCAL (symp)
+ && !SF_GET_STATICS (symp)
+ && (real = symbol_find_base (S_GET_NAME (symp), DO_NOT_STRIP))
+ && real != symp)
+ {
+ c_symbol_merge (symp, real);
+ *punt = 1;
+ }
+ if (!S_IS_DEFINED (symp) && !SF_GET_LOCAL (symp))
+ {
+ assert (S_GET_VALUE (symp) == 0);
+ S_SET_EXTERNAL (symp);
+ }
+ else if (S_GET_STORAGE_CLASS (symp) == C_NULL)
+ {
+ if (S_GET_SEGMENT (symp) == text_section
+ && symp != seg_info (text_section)->sym)
+ S_SET_STORAGE_CLASS (symp, C_LABEL);
+ else
+ S_SET_STORAGE_CLASS (symp, C_STAT);
+ }
+ if (SF_GET_PROCESS (symp))
+ {
+ if (S_GET_STORAGE_CLASS (symp) == C_BLOCK)
+ {
+ if (!strcmp (S_GET_NAME (symp), ".bb"))
+ stack_push (block_stack, (char *) &symp);
+ else
+ {
+ symbolS *begin;
+ begin = *(symbolS **) stack_pop (block_stack);
+ if (begin == 0)
+ as_warn (_("mismatched .eb"));
+ else
+ next_set_end = begin;
+ }
+ }
+ if (coff_last_function == 0 && SF_GET_FUNCTION (symp))
+ {
+ union internal_auxent *auxp;
+ coff_last_function = symp;
+ if (S_GET_NUMBER_AUXILIARY (symp) < 1)
+ S_SET_NUMBER_AUXILIARY (symp, 1);
+ auxp = &coffsymbol (symp->bsym)->native[1].u.auxent;
+ memset (auxp->x_sym.x_fcnary.x_ary.x_dimen, 0,
+ sizeof (auxp->x_sym.x_fcnary.x_ary.x_dimen));
+ }
+ if (S_GET_STORAGE_CLASS (symp) == C_EFCN)
+ {
+ if (coff_last_function == 0)
+ as_fatal (_("C_EFCN symbol out of scope"));
+ SA_SET_SYM_FSIZE (coff_last_function,
+ (long) (S_GET_VALUE (symp)
+ - S_GET_VALUE (coff_last_function)));
+ next_set_end = coff_last_function;
+ coff_last_function = 0;
+ }
+ }
+ if (S_IS_EXTERNAL (symp))
+ S_SET_STORAGE_CLASS (symp, C_EXT);
+ else if (SF_GET_LOCAL (symp))
+ *punt = 1;
+
+ if (SF_GET_FUNCTION (symp))
+ symp->bsym->flags |= BSF_FUNCTION;
+
+ /* more ... */
+ }
+
+ if (SF_GET_TAG (symp))
+ last_tagP = symp;
+ else if (S_GET_STORAGE_CLASS (symp) == C_EOS)
+ next_set_end = last_tagP;
+
+#ifdef OBJ_XCOFF
+ /* This is pretty horrible, but we have to set *punt correctly in
+ order to call SA_SET_SYM_ENDNDX correctly. */
+ if (! symp->sy_used_in_reloc
+ && ((symp->bsym->flags & BSF_SECTION_SYM) != 0
+ || (! S_IS_EXTERNAL (symp)
+ && ! symp->sy_tc.output
+ && S_GET_STORAGE_CLASS (symp) != C_FILE)))
+ *punt = 1;
+#endif
+
+ if (set_end != (symbolS *) NULL
+ && ! *punt
+ && ((symp->bsym->flags & BSF_NOT_AT_END) != 0
+ || (S_IS_DEFINED (symp)
+ && ! S_IS_COMMON (symp)
+ && (! S_IS_EXTERNAL (symp) || SF_GET_FUNCTION (symp)))))
+ {
+ SA_SET_SYM_ENDNDX (set_end, symp);
+ set_end = NULL;
+ }
+
+ if (next_set_end != NULL
+ && ! *punt)
+ set_end = next_set_end;
+
+ if (! *punt
+ && S_GET_STORAGE_CLASS (symp) == C_FCN
+ && strcmp (S_GET_NAME (symp), ".bf") == 0)
+ {
+ if (coff_last_bf != NULL)
+ SA_SET_SYM_ENDNDX (coff_last_bf, symp);
+ coff_last_bf = symp;
+ }
+
+ if (coffsymbol (symp->bsym)->lineno)
+ {
+ int i;
+ struct line_no *lptr;
+ alent *l;
+
+ lptr = (struct line_no *) coffsymbol (symp->bsym)->lineno;
+ for (i = 0; lptr; lptr = lptr->next)
+ i++;
+ lptr = (struct line_no *) coffsymbol (symp->bsym)->lineno;
+
+ /* We need i entries for line numbers, plus 1 for the first
+ entry which BFD will override, plus 1 for the last zero
+ entry (a marker for BFD). */
+ l = (alent *) xmalloc ((i + 2) * sizeof (alent));
+ coffsymbol (symp->bsym)->lineno = l;
+ l[i + 1].line_number = 0;
+ l[i + 1].u.sym = NULL;
+ for (; i > 0; i--)
+ {
+ if (lptr->frag)
+ lptr->l.u.offset += lptr->frag->fr_address;
+ l[i] = lptr->l;
+ lptr = lptr->next;
+ }
+ }
+}
+
+void
+coff_adjust_section_syms (abfd, sec, x)
+ bfd *abfd;
+ asection *sec;
+ PTR x;
+{
+ symbolS *secsym;
+ segment_info_type *seginfo = seg_info (sec);
+ int nlnno, nrelocs = 0;
+
+ /* RS/6000 gas creates a .debug section manually in ppc_frob_file in
+ tc-ppc.c. Do not get confused by it. */
+ if (seginfo == NULL)
+ return;
+
+ if (!strcmp (sec->name, ".text"))
+ nlnno = coff_n_line_nos;
+ else
+ nlnno = 0;
+ {
+ /* @@ Hope that none of the fixups expand to more than one reloc
+ entry... */
+ fixS *fixp = seginfo->fix_root;
+ while (fixp)
+ {
+ if (! fixp->fx_done)
+ nrelocs++;
+ fixp = fixp->fx_next;
+ }
+ }
+ if (bfd_get_section_size_before_reloc (sec) == 0
+ && nrelocs == 0
+ && nlnno == 0
+ && sec != text_section
+ && sec != data_section
+ && sec != bss_section)
+ return;
+ secsym = section_symbol (sec);
+ SA_SET_SCN_NRELOC (secsym, nrelocs);
+ SA_SET_SCN_NLINNO (secsym, nlnno);
+}
+
+void
+coff_frob_file_after_relocs ()
+{
+ bfd_map_over_sections (stdoutput, coff_adjust_section_syms, (char*) 0);
+}
+
+/*
+ * implement the .section pseudo op:
+ * .section name {, "flags"}
+ * ^ ^
+ * | +--- optional flags: 'b' for bss
+ * | 'i' for info
+ * +-- section name 'l' for lib
+ * 'n' for noload
+ * 'o' for over
+ * 'w' for data
+ * 'd' (apparently m88k for data)
+ * 'x' for text
+ * 'r' for read-only data
+ * But if the argument is not a quoted string, treat it as a
+ * subsegment number.
+ */
+
+void
+obj_coff_section (ignore)
+ int ignore;
+{
+ /* Strip out the section name */
+ char *section_name;
+ char c;
+ char *name;
+ unsigned int exp;
+ flagword flags;
+ asection *sec;
+
+ if (flag_mri)
+ {
+ char type;
+
+ s_mri_sect (&type);
+ return;
+ }
+
+ section_name = input_line_pointer;
+ c = get_symbol_end ();
+
+ name = xmalloc (input_line_pointer - section_name + 1);
+ strcpy (name, section_name);
+
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE ();
+
+ exp = 0;
+ flags = SEC_NO_FLAGS;
+
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != '"')
+ exp = get_absolute_expression ();
+ else
+ {
+ ++input_line_pointer;
+ while (*input_line_pointer != '"'
+ && ! is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ switch (*input_line_pointer)
+ {
+ case 'b': flags |= SEC_ALLOC; flags &=~ SEC_LOAD; break;
+ case 'n': flags &=~ SEC_LOAD; break;
+ case 'd':
+ case 'w': flags &=~ SEC_READONLY; break;
+ case 'x': flags |= SEC_CODE; break;
+ case 'r': flags |= SEC_READONLY; break;
+
+ case 'i': /* STYP_INFO */
+ case 'l': /* STYP_LIB */
+ case 'o': /* STYP_OVER */
+ as_warn (_("unsupported section attribute '%c'"),
+ *input_line_pointer);
+ break;
+
+ default:
+ as_warn(_("unknown section attribute '%c'"),
+ *input_line_pointer);
+ break;
+ }
+ ++input_line_pointer;
+ }
+ if (*input_line_pointer == '"')
+ ++input_line_pointer;
+ }
+ }
+
+ sec = subseg_new (name, (subsegT) exp);
+
+ if (flags != SEC_NO_FLAGS)
+ {
+ flagword oldflags;
+
+ oldflags = bfd_get_section_flags (stdoutput, sec);
+ oldflags &= SEC_LINK_ONCE | SEC_LINK_DUPLICATES;
+ flags |= oldflags;
+
+ if (! bfd_set_section_flags (stdoutput, sec, flags))
+ as_warn (_("error setting flags for \"%s\": %s"),
+ bfd_section_name (stdoutput, sec),
+ bfd_errmsg (bfd_get_error ()));
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+void
+coff_adjust_symtab ()
+{
+ if (symbol_rootP == NULL
+ || S_GET_STORAGE_CLASS (symbol_rootP) != C_FILE)
+ c_dot_file_symbol ("fake");
+}
+
+void
+coff_frob_section (sec)
+ segT sec;
+{
+ segT strsec;
+ char *p;
+ fragS *fragp;
+ bfd_vma size, n_entries, mask;
+
+ /* The COFF back end in BFD requires that all section sizes be
+ rounded up to multiples of the corresponding section alignments.
+ Seems kinda silly to me, but that's the way it is. */
+ size = bfd_get_section_size_before_reloc (sec);
+ mask = ((bfd_vma) 1 << (bfd_vma) sec->alignment_power) - 1;
+ if (size & mask)
+ {
+ size = (size + mask) & ~mask;
+ bfd_set_section_size (stdoutput, sec, size);
+ }
+
+ /* If the section size is non-zero, the section symbol needs an aux
+ entry associated with it, indicating the size. We don't know
+ all the values yet; coff_frob_symbol will fill them in later. */
+ if (size != 0
+ || sec == text_section
+ || sec == data_section
+ || sec == bss_section)
+ {
+ symbolS *secsym = section_symbol (sec);
+
+ S_SET_STORAGE_CLASS (secsym, C_STAT);
+ S_SET_NUMBER_AUXILIARY (secsym, 1);
+ SF_SET_STATICS (secsym);
+ SA_SET_SCN_SCNLEN (secsym, size);
+ }
+
+ /* @@ these should be in a "stabs.h" file, or maybe as.h */
+#ifndef STAB_SECTION_NAME
+#define STAB_SECTION_NAME ".stab"
+#endif
+#ifndef STAB_STRING_SECTION_NAME
+#define STAB_STRING_SECTION_NAME ".stabstr"
+#endif
+ if (strcmp (STAB_STRING_SECTION_NAME, sec->name))
+ return;
+
+ strsec = sec;
+ sec = subseg_get (STAB_SECTION_NAME, 0);
+ /* size is already rounded up, since other section will be listed first */
+ size = bfd_get_section_size_before_reloc (strsec);
+
+ n_entries = bfd_get_section_size_before_reloc (sec) / 12 - 1;
+
+ /* Find first non-empty frag. It should be large enough. */
+ fragp = seg_info (sec)->frchainP->frch_root;
+ while (fragp && fragp->fr_fix == 0)
+ fragp = fragp->fr_next;
+ assert (fragp != 0 && fragp->fr_fix >= 12);
+
+ /* Store the values. */
+ p = fragp->fr_literal;
+ bfd_h_put_16 (stdoutput, n_entries, (bfd_byte *) p + 6);
+ bfd_h_put_32 (stdoutput, size, (bfd_byte *) p + 8);
+}
+
+void
+obj_coff_init_stab_section (seg)
+ segT seg;
+{
+ char *file;
+ char *p;
+ char *stabstr_name;
+ unsigned int stroff;
+
+ /* Make space for this first symbol. */
+ p = frag_more (12);
+ /* Zero it out. */
+ memset (p, 0, 12);
+ as_where (&file, (unsigned int *) NULL);
+ stabstr_name = (char *) alloca (strlen (seg->name) + 4);
+ strcpy (stabstr_name, seg->name);
+ strcat (stabstr_name, "str");
+ stroff = get_stab_string_offset (file, stabstr_name);
+ know (stroff == 1);
+ md_number_to_chars (p, stroff, 4);
+}
+
+#ifdef DEBUG
+/* for debugging */
+const char *
+s_get_name (s)
+ symbolS *s;
+{
+ return ((s == NULL) ? "(NULL)" : S_GET_NAME (s));
+}
+
+void
+symbol_dump ()
+{
+ symbolS *symbolP;
+
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next (symbolP))
+ {
+ printf(_("0x%lx: \"%s\" type = %ld, class = %d, segment = %d\n"),
+ (unsigned long) symbolP,
+ S_GET_NAME(symbolP),
+ (long) S_GET_DATA_TYPE(symbolP),
+ S_GET_STORAGE_CLASS(symbolP),
+ (int) S_GET_SEGMENT(symbolP));
+ }
+}
+
+#endif /* DEBUG */
+
+#else /* not BFD_ASSEMBLER */
+
+#include "frags.h"
+/* This is needed because we include internal bfd things. */
+#include <time.h>
+
+#include "libbfd.h"
+#include "libcoff.h"
+
+#ifdef TE_PE
+#include "coff/pe.h"
+#endif
+
+/* The NOP_OPCODE is for the alignment fill value. Fill with nop so
+ that we can stick sections together without causing trouble. */
+#ifndef NOP_OPCODE
+#define NOP_OPCODE 0x00
+#endif
+
+/* The zeroes if symbol name is longer than 8 chars */
+#define S_SET_ZEROES(s,v) ((s)->sy_symbol.ost_entry.n_zeroes = (v))
+
+#define MIN(a,b) ((a) < (b)? (a) : (b))
+/* This vector is used to turn an internal segment into a section #
+ suitable for insertion into a coff symbol table
+ */
+
+const short seg_N_TYPE[] =
+{ /* in: segT out: N_TYPE bits */
+ C_ABS_SECTION,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ C_UNDEF_SECTION, /* SEG_UNKNOWN */
+ C_UNDEF_SECTION, /* SEG_GOOF */
+ C_UNDEF_SECTION, /* SEG_EXPR */
+ C_DEBUG_SECTION, /* SEG_DEBUG */
+ C_NTV_SECTION, /* SEG_NTV */
+ C_PTV_SECTION, /* SEG_PTV */
+ C_REGISTER_SECTION, /* SEG_REGISTER */
+};
+
+int function_lineoff = -1; /* Offset in line#s where the last function
+ started (the odd entry for line #0) */
+
+/* structure used to keep the filenames which
+ are too long around so that we can stick them
+ into the string table */
+struct filename_list
+{
+ char *filename;
+ struct filename_list *next;
+};
+
+static struct filename_list *filename_list_head;
+static struct filename_list *filename_list_tail;
+
+static symbolS *last_line_symbol;
+
+/* Add 4 to the real value to get the index and compensate the
+ negatives. This vector is used by S_GET_SEGMENT to turn a coff
+ section number into a segment number
+*/
+static symbolS *previous_file_symbol;
+void c_symbol_merge ();
+static int line_base;
+
+symbolS *c_section_symbol ();
+bfd *abfd;
+
+static void fixup_segment PARAMS ((segment_info_type *segP,
+ segT this_segment_type));
+
+
+static void fixup_mdeps PARAMS ((fragS *,
+ object_headers *,
+ segT));
+
+
+static void fill_section PARAMS ((bfd * abfd,
+ object_headers *,
+ unsigned long *));
+
+
+static int c_line_new PARAMS ((symbolS * symbol, long paddr,
+ int line_number,
+ fragS * frag));
+
+
+static void w_symbols PARAMS ((bfd * abfd, char *where,
+ symbolS * symbol_rootP));
+
+static void adjust_stab_section PARAMS ((bfd *abfd, segT seg));
+
+static void obj_coff_lcomm PARAMS ((int));
+static void obj_coff_text PARAMS ((int));
+static void obj_coff_data PARAMS ((int));
+static void obj_coff_ident PARAMS ((int));
+void obj_coff_section PARAMS ((int));
+
+/* Section stuff
+
+ We allow more than just the standard 3 sections, infact, we allow
+ 40 sections, (though the usual three have to be there).
+
+ This structure performs the mappings for us:
+*/
+
+
+typedef struct
+{
+ segT seg_t;
+ int i;
+} seg_info_type;
+
+static const seg_info_type seg_info_off_by_4[] =
+{
+ {SEG_PTV, },
+ {SEG_NTV, },
+ {SEG_DEBUG, },
+ {SEG_ABSOLUTE, },
+ {SEG_UNKNOWN, },
+ {SEG_E0}, {SEG_E1}, {SEG_E2}, {SEG_E3}, {SEG_E4},
+ {SEG_E5}, {SEG_E6}, {SEG_E7}, {SEG_E8}, {SEG_E9},
+ {SEG_E10},{SEG_E11},{SEG_E12},{SEG_E13},{SEG_E14},
+ {SEG_E15},{SEG_E16},{SEG_E17},{SEG_E18},{SEG_E19},
+ {SEG_E20},{SEG_E21},{SEG_E22},{SEG_E23},{SEG_E24},
+ {SEG_E25},{SEG_E26},{SEG_E27},{SEG_E28},{SEG_E29},
+ {SEG_E30},{SEG_E31},{SEG_E32},{SEG_E33},{SEG_E34},
+ {SEG_E35},{SEG_E36},{SEG_E37},{SEG_E38},{SEG_E39},
+ {(segT)40},
+ {(segT)41},
+ {(segT)42},
+ {(segT)43},
+ {(segT)44},
+ {(segT)45},
+ {(segT)0},
+ {(segT)0},
+ {(segT)0},
+ {SEG_REGISTER}
+};
+
+
+
+#define SEG_INFO_FROM_SECTION_NUMBER(x) (seg_info_off_by_4[(x)+4])
+
+static relax_addressT
+relax_align (address, alignment)
+ relax_addressT address;
+ long alignment;
+{
+ relax_addressT mask;
+ relax_addressT new_address;
+
+ mask = ~((~0) << alignment);
+ new_address = (address + mask) & (~mask);
+ return (new_address - address);
+}
+
+
+segT
+s_get_segment (x)
+ symbolS * x;
+{
+ return SEG_INFO_FROM_SECTION_NUMBER (x->sy_symbol.ost_entry.n_scnum).seg_t;
+}
+
+/* calculate the size of the frag chain and fill in the section header
+ to contain all of it, also fill in the addr of the sections */
+static unsigned int
+size_section (abfd, idx)
+ bfd * abfd;
+ unsigned int idx;
+{
+
+ unsigned int size = 0;
+ fragS *frag = segment_info[idx].frchainP->frch_root;
+ while (frag)
+ {
+ size = frag->fr_address;
+ if (frag->fr_address != size)
+ {
+ fprintf (stderr, _("Out of step\n"));
+ size = frag->fr_address;
+ }
+
+ switch (frag->fr_type)
+ {
+#ifdef TC_COFF_SIZEMACHDEP
+ case rs_machine_dependent:
+ size += TC_COFF_SIZEMACHDEP (frag);
+ break;
+#endif
+ case rs_space:
+ assert (frag->fr_symbol == 0);
+ case rs_fill:
+ case rs_org:
+ size += frag->fr_fix;
+ size += frag->fr_offset * frag->fr_var;
+ break;
+ case rs_align:
+ case rs_align_code:
+ {
+ addressT off;
+
+ size += frag->fr_fix;
+ off = relax_align (size, frag->fr_offset);
+ if (frag->fr_subtype != 0 && off > frag->fr_subtype)
+ off = 0;
+ size += off;
+ }
+ break;
+ default:
+ BAD_CASE (frag->fr_type);
+ break;
+ }
+ frag = frag->fr_next;
+ }
+ segment_info[idx].scnhdr.s_size = size;
+ return size;
+}
+
+
+static unsigned int
+count_entries_in_chain (idx)
+ unsigned int idx;
+{
+ unsigned int nrelocs;
+ fixS *fixup_ptr;
+
+ /* Count the relocations */
+ fixup_ptr = segment_info[idx].fix_root;
+ nrelocs = 0;
+ while (fixup_ptr != (fixS *) NULL)
+ {
+ if (fixup_ptr->fx_done == 0 && TC_COUNT_RELOC (fixup_ptr))
+ {
+#ifdef TC_A29K
+ if (fixup_ptr->fx_r_type == RELOC_CONSTH)
+ nrelocs += 2;
+ else
+ nrelocs++;
+#else
+ nrelocs++;
+#endif
+ }
+
+ fixup_ptr = fixup_ptr->fx_next;
+ }
+ return nrelocs;
+}
+
+#ifdef TE_AUX
+
+static int compare_external_relocs PARAMS ((const PTR, const PTR));
+
+/* AUX's ld expects relocations to be sorted */
+static int
+compare_external_relocs (x, y)
+ const PTR x;
+ const PTR y;
+{
+ struct external_reloc *a = (struct external_reloc *) x;
+ struct external_reloc *b = (struct external_reloc *) y;
+ bfd_vma aadr = bfd_getb32 (a->r_vaddr);
+ bfd_vma badr = bfd_getb32 (b->r_vaddr);
+ return (aadr < badr ? -1 : badr < aadr ? 1 : 0);
+}
+
+#endif
+
+/* output all the relocations for a section */
+void
+do_relocs_for (abfd, h, file_cursor)
+ bfd * abfd;
+ object_headers * h;
+ unsigned long *file_cursor;
+{
+ unsigned int nrelocs;
+ unsigned int idx;
+ unsigned long reloc_start = *file_cursor;
+
+ for (idx = SEG_E0; idx < SEG_LAST; idx++)
+ {
+ if (segment_info[idx].scnhdr.s_name[0])
+ {
+ struct external_reloc *ext_ptr;
+ struct external_reloc *external_reloc_vec;
+ unsigned int external_reloc_size;
+ unsigned int base = segment_info[idx].scnhdr.s_paddr;
+ fixS *fix_ptr = segment_info[idx].fix_root;
+ nrelocs = count_entries_in_chain (idx);
+
+ if (nrelocs)
+ /* Bypass this stuff if no relocs. This also incidentally
+ avoids a SCO bug, where free(malloc(0)) tends to crash. */
+ {
+ external_reloc_size = nrelocs * RELSZ;
+ external_reloc_vec =
+ (struct external_reloc *) malloc (external_reloc_size);
+
+ ext_ptr = external_reloc_vec;
+
+ /* Fill in the internal coff style reloc struct from the
+ internal fix list. */
+ while (fix_ptr)
+ {
+ struct internal_reloc intr;
+
+ /* Only output some of the relocations */
+ if (fix_ptr->fx_done == 0 && TC_COUNT_RELOC (fix_ptr))
+ {
+#ifdef TC_RELOC_MANGLE
+ TC_RELOC_MANGLE (&segment_info[idx], fix_ptr, &intr,
+ base);
+
+#else
+ symbolS *dot;
+ symbolS *symbol_ptr = fix_ptr->fx_addsy;
+
+ intr.r_type = TC_COFF_FIX2RTYPE (fix_ptr);
+ intr.r_vaddr =
+ base + fix_ptr->fx_frag->fr_address + fix_ptr->fx_where;
+
+#ifdef TC_KEEP_FX_OFFSET
+ intr.r_offset = fix_ptr->fx_offset;
+#else
+ intr.r_offset = 0;
+#endif
+
+ while (symbol_ptr->sy_value.X_op == O_symbol
+ && (! S_IS_DEFINED (symbol_ptr)
+ || S_IS_COMMON (symbol_ptr)))
+ {
+ symbolS *n;
+
+ /* We must avoid looping, as that can occur
+ with a badly written program. */
+ n = symbol_ptr->sy_value.X_add_symbol;
+ if (n == symbol_ptr)
+ break;
+ symbol_ptr = n;
+ }
+
+ /* Turn the segment of the symbol into an offset. */
+ if (symbol_ptr)
+ {
+ resolve_symbol_value (symbol_ptr, 1);
+ if (! symbol_ptr->sy_resolved)
+ {
+ char *file;
+ unsigned int line;
+
+ if (expr_symbol_where (symbol_ptr, &file, &line))
+ as_bad_where (file, line,
+ _("unresolved relocation"));
+ else
+ as_bad (_("bad relocation: symbol `%s' not in symbol table"),
+ S_GET_NAME (symbol_ptr));
+ }
+ dot = segment_info[S_GET_SEGMENT (symbol_ptr)].dot;
+ if (dot)
+ {
+ intr.r_symndx = dot->sy_number;
+ }
+ else
+ {
+ intr.r_symndx = symbol_ptr->sy_number;
+ }
+
+ }
+ else
+ {
+ intr.r_symndx = -1;
+ }
+#endif
+
+ (void) bfd_coff_swap_reloc_out (abfd, &intr, ext_ptr);
+ ext_ptr++;
+
+#if defined(TC_A29K)
+
+ /* The 29k has a special kludge for the high 16 bit
+ reloc. Two relocations are emited, R_IHIHALF,
+ and R_IHCONST. The second one doesn't contain a
+ symbol, but uses the value for offset. */
+
+ if (intr.r_type == R_IHIHALF)
+ {
+ /* now emit the second bit */
+ intr.r_type = R_IHCONST;
+ intr.r_symndx = fix_ptr->fx_addnumber;
+ (void) bfd_coff_swap_reloc_out (abfd, &intr, ext_ptr);
+ ext_ptr++;
+ }
+#endif
+ }
+
+ fix_ptr = fix_ptr->fx_next;
+ }
+
+#ifdef TE_AUX
+ /* Sort the reloc table */
+ qsort ((PTR) external_reloc_vec, nrelocs,
+ sizeof (struct external_reloc), compare_external_relocs);
+#endif
+
+ /* Write out the reloc table */
+ bfd_write ((PTR) external_reloc_vec, 1, external_reloc_size,
+ abfd);
+ free (external_reloc_vec);
+
+ /* Fill in section header info. */
+ segment_info[idx].scnhdr.s_relptr = *file_cursor;
+ *file_cursor += external_reloc_size;
+ segment_info[idx].scnhdr.s_nreloc = nrelocs;
+ }
+ else
+ {
+ /* No relocs */
+ segment_info[idx].scnhdr.s_relptr = 0;
+ }
+ }
+ }
+ /* Set relocation_size field in file headers */
+ H_SET_RELOCATION_SIZE (h, *file_cursor - reloc_start, 0);
+}
+
+
+/* run through a frag chain and write out the data to go with it, fill
+ in the scnhdrs with the info on the file postions
+*/
+static void
+fill_section (abfd, h, file_cursor)
+ bfd * abfd;
+ object_headers *h;
+ unsigned long *file_cursor;
+{
+
+ unsigned int i;
+ unsigned int paddr = 0;
+
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ {
+ unsigned int offset = 0;
+ struct internal_scnhdr *s = &(segment_info[i].scnhdr);
+
+ PROGRESS (1);
+
+ if (s->s_name[0])
+ {
+ fragS *frag = segment_info[i].frchainP->frch_root;
+ char *buffer;
+
+ if (s->s_size == 0)
+ s->s_scnptr = 0;
+ else
+ {
+ buffer = xmalloc (s->s_size);
+ s->s_scnptr = *file_cursor;
+ }
+ know (s->s_paddr == paddr);
+
+ if (strcmp (s->s_name, ".text") == 0)
+ s->s_flags |= STYP_TEXT;
+ else if (strcmp (s->s_name, ".data") == 0)
+ s->s_flags |= STYP_DATA;
+ else if (strcmp (s->s_name, ".bss") == 0)
+ {
+ s->s_scnptr = 0;
+ s->s_flags |= STYP_BSS;
+
+ /* @@ Should make the i386 and a29k coff targets define
+ COFF_NOLOAD_PROBLEM, and have only one test here. */
+#ifndef TC_I386
+#ifndef TC_A29K
+#ifndef COFF_NOLOAD_PROBLEM
+ /* Apparently the SVR3 linker (and exec syscall) and UDI
+ mondfe progrem are confused by noload sections. */
+ s->s_flags |= STYP_NOLOAD;
+#endif
+#endif
+#endif
+ }
+ else if (strcmp (s->s_name, ".lit") == 0)
+ s->s_flags = STYP_LIT | STYP_TEXT;
+ else if (strcmp (s->s_name, ".init") == 0)
+ s->s_flags |= STYP_TEXT;
+ else if (strcmp (s->s_name, ".fini") == 0)
+ s->s_flags |= STYP_TEXT;
+ else if (strncmp (s->s_name, ".comment", 8) == 0)
+ s->s_flags |= STYP_INFO;
+
+ while (frag)
+ {
+ unsigned int fill_size;
+ switch (frag->fr_type)
+ {
+ case rs_machine_dependent:
+ if (frag->fr_fix)
+ {
+ memcpy (buffer + frag->fr_address,
+ frag->fr_literal,
+ (unsigned int) frag->fr_fix);
+ offset += frag->fr_fix;
+ }
+
+ break;
+ case rs_space:
+ assert (frag->fr_symbol == 0);
+ case rs_fill:
+ case rs_align:
+ case rs_align_code:
+ case rs_org:
+ if (frag->fr_fix)
+ {
+ memcpy (buffer + frag->fr_address,
+ frag->fr_literal,
+ (unsigned int) frag->fr_fix);
+ offset += frag->fr_fix;
+ }
+
+ fill_size = frag->fr_var;
+ if (fill_size && frag->fr_offset > 0)
+ {
+ unsigned int count;
+ unsigned int off = frag->fr_fix;
+ for (count = frag->fr_offset; count; count--)
+ {
+ if (fill_size + frag->fr_address + off <= s->s_size)
+ {
+ memcpy (buffer + frag->fr_address + off,
+ frag->fr_literal + frag->fr_fix,
+ fill_size);
+ off += fill_size;
+ offset += fill_size;
+ }
+ }
+ }
+ break;
+ case rs_broken_word:
+ break;
+ default:
+ abort ();
+ }
+ frag = frag->fr_next;
+ }
+
+ if (s->s_size != 0)
+ {
+ if (s->s_scnptr != 0)
+ {
+ bfd_write (buffer, s->s_size, 1, abfd);
+ *file_cursor += s->s_size;
+ }
+ free (buffer);
+ }
+ paddr += s->s_size;
+ }
+ }
+}
+
+/* Coff file generation & utilities */
+
+static void
+coff_header_append (abfd, h)
+ bfd * abfd;
+ object_headers * h;
+{
+ unsigned int i;
+ char buffer[1000];
+ char buffero[1000];
+#ifdef COFF_LONG_SECTION_NAMES
+ unsigned long string_size = 4;
+#endif
+
+ bfd_seek (abfd, 0, 0);
+
+#ifndef OBJ_COFF_OMIT_OPTIONAL_HEADER
+ H_SET_MAGIC_NUMBER (h, COFF_MAGIC);
+ H_SET_VERSION_STAMP (h, 0);
+ H_SET_ENTRY_POINT (h, 0);
+ H_SET_TEXT_START (h, segment_info[SEG_E0].frchainP->frch_root->fr_address);
+ H_SET_DATA_START (h, segment_info[SEG_E1].frchainP->frch_root->fr_address);
+ H_SET_SIZEOF_OPTIONAL_HEADER (h, bfd_coff_swap_aouthdr_out(abfd, &h->aouthdr,
+ buffero));
+#else /* defined (OBJ_COFF_OMIT_OPTIONAL_HEADER) */
+ H_SET_SIZEOF_OPTIONAL_HEADER (h, 0);
+#endif /* defined (OBJ_COFF_OMIT_OPTIONAL_HEADER) */
+
+ i = bfd_coff_swap_filehdr_out (abfd, &h->filehdr, buffer);
+
+ bfd_write (buffer, i, 1, abfd);
+ bfd_write (buffero, H_GET_SIZEOF_OPTIONAL_HEADER (h), 1, abfd);
+
+ for (i = SEG_E0; i < SEG_LAST; i++)
+ {
+ if (segment_info[i].scnhdr.s_name[0])
+ {
+ unsigned int size;
+
+#ifdef COFF_LONG_SECTION_NAMES
+ /* Support long section names as found in PE. This code
+ must coordinate with that in write_object_file and
+ w_strings. */
+ if (strlen (segment_info[i].name) > SCNNMLEN)
+ {
+ memset (segment_info[i].scnhdr.s_name, 0, SCNNMLEN);
+ sprintf (segment_info[i].scnhdr.s_name, "/%lu", string_size);
+ string_size += strlen (segment_info[i].name) + 1;
+ }
+#endif
+
+ size = bfd_coff_swap_scnhdr_out (abfd,
+ &(segment_info[i].scnhdr),
+ buffer);
+ if (size == 0)
+ as_bad (_("bfd_coff_swap_scnhdr_out failed"));
+ bfd_write (buffer, size, 1, abfd);
+ }
+ }
+}
+
+
+char *
+symbol_to_chars (abfd, where, symbolP)
+ bfd * abfd;
+ char *where;
+ symbolS * symbolP;
+{
+ unsigned int numaux = symbolP->sy_symbol.ost_entry.n_numaux;
+ unsigned int i;
+ valueT val;
+
+ /* Turn any symbols with register attributes into abs symbols */
+ if (S_GET_SEGMENT (symbolP) == reg_section)
+ {
+ S_SET_SEGMENT (symbolP, absolute_section);
+ }
+ /* At the same time, relocate all symbols to their output value */
+
+#ifndef TE_PE
+ val = (segment_info[S_GET_SEGMENT (symbolP)].scnhdr.s_paddr
+ + S_GET_VALUE (symbolP));
+#else
+ val = S_GET_VALUE (symbolP);
+#endif
+
+ S_SET_VALUE (symbolP, val);
+
+ symbolP->sy_symbol.ost_entry.n_value = val;
+
+ where += bfd_coff_swap_sym_out (abfd, &symbolP->sy_symbol.ost_entry,
+ where);
+
+ for (i = 0; i < numaux; i++)
+ {
+ where += bfd_coff_swap_aux_out (abfd,
+ &symbolP->sy_symbol.ost_auxent[i],
+ S_GET_DATA_TYPE (symbolP),
+ S_GET_STORAGE_CLASS (symbolP),
+ i, numaux, where);
+ }
+ return where;
+
+}
+
+void
+coff_obj_symbol_new_hook (symbolP)
+ symbolS *symbolP;
+{
+ char underscore = 0; /* Symbol has leading _ */
+
+ /* Effective symbol */
+ /* Store the pointer in the offset. */
+ S_SET_ZEROES (symbolP, 0L);
+ S_SET_DATA_TYPE (symbolP, T_NULL);
+ S_SET_STORAGE_CLASS (symbolP, 0);
+ S_SET_NUMBER_AUXILIARY (symbolP, 0);
+ /* Additional information */
+ symbolP->sy_symbol.ost_flags = 0;
+ /* Auxiliary entries */
+ memset ((char *) &symbolP->sy_symbol.ost_auxent[0], 0, AUXESZ);
+
+ if (S_IS_STRING (symbolP))
+ SF_SET_STRING (symbolP);
+ if (!underscore && S_IS_LOCAL (symbolP))
+ SF_SET_LOCAL (symbolP);
+}
+
+/*
+ * Handle .ln directives.
+ */
+
+static void
+obj_coff_ln (appline)
+ int appline;
+{
+ int l;
+
+ if (! appline && def_symbol_in_progress != NULL)
+ {
+ as_warn (_(".ln pseudo-op inside .def/.endef: ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ } /* wrong context */
+
+ l = get_absolute_expression ();
+ c_line_new (0, frag_now_fix (), l, frag_now);
+
+ if (appline)
+ new_logical_line ((char *) NULL, l - 1);
+
+#ifndef NO_LISTING
+ {
+ extern int listing;
+
+ if (listing)
+ {
+ if (! appline)
+ l += line_base - 1;
+ listing_source_line ((unsigned int) l);
+ }
+
+ }
+#endif
+ demand_empty_rest_of_line ();
+}
+
+/*
+ * def()
+ *
+ * Handle .def directives.
+ *
+ * One might ask : why can't we symbol_new if the symbol does not
+ * already exist and fill it with debug information. Because of
+ * the C_EFCN special symbol. It would clobber the value of the
+ * function symbol before we have a chance to notice that it is
+ * a C_EFCN. And a second reason is that the code is more clear this
+ * way. (at least I think it is :-).
+ *
+ */
+
+#define SKIP_SEMI_COLON() while (*input_line_pointer++ != ';')
+#define SKIP_WHITESPACES() while (*input_line_pointer == ' ' || \
+ *input_line_pointer == '\t') \
+ input_line_pointer++;
+
+static void
+obj_coff_def (what)
+ int what;
+{
+ char name_end; /* Char after the end of name */
+ char *symbol_name; /* Name of the debug symbol */
+ char *symbol_name_copy; /* Temporary copy of the name */
+ unsigned int symbol_name_length;
+
+ if (def_symbol_in_progress != NULL)
+ {
+ as_warn (_(".def pseudo-op used inside of .def/.endef: ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ } /* if not inside .def/.endef */
+
+ SKIP_WHITESPACES ();
+
+ def_symbol_in_progress = (symbolS *) obstack_alloc (&notes, sizeof (*def_symbol_in_progress));
+ memset (def_symbol_in_progress, 0, sizeof (*def_symbol_in_progress));
+
+ symbol_name = input_line_pointer;
+ name_end = get_symbol_end ();
+ symbol_name_length = strlen (symbol_name);
+ symbol_name_copy = xmalloc (symbol_name_length + 1);
+ strcpy (symbol_name_copy, symbol_name);
+#ifdef tc_canonicalize_symbol_name
+ symbol_name_copy = tc_canonicalize_symbol_name (symbol_name_copy);
+#endif
+
+ /* Initialize the new symbol */
+#ifdef STRIP_UNDERSCORE
+ S_SET_NAME (def_symbol_in_progress, (*symbol_name_copy == '_'
+ ? symbol_name_copy + 1
+ : symbol_name_copy));
+#else /* STRIP_UNDERSCORE */
+ S_SET_NAME (def_symbol_in_progress, symbol_name_copy);
+#endif /* STRIP_UNDERSCORE */
+ /* free(symbol_name_copy); */
+ def_symbol_in_progress->sy_name_offset = (unsigned long) ~0;
+ def_symbol_in_progress->sy_number = ~0;
+ def_symbol_in_progress->sy_frag = &zero_address_frag;
+ S_SET_VALUE (def_symbol_in_progress, 0);
+
+ if (S_IS_STRING (def_symbol_in_progress))
+ SF_SET_STRING (def_symbol_in_progress);
+
+ *input_line_pointer = name_end;
+
+ demand_empty_rest_of_line ();
+}
+
+unsigned int dim_index;
+
+
+static void
+obj_coff_endef (ignore)
+ int ignore;
+{
+ symbolS *symbolP = 0;
+ /* DIM BUG FIX sac@cygnus.com */
+ dim_index = 0;
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".endef pseudo-op used outside of .def/.endef: ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ } /* if not inside .def/.endef */
+
+ /* Set the section number according to storage class. */
+ switch (S_GET_STORAGE_CLASS (def_symbol_in_progress))
+ {
+ case C_STRTAG:
+ case C_ENTAG:
+ case C_UNTAG:
+ SF_SET_TAG (def_symbol_in_progress);
+ /* intentional fallthrough */
+ case C_FILE:
+ case C_TPDEF:
+ SF_SET_DEBUG (def_symbol_in_progress);
+ S_SET_SEGMENT (def_symbol_in_progress, SEG_DEBUG);
+ break;
+
+ case C_EFCN:
+ SF_SET_LOCAL (def_symbol_in_progress); /* Do not emit this symbol. */
+ /* intentional fallthrough */
+ case C_BLOCK:
+ SF_SET_PROCESS (def_symbol_in_progress); /* Will need processing before writing */
+ /* intentional fallthrough */
+ case C_FCN:
+ S_SET_SEGMENT (def_symbol_in_progress, SEG_E0);
+
+ if (strcmp (S_GET_NAME (def_symbol_in_progress), ".bf") == 0)
+ { /* .bf */
+ if (function_lineoff < 0)
+ {
+ fprintf (stderr, _("`.bf' symbol without preceding function\n"));
+ } /* missing function symbol */
+ SA_GET_SYM_LNNOPTR (last_line_symbol) = function_lineoff;
+
+ SF_SET_PROCESS (last_line_symbol);
+ SF_SET_ADJ_LNNOPTR (last_line_symbol);
+ SF_SET_PROCESS (def_symbol_in_progress);
+ function_lineoff = -1;
+ }
+ /* Value is always set to . */
+ def_symbol_in_progress->sy_frag = frag_now;
+ S_SET_VALUE (def_symbol_in_progress, (valueT) frag_now_fix ());
+ break;
+
+#ifdef C_AUTOARG
+ case C_AUTOARG:
+#endif /* C_AUTOARG */
+ case C_AUTO:
+ case C_REG:
+ case C_MOS:
+ case C_MOE:
+ case C_MOU:
+ case C_ARG:
+ case C_REGPARM:
+ case C_FIELD:
+ case C_EOS:
+ SF_SET_DEBUG (def_symbol_in_progress);
+ S_SET_SEGMENT (def_symbol_in_progress, absolute_section);
+ break;
+
+ case C_EXT:
+ case C_WEAKEXT:
+#ifdef TE_PE
+ case C_NT_WEAK:
+#endif
+ case C_STAT:
+ case C_LABEL:
+ /* Valid but set somewhere else (s_comm, s_lcomm, colon) */
+ break;
+
+ case C_USTATIC:
+ case C_EXTDEF:
+ case C_ULABEL:
+ as_warn (_("unexpected storage class %d"), S_GET_STORAGE_CLASS (def_symbol_in_progress));
+ break;
+ } /* switch on storage class */
+
+ /* Now that we have built a debug symbol, try to find if we should
+ merge with an existing symbol or not. If a symbol is C_EFCN or
+ absolute_section or untagged SEG_DEBUG it never merges. We also
+ don't merge labels, which are in a different namespace, nor
+ symbols which have not yet been defined since they are typically
+ unique, nor do we merge tags with non-tags. */
+
+ /* Two cases for functions. Either debug followed by definition or
+ definition followed by debug. For definition first, we will
+ merge the debug symbol into the definition. For debug first, the
+ lineno entry MUST point to the definition function or else it
+ will point off into space when crawl_symbols() merges the debug
+ symbol into the real symbol. Therefor, let's presume the debug
+ symbol is a real function reference. */
+
+ /* FIXME-SOON If for some reason the definition label/symbol is
+ never seen, this will probably leave an undefined symbol at link
+ time. */
+
+ if (S_GET_STORAGE_CLASS (def_symbol_in_progress) == C_EFCN
+ || S_GET_STORAGE_CLASS (def_symbol_in_progress) == C_LABEL
+ || (S_GET_SEGMENT (def_symbol_in_progress) == SEG_DEBUG
+ && !SF_GET_TAG (def_symbol_in_progress))
+ || S_GET_SEGMENT (def_symbol_in_progress) == absolute_section
+ || def_symbol_in_progress->sy_value.X_op != O_constant
+ || (symbolP = symbol_find_base (S_GET_NAME (def_symbol_in_progress), DO_NOT_STRIP)) == NULL
+ || (SF_GET_TAG (def_symbol_in_progress) != SF_GET_TAG (symbolP)))
+ {
+ symbol_append (def_symbol_in_progress, symbol_lastP, &symbol_rootP,
+ &symbol_lastP);
+ }
+ else
+ {
+ /* This symbol already exists, merge the newly created symbol
+ into the old one. This is not mandatory. The linker can
+ handle duplicate symbols correctly. But I guess that it save
+ a *lot* of space if the assembly file defines a lot of
+ symbols. [loic] */
+
+ /* The debug entry (def_symbol_in_progress) is merged into the
+ previous definition. */
+
+ c_symbol_merge (def_symbol_in_progress, symbolP);
+ /* FIXME-SOON Should *def_symbol_in_progress be free'd? xoxorich. */
+ def_symbol_in_progress = symbolP;
+
+ if (SF_GET_FUNCTION (def_symbol_in_progress)
+ || SF_GET_TAG (def_symbol_in_progress)
+ || S_GET_STORAGE_CLASS (def_symbol_in_progress) == C_STAT)
+ {
+ /* For functions, and tags, and static symbols, the symbol
+ *must* be where the debug symbol appears. Move the
+ existing symbol to the current place. */
+ /* If it already is at the end of the symbol list, do nothing */
+ if (def_symbol_in_progress != symbol_lastP)
+ {
+ symbol_remove (def_symbol_in_progress, &symbol_rootP,
+ &symbol_lastP);
+ symbol_append (def_symbol_in_progress, symbol_lastP,
+ &symbol_rootP, &symbol_lastP);
+ } /* if not already in place */
+ } /* if function */
+ } /* normal or mergable */
+
+ if (SF_GET_TAG (def_symbol_in_progress))
+ {
+ symbolS *oldtag;
+
+ oldtag = symbol_find_base (S_GET_NAME (def_symbol_in_progress),
+ DO_NOT_STRIP);
+ if (oldtag == NULL || ! SF_GET_TAG (oldtag))
+ tag_insert (S_GET_NAME (def_symbol_in_progress),
+ def_symbol_in_progress);
+ }
+
+ if (SF_GET_FUNCTION (def_symbol_in_progress))
+ {
+ know (sizeof (def_symbol_in_progress) <= sizeof (long));
+ function_lineoff
+ = c_line_new (def_symbol_in_progress, 0, 0, &zero_address_frag);
+
+ SF_SET_PROCESS (def_symbol_in_progress);
+
+ if (symbolP == NULL)
+ {
+ /* That is, if this is the first time we've seen the
+ function... */
+ symbol_table_insert (def_symbol_in_progress);
+ } /* definition follows debug */
+ } /* Create the line number entry pointing to the function being defined */
+
+ def_symbol_in_progress = NULL;
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_dim (ignore)
+ int ignore;
+{
+ int dim_index;
+
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".dim pseudo-op used outside of .def/.endef: ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ } /* if not inside .def/.endef */
+
+ S_SET_NUMBER_AUXILIARY (def_symbol_in_progress, 1);
+
+ for (dim_index = 0; dim_index < DIMNUM; dim_index++)
+ {
+ SKIP_WHITESPACES ();
+ SA_SET_SYM_DIMEN (def_symbol_in_progress, dim_index,
+ get_absolute_expression ());
+
+ switch (*input_line_pointer)
+ {
+ case ',':
+ input_line_pointer++;
+ break;
+
+ default:
+ as_warn (_("badly formed .dim directive ignored"));
+ /* intentional fallthrough */
+ case '\n':
+ case ';':
+ dim_index = DIMNUM;
+ break;
+ }
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_line (ignore)
+ int ignore;
+{
+ int this_base;
+ const char *name;
+
+ if (def_symbol_in_progress == NULL)
+ {
+ obj_coff_ln (0);
+ return;
+ }
+
+ name = S_GET_NAME (def_symbol_in_progress);
+ this_base = get_absolute_expression ();
+
+ /* Only .bf symbols indicate the use of a new base line number; the
+ line numbers associated with .ef, .bb, .eb are relative to the
+ start of the containing function. */
+ if (!strcmp (".bf", name))
+ {
+#if 0 /* XXX Can we ever have line numbers going backwards? */
+ if (this_base > line_base)
+#endif
+ {
+ line_base = this_base;
+ }
+
+#ifndef NO_LISTING
+ {
+ extern int listing;
+ if (listing)
+ {
+ listing_source_line ((unsigned int) line_base);
+ }
+ }
+#endif
+ }
+
+ S_SET_NUMBER_AUXILIARY (def_symbol_in_progress, 1);
+ SA_SET_SYM_LNNO (def_symbol_in_progress, this_base);
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_size (ignore)
+ int ignore;
+{
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".size pseudo-op used outside of .def/.endef ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ } /* if not inside .def/.endef */
+
+ S_SET_NUMBER_AUXILIARY (def_symbol_in_progress, 1);
+ SA_SET_SYM_SIZE (def_symbol_in_progress, get_absolute_expression ());
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_scl (ignore)
+ int ignore;
+{
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".scl pseudo-op used outside of .def/.endef ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ } /* if not inside .def/.endef */
+
+ S_SET_STORAGE_CLASS (def_symbol_in_progress, get_absolute_expression ());
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_tag (ignore)
+ int ignore;
+{
+ char *symbol_name;
+ char name_end;
+
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".tag pseudo-op used outside of .def/.endef ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ S_SET_NUMBER_AUXILIARY (def_symbol_in_progress, 1);
+ symbol_name = input_line_pointer;
+ name_end = get_symbol_end ();
+#ifdef tc_canonicalize_symbol_name
+ symbol_name = tc_canonicalize_symbol_name (symbol_name);
+#endif
+
+ /* Assume that the symbol referred to by .tag is always defined.
+ This was a bad assumption. I've added find_or_make. xoxorich. */
+ SA_SET_SYM_TAGNDX (def_symbol_in_progress,
+ (long) tag_find_or_make (symbol_name));
+ if (SA_GET_SYM_TAGNDX (def_symbol_in_progress) == 0L)
+ {
+ as_warn (_("tag not found for .tag %s"), symbol_name);
+ } /* not defined */
+
+ SF_SET_TAGGED (def_symbol_in_progress);
+ *input_line_pointer = name_end;
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_type (ignore)
+ int ignore;
+{
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".type pseudo-op used outside of .def/.endef ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ } /* if not inside .def/.endef */
+
+ S_SET_DATA_TYPE (def_symbol_in_progress, get_absolute_expression ());
+
+ if (ISFCN (S_GET_DATA_TYPE (def_symbol_in_progress)) &&
+ S_GET_STORAGE_CLASS (def_symbol_in_progress) != C_TPDEF)
+ {
+ SF_SET_FUNCTION (def_symbol_in_progress);
+ } /* is a function */
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_val (ignore)
+ int ignore;
+{
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".val pseudo-op used outside of .def/.endef ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ } /* if not inside .def/.endef */
+
+ if (is_name_beginner (*input_line_pointer))
+ {
+ char *symbol_name = input_line_pointer;
+ char name_end = get_symbol_end ();
+
+#ifdef tc_canonicalize_symbol_name
+ symbol_name = tc_canonicalize_symbol_name (symbol_name);
+#endif
+
+ if (!strcmp (symbol_name, "."))
+ {
+ def_symbol_in_progress->sy_frag = frag_now;
+ S_SET_VALUE (def_symbol_in_progress, (valueT) frag_now_fix ());
+ /* If the .val is != from the .def (e.g. statics) */
+ }
+ else if (strcmp (S_GET_NAME (def_symbol_in_progress), symbol_name))
+ {
+ def_symbol_in_progress->sy_value.X_op = O_symbol;
+ def_symbol_in_progress->sy_value.X_add_symbol =
+ symbol_find_or_make (symbol_name);
+ def_symbol_in_progress->sy_value.X_op_symbol = NULL;
+ def_symbol_in_progress->sy_value.X_add_number = 0;
+
+ /* If the segment is undefined when the forward reference is
+ resolved, then copy the segment id from the forward
+ symbol. */
+ SF_SET_GET_SEGMENT (def_symbol_in_progress);
+
+ /* FIXME: gcc can generate address expressions
+ here in unusual cases (search for "obscure"
+ in sdbout.c). We just ignore the offset
+ here, thus generating incorrect debugging
+ information. We ignore the rest of the
+ line just below. */
+ }
+ /* Otherwise, it is the name of a non debug symbol and
+ its value will be calculated later. */
+ *input_line_pointer = name_end;
+
+ /* FIXME: this is to avoid an error message in the
+ FIXME case mentioned just above. */
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+ else
+ {
+ S_SET_VALUE (def_symbol_in_progress,
+ (valueT) get_absolute_expression ());
+ } /* if symbol based */
+
+ demand_empty_rest_of_line ();
+}
+
+#ifdef TE_PE
+
+/* Handle the .linkonce pseudo-op. This is parsed by s_linkonce in
+ read.c, which then calls this object file format specific routine. */
+
+void
+obj_coff_pe_handle_link_once (type)
+ enum linkonce_type type;
+{
+ seg_info (now_seg)->scnhdr.s_flags |= IMAGE_SCN_LNK_COMDAT;
+
+ /* We store the type in the seg_info structure, and use it to set up
+ the auxiliary entry for the section symbol in c_section_symbol. */
+ seg_info (now_seg)->linkonce = type;
+}
+
+#endif /* TE_PE */
+
+void
+coff_obj_read_begin_hook ()
+{
+ /* These had better be the same. Usually 18 bytes. */
+#ifndef BFD_HEADERS
+ know (sizeof (SYMENT) == sizeof (AUXENT));
+ know (SYMESZ == AUXESZ);
+#endif
+ tag_init ();
+}
+
+/* This function runs through the symbol table and puts all the
+ externals onto another chain */
+
+/* The chain of globals. */
+symbolS *symbol_globalP;
+symbolS *symbol_global_lastP;
+
+/* The chain of externals */
+symbolS *symbol_externP;
+symbolS *symbol_extern_lastP;
+
+stack *block_stack;
+symbolS *last_functionP;
+static symbolS *last_bfP;
+symbolS *last_tagP;
+
+static unsigned int
+yank_symbols ()
+{
+ symbolS *symbolP;
+ unsigned int symbol_number = 0;
+ unsigned int last_file_symno = 0;
+
+ struct filename_list *filename_list_scan = filename_list_head;
+
+ for (symbolP = symbol_rootP;
+ symbolP;
+ symbolP = symbolP ? symbol_next (symbolP) : symbol_rootP)
+ {
+ if (symbolP->sy_mri_common)
+ {
+ if (S_GET_STORAGE_CLASS (symbolP) == C_EXT
+#ifdef TE_PE
+ || S_GET_STORAGE_CLASS (symbolP) == C_NT_WEAK
+#endif
+ || S_GET_STORAGE_CLASS (symbolP) == C_WEAKEXT)
+ as_bad (_("%s: global symbols not supported in common sections"),
+ S_GET_NAME (symbolP));
+ symbol_remove (symbolP, &symbol_rootP, &symbol_lastP);
+ continue;
+ }
+
+ if (!SF_GET_DEBUG (symbolP))
+ {
+ /* Debug symbols do not need all this rubbish */
+ symbolS *real_symbolP;
+
+ /* L* and C_EFCN symbols never merge. */
+ if (!SF_GET_LOCAL (symbolP)
+ && !SF_GET_STATICS (symbolP)
+ && S_GET_STORAGE_CLASS (symbolP) != C_LABEL
+ && symbolP->sy_value.X_op == O_constant
+ && (real_symbolP = symbol_find_base (S_GET_NAME (symbolP), DO_NOT_STRIP))
+ && real_symbolP != symbolP)
+ {
+ /* FIXME-SOON: where do dups come from?
+ Maybe tag references before definitions? xoxorich. */
+ /* Move the debug data from the debug symbol to the
+ real symbol. Do NOT do the oposite (i.e. move from
+ real symbol to debug symbol and remove real symbol from the
+ list.) Because some pointers refer to the real symbol
+ whereas no pointers refer to the debug symbol. */
+ c_symbol_merge (symbolP, real_symbolP);
+ /* Replace the current symbol by the real one */
+ /* The symbols will never be the last or the first
+ because : 1st symbol is .file and 3 last symbols are
+ .text, .data, .bss */
+ symbol_remove (real_symbolP, &symbol_rootP, &symbol_lastP);
+ symbol_insert (real_symbolP, symbolP, &symbol_rootP, &symbol_lastP);
+ symbol_remove (symbolP, &symbol_rootP, &symbol_lastP);
+ symbolP = real_symbolP;
+ } /* if not local but dup'd */
+
+ if (flag_readonly_data_in_text && (S_GET_SEGMENT (symbolP) == SEG_E1))
+ {
+ S_SET_SEGMENT (symbolP, SEG_E0);
+ } /* push data into text */
+
+ resolve_symbol_value (symbolP, 1);
+
+ if (S_GET_STORAGE_CLASS (symbolP) == C_NULL)
+ {
+ if (!S_IS_DEFINED (symbolP) && !SF_GET_LOCAL (symbolP))
+ {
+ S_SET_EXTERNAL (symbolP);
+ }
+ else if (S_GET_SEGMENT (symbolP) == SEG_E0)
+ {
+ S_SET_STORAGE_CLASS (symbolP, C_LABEL);
+ }
+ else
+ {
+ S_SET_STORAGE_CLASS (symbolP, C_STAT);
+ }
+ }
+
+ /* Mainly to speed up if not -g */
+ if (SF_GET_PROCESS (symbolP))
+ {
+ /* Handle the nested blocks auxiliary info. */
+ if (S_GET_STORAGE_CLASS (symbolP) == C_BLOCK)
+ {
+ if (!strcmp (S_GET_NAME (symbolP), ".bb"))
+ stack_push (block_stack, (char *) &symbolP);
+ else
+ { /* .eb */
+ register symbolS *begin_symbolP;
+ begin_symbolP = *(symbolS **) stack_pop (block_stack);
+ if (begin_symbolP == (symbolS *) 0)
+ as_warn (_("mismatched .eb"));
+ else
+ SA_SET_SYM_ENDNDX (begin_symbolP, symbol_number + 2);
+ }
+ }
+ /* If we are able to identify the type of a function, and we
+ are out of a function (last_functionP == 0) then, the
+ function symbol will be associated with an auxiliary
+ entry. */
+ if (last_functionP == (symbolS *) 0 &&
+ SF_GET_FUNCTION (symbolP))
+ {
+ last_functionP = symbolP;
+
+ if (S_GET_NUMBER_AUXILIARY (symbolP) < 1)
+ {
+ S_SET_NUMBER_AUXILIARY (symbolP, 1);
+ } /* make it at least 1 */
+
+ /* Clobber possible stale .dim information. */
+#if 0
+ /* Iffed out by steve - this fries the lnnoptr info too */
+ bzero (symbolP->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_ary.x_dimen,
+ sizeof (symbolP->sy_symbol.ost_auxent[0].x_sym.x_fcnary.x_ary.x_dimen));
+#endif
+ }
+ if (S_GET_STORAGE_CLASS (symbolP) == C_FCN)
+ {
+ if (strcmp (S_GET_NAME (symbolP), ".bf") == 0)
+ {
+ if (last_bfP != NULL)
+ SA_SET_SYM_ENDNDX (last_bfP, symbol_number);
+ last_bfP = symbolP;
+ }
+ }
+ else if (S_GET_STORAGE_CLASS (symbolP) == C_EFCN)
+ {
+ /* I don't even know if this is needed for sdb. But
+ the standard assembler generates it, so... */
+ if (last_functionP == (symbolS *) 0)
+ as_fatal (_("C_EFCN symbol out of scope"));
+ SA_SET_SYM_FSIZE (last_functionP,
+ (long) (S_GET_VALUE (symbolP) -
+ S_GET_VALUE (last_functionP)));
+ SA_SET_SYM_ENDNDX (last_functionP, symbol_number);
+ last_functionP = (symbolS *) 0;
+ }
+ }
+ }
+ else if (SF_GET_TAG (symbolP))
+ {
+ /* First descriptor of a structure must point to
+ the first slot after the structure description. */
+ last_tagP = symbolP;
+
+ }
+ else if (S_GET_STORAGE_CLASS (symbolP) == C_EOS)
+ {
+ /* +2 take in account the current symbol */
+ SA_SET_SYM_ENDNDX (last_tagP, symbol_number + 2);
+ }
+ else if (S_GET_STORAGE_CLASS (symbolP) == C_FILE)
+ {
+ /* If the filename was too long to fit in the
+ auxent, put it in the string table */
+ if (SA_GET_FILE_FNAME_ZEROS (symbolP) == 0
+ && SA_GET_FILE_FNAME_OFFSET (symbolP) != 0)
+ {
+ SA_SET_FILE_FNAME_OFFSET (symbolP, string_byte_count);
+ string_byte_count += strlen (filename_list_scan->filename) + 1;
+ filename_list_scan = filename_list_scan->next;
+ }
+ if (S_GET_VALUE (symbolP))
+ {
+ S_SET_VALUE (symbolP, last_file_symno);
+ last_file_symno = symbol_number;
+ } /* no one points at the first .file symbol */
+ } /* if debug or tag or eos or file */
+
+#ifdef tc_frob_coff_symbol
+ tc_frob_coff_symbol (symbolP);
+#endif
+
+ /* We must put the external symbols apart. The loader
+ does not bomb if we do not. But the references in
+ the endndx field for a .bb symbol are not corrected
+ if an external symbol is removed between .bb and .be.
+ I.e in the following case :
+ [20] .bb endndx = 22
+ [21] foo external
+ [22] .be
+ ld will move the symbol 21 to the end of the list but
+ endndx will still be 22 instead of 21. */
+
+
+ if (SF_GET_LOCAL (symbolP))
+ {
+ /* remove C_EFCN and LOCAL (L...) symbols */
+ /* next pointer remains valid */
+ symbol_remove (symbolP, &symbol_rootP, &symbol_lastP);
+
+ }
+ else if (symbolP->sy_value.X_op == O_symbol
+ && (! S_IS_DEFINED (symbolP) || S_IS_COMMON (symbolP)))
+ {
+ /* Skip symbols which were equated to undefined or common
+ symbols. */
+ symbol_remove (symbolP, &symbol_rootP, &symbol_lastP);
+ }
+ else if (!S_IS_DEFINED (symbolP)
+ && !S_IS_DEBUG (symbolP)
+ && !SF_GET_STATICS (symbolP)
+ && (S_GET_STORAGE_CLASS (symbolP) == C_EXT
+#ifdef TE_PE
+ || S_GET_STORAGE_CLASS (symbolP) == C_NT_WEAK
+#endif
+ || S_GET_STORAGE_CLASS (symbolP) == C_WEAKEXT))
+ {
+ /* if external, Remove from the list */
+ symbolS *hold = symbol_previous (symbolP);
+
+ symbol_remove (symbolP, &symbol_rootP, &symbol_lastP);
+ symbol_clear_list_pointers (symbolP);
+ symbol_append (symbolP, symbol_extern_lastP, &symbol_externP, &symbol_extern_lastP);
+ symbolP = hold;
+ }
+ else if (! S_IS_DEBUG (symbolP)
+ && ! SF_GET_STATICS (symbolP)
+ && ! SF_GET_FUNCTION (symbolP)
+ && (S_GET_STORAGE_CLASS (symbolP) == C_EXT
+#ifdef TE_PE
+ || S_GET_STORAGE_CLASS (symbolP) == C_NT_WEAK
+#endif
+ || S_GET_STORAGE_CLASS (symbolP) == C_NT_WEAK))
+ {
+ symbolS *hold = symbol_previous (symbolP);
+
+ /* The O'Reilly COFF book says that defined global symbols
+ come at the end of the symbol table, just before
+ undefined global symbols. */
+
+ symbol_remove (symbolP, &symbol_rootP, &symbol_lastP);
+ symbol_clear_list_pointers (symbolP);
+ symbol_append (symbolP, symbol_global_lastP, &symbol_globalP,
+ &symbol_global_lastP);
+ symbolP = hold;
+ }
+ else
+ {
+ if (SF_GET_STRING (symbolP))
+ {
+ symbolP->sy_name_offset = string_byte_count;
+ string_byte_count += strlen (S_GET_NAME (symbolP)) + 1;
+ }
+ else
+ {
+ symbolP->sy_name_offset = 0;
+ } /* fix "long" names */
+
+ symbolP->sy_number = symbol_number;
+ symbol_number += 1 + S_GET_NUMBER_AUXILIARY (symbolP);
+ } /* if local symbol */
+ } /* traverse the symbol list */
+ return symbol_number;
+
+}
+
+
+static unsigned int
+glue_symbols (head, tail)
+ symbolS **head;
+ symbolS **tail;
+{
+ unsigned int symbol_number = 0;
+
+ while (*head != NULL)
+ {
+ symbolS *tmp = *head;
+
+ /* append */
+ symbol_remove (tmp, head, tail);
+ symbol_append (tmp, symbol_lastP, &symbol_rootP, &symbol_lastP);
+
+ /* and process */
+ if (SF_GET_STRING (tmp))
+ {
+ tmp->sy_name_offset = string_byte_count;
+ string_byte_count += strlen (S_GET_NAME (tmp)) + 1;
+ }
+ else
+ {
+ tmp->sy_name_offset = 0;
+ } /* fix "long" names */
+
+ tmp->sy_number = symbol_number;
+ symbol_number += 1 + S_GET_NUMBER_AUXILIARY (tmp);
+ } /* append the entire extern chain */
+
+ return symbol_number;
+}
+
+static unsigned int
+tie_tags ()
+{
+ unsigned int symbol_number = 0;
+ symbolS *symbolP;
+
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next (symbolP))
+ {
+ symbolP->sy_number = symbol_number;
+
+ if (SF_GET_TAGGED (symbolP))
+ {
+ SA_SET_SYM_TAGNDX
+ (symbolP,
+ ((symbolS *) SA_GET_SYM_TAGNDX (symbolP))->sy_number);
+ }
+
+ symbol_number += 1 + S_GET_NUMBER_AUXILIARY (symbolP);
+ }
+
+ return symbol_number;
+}
+
+static void
+crawl_symbols (h, abfd)
+ object_headers *h;
+ bfd * abfd;
+{
+ unsigned int i;
+
+ /* Initialize the stack used to keep track of the matching .bb .be */
+
+ block_stack = stack_init (512, sizeof (symbolS *));
+
+ /* The symbol list should be ordered according to the following sequence
+ * order :
+ * . .file symbol
+ * . debug entries for functions
+ * . fake symbols for the sections, including .text .data and .bss
+ * . defined symbols
+ * . undefined symbols
+ * But this is not mandatory. The only important point is to put the
+ * undefined symbols at the end of the list.
+ */
+
+ /* Is there a .file symbol ? If not insert one at the beginning. */
+ if (symbol_rootP == NULL
+ || S_GET_STORAGE_CLASS (symbol_rootP) != C_FILE)
+ {
+ c_dot_file_symbol ("fake");
+ }
+
+ /*
+ * Build up static symbols for the sections, they are filled in later
+ */
+
+
+ for (i = SEG_E0; i < SEG_LAST; i++)
+ if (segment_info[i].scnhdr.s_name[0])
+ segment_info[i].dot = c_section_symbol (segment_info[i].name,
+ i - SEG_E0 + 1);
+
+ /* Take all the externals out and put them into another chain */
+ H_SET_SYMBOL_TABLE_SIZE (h, yank_symbols ());
+ /* Take the externals and glue them onto the end.*/
+ H_SET_SYMBOL_TABLE_SIZE (h,
+ (H_GET_SYMBOL_COUNT (h)
+ + glue_symbols (&symbol_globalP,
+ &symbol_global_lastP)
+ + glue_symbols (&symbol_externP,
+ &symbol_extern_lastP)));
+
+ H_SET_SYMBOL_TABLE_SIZE (h, tie_tags ());
+ know (symbol_globalP == NULL);
+ know (symbol_global_lastP == NULL);
+ know (symbol_externP == NULL);
+ know (symbol_extern_lastP == NULL);
+}
+
+/*
+ * Find strings by crawling along symbol table chain.
+ */
+
+void
+w_strings (where)
+ char *where;
+{
+ symbolS *symbolP;
+ struct filename_list *filename_list_scan = filename_list_head;
+
+ /* Gotta do md_ byte-ordering stuff for string_byte_count first - KWK */
+ md_number_to_chars (where, (valueT) string_byte_count, 4);
+ where += 4;
+
+#ifdef COFF_LONG_SECTION_NAMES
+ /* Support long section names as found in PE. This code must
+ coordinate with that in coff_header_append and write_object_file. */
+ {
+ unsigned int i;
+
+ for (i = SEG_E0; i < SEG_LAST; i++)
+ {
+ if (segment_info[i].scnhdr.s_name[0]
+ && strlen (segment_info[i].name) > SCNNMLEN)
+ {
+ unsigned int size;
+
+ size = strlen (segment_info[i].name) + 1;
+ memcpy (where, segment_info[i].name, size);
+ where += size;
+ }
+ }
+ }
+#endif /* COFF_LONG_SECTION_NAMES */
+
+ for (symbolP = symbol_rootP;
+ symbolP;
+ symbolP = symbol_next (symbolP))
+ {
+ unsigned int size;
+
+ if (SF_GET_STRING (symbolP))
+ {
+ size = strlen (S_GET_NAME (symbolP)) + 1;
+ memcpy (where, S_GET_NAME (symbolP), size);
+ where += size;
+ }
+ if (S_GET_STORAGE_CLASS (symbolP) == C_FILE
+ && SA_GET_FILE_FNAME_ZEROS (symbolP) == 0
+ && SA_GET_FILE_FNAME_OFFSET (symbolP) != 0)
+ {
+ size = strlen (filename_list_scan->filename) + 1;
+ memcpy (where, filename_list_scan->filename, size);
+ filename_list_scan = filename_list_scan ->next;
+ where += size;
+ }
+ }
+}
+
+static void
+do_linenos_for (abfd, h, file_cursor)
+ bfd * abfd;
+ object_headers * h;
+ unsigned long *file_cursor;
+{
+ unsigned int idx;
+ unsigned long start = *file_cursor;
+
+ for (idx = SEG_E0; idx < SEG_LAST; idx++)
+ {
+ segment_info_type *s = segment_info + idx;
+
+
+ if (s->scnhdr.s_nlnno != 0)
+ {
+ struct lineno_list *line_ptr;
+
+ struct external_lineno *buffer =
+ (struct external_lineno *) xmalloc (s->scnhdr.s_nlnno * LINESZ);
+
+ struct external_lineno *dst = buffer;
+
+ /* Run through the table we've built and turn it into its external
+ form, take this chance to remove duplicates */
+
+ for (line_ptr = s->lineno_list_head;
+ line_ptr != (struct lineno_list *) NULL;
+ line_ptr = line_ptr->next)
+ {
+
+ if (line_ptr->line.l_lnno == 0)
+ {
+ /* Turn a pointer to a symbol into the symbols' index */
+ line_ptr->line.l_addr.l_symndx =
+ ((symbolS *) line_ptr->line.l_addr.l_symndx)->sy_number;
+ }
+ else
+ {
+ line_ptr->line.l_addr.l_paddr += ((struct frag *) (line_ptr->frag))->fr_address;
+ }
+
+
+ (void) bfd_coff_swap_lineno_out (abfd, &(line_ptr->line), dst);
+ dst++;
+
+ }
+
+ s->scnhdr.s_lnnoptr = *file_cursor;
+
+ bfd_write (buffer, 1, s->scnhdr.s_nlnno * LINESZ, abfd);
+ free (buffer);
+
+ *file_cursor += s->scnhdr.s_nlnno * LINESZ;
+ }
+ }
+ H_SET_LINENO_SIZE (h, *file_cursor - start);
+}
+
+
+/* Now we run through the list of frag chains in a segment and
+ make all the subsegment frags appear at the end of the
+ list, as if the seg 0 was extra long */
+
+static void
+remove_subsegs ()
+{
+ unsigned int i;
+
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ {
+ frchainS *head = segment_info[i].frchainP;
+ fragS dummy;
+ fragS *prev_frag = &dummy;
+
+ while (head && head->frch_seg == i)
+ {
+ prev_frag->fr_next = head->frch_root;
+ prev_frag = head->frch_last;
+ head = head->frch_next;
+ }
+ prev_frag->fr_next = 0;
+ }
+}
+
+unsigned long machine;
+int coff_flags;
+extern void
+write_object_file ()
+{
+ int i;
+ const char *name;
+ struct frchain *frchain_ptr;
+
+ object_headers headers;
+ unsigned long file_cursor;
+ bfd *abfd;
+ unsigned int addr;
+ abfd = bfd_openw (out_file_name, TARGET_FORMAT);
+
+
+ if (abfd == 0)
+ {
+ as_perror (_("FATAL: Can't create %s"), out_file_name);
+ exit (EXIT_FAILURE);
+ }
+ bfd_set_format (abfd, bfd_object);
+ bfd_set_arch_mach (abfd, BFD_ARCH, machine);
+
+ string_byte_count = 4;
+
+ for (frchain_ptr = frchain_root;
+ frchain_ptr != (struct frchain *) NULL;
+ frchain_ptr = frchain_ptr->frch_next)
+ {
+ /* Run through all the sub-segments and align them up. Also
+ close any open frags. We tack a .fill onto the end of the
+ frag chain so that any .align's size can be worked by looking
+ at the next frag. */
+
+ subseg_set (frchain_ptr->frch_seg, frchain_ptr->frch_subseg);
+#ifndef SUB_SEGMENT_ALIGN
+#define SUB_SEGMENT_ALIGN(SEG) 1
+#endif
+#ifdef md_do_align
+ md_do_align (SUB_SEGMENT_ALIGN (now_seg), (char *) NULL, 0, 0,
+ alignment_done);
+#endif
+ frag_align (SUB_SEGMENT_ALIGN (now_seg), NOP_OPCODE, 0);
+#ifdef md_do_align
+ alignment_done:
+#endif
+ frag_wane (frag_now);
+ frag_now->fr_fix = 0;
+ know (frag_now->fr_next == NULL);
+ }
+
+
+ remove_subsegs ();
+
+
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ {
+ relax_segment (segment_info[i].frchainP->frch_root, i);
+ }
+
+ H_SET_NUMBER_OF_SECTIONS (&headers, 0);
+
+ /* Find out how big the sections are, and set the addresses. */
+ addr = 0;
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ {
+ long size;
+
+ segment_info[i].scnhdr.s_paddr = addr;
+ segment_info[i].scnhdr.s_vaddr = addr;
+
+ if (segment_info[i].scnhdr.s_name[0])
+ {
+ H_SET_NUMBER_OF_SECTIONS (&headers,
+ H_GET_NUMBER_OF_SECTIONS (&headers) + 1);
+
+#ifdef COFF_LONG_SECTION_NAMES
+ /* Support long section names as found in PE. This code
+ must coordinate with that in coff_header_append and
+ w_strings. */
+ {
+ unsigned int len;
+
+ len = strlen (segment_info[i].name);
+ if (len > SCNNMLEN)
+ string_byte_count += len + 1;
+ }
+#endif /* COFF_LONG_SECTION_NAMES */
+ }
+
+ size = size_section (abfd, (unsigned int) i);
+ addr += size;
+
+ /* I think the section alignment is only used on the i960; the
+ i960 needs it, and it should do no harm on other targets. */
+#ifdef ALIGNMENT_IN_S_FLAGS
+ segment_info[i].scnhdr.s_flags |= (section_alignment[i] & 0xF) << 8;
+#else
+ segment_info[i].scnhdr.s_align = 1 << section_alignment[i];
+#endif
+
+ if (i == SEG_E0)
+ H_SET_TEXT_SIZE (&headers, size);
+ else if (i == SEG_E1)
+ H_SET_DATA_SIZE (&headers, size);
+ else if (i == SEG_E2)
+ H_SET_BSS_SIZE (&headers, size);
+ }
+
+ /* Turn the gas native symbol table shape into a coff symbol table */
+ crawl_symbols (&headers, abfd);
+
+ if (string_byte_count == 4)
+ string_byte_count = 0;
+
+ H_SET_STRING_SIZE (&headers, string_byte_count);
+
+#ifdef tc_frob_file
+ tc_frob_file ();
+#endif
+
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ {
+ fixup_mdeps (segment_info[i].frchainP->frch_root, &headers, i);
+ fixup_segment (&segment_info[i], i);
+ }
+
+ /* Look for ".stab" segments and fill in their initial symbols
+ correctly. */
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ {
+ name = segment_info[i].name;
+
+ if (name != NULL
+ && strncmp (".stab", name, 5) == 0
+ && strncmp (".stabstr", name, 8) != 0)
+ adjust_stab_section (abfd, i);
+ }
+
+ file_cursor = H_GET_TEXT_FILE_OFFSET (&headers);
+
+ bfd_seek (abfd, (file_ptr) file_cursor, 0);
+
+ /* Plant the data */
+
+ fill_section (abfd, &headers, &file_cursor);
+
+ do_relocs_for (abfd, &headers, &file_cursor);
+
+ do_linenos_for (abfd, &headers, &file_cursor);
+
+ H_SET_FILE_MAGIC_NUMBER (&headers, COFF_MAGIC);
+#ifndef OBJ_COFF_OMIT_TIMESTAMP
+ H_SET_TIME_STAMP (&headers, (long)time((time_t *)0));
+#else
+ H_SET_TIME_STAMP (&headers, 0);
+#endif
+#ifdef TC_COFF_SET_MACHINE
+ TC_COFF_SET_MACHINE (&headers);
+#endif
+
+#ifndef COFF_FLAGS
+#define COFF_FLAGS 0
+#endif
+
+#ifdef KEEP_RELOC_INFO
+ H_SET_FLAGS (&headers, ((H_GET_LINENO_SIZE(&headers) ? 0 : F_LNNO) |
+ COFF_FLAGS | coff_flags));
+#else
+ H_SET_FLAGS (&headers, ((H_GET_LINENO_SIZE(&headers) ? 0 : F_LNNO) |
+ (H_GET_RELOCATION_SIZE(&headers) ? 0 : F_RELFLG) |
+ COFF_FLAGS | coff_flags));
+#endif
+
+ {
+ unsigned int symtable_size = H_GET_SYMBOL_TABLE_SIZE (&headers);
+ char *buffer1 = xmalloc (symtable_size + string_byte_count + 1);
+
+ H_SET_SYMBOL_TABLE_POINTER (&headers, bfd_tell (abfd));
+ w_symbols (abfd, buffer1, symbol_rootP);
+ if (string_byte_count > 0)
+ w_strings (buffer1 + symtable_size);
+ bfd_write (buffer1, 1, symtable_size + string_byte_count, abfd);
+ free (buffer1);
+ }
+
+ coff_header_append (abfd, &headers);
+#if 0
+ /* Recent changes to write need this, but where it should
+ go is up to Ken.. */
+ if (bfd_close_all_done (abfd) == false)
+ as_fatal (_("Can't close %s: %s"), out_file_name,
+ bfd_errmsg (bfd_get_error ()));
+#else
+ {
+ extern bfd *stdoutput;
+ stdoutput = abfd;
+ }
+#endif
+
+}
+
+/* Add a new segment. This is called from subseg_new via the
+ obj_new_segment macro. */
+
+segT
+obj_coff_add_segment (name)
+ const char *name;
+{
+ unsigned int i;
+
+#ifndef COFF_LONG_SECTION_NAMES
+ char buf[SCNNMLEN + 1];
+
+ strncpy (buf, name, SCNNMLEN);
+ buf[SCNNMLEN] = '\0';
+ name = buf;
+#endif
+
+ for (i = SEG_E0; i < SEG_LAST && segment_info[i].scnhdr.s_name[0]; i++)
+ if (strcmp (name, segment_info[i].name) == 0)
+ return (segT) i;
+
+ if (i == SEG_LAST)
+ {
+ as_bad (_("Too many new sections; can't add \"%s\""), name);
+ return now_seg;
+ }
+
+ /* Add a new section. */
+ strncpy (segment_info[i].scnhdr.s_name, name,
+ sizeof (segment_info[i].scnhdr.s_name));
+ segment_info[i].scnhdr.s_flags = STYP_REG;
+ segment_info[i].name = xstrdup (name);
+
+ return (segT) i;
+}
+
+/*
+ * implement the .section pseudo op:
+ * .section name {, "flags"}
+ * ^ ^
+ * | +--- optional flags: 'b' for bss
+ * | 'i' for info
+ * +-- section name 'l' for lib
+ * 'n' for noload
+ * 'o' for over
+ * 'w' for data
+ * 'd' (apparently m88k for data)
+ * 'x' for text
+ * 'r' for read-only data
+ * But if the argument is not a quoted string, treat it as a
+ * subsegment number.
+ */
+
+void
+obj_coff_section (ignore)
+ int ignore;
+{
+ /* Strip out the section name */
+ char *section_name, *name;
+ char c;
+ unsigned int exp;
+ long flags;
+
+ if (flag_mri)
+ {
+ char type;
+
+ s_mri_sect (&type);
+ flags = 0;
+ if (type == 'C')
+ flags = STYP_TEXT;
+ else if (type == 'D')
+ flags = STYP_DATA;
+ segment_info[now_seg].scnhdr.s_flags |= flags;
+
+ return;
+ }
+
+ section_name = input_line_pointer;
+ c = get_symbol_end ();
+
+ name = xmalloc (input_line_pointer - section_name + 1);
+ strcpy (name, section_name);
+
+ *input_line_pointer = c;
+
+ exp = 0;
+ flags = 0;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != '"')
+ exp = get_absolute_expression ();
+ else
+ {
+ ++input_line_pointer;
+ while (*input_line_pointer != '"'
+ && ! is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ switch (*input_line_pointer)
+ {
+ case 'b': flags |= STYP_BSS; break;
+ case 'i': flags |= STYP_INFO; break;
+ case 'l': flags |= STYP_LIB; break;
+ case 'n': flags |= STYP_NOLOAD; break;
+ case 'o': flags |= STYP_OVER; break;
+ case 'd':
+ case 'w': flags |= STYP_DATA; break;
+ case 'x': flags |= STYP_TEXT; break;
+ case 'r': flags |= STYP_LIT; break;
+ default:
+ as_warn(_("unknown section attribute '%c'"),
+ *input_line_pointer);
+ break;
+ }
+ ++input_line_pointer;
+ }
+ if (*input_line_pointer == '"')
+ ++input_line_pointer;
+ }
+ }
+
+ subseg_new (name, (subsegT) exp);
+
+ segment_info[now_seg].scnhdr.s_flags |= flags;
+
+ demand_empty_rest_of_line ();
+}
+
+
+static void
+obj_coff_text (ignore)
+ int ignore;
+{
+ subseg_new (".text", get_absolute_expression ());
+}
+
+
+static void
+obj_coff_data (ignore)
+ int ignore;
+{
+ if (flag_readonly_data_in_text)
+ subseg_new (".text", get_absolute_expression () + 1000);
+ else
+ subseg_new (".data", get_absolute_expression ());
+}
+
+static void
+obj_coff_ident (ignore)
+ int ignore;
+{
+ segT current_seg = now_seg; /* save current seg */
+ subsegT current_subseg = now_subseg;
+ subseg_new (".comment", 0); /* .comment seg */
+ stringer (1); /* read string */
+ subseg_set (current_seg, current_subseg); /* restore current seg */
+}
+
+void
+c_symbol_merge (debug, normal)
+ symbolS *debug;
+ symbolS *normal;
+{
+ S_SET_DATA_TYPE (normal, S_GET_DATA_TYPE (debug));
+ S_SET_STORAGE_CLASS (normal, S_GET_STORAGE_CLASS (debug));
+
+ if (S_GET_NUMBER_AUXILIARY (debug) > S_GET_NUMBER_AUXILIARY (normal))
+ {
+ S_SET_NUMBER_AUXILIARY (normal, S_GET_NUMBER_AUXILIARY (debug));
+ } /* take the most we have */
+
+ if (S_GET_NUMBER_AUXILIARY (debug) > 0)
+ {
+ memcpy ((char *) &normal->sy_symbol.ost_auxent[0],
+ (char *) &debug->sy_symbol.ost_auxent[0],
+ (unsigned int) (S_GET_NUMBER_AUXILIARY (debug) * AUXESZ));
+ } /* Move all the auxiliary information */
+
+ /* Move the debug flags. */
+ SF_SET_DEBUG_FIELD (normal, SF_GET_DEBUG_FIELD (debug));
+} /* c_symbol_merge() */
+
+static int
+c_line_new (symbol, paddr, line_number, frag)
+ symbolS * symbol;
+ long paddr;
+ int line_number;
+ fragS * frag;
+{
+ struct lineno_list *new_line =
+ (struct lineno_list *) xmalloc (sizeof (struct lineno_list));
+
+ segment_info_type *s = segment_info + now_seg;
+ new_line->line.l_lnno = line_number;
+
+ if (line_number == 0)
+ {
+ last_line_symbol = symbol;
+ new_line->line.l_addr.l_symndx = (long) symbol;
+ }
+ else
+ {
+ new_line->line.l_addr.l_paddr = paddr;
+ }
+
+ new_line->frag = (char *) frag;
+ new_line->next = (struct lineno_list *) NULL;
+
+
+ if (s->lineno_list_head == (struct lineno_list *) NULL)
+ {
+ s->lineno_list_head = new_line;
+ }
+ else
+ {
+ s->lineno_list_tail->next = new_line;
+ }
+ s->lineno_list_tail = new_line;
+ return LINESZ * s->scnhdr.s_nlnno++;
+}
+
+void
+c_dot_file_symbol (filename)
+ char *filename;
+{
+ symbolS *symbolP;
+
+ symbolP = symbol_new (".file",
+ SEG_DEBUG,
+ 0,
+ &zero_address_frag);
+
+ S_SET_STORAGE_CLASS (symbolP, C_FILE);
+ S_SET_NUMBER_AUXILIARY (symbolP, 1);
+
+ if (strlen (filename) > FILNMLEN)
+ {
+ /* Filename is too long to fit into an auxent,
+ we stick it into the string table instead. We keep
+ a linked list of the filenames we find so we can emit
+ them later.*/
+ struct filename_list *f = ((struct filename_list *)
+ xmalloc (sizeof (struct filename_list)));
+
+ f->filename = filename;
+ f->next = 0;
+
+ SA_SET_FILE_FNAME_ZEROS (symbolP, 0);
+ SA_SET_FILE_FNAME_OFFSET (symbolP, 1);
+
+ if (filename_list_tail)
+ filename_list_tail->next = f;
+ else
+ filename_list_head = f;
+ filename_list_tail = f;
+ }
+ else
+ {
+ SA_SET_FILE_FNAME (symbolP, filename);
+ }
+#ifndef NO_LISTING
+ {
+ extern int listing;
+ if (listing)
+ {
+ listing_source_file (filename);
+ }
+
+ }
+
+#endif
+ SF_SET_DEBUG (symbolP);
+ S_SET_VALUE (symbolP, (valueT) previous_file_symbol);
+
+ previous_file_symbol = symbolP;
+
+ /* Make sure that the symbol is first on the symbol chain */
+ if (symbol_rootP != symbolP)
+ {
+ symbol_remove (symbolP, &symbol_rootP, &symbol_lastP);
+ symbol_insert (symbolP, symbol_rootP, &symbol_rootP, &symbol_lastP);
+ }
+} /* c_dot_file_symbol() */
+
+/*
+ * Build a 'section static' symbol.
+ */
+
+symbolS *
+c_section_symbol (name, idx)
+ char *name;
+ int idx;
+{
+ symbolS *symbolP;
+
+ symbolP = symbol_find_base (name, DO_NOT_STRIP);
+ if (symbolP == NULL)
+ symbolP = symbol_new (name, idx, 0, &zero_address_frag);
+ else
+ {
+ /* Mmmm. I just love violating interfaces. Makes me feel...dirty. */
+ S_SET_SEGMENT (symbolP, idx);
+ symbolP->sy_frag = &zero_address_frag;
+ }
+
+ S_SET_STORAGE_CLASS (symbolP, C_STAT);
+ S_SET_NUMBER_AUXILIARY (symbolP, 1);
+
+ SF_SET_STATICS (symbolP);
+
+#ifdef TE_DELTA
+ /* manfred@s-direktnet.de: section symbols *must* have the LOCAL bit cleared,
+ which is set by the new definition of LOCAL_LABEL in tc-m68k.h. */
+ SF_CLEAR_LOCAL (symbolP);
+#endif
+#ifdef TE_PE
+ /* If the .linkonce pseudo-op was used for this section, we must
+ store the information in the auxiliary entry for the section
+ symbol. */
+ if (segment_info[idx].linkonce != LINKONCE_UNSET)
+ {
+ int type;
+
+ switch (segment_info[idx].linkonce)
+ {
+ default:
+ abort ();
+ case LINKONCE_DISCARD:
+ type = IMAGE_COMDAT_SELECT_ANY;
+ break;
+ case LINKONCE_ONE_ONLY:
+ type = IMAGE_COMDAT_SELECT_NODUPLICATES;
+ break;
+ case LINKONCE_SAME_SIZE:
+ type = IMAGE_COMDAT_SELECT_SAME_SIZE;
+ break;
+ case LINKONCE_SAME_CONTENTS:
+ type = IMAGE_COMDAT_SELECT_EXACT_MATCH;
+ break;
+ }
+
+ SYM_AUXENT (symbolP)->x_scn.x_comdat = type;
+ }
+#endif /* TE_PE */
+
+ return symbolP;
+} /* c_section_symbol() */
+
+static void
+w_symbols (abfd, where, symbol_rootP)
+ bfd * abfd;
+ char *where;
+ symbolS * symbol_rootP;
+{
+ symbolS *symbolP;
+ unsigned int i;
+
+ /* First fill in those values we have only just worked out */
+ for (i = SEG_E0; i < SEG_LAST; i++)
+ {
+ symbolP = segment_info[i].dot;
+ if (symbolP)
+ {
+ SA_SET_SCN_SCNLEN (symbolP, segment_info[i].scnhdr.s_size);
+ SA_SET_SCN_NRELOC (symbolP, segment_info[i].scnhdr.s_nreloc);
+ SA_SET_SCN_NLINNO (symbolP, segment_info[i].scnhdr.s_nlnno);
+ }
+ }
+
+ /*
+ * Emit all symbols left in the symbol chain.
+ */
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next (symbolP))
+ {
+ /* Used to save the offset of the name. It is used to point
+ to the string in memory but must be a file offset. */
+ register char *temp;
+
+ /* We can't fix the lnnoptr field in yank_symbols with the other
+ adjustments, because we have to wait until we know where they
+ go in the file. */
+ if (SF_GET_ADJ_LNNOPTR (symbolP))
+ {
+ SA_GET_SYM_LNNOPTR (symbolP) +=
+ segment_info[S_GET_SEGMENT (symbolP)].scnhdr.s_lnnoptr;
+ }
+
+ tc_coff_symbol_emit_hook (symbolP);
+
+ temp = S_GET_NAME (symbolP);
+ if (SF_GET_STRING (symbolP))
+ {
+ S_SET_OFFSET (symbolP, symbolP->sy_name_offset);
+ S_SET_ZEROES (symbolP, 0);
+ }
+ else
+ {
+ memset (symbolP->sy_symbol.ost_entry.n_name, 0, SYMNMLEN);
+ strncpy (symbolP->sy_symbol.ost_entry.n_name, temp, SYMNMLEN);
+ }
+ where = symbol_to_chars (abfd, where, symbolP);
+ S_SET_NAME (symbolP, temp);
+ }
+
+} /* w_symbols() */
+
+static void
+obj_coff_lcomm (ignore)
+ int ignore;
+{
+ s_lcomm(0);
+ return;
+#if 0
+ char *name;
+ char c;
+ int temp;
+ char *p;
+
+ symbolS *symbolP;
+
+ name = input_line_pointer;
+
+ c = get_symbol_end ();
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("Expected comma after name"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (*input_line_pointer == '\n')
+ {
+ as_bad (_("Missing size expression"));
+ return;
+ }
+ input_line_pointer++;
+ if ((temp = get_absolute_expression ()) < 0)
+ {
+ as_warn (_("lcomm length (%d.) <0! Ignored."), temp);
+ ignore_rest_of_line ();
+ return;
+ }
+ *p = 0;
+
+ symbolP = symbol_find_or_make(name);
+
+ if (S_GET_SEGMENT(symbolP) == SEG_UNKNOWN &&
+ S_GET_VALUE(symbolP) == 0)
+ {
+ if (! need_pass_2)
+ {
+ char *p;
+ segT current_seg = now_seg; /* save current seg */
+ subsegT current_subseg = now_subseg;
+
+ subseg_set (SEG_E2, 1);
+ symbolP->sy_frag = frag_now;
+ p = frag_var(rs_org, 1, 1, (relax_substateT)0, symbolP,
+ (offsetT) temp, (char *) 0);
+ *p = 0;
+ subseg_set (current_seg, current_subseg); /* restore current seg */
+ S_SET_SEGMENT(symbolP, SEG_E2);
+ S_SET_STORAGE_CLASS(symbolP, C_STAT);
+ }
+ }
+ else
+ as_bad(_("Symbol %s already defined"), name);
+
+ demand_empty_rest_of_line();
+#endif
+}
+
+static void
+fixup_mdeps (frags, h, this_segment)
+ fragS * frags;
+ object_headers * h;
+ segT this_segment;
+{
+ subseg_change (this_segment, 0);
+ while (frags)
+ {
+ switch (frags->fr_type)
+ {
+ case rs_align:
+ case rs_align_code:
+ case rs_org:
+#ifdef HANDLE_ALIGN
+ HANDLE_ALIGN (frags);
+#endif
+ frags->fr_type = rs_fill;
+ frags->fr_offset =
+ ((frags->fr_next->fr_address - frags->fr_address - frags->fr_fix)
+ / frags->fr_var);
+ break;
+ case rs_machine_dependent:
+ md_convert_frag (h, this_segment, frags);
+ frag_wane (frags);
+ break;
+ default:
+ ;
+ }
+ frags = frags->fr_next;
+ }
+}
+
+#if 1
+
+#ifndef TC_FORCE_RELOCATION
+#define TC_FORCE_RELOCATION(fix) 0
+#endif
+
+static void
+fixup_segment (segP, this_segment_type)
+ segment_info_type * segP;
+ segT this_segment_type;
+{
+ register fixS * fixP;
+ register symbolS *add_symbolP;
+ register symbolS *sub_symbolP;
+ long add_number;
+ register int size;
+ register char *place;
+ register long where;
+ register char pcrel;
+ register fragS *fragP;
+ register segT add_symbol_segment = absolute_section;
+
+ for (fixP = segP->fix_root; fixP; fixP = fixP->fx_next)
+ {
+ fragP = fixP->fx_frag;
+ know (fragP);
+ where = fixP->fx_where;
+ place = fragP->fr_literal + where;
+ size = fixP->fx_size;
+ add_symbolP = fixP->fx_addsy;
+ sub_symbolP = fixP->fx_subsy;
+ add_number = fixP->fx_offset;
+ pcrel = fixP->fx_pcrel;
+
+ /* We want function-relative stabs to work on systems which
+ may use a relaxing linker; thus we must handle the sym1-sym2
+ fixups function-relative stabs generates.
+
+ Of course, if you actually enable relaxing in the linker, the
+ line and block scoping information is going to be incorrect
+ in some cases. The only way to really fix this is to support
+ a reloc involving the difference of two symbols. */
+ if (linkrelax
+ && (!sub_symbolP || pcrel))
+ continue;
+
+#ifdef TC_I960
+ if (fixP->fx_tcbit && SF_GET_CALLNAME (add_symbolP))
+ {
+ /* Relocation should be done via the associated 'bal' entry
+ point symbol. */
+
+ if (!SF_GET_BALNAME (tc_get_bal_of_call (add_symbolP)))
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("No 'bal' entry point for leafproc %s"),
+ S_GET_NAME (add_symbolP));
+ continue;
+ }
+ fixP->fx_addsy = add_symbolP = tc_get_bal_of_call (add_symbolP);
+ }
+#endif
+
+ /* Make sure the symbols have been resolved; this may not have
+ happened if these are expression symbols. */
+ if (add_symbolP != NULL && ! add_symbolP->sy_resolved)
+ resolve_symbol_value (add_symbolP, 1);
+
+ if (add_symbolP != NULL)
+ {
+ /* If this fixup is against a symbol which has been equated
+ to another symbol, convert it to the other symbol. */
+ if (add_symbolP->sy_value.X_op == O_symbol
+ && (! S_IS_DEFINED (add_symbolP)
+ || S_IS_COMMON (add_symbolP)))
+ {
+ while (add_symbolP->sy_value.X_op == O_symbol
+ && (! S_IS_DEFINED (add_symbolP)
+ || S_IS_COMMON (add_symbolP)))
+ {
+ symbolS *n;
+
+ /* We must avoid looping, as that can occur with a
+ badly written program. */
+ n = add_symbolP->sy_value.X_add_symbol;
+ if (n == add_symbolP)
+ break;
+ add_number += add_symbolP->sy_value.X_add_number;
+ add_symbolP = n;
+ }
+ fixP->fx_addsy = add_symbolP;
+ fixP->fx_offset = add_number;
+ }
+ }
+
+ if (sub_symbolP != NULL && ! sub_symbolP->sy_resolved)
+ resolve_symbol_value (sub_symbolP, 1);
+
+ if (add_symbolP != NULL
+ && add_symbolP->sy_mri_common)
+ {
+ know (add_symbolP->sy_value.X_op == O_symbol);
+ add_number += S_GET_VALUE (add_symbolP);
+ fixP->fx_offset = add_number;
+ add_symbolP = fixP->fx_addsy = add_symbolP->sy_value.X_add_symbol;
+ }
+
+ if (add_symbolP)
+ {
+ add_symbol_segment = S_GET_SEGMENT (add_symbolP);
+ } /* if there is an addend */
+
+ if (sub_symbolP)
+ {
+ if (add_symbolP == NULL || add_symbol_segment == absolute_section)
+ {
+ if (add_symbolP != NULL)
+ {
+ add_number += S_GET_VALUE (add_symbolP);
+ add_symbolP = NULL;
+ fixP->fx_addsy = NULL;
+ }
+
+ /* It's just -sym. */
+ if (S_GET_SEGMENT (sub_symbolP) == absolute_section)
+ {
+ add_number -= S_GET_VALUE (sub_symbolP);
+ fixP->fx_subsy = 0;
+ fixP->fx_done = 1;
+ }
+ else
+ {
+#ifndef TC_M68K
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Negative of non-absolute symbol %s"),
+ S_GET_NAME (sub_symbolP));
+#endif
+ add_number -= S_GET_VALUE (sub_symbolP);
+ } /* not absolute */
+
+ /* if sub_symbol is in the same segment that add_symbol
+ and add_symbol is either in DATA, TEXT, BSS or ABSOLUTE */
+ }
+ else if (S_GET_SEGMENT (sub_symbolP) == add_symbol_segment
+ && SEG_NORMAL (add_symbol_segment))
+ {
+ /* Difference of 2 symbols from same segment. Can't
+ make difference of 2 undefineds: 'value' means
+ something different for N_UNDF. */
+#ifdef TC_I960
+ /* Makes no sense to use the difference of 2 arbitrary symbols
+ as the target of a call instruction. */
+ if (fixP->fx_tcbit)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("callj to difference of 2 symbols"));
+ }
+#endif /* TC_I960 */
+ add_number += S_GET_VALUE (add_symbolP) -
+ S_GET_VALUE (sub_symbolP);
+ add_symbolP = NULL;
+
+ if (!TC_FORCE_RELOCATION (fixP))
+ {
+ fixP->fx_addsy = NULL;
+ fixP->fx_subsy = NULL;
+ fixP->fx_done = 1;
+#ifdef TC_M68K /* is this right? */
+ pcrel = 0;
+ fixP->fx_pcrel = 0;
+#endif
+ }
+ }
+ else
+ {
+ /* Different segments in subtraction. */
+ know (!(S_IS_EXTERNAL (sub_symbolP) && (S_GET_SEGMENT (sub_symbolP) == absolute_section)));
+
+ if ((S_GET_SEGMENT (sub_symbolP) == absolute_section))
+ {
+ add_number -= S_GET_VALUE (sub_symbolP);
+ }
+#ifdef DIFF_EXPR_OK
+ else if (S_GET_SEGMENT (sub_symbolP) == this_segment_type
+#if 0 /* Okay for 68k, at least... */
+ && !pcrel
+#endif
+ )
+ {
+ /* Make it pc-relative. */
+ add_number += (md_pcrel_from (fixP)
+ - S_GET_VALUE (sub_symbolP));
+ pcrel = 1;
+ fixP->fx_pcrel = 1;
+ sub_symbolP = 0;
+ fixP->fx_subsy = 0;
+ }
+#endif
+ else
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Can't emit reloc {- %s-seg symbol \"%s\"} @ file address %ld."),
+ segment_name (S_GET_SEGMENT (sub_symbolP)),
+ S_GET_NAME (sub_symbolP),
+ (long) (fragP->fr_address + where));
+ } /* if absolute */
+ }
+ } /* if sub_symbolP */
+
+ if (add_symbolP)
+ {
+ if (add_symbol_segment == this_segment_type && pcrel)
+ {
+ /*
+ * This fixup was made when the symbol's segment was
+ * SEG_UNKNOWN, but it is now in the local segment.
+ * So we know how to do the address without relocation.
+ */
+#ifdef TC_I960
+ /* reloc_callj() may replace a 'call' with a 'calls' or a 'bal',
+ * in which cases it modifies *fixP as appropriate. In the case
+ * of a 'calls', no further work is required, and *fixP has been
+ * set up to make the rest of the code below a no-op.
+ */
+ reloc_callj (fixP);
+#endif /* TC_I960 */
+
+ add_number += S_GET_VALUE (add_symbolP);
+ add_number -= md_pcrel_from (fixP);
+
+ /* We used to do
+ add_number -= segP->scnhdr.s_vaddr;
+ if defined (TC_I386) || defined (TE_LYNX). I now
+ think that was an error propagated from the case when
+ we are going to emit the relocation. If we are not
+ going to emit the relocation, then we just want to
+ set add_number to the difference between the symbols.
+ This is a case that would only arise when there is a
+ PC relative reference from a section other than .text
+ to a symbol defined in the same section, and the
+ reference is not relaxed. Since jump instructions on
+ the i386 are relaxed, this could only arise with a
+ call instruction. */
+
+ pcrel = 0; /* Lie. Don't want further pcrel processing. */
+ if (!TC_FORCE_RELOCATION (fixP))
+ {
+ fixP->fx_addsy = NULL;
+ fixP->fx_done = 1;
+ }
+ }
+ else
+ {
+ switch (add_symbol_segment)
+ {
+ case absolute_section:
+#ifdef TC_I960
+ reloc_callj (fixP); /* See comment about reloc_callj() above*/
+#endif /* TC_I960 */
+ add_number += S_GET_VALUE (add_symbolP);
+ add_symbolP = NULL;
+
+ if (!TC_FORCE_RELOCATION (fixP))
+ {
+ fixP->fx_addsy = NULL;
+ fixP->fx_done = 1;
+ }
+ break;
+ default:
+
+
+#if defined(TC_A29K) || (defined(TE_PE) && defined(TC_I386)) || defined(TC_M88K)
+ /* This really should be handled in the linker, but
+ backward compatibility forbids. */
+ add_number += S_GET_VALUE (add_symbolP);
+#else
+ add_number += S_GET_VALUE (add_symbolP) +
+ segment_info[S_GET_SEGMENT (add_symbolP)].scnhdr.s_paddr;
+#endif
+ break;
+
+ case SEG_UNKNOWN:
+#ifdef TC_I960
+ if ((int) fixP->fx_bit_fixP == 13)
+ {
+ /* This is a COBR instruction. They have only a
+ * 13-bit displacement and are only to be used
+ * for local branches: flag as error, don't generate
+ * relocation.
+ */
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("can't use COBR format with external label"));
+ fixP->fx_addsy = NULL;
+ fixP->fx_done = 1;
+ continue;
+ } /* COBR */
+#endif /* TC_I960 */
+#if ((defined (TC_I386) || defined (TE_LYNX) || defined (TE_AUX)) && !defined(TE_PE)) || defined (COFF_COMMON_ADDEND)
+ /* 386 COFF uses a peculiar format in which the
+ value of a common symbol is stored in the .text
+ segment (I've checked this on SVR3.2 and SCO
+ 3.2.2) Ian Taylor <ian@cygnus.com>. */
+ /* This is also true for 68k COFF on sysv machines
+ (Checked on Motorola sysv68 R3V6 and R3V7.1, and also on
+ UNIX System V/M68000, Release 1.0 from ATT/Bell Labs)
+ Philippe De Muyter <phdm@info.ucl.ac.be>. */
+ if (S_IS_COMMON (add_symbolP))
+ add_number += S_GET_VALUE (add_symbolP);
+#endif
+ break;
+
+
+ } /* switch on symbol seg */
+ } /* if not in local seg */
+ } /* if there was a + symbol */
+
+ if (pcrel)
+ {
+#if !defined(TC_M88K) && !(defined(TE_PE) && defined(TC_I386)) && !defined(TC_A29K)
+ /* This adjustment is not correct on the m88k, for which the
+ linker does all the computation. */
+ add_number -= md_pcrel_from (fixP);
+#endif
+ if (add_symbolP == 0)
+ {
+ fixP->fx_addsy = &abs_symbol;
+ } /* if there's an add_symbol */
+#if defined (TC_I386) || defined (TE_LYNX) || defined (TC_I960) || defined (TC_M68K)
+ /* On the 386 we must adjust by the segment vaddr as well.
+ Ian Taylor.
+
+ I changed the i960 to work this way as well. This is
+ compatible with the current GNU linker behaviour. I do
+ not know what other i960 COFF assemblers do. This is not
+ a common case: normally, only assembler code will contain
+ a PC relative reloc, and only branches which do not
+ originate in the .text section will have a non-zero
+ address.
+
+ I changed the m68k to work this way as well. This will
+ break existing PC relative relocs from sections which do
+ not start at address 0, but it will make ld -r work.
+ Ian Taylor, 4 Oct 96. */
+
+ add_number -= segP->scnhdr.s_vaddr;
+#endif
+ } /* if pcrel */
+
+ if (!fixP->fx_bit_fixP && ! fixP->fx_no_overflow)
+ {
+#ifndef TC_M88K
+ /* The m88k uses the offset field of the reloc to get around
+ this problem. */
+ if ((size == 1
+ && ((add_number & ~0xFF)
+ || (fixP->fx_signed && (add_number & 0x80)))
+ && ((add_number & ~0xFF) != (-1 & ~0xFF)
+ || (add_number & 0x80) == 0))
+ || (size == 2
+ && ((add_number & ~0xFFFF)
+ || (fixP->fx_signed && (add_number & 0x8000)))
+ && ((add_number & ~0xFFFF) != (-1 & ~0xFFFF)
+ || (add_number & 0x8000) == 0)))
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Value of %ld too large for field of %d bytes at 0x%lx"),
+ (long) add_number, size,
+ (unsigned long) (fragP->fr_address + where));
+ }
+#endif
+#ifdef WARN_SIGNED_OVERFLOW_WORD
+ /* Warn if a .word value is too large when treated as a
+ signed number. We already know it is not too negative.
+ This is to catch over-large switches generated by gcc on
+ the 68k. */
+ if (!flag_signed_overflow_ok
+ && size == 2
+ && add_number > 0x7fff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Signed .word overflow; switch may be too large; %ld at 0x%lx"),
+ (long) add_number,
+ (unsigned long) (fragP->fr_address + where));
+#endif
+ } /* not a bit fix */
+ /* Once this fix has been applied, we don't have to output
+ anything nothing more need be done. */
+#ifdef MD_APPLY_FIX3
+ md_apply_fix3 (fixP, (valueT *) &add_number, this_segment_type);
+#else
+ md_apply_fix (fixP, add_number);
+#endif
+ } /* For each fixS in this segment. */
+} /* fixup_segment() */
+
+#endif
+
+/* The first entry in a .stab section is special. */
+
+void
+obj_coff_init_stab_section (seg)
+ segT seg;
+{
+ char *file;
+ char *p;
+ char *stabstr_name;
+ unsigned int stroff;
+
+ /* Make space for this first symbol. */
+ p = frag_more (12);
+ /* Zero it out. */
+ memset (p, 0, 12);
+ as_where (&file, (unsigned int *) NULL);
+ stabstr_name = (char *) alloca (strlen (segment_info[seg].name) + 4);
+ strcpy (stabstr_name, segment_info[seg].name);
+ strcat (stabstr_name, "str");
+ stroff = get_stab_string_offset (file, stabstr_name);
+ know (stroff == 1);
+ md_number_to_chars (p, stroff, 4);
+}
+
+/* Fill in the counts in the first entry in a .stab section. */
+
+static void
+adjust_stab_section(abfd, seg)
+ bfd *abfd;
+ segT seg;
+{
+ segT stabstrseg = SEG_UNKNOWN;
+ const char *secname, *name2;
+ char *name;
+ char *p = NULL;
+ int i, strsz = 0, nsyms;
+ fragS *frag = segment_info[seg].frchainP->frch_root;
+
+ /* Look for the associated string table section. */
+
+ secname = segment_info[seg].name;
+ name = (char *) alloca (strlen (secname) + 4);
+ strcpy (name, secname);
+ strcat (name, "str");
+
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ {
+ name2 = segment_info[i].name;
+ if (name2 != NULL && strncmp(name2, name, 8) == 0)
+ {
+ stabstrseg = i;
+ break;
+ }
+ }
+
+ /* If we found the section, get its size. */
+ if (stabstrseg != SEG_UNKNOWN)
+ strsz = size_section (abfd, stabstrseg);
+
+ nsyms = size_section (abfd, seg) / 12 - 1;
+
+ /* Look for the first frag of sufficient size for the initial stab
+ symbol, and collect a pointer to it. */
+ while (frag && frag->fr_fix < 12)
+ frag = frag->fr_next;
+ assert (frag != 0);
+ p = frag->fr_literal;
+ assert (p != 0);
+
+ /* Write in the number of stab symbols and the size of the string
+ table. */
+ bfd_h_put_16 (abfd, (bfd_vma) nsyms, (bfd_byte *) p + 6);
+ bfd_h_put_32 (abfd, (bfd_vma) strsz, (bfd_byte *) p + 8);
+}
+
+#endif /* not BFD_ASSEMBLER */
+
+const pseudo_typeS obj_pseudo_table[] =
+{
+ {"def", obj_coff_def, 0},
+ {"dim", obj_coff_dim, 0},
+ {"endef", obj_coff_endef, 0},
+ {"line", obj_coff_line, 0},
+ {"ln", obj_coff_ln, 0},
+ {"appline", obj_coff_ln, 1},
+ {"scl", obj_coff_scl, 0},
+ {"size", obj_coff_size, 0},
+ {"tag", obj_coff_tag, 0},
+ {"type", obj_coff_type, 0},
+ {"val", obj_coff_val, 0},
+ {"section", obj_coff_section, 0},
+ {"sect", obj_coff_section, 0},
+ /* FIXME: We ignore the MRI short attribute. */
+ {"section.s", obj_coff_section, 0},
+ {"sect.s", obj_coff_section, 0},
+ /* We accept the .bss directive for backward compatibility with
+ earlier versions of gas. */
+ {"bss", obj_coff_bss, 0},
+ {"weak", obj_coff_weak, 0},
+#ifndef BFD_ASSEMBLER
+ {"use", obj_coff_section, 0},
+ {"text", obj_coff_text, 0},
+ {"data", obj_coff_data, 0},
+ {"lcomm", obj_coff_lcomm, 0},
+ {"ident", obj_coff_ident, 0},
+#else
+ {"optim", s_ignore, 0}, /* For sun386i cc (?) */
+ {"ident", s_ignore, 0}, /* we don't yet handle this. */
+#endif
+ {"version", s_ignore, 0},
+ {"ABORT", s_abort, 0},
+#ifdef TC_M88K
+ /* The m88k uses sdef instead of def. */
+ {"sdef", obj_coff_def, 0},
+#endif
+ {NULL} /* end sentinel */
+}; /* obj_pseudo_table */
+
+#ifdef BFD_ASSEMBLER
+
+/* Support for a COFF emulation. */
+
+static void
+coff_pop_insert ()
+{
+ pop_insert (obj_pseudo_table);
+}
+
+static int
+coff_sec_sym_ok_for_reloc (sec)
+ asection *sec;
+{
+ return 0;
+}
+
+static void
+no_func ()
+{
+ abort ();
+}
+
+const struct format_ops coff_format_ops =
+{
+ bfd_target_coff_flavour,
+ 0,
+ 1,
+ coff_frob_symbol,
+ no_func,
+ coff_frob_file_after_relocs,
+ 0, 0,
+ 0, 0,
+ 0,
+#if 0
+ obj_generate_asm_lineno,
+#else
+ no_func,
+#endif
+#if 0
+ obj_stab,
+#else
+ no_func,
+#endif
+ coff_sec_sym_ok_for_reloc,
+ coff_pop_insert,
+#if 0
+ obj_set_ext,
+#else
+ no_func,
+#endif
+ coff_obj_read_begin_hook,
+ coff_obj_symbol_new_hook,
+};
+
+#endif
diff --git a/gas/config/obj-coff.h b/gas/config/obj-coff.h
new file mode 100644
index 0000000..4650477
--- /dev/null
+++ b/gas/config/obj-coff.h
@@ -0,0 +1,841 @@
+/* coff object file format
+ Copyright (C) 1989, 90, 91, 92, 94, 95, 96, 97, 98, 1999
+ Free Software Foundation, Inc.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifndef OBJ_FORMAT_H
+#define OBJ_FORMAT_H
+
+#define OBJ_COFF 1
+
+#ifndef BFD_ASSEMBLER
+
+#define WORKING_DOT_WORD
+#define WARN_SIGNED_OVERFLOW_WORD
+#define OBJ_COFF_OMIT_OPTIONAL_HEADER
+#define BFD_HEADERS
+#define BFD
+
+#endif
+
+#include "targ-cpu.h"
+
+#include "bfd.h"
+
+/* This internal_lineno crap is to stop namespace pollution from the
+ bfd internal coff headerfile. */
+#define internal_lineno bfd_internal_lineno
+#include "coff/internal.h"
+#undef internal_lineno
+
+/* CPU-specific setup: */
+
+#ifdef TC_ARM
+#include "coff/arm.h"
+#ifndef TARGET_FORMAT
+#define TARGET_FORMAT "coff-arm"
+#endif
+#endif
+
+#ifdef TC_PPC
+#ifdef TE_PE
+#include "coff/powerpc.h"
+#else
+#include "coff/rs6000.h"
+#endif
+#endif
+
+#ifdef TC_SPARC
+#include "coff/sparc.h"
+#endif
+
+#ifdef TC_I386
+#include "coff/i386.h"
+
+#ifdef TE_PE
+#define TARGET_FORMAT "pe-i386"
+#endif
+
+#ifndef TARGET_FORMAT
+#define TARGET_FORMAT "coff-i386"
+#endif
+#endif
+
+#ifdef TC_M68K
+#include "coff/m68k.h"
+#ifndef TARGET_FORMAT
+#define TARGET_FORMAT "coff-m68k"
+#endif
+#endif
+
+#ifdef TC_A29K
+#include "coff/a29k.h"
+#define TARGET_FORMAT "coff-a29k-big"
+#endif
+
+#ifdef TC_I960
+#include "coff/i960.h"
+#define TARGET_FORMAT "coff-Intel-little"
+#endif
+
+#ifdef TC_Z8K
+#include "coff/z8k.h"
+#define TARGET_FORMAT "coff-z8k"
+#endif
+
+#ifdef TC_H8300
+#include "coff/h8300.h"
+#define TARGET_FORMAT "coff-h8300"
+#endif
+
+#ifdef TC_H8500
+#include "coff/h8500.h"
+#define TARGET_FORMAT "coff-h8500"
+#endif
+
+#ifdef TC_SH
+#include "coff/sh.h"
+#define TARGET_FORMAT \
+ (shl \
+ ? (sh_small ? "coff-shl-small" : "coff-shl") \
+ : (sh_small ? "coff-sh-small" : "coff-sh"))
+#endif
+
+#ifdef TC_M88K
+#include "coff/m88k.h"
+#define TARGET_FORMAT "coff-m88kbcs"
+#endif
+
+#ifdef TC_W65
+#include "coff/w65.h"
+#define TARGET_FORMAT "coff-w65"
+#endif
+
+#ifdef TC_TIC30
+#include "coff/tic30.h"
+#define TARGET_FORMAT "coff-tic30"
+#endif
+
+#ifdef TC_TIC80
+#include "coff/tic80.h"
+#define TARGET_FORMAT "coff-tic80"
+#define ALIGNMENT_IN_S_FLAGS 1
+#endif
+
+#ifdef TC_MCORE
+#include "coff/mcore.h"
+#ifndef TARGET_FORMAT
+#define TARGET_FORMAT "pe-mcore"
+#endif
+#endif
+
+/* Targets may also set this. Also, if BFD_ASSEMBLER is defined, this
+ will already have been defined. */
+#undef SYMBOLS_NEED_BACKPOINTERS
+#define SYMBOLS_NEED_BACKPOINTERS 1
+
+#ifndef OBJ_COFF_MAX_AUXENTRIES
+#define OBJ_COFF_MAX_AUXENTRIES 1
+#endif /* OBJ_COFF_MAX_AUXENTRIES */
+
+extern void coff_obj_symbol_new_hook PARAMS ((struct symbol *));
+#define obj_symbol_new_hook coff_obj_symbol_new_hook
+
+extern void coff_obj_read_begin_hook PARAMS ((void));
+#define obj_read_begin_hook coff_obj_read_begin_hook
+
+/* ***********************************************************************
+
+ This file really contains two implementations of the COFF back end.
+ They are in the process of being merged, but this is only a
+ preliminary, mechanical merging. Many definitions that are
+ identical between the two are still found in both versions.
+
+ The first version, with BFD_ASSEMBLER defined, uses high-level BFD
+ interfaces and data structures. The second version, with
+ BFD_ASSEMBLER not defined, also uses BFD, but mostly for swapping
+ data structures and for doing the actual I/O. The latter defines
+ the preprocessor symbols BFD and BFD_HEADERS. Try not to let this
+ confuse you.
+
+ These two are in the process of being merged, and eventually the
+ BFD_ASSEMBLER version should take over completely. Release timing
+ issues and namespace problems convinced me to merge the two
+ together in this fashion, a little sooner than I would have liked.
+ The real merge should be much better done by the time the next
+ release comes out.
+
+ For now, the structure of this file is:
+ <common>
+ #ifdef BFD_ASSEMBLER
+ <one version>
+ #else
+ <other version>
+ #endif
+ <common>
+ Unfortunately, the common portions are very small at the moment,
+ and many declarations or definitions are duplicated. The structure
+ of obj-coff.c is similar.
+
+ See doc/internals.texi for a brief discussion of the history, if
+ you care.
+
+ Ken Raeburn, 5 May 1994
+
+ *********************************************************************** */
+
+#ifdef BFD_ASSEMBLER
+
+#include "bfd/libcoff.h"
+
+#define OUTPUT_FLAVOR bfd_target_coff_flavour
+
+/* SYMBOL TABLE */
+
+/* Alter the field names, for now, until we've fixed up the other
+ references to use the new name. */
+#ifdef TC_I960
+#define TC_SYMFIELD_TYPE struct symbol *
+#define sy_tc bal
+#endif
+
+#define OBJ_SYMFIELD_TYPE unsigned long
+#define sy_obj sy_flags
+
+#define SYM_AUXENT(S) (&coffsymbol ((S)->bsym)->native[1].u.auxent)
+#define SYM_AUXINFO(S) (&coffsymbol ((S)->bsym)->native[1])
+
+#define DO_NOT_STRIP 0
+
+extern void obj_coff_section PARAMS ((int));
+
+/* The number of auxiliary entries */
+#define S_GET_NUMBER_AUXILIARY(s) (coffsymbol((s)->bsym)->native->u.syment.n_numaux)
+/* The number of auxiliary entries */
+#define S_SET_NUMBER_AUXILIARY(s,v) (S_GET_NUMBER_AUXILIARY (s) = (v))
+
+/* True if a symbol name is in the string table, i.e. its length is > 8. */
+#define S_IS_STRING(s) (strlen(S_GET_NAME(s)) > 8 ? 1 : 0)
+
+extern int S_SET_DATA_TYPE PARAMS ((struct symbol *, int));
+extern int S_SET_STORAGE_CLASS PARAMS ((struct symbol *, int));
+extern int S_GET_STORAGE_CLASS PARAMS ((struct symbol *));
+extern void SA_SET_SYM_ENDNDX PARAMS ((struct symbol *, struct symbol *));
+
+/* Auxiliary entry macros. SA_ stands for symbol auxiliary */
+/* Omit the tv related fields */
+/* Accessors */
+
+#define SA_GET_SYM_TAGNDX(s) (SYM_AUXENT (s)->x_sym.x_tagndx.l)
+#define SA_GET_SYM_LNNO(s) (SYM_AUXENT (s)->x_sym.x_misc.x_lnsz.x_lnno)
+#define SA_GET_SYM_SIZE(s) (SYM_AUXENT (s)->x_sym.x_misc.x_lnsz.x_size)
+#define SA_GET_SYM_FSIZE(s) (SYM_AUXENT (s)->x_sym.x_misc.x_fsize)
+#define SA_GET_SYM_LNNOPTR(s) (SYM_AUXENT (s)->x_sym.x_fcnary.x_fcn.x_lnnoptr)
+#define SA_GET_SYM_ENDNDX(s) (SYM_AUXENT (s)->x_sym.x_fcnary.x_fcn.x_endndx)
+#define SA_GET_SYM_DIMEN(s,i) (SYM_AUXENT (s)->x_sym.x_fcnary.x_ary.x_dimen[(i)])
+#define SA_GET_FILE_FNAME(s) (SYM_AUXENT (s)->x_file.x_fname)
+#define SA_GET_SCN_SCNLEN(s) (SYM_AUXENT (s)->x_scn.x_scnlen)
+#define SA_GET_SCN_NRELOC(s) (SYM_AUXENT (s)->x_scn.x_nreloc)
+#define SA_GET_SCN_NLINNO(s) (SYM_AUXENT (s)->x_scn.x_nlinno)
+
+#define SA_SET_SYM_LNNO(s,v) (SYM_AUXENT (s)->x_sym.x_misc.x_lnsz.x_lnno=(v))
+#define SA_SET_SYM_SIZE(s,v) (SYM_AUXENT (s)->x_sym.x_misc.x_lnsz.x_size=(v))
+#define SA_SET_SYM_FSIZE(s,v) (SYM_AUXENT (s)->x_sym.x_misc.x_fsize=(v))
+#define SA_SET_SYM_LNNOPTR(s,v) (SYM_AUXENT (s)->x_sym.x_fcnary.x_fcn.x_lnnoptr=(v))
+#define SA_SET_SYM_DIMEN(s,i,v) (SYM_AUXENT (s)->x_sym.x_fcnary.x_ary.x_dimen[(i)]=(v))
+#define SA_SET_FILE_FNAME(s,v) strncpy(SYM_AUXENT (s)->x_file.x_fname,(v),FILNMLEN)
+#define SA_SET_SCN_SCNLEN(s,v) (SYM_AUXENT (s)->x_scn.x_scnlen=(v))
+#define SA_SET_SCN_NRELOC(s,v) (SYM_AUXENT (s)->x_scn.x_nreloc=(v))
+#define SA_SET_SCN_NLINNO(s,v) (SYM_AUXENT (s)->x_scn.x_nlinno=(v))
+
+/*
+ * Internal use only definitions. SF_ stands for symbol flags.
+ *
+ * These values can be assigned to sy_symbol.ost_flags field of a symbolS.
+ *
+ * You'll break i960 if you shift the SYSPROC bits anywhere else. for
+ * more on the balname/callname hack, see tc-i960.h. b.out is done
+ * differently.
+ */
+
+#define SF_I960_MASK (0x000001ff) /* Bits 0-8 are used by the i960 port. */
+#define SF_SYSPROC (0x0000003f) /* bits 0-5 are used to store the sysproc number */
+#define SF_IS_SYSPROC (0x00000040) /* bit 6 marks symbols that are sysprocs */
+#define SF_BALNAME (0x00000080) /* bit 7 marks BALNAME symbols */
+#define SF_CALLNAME (0x00000100) /* bit 8 marks CALLNAME symbols */
+
+#define SF_NORMAL_MASK (0x0000ffff) /* bits 12-15 are general purpose. */
+
+#define SF_STATICS (0x00001000) /* Mark the .text & all symbols */
+#define SF_DEFINED (0x00002000) /* Symbol is defined in this file */
+#define SF_STRING (0x00004000) /* Symbol name length > 8 */
+#define SF_LOCAL (0x00008000) /* Symbol must not be emitted */
+
+#define SF_DEBUG_MASK (0xffff0000) /* bits 16-31 are debug info */
+
+#define SF_FUNCTION (0x00010000) /* The symbol is a function */
+#define SF_PROCESS (0x00020000) /* Process symbol before write */
+#define SF_TAGGED (0x00040000) /* Is associated with a tag */
+#define SF_TAG (0x00080000) /* Is a tag */
+#define SF_DEBUG (0x00100000) /* Is in debug or abs section */
+#define SF_GET_SEGMENT (0x00200000) /* Get the section of the forward symbol. */
+/* All other bits are unused. */
+
+/* Accessors */
+#define SF_GET(s) ((s)->sy_flags)
+#define SF_GET_DEBUG(s) ((s)->bsym->flags & BSF_DEBUGGING)
+#define SF_SET_DEBUG(s) ((s)->bsym->flags |= BSF_DEBUGGING)
+#define SF_GET_NORMAL_FIELD(s) (SF_GET (s) & SF_NORMAL_MASK)
+#define SF_GET_DEBUG_FIELD(s) (SF_GET (s) & SF_DEBUG_MASK)
+#define SF_GET_FILE(s) (SF_GET (s) & SF_FILE)
+#define SF_GET_STATICS(s) (SF_GET (s) & SF_STATICS)
+#define SF_GET_DEFINED(s) (SF_GET (s) & SF_DEFINED)
+#define SF_GET_STRING(s) (SF_GET (s) & SF_STRING)
+#define SF_GET_LOCAL(s) (SF_GET (s) & SF_LOCAL)
+#define SF_GET_FUNCTION(s) (SF_GET (s) & SF_FUNCTION)
+#define SF_GET_PROCESS(s) (SF_GET (s) & SF_PROCESS)
+#define SF_GET_TAGGED(s) (SF_GET (s) & SF_TAGGED)
+#define SF_GET_TAG(s) (SF_GET (s) & SF_TAG)
+#define SF_GET_GET_SEGMENT(s) (SF_GET (s) & SF_GET_SEGMENT)
+#define SF_GET_I960(s) (SF_GET (s) & SF_I960_MASK) /* used by i960 */
+#define SF_GET_BALNAME(s) (SF_GET (s) & SF_BALNAME) /* used by i960 */
+#define SF_GET_CALLNAME(s) (SF_GET (s) & SF_CALLNAME) /* used by i960 */
+#define SF_GET_IS_SYSPROC(s) (SF_GET (s) & SF_IS_SYSPROC) /* used by i960 */
+#define SF_GET_SYSPROC(s) (SF_GET (s) & SF_SYSPROC) /* used by i960 */
+
+/* Modifiers */
+#define SF_SET(s,v) (SF_GET (s) = (v))
+#define SF_SET_NORMAL_FIELD(s,v)(SF_GET (s) |= ((v) & SF_NORMAL_MASK))
+#define SF_SET_DEBUG_FIELD(s,v) (SF_GET (s) |= ((v) & SF_DEBUG_MASK))
+#define SF_SET_FILE(s) (SF_GET (s) |= SF_FILE)
+#define SF_SET_STATICS(s) (SF_GET (s) |= SF_STATICS)
+#define SF_SET_DEFINED(s) (SF_GET (s) |= SF_DEFINED)
+#define SF_SET_STRING(s) (SF_GET (s) |= SF_STRING)
+#define SF_SET_LOCAL(s) (SF_GET (s) |= SF_LOCAL)
+#define SF_CLEAR_LOCAL(s) (SF_GET (s) &= ~SF_LOCAL)
+#define SF_SET_FUNCTION(s) (SF_GET (s) |= SF_FUNCTION)
+#define SF_SET_PROCESS(s) (SF_GET (s) |= SF_PROCESS)
+#define SF_SET_TAGGED(s) (SF_GET (s) |= SF_TAGGED)
+#define SF_SET_TAG(s) (SF_GET (s) |= SF_TAG)
+#define SF_SET_GET_SEGMENT(s) (SF_GET (s) |= SF_GET_SEGMENT)
+#define SF_SET_I960(s,v) (SF_GET (s) |= ((v) & SF_I960_MASK)) /* used by i960 */
+#define SF_SET_BALNAME(s) (SF_GET (s) |= SF_BALNAME) /* used by i960 */
+#define SF_SET_CALLNAME(s) (SF_GET (s) |= SF_CALLNAME) /* used by i960 */
+#define SF_SET_IS_SYSPROC(s) (SF_GET (s) |= SF_IS_SYSPROC) /* used by i960 */
+#define SF_SET_SYSPROC(s,v) (SF_GET (s) |= ((v) & SF_SYSPROC)) /* used by i960 */
+
+/* -------------- Line number handling ------- */
+extern int text_lineno_number;
+extern int coff_line_base;
+extern int coff_n_line_nos;
+
+#define obj_emit_lineno(WHERE,LINE,FILE_START) abort ()
+extern void coff_add_linesym PARAMS ((struct symbol *));
+
+
+void c_dot_file_symbol PARAMS ((char *filename));
+#define obj_app_file c_dot_file_symbol
+
+extern void coff_frob_symbol PARAMS ((struct symbol *, int *));
+extern void coff_adjust_symtab PARAMS ((void));
+extern void coff_frob_section PARAMS ((segT));
+extern void coff_adjust_section_syms PARAMS ((bfd *, asection *, PTR));
+extern void coff_frob_file_after_relocs PARAMS ((void));
+#define obj_frob_symbol(S,P) coff_frob_symbol(S,&P)
+#ifndef obj_adjust_symtab
+#define obj_adjust_symtab() coff_adjust_symtab()
+#endif
+#define obj_frob_section(S) coff_frob_section (S)
+#define obj_frob_file_after_relocs() coff_frob_file_after_relocs ()
+
+extern struct symbol *coff_last_function;
+
+/* Forward the segment of a forwarded symbol, handle assignments that
+ just copy symbol values, etc. */
+#ifndef TE_I386AIX
+#define OBJ_COPY_SYMBOL_ATTRIBUTES(dest,src) \
+ (SF_GET_GET_SEGMENT (dest) \
+ ? (S_SET_SEGMENT (dest, S_GET_SEGMENT (src)), 0) \
+ : 0)
+#else
+#define OBJ_COPY_SYMBOL_ATTRIBUTES(dest,src) \
+ (SF_GET_GET_SEGMENT (dest) && S_GET_SEGMENT (dest) == SEG_UNKNOWN \
+ ? (S_SET_SEGMENT (dest, S_GET_SEGMENT (src)), 0) \
+ : 0)
+#endif
+
+/* sanity check */
+
+#ifdef TC_I960
+#ifndef C_LEAFSTAT
+hey ! Where is the C_LEAFSTAT definition ? i960 - coff support is depending on it.
+#endif /* no C_LEAFSTAT */
+#endif /* TC_I960 */
+
+#else /* not BFD_ASSEMBLER */
+
+#ifdef TC_A29K
+/* Allow translate from aout relocs to coff relocs */
+#define NO_RELOC 20
+#define RELOC_32 1
+#define RELOC_8 2
+#define RELOC_CONST 3
+#define RELOC_CONSTH 4
+#define RELOC_JUMPTARG 5
+#define RELOC_BASE22 6
+#define RELOC_HI22 7
+#define RELOC_LO10 8
+#define RELOC_BASE13 9
+#define RELOC_WDISP22 10
+#define RELOC_WDISP30 11
+#endif
+
+extern const segT N_TYPE_seg[];
+
+/* Magic number of paged executable. */
+#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE 0x8300
+
+
+/* SYMBOL TABLE */
+
+/* Symbol table entry data type */
+
+typedef struct
+{
+ /* Basic symbol */
+ struct internal_syment ost_entry;
+ /* Auxiliary entry. */
+ union internal_auxent ost_auxent[OBJ_COFF_MAX_AUXENTRIES];
+ /* obj_coff internal use only flags */
+ unsigned int ost_flags;
+} obj_symbol_type;
+
+#ifndef DO_NOT_STRIP
+#define DO_NOT_STRIP 0
+#endif
+/* Symbol table macros and constants */
+
+/* Possible and usefull section number in symbol table
+ * The values of TEXT, DATA and BSS may not be portable.
+ */
+
+#define C_ABS_SECTION N_ABS
+#define C_UNDEF_SECTION N_UNDEF
+#define C_DEBUG_SECTION N_DEBUG
+#define C_NTV_SECTION N_TV
+#define C_PTV_SECTION P_TV
+#define C_REGISTER_SECTION 50
+
+/*
+ * Macros to extract information from a symbol table entry.
+ * This syntaxic indirection allows independence regarding a.out or coff.
+ * The argument (s) of all these macros is a pointer to a symbol table entry.
+ */
+
+/* Predicates */
+/* True if the symbol is external */
+#define S_IS_EXTERNAL(s) ((s)->sy_symbol.ost_entry.n_scnum == C_UNDEF_SECTION)
+/* True if symbol has been defined, ie :
+ section > 0 (DATA, TEXT or BSS)
+ section == 0 and value > 0 (external bss symbol) */
+#define S_IS_DEFINED(s) \
+ ((s)->sy_symbol.ost_entry.n_scnum > C_UNDEF_SECTION \
+ || ((s)->sy_symbol.ost_entry.n_scnum == C_UNDEF_SECTION \
+ && S_GET_VALUE (s) > 0) \
+ || ((s)->sy_symbol.ost_entry.n_scnum == C_ABS_SECTION))
+/* True if a debug special symbol entry */
+#define S_IS_DEBUG(s) ((s)->sy_symbol.ost_entry.n_scnum == C_DEBUG_SECTION)
+/* True if a symbol is local symbol name */
+/* A symbol name whose name includes ^A is a gas internal pseudo symbol */
+#define S_IS_LOCAL(s) \
+ ((s)->sy_symbol.ost_entry.n_scnum == C_REGISTER_SECTION \
+ || (S_LOCAL_NAME(s) && ! flag_keep_locals && ! S_IS_DEBUG (s)) \
+ || strchr (S_GET_NAME (s), '\001') != NULL \
+ || strchr (S_GET_NAME (s), '\002') != NULL \
+ || (flag_strip_local_absolute \
+ && !S_IS_EXTERNAL(s) \
+ && (s)->sy_symbol.ost_entry.n_scnum == C_ABS_SECTION))
+/* True if a symbol is not defined in this file */
+#define S_IS_EXTERN(s) ((s)->sy_symbol.ost_entry.n_scnum == 0 \
+ && S_GET_VALUE (s) == 0)
+/*
+ * True if a symbol can be multiply defined (bss symbols have this def
+ * though it is bad practice)
+ */
+#define S_IS_COMMON(s) ((s)->sy_symbol.ost_entry.n_scnum == 0 \
+ && S_GET_VALUE (s) != 0)
+/* True if a symbol name is in the string table, i.e. its length is > 8. */
+#define S_IS_STRING(s) (strlen(S_GET_NAME(s)) > 8 ? 1 : 0)
+
+/* True if a symbol is defined as weak. */
+#ifdef TE_PE
+#define S_IS_WEAK(s) \
+ ((s)->sy_symbol.ost_entry.n_sclass == C_NT_WEAK \
+ || (s)->sy_symbol.ost_entry.n_sclass == C_WEAKEXT)
+#else
+#define S_IS_WEAK(s) \
+ ((s)->sy_symbol.ost_entry.n_sclass == C_WEAKEXT)
+#endif
+
+/* Accessors */
+/* The name of the symbol */
+#define S_GET_NAME(s) ((char*)(s)->sy_symbol.ost_entry.n_offset)
+/* The pointer to the string table */
+#define S_GET_OFFSET(s) ((s)->sy_symbol.ost_entry.n_offset)
+/* The numeric value of the segment */
+#define S_GET_SEGMENT(s) s_get_segment(s)
+/* The data type */
+#define S_GET_DATA_TYPE(s) ((s)->sy_symbol.ost_entry.n_type)
+/* The storage class */
+#define S_GET_STORAGE_CLASS(s) ((s)->sy_symbol.ost_entry.n_sclass)
+/* The number of auxiliary entries */
+#define S_GET_NUMBER_AUXILIARY(s) ((s)->sy_symbol.ost_entry.n_numaux)
+
+/* Modifiers */
+/* Set the name of the symbol */
+#define S_SET_NAME(s,v) ((s)->sy_symbol.ost_entry.n_offset = (unsigned long)(v))
+/* Set the offset of the symbol */
+#define S_SET_OFFSET(s,v) ((s)->sy_symbol.ost_entry.n_offset = (v))
+/* The numeric value of the segment */
+#define S_SET_SEGMENT(s,v) ((s)->sy_symbol.ost_entry.n_scnum = SEGMENT_TO_SYMBOL_TYPE(v))
+/* The data type */
+#define S_SET_DATA_TYPE(s,v) ((s)->sy_symbol.ost_entry.n_type = (v))
+/* The storage class */
+#define S_SET_STORAGE_CLASS(s,v) ((s)->sy_symbol.ost_entry.n_sclass = (v))
+/* The number of auxiliary entries */
+#define S_SET_NUMBER_AUXILIARY(s,v) ((s)->sy_symbol.ost_entry.n_numaux = (v))
+
+/* Additional modifiers */
+/* The symbol is external (does not mean undefined) */
+#define S_SET_EXTERNAL(s) { S_SET_STORAGE_CLASS(s, C_EXT) ; SF_CLEAR_LOCAL(s); }
+
+/* Auxiliary entry macros. SA_ stands for symbol auxiliary */
+/* Omit the tv related fields */
+/* Accessors */
+#define SYM_AUXENT(S) (&(S)->sy_symbol.ost_auxent[0])
+
+#define SA_GET_SYM_TAGNDX(s) (SYM_AUXENT (s)->x_sym.x_tagndx.l)
+#define SA_GET_SYM_LNNO(s) (SYM_AUXENT (s)->x_sym.x_misc.x_lnsz.x_lnno)
+#define SA_GET_SYM_SIZE(s) (SYM_AUXENT (s)->x_sym.x_misc.x_lnsz.x_size)
+#define SA_GET_SYM_FSIZE(s) (SYM_AUXENT (s)->x_sym.x_misc.x_fsize)
+#define SA_GET_SYM_LNNOPTR(s) (SYM_AUXENT (s)->x_sym.x_fcnary.x_fcn.x_lnnoptr)
+#define SA_GET_SYM_ENDNDX(s) (SYM_AUXENT (s)->x_sym.x_fcnary.x_fcn.x_endndx.l)
+#define SA_GET_SYM_DIMEN(s,i) (SYM_AUXENT (s)->x_sym.x_fcnary.x_ary.x_dimen[(i)])
+#define SA_GET_FILE_FNAME(s) (SYM_AUXENT (s)->x_file.x_fname)
+#define SA_GET_FILE_FNAME_OFFSET(s) (SYM_AUXENT (s)->x_file.x_n.x_offset)
+#define SA_GET_FILE_FNAME_ZEROS(s) (SYM_AUXENT (s)->x_file.x_n.x_zeroes)
+#define SA_GET_SCN_SCNLEN(s) (SYM_AUXENT (s)->x_scn.x_scnlen)
+#define SA_GET_SCN_NRELOC(s) (SYM_AUXENT (s)->x_scn.x_nreloc)
+#define SA_GET_SCN_NLINNO(s) (SYM_AUXENT (s)->x_scn.x_nlinno)
+
+/* Modifiers */
+#define SA_SET_SYM_TAGNDX(s,v) (SYM_AUXENT (s)->x_sym.x_tagndx.l=(v))
+#define SA_SET_SYM_LNNO(s,v) (SYM_AUXENT (s)->x_sym.x_misc.x_lnsz.x_lnno=(v))
+#define SA_SET_SYM_SIZE(s,v) (SYM_AUXENT (s)->x_sym.x_misc.x_lnsz.x_size=(v))
+#define SA_SET_SYM_FSIZE(s,v) (SYM_AUXENT (s)->x_sym.x_misc.x_fsize=(v))
+#define SA_SET_SYM_LNNOPTR(s,v) (SYM_AUXENT (s)->x_sym.x_fcnary.x_fcn.x_lnnoptr=(v))
+#define SA_SET_SYM_ENDNDX(s,v) (SYM_AUXENT (s)->x_sym.x_fcnary.x_fcn.x_endndx.l=(v))
+#define SA_SET_SYM_DIMEN(s,i,v) (SYM_AUXENT (s)->x_sym.x_fcnary.x_ary.x_dimen[(i)]=(v))
+#define SA_SET_FILE_FNAME(s,v) strncpy(SYM_AUXENT (s)->x_file.x_fname,(v),FILNMLEN)
+#define SA_SET_FILE_FNAME_OFFSET(s,v) (SYM_AUXENT (s)->x_file.x_n.x_offset=(v))
+#define SA_SET_FILE_FNAME_ZEROS(s,v) (SYM_AUXENT (s)->x_file.x_n.x_zeroes=(v))
+#define SA_SET_SCN_SCNLEN(s,v) (SYM_AUXENT (s)->x_scn.x_scnlen=(v))
+#define SA_SET_SCN_NRELOC(s,v) (SYM_AUXENT (s)->x_scn.x_nreloc=(v))
+#define SA_SET_SCN_NLINNO(s,v) (SYM_AUXENT (s)->x_scn.x_nlinno=(v))
+
+/*
+ * Internal use only definitions. SF_ stands for symbol flags.
+ *
+ * These values can be assigned to sy_symbol.ost_flags field of a symbolS.
+ *
+ * You'll break i960 if you shift the SYSPROC bits anywhere else. for
+ * more on the balname/callname hack, see tc-i960.h. b.out is done
+ * differently.
+ */
+
+#define SF_I960_MASK (0x000001ff) /* Bits 0-8 are used by the i960 port. */
+#define SF_SYSPROC (0x0000003f) /* bits 0-5 are used to store the sysproc number */
+#define SF_IS_SYSPROC (0x00000040) /* bit 6 marks symbols that are sysprocs */
+#define SF_BALNAME (0x00000080) /* bit 7 marks BALNAME symbols */
+#define SF_CALLNAME (0x00000100) /* bit 8 marks CALLNAME symbols */
+
+#define SF_NORMAL_MASK (0x0000ffff) /* bits 12-15 are general purpose. */
+
+#define SF_STATICS (0x00001000) /* Mark the .text & all symbols */
+#define SF_DEFINED (0x00002000) /* Symbol is defined in this file */
+#define SF_STRING (0x00004000) /* Symbol name length > 8 */
+#define SF_LOCAL (0x00008000) /* Symbol must not be emitted */
+
+#define SF_DEBUG_MASK (0xffff0000) /* bits 16-31 are debug info */
+
+#define SF_FUNCTION (0x00010000) /* The symbol is a function */
+#define SF_PROCESS (0x00020000) /* Process symbol before write */
+#define SF_TAGGED (0x00040000) /* Is associated with a tag */
+#define SF_TAG (0x00080000) /* Is a tag */
+#define SF_DEBUG (0x00100000) /* Is in debug or abs section */
+#define SF_GET_SEGMENT (0x00200000) /* Get the section of the forward symbol. */
+#define SF_ADJ_LNNOPTR (0x00400000) /* Has a lnnoptr */
+/* All other bits are unused. */
+
+/* Accessors */
+#define SF_GET(s) ((s)->sy_symbol.ost_flags)
+#define SF_GET_NORMAL_FIELD(s) (SF_GET (s) & SF_NORMAL_MASK)
+#define SF_GET_DEBUG_FIELD(s) (SF_GET (s) & SF_DEBUG_MASK)
+#define SF_GET_FILE(s) (SF_GET (s) & SF_FILE)
+#define SF_GET_STATICS(s) (SF_GET (s) & SF_STATICS)
+#define SF_GET_DEFINED(s) (SF_GET (s) & SF_DEFINED)
+#define SF_GET_STRING(s) (SF_GET (s) & SF_STRING)
+#define SF_GET_LOCAL(s) (SF_GET (s) & SF_LOCAL)
+#define SF_GET_FUNCTION(s) (SF_GET (s) & SF_FUNCTION)
+#define SF_GET_PROCESS(s) (SF_GET (s) & SF_PROCESS)
+#define SF_GET_DEBUG(s) (SF_GET (s) & SF_DEBUG)
+#define SF_GET_TAGGED(s) (SF_GET (s) & SF_TAGGED)
+#define SF_GET_TAG(s) (SF_GET (s) & SF_TAG)
+#define SF_GET_GET_SEGMENT(s) (SF_GET (s) & SF_GET_SEGMENT)
+#define SF_GET_ADJ_LNNOPTR(s) (SF_GET (s) & SF_ADJ_LNNOPTR)
+#define SF_GET_I960(s) (SF_GET (s) & SF_I960_MASK) /* used by i960 */
+#define SF_GET_BALNAME(s) (SF_GET (s) & SF_BALNAME) /* used by i960 */
+#define SF_GET_CALLNAME(s) (SF_GET (s) & SF_CALLNAME) /* used by i960 */
+#define SF_GET_IS_SYSPROC(s) (SF_GET (s) & SF_IS_SYSPROC) /* used by i960 */
+#define SF_GET_SYSPROC(s) (SF_GET (s) & SF_SYSPROC) /* used by i960 */
+
+/* Modifiers */
+#define SF_SET(s,v) (SF_GET (s) = (v))
+#define SF_SET_NORMAL_FIELD(s,v)(SF_GET (s) |= ((v) & SF_NORMAL_MASK))
+#define SF_SET_DEBUG_FIELD(s,v) (SF_GET (s) |= ((v) & SF_DEBUG_MASK))
+#define SF_SET_FILE(s) (SF_GET (s) |= SF_FILE)
+#define SF_SET_STATICS(s) (SF_GET (s) |= SF_STATICS)
+#define SF_SET_DEFINED(s) (SF_GET (s) |= SF_DEFINED)
+#define SF_SET_STRING(s) (SF_GET (s) |= SF_STRING)
+#define SF_SET_LOCAL(s) (SF_GET (s) |= SF_LOCAL)
+#define SF_CLEAR_LOCAL(s) (SF_GET (s) &= ~SF_LOCAL)
+#define SF_SET_FUNCTION(s) (SF_GET (s) |= SF_FUNCTION)
+#define SF_SET_PROCESS(s) (SF_GET (s) |= SF_PROCESS)
+#define SF_SET_DEBUG(s) (SF_GET (s) |= SF_DEBUG)
+#define SF_SET_TAGGED(s) (SF_GET (s) |= SF_TAGGED)
+#define SF_SET_TAG(s) (SF_GET (s) |= SF_TAG)
+#define SF_SET_GET_SEGMENT(s) (SF_GET (s) |= SF_GET_SEGMENT)
+#define SF_SET_ADJ_LNNOPTR(s) (SF_GET (s) |= SF_ADJ_LNNOPTR)
+#define SF_SET_I960(s,v) (SF_GET (s) |= ((v) & SF_I960_MASK)) /* used by i960 */
+#define SF_SET_BALNAME(s) (SF_GET (s) |= SF_BALNAME) /* used by i960 */
+#define SF_SET_CALLNAME(s) (SF_GET (s) |= SF_CALLNAME) /* used by i960 */
+#define SF_SET_IS_SYSPROC(s) (SF_GET (s) |= SF_IS_SYSPROC) /* used by i960 */
+#define SF_SET_SYSPROC(s,v) (SF_GET (s) |= ((v) & SF_SYSPROC)) /* used by i960 */
+
+/* File header macro and type definition */
+
+/*
+ * File position calculators. Beware to use them when all the
+ * appropriate fields are set in the header.
+ */
+
+#ifdef OBJ_COFF_OMIT_OPTIONAL_HEADER
+#define OBJ_COFF_AOUTHDRSZ (0)
+#else
+#define OBJ_COFF_AOUTHDRSZ (AOUTHDRSZ)
+#endif /* OBJ_COFF_OMIT_OPTIONAL_HEADER */
+
+#define H_GET_FILE_SIZE(h) \
+ (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \
+ H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ + \
+ H_GET_TEXT_SIZE(h) + H_GET_DATA_SIZE(h) + \
+ H_GET_RELOCATION_SIZE(h) + H_GET_LINENO_SIZE(h) + \
+ H_GET_SYMBOL_TABLE_SIZE(h) + \
+ (h)->string_table_size)
+#define H_GET_TEXT_FILE_OFFSET(h) \
+ (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \
+ H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ)
+#define H_GET_DATA_FILE_OFFSET(h) \
+ (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \
+ H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ + \
+ H_GET_TEXT_SIZE(h))
+#define H_GET_BSS_FILE_OFFSET(h) 0
+#define H_GET_RELOCATION_FILE_OFFSET(h) \
+ (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \
+ H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ + \
+ H_GET_TEXT_SIZE(h) + H_GET_DATA_SIZE(h))
+#define H_GET_LINENO_FILE_OFFSET(h) \
+ (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \
+ H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ + \
+ H_GET_TEXT_SIZE(h) + H_GET_DATA_SIZE(h) + \
+ H_GET_RELOCATION_SIZE(h))
+#define H_GET_SYMBOL_TABLE_FILE_OFFSET(h) \
+ (long)(FILHSZ + OBJ_COFF_AOUTHDRSZ + \
+ H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ + \
+ H_GET_TEXT_SIZE(h) + H_GET_DATA_SIZE(h) + \
+ H_GET_RELOCATION_SIZE(h) + H_GET_LINENO_SIZE(h))
+
+/* Accessors */
+/* aouthdr */
+#define H_GET_MAGIC_NUMBER(h) ((h)->aouthdr.magic)
+#define H_GET_VERSION_STAMP(h) ((h)->aouthdr.vstamp)
+#define H_GET_TEXT_SIZE(h) ((h)->aouthdr.tsize)
+#define H_GET_DATA_SIZE(h) ((h)->aouthdr.dsize)
+#define H_GET_BSS_SIZE(h) ((h)->aouthdr.bsize)
+#define H_GET_ENTRY_POINT(h) ((h)->aouthdr.entry)
+#define H_GET_TEXT_START(h) ((h)->aouthdr.text_start)
+#define H_GET_DATA_START(h) ((h)->aouthdr.data_start)
+/* filehdr */
+#define H_GET_FILE_MAGIC_NUMBER(h) ((h)->filehdr.f_magic)
+#define H_GET_NUMBER_OF_SECTIONS(h) ((h)->filehdr.f_nscns)
+#define H_GET_TIME_STAMP(h) ((h)->filehdr.f_timdat)
+#define H_GET_SYMBOL_TABLE_POINTER(h) ((h)->filehdr.f_symptr)
+#define H_GET_SYMBOL_COUNT(h) ((h)->filehdr.f_nsyms)
+#define H_GET_SYMBOL_TABLE_SIZE(h) (H_GET_SYMBOL_COUNT(h) * SYMESZ)
+#define H_GET_SIZEOF_OPTIONAL_HEADER(h) ((h)->filehdr.f_opthdr)
+#define H_GET_FLAGS(h) ((h)->filehdr.f_flags)
+/* Extra fields to achieve bsd a.out compatibility and for convenience */
+#define H_GET_RELOCATION_SIZE(h) ((h)->relocation_size)
+#define H_GET_STRING_SIZE(h) ((h)->string_table_size)
+#define H_GET_LINENO_SIZE(h) ((h)->lineno_size)
+
+#ifndef OBJ_COFF_OMIT_OPTIONAL_HEADER
+#define H_GET_HEADER_SIZE(h) (sizeof(FILHDR) \
+ + sizeof(AOUTHDR)\
+ + (H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ))
+#else /* OBJ_COFF_OMIT_OPTIONAL_HEADER */
+#define H_GET_HEADER_SIZE(h) (sizeof(FILHDR) \
+ + (H_GET_NUMBER_OF_SECTIONS(h) * SCNHSZ))
+#endif /* OBJ_COFF_OMIT_OPTIONAL_HEADER */
+
+#define H_GET_TEXT_RELOCATION_SIZE(h) (text_section_header.s_nreloc * RELSZ)
+#define H_GET_DATA_RELOCATION_SIZE(h) (data_section_header.s_nreloc * RELSZ)
+
+/* Modifiers */
+/* aouthdr */
+#define H_SET_MAGIC_NUMBER(h,v) ((h)->aouthdr.magic = (v))
+#define H_SET_VERSION_STAMP(h,v) ((h)->aouthdr.vstamp = (v))
+#define H_SET_TEXT_SIZE(h,v) ((h)->aouthdr.tsize = (v))
+#define H_SET_DATA_SIZE(h,v) ((h)->aouthdr.dsize = (v))
+#define H_SET_BSS_SIZE(h,v) ((h)->aouthdr.bsize = (v))
+#define H_SET_ENTRY_POINT(h,v) ((h)->aouthdr.entry = (v))
+#define H_SET_TEXT_START(h,v) ((h)->aouthdr.text_start = (v))
+#define H_SET_DATA_START(h,v) ((h)->aouthdr.data_start = (v))
+/* filehdr */
+#define H_SET_FILE_MAGIC_NUMBER(h,v) ((h)->filehdr.f_magic = (v))
+#define H_SET_NUMBER_OF_SECTIONS(h,v) ((h)->filehdr.f_nscns = (v))
+#define H_SET_TIME_STAMP(h,v) ((h)->filehdr.f_timdat = (v))
+#define H_SET_SYMBOL_TABLE_POINTER(h,v) ((h)->filehdr.f_symptr = (v))
+#define H_SET_SYMBOL_TABLE_SIZE(h,v) ((h)->filehdr.f_nsyms = (v))
+#define H_SET_SIZEOF_OPTIONAL_HEADER(h,v) ((h)->filehdr.f_opthdr = (v))
+#define H_SET_FLAGS(h,v) ((h)->filehdr.f_flags = (v))
+/* Extra fields to achieve bsd a.out compatibility and for convinience */
+#define H_SET_RELOCATION_SIZE(h,t,d) ((h)->relocation_size = (t)+(d))
+#define H_SET_STRING_SIZE(h,v) ((h)->string_table_size = (v))
+#define H_SET_LINENO_SIZE(h,v) ((h)->lineno_size = (v))
+
+/* Segment flipping */
+
+typedef struct
+{
+ struct internal_aouthdr aouthdr; /* a.out header */
+ struct internal_filehdr filehdr; /* File header, not machine dep. */
+ long string_table_size; /* names + '\0' + sizeof(int) */
+ long relocation_size; /* Cumulated size of relocation
+ information for all sections in
+ bytes. */
+ long lineno_size; /* Size of the line number information
+ table in bytes */
+} object_headers;
+
+
+
+struct lineno_list
+{
+ struct bfd_internal_lineno line;
+ char *frag; /* Frag to which the line number is related */
+ struct lineno_list *next; /* Forward chain pointer */
+};
+
+
+
+
+#define obj_segment_name(i) (segment_info[(int) (i)].scnhdr.s_name)
+
+#define obj_add_segment(s) obj_coff_add_segment (s)
+
+extern segT obj_coff_add_segment PARAMS ((const char *));
+
+extern void obj_coff_section PARAMS ((int));
+
+extern void c_dot_file_symbol PARAMS ((char *filename));
+#define obj_app_file c_dot_file_symbol
+extern void obj_extra_stuff PARAMS ((object_headers * headers));
+
+extern segT s_get_segment PARAMS ((struct symbol * ptr));
+
+extern void c_section_header PARAMS ((struct internal_scnhdr * header,
+ char *name,
+ long core_address,
+ long size,
+ long data_ptr,
+ long reloc_ptr,
+ long lineno_ptr,
+ long reloc_number,
+ long lineno_number,
+ long alignment));
+
+#ifndef tc_coff_symbol_emit_hook
+void tc_coff_symbol_emit_hook PARAMS ((struct symbol *));
+#endif
+
+/* sanity check */
+
+#ifdef TC_I960
+#ifndef C_LEAFSTAT
+hey ! Where is the C_LEAFSTAT definition ? i960 - coff support is depending on it.
+#endif /* no C_LEAFSTAT */
+#endif /* TC_I960 */
+extern struct internal_scnhdr data_section_header;
+extern struct internal_scnhdr text_section_header;
+
+/* Forward the segment of a forwarded symbol. */
+#define OBJ_COPY_SYMBOL_ATTRIBUTES(dest,src) \
+ (SF_GET_GET_SEGMENT (dest) \
+ ? (S_SET_SEGMENT (dest, S_GET_SEGMENT (src)), 0) \
+ : 0)
+
+#ifdef TE_PE
+#define obj_handle_link_once(t) obj_coff_pe_handle_link_once (t)
+extern void obj_coff_pe_handle_link_once ();
+#endif
+
+#endif /* not BFD_ASSEMBLER */
+
+/* In COFF, if a symbol is defined using .def/.val SYM/.endef, it's OK
+ to redefine the symbol later on. This can happen if C symbols use
+ a prefix, and a symbol is defined both with and without the prefix,
+ as in start/_start/__start in gcc/libgcc1-test.c. */
+#define RESOLVE_SYMBOL_REDEFINITION(sym) \
+(SF_GET_GET_SEGMENT (sym) \
+ ? (sym->sy_frag = frag_now, \
+ S_SET_VALUE (sym, frag_now_fix ()), \
+ S_SET_SEGMENT (sym, now_seg), \
+ 0) \
+ : 0)
+
+/* Stabs in a coff file go into their own section. */
+#define SEPARATE_STAB_SECTIONS 1
+
+/* We need 12 bytes at the start of the section to hold some initial
+ information. */
+extern void obj_coff_init_stab_section PARAMS ((segT));
+#define INIT_STAB_SECTION(seg) obj_coff_init_stab_section (seg)
+
+#endif /* OBJ_FORMAT_H */
diff --git a/gas/config/obj-ecoff.c b/gas/config/obj-ecoff.c
new file mode 100644
index 0000000..ec3ce88
--- /dev/null
+++ b/gas/config/obj-ecoff.c
@@ -0,0 +1,309 @@
+/* ECOFF object file format.
+ Copyright (C) 1993, 94, 95, 96, 1997, 1998 Free Software Foundation, Inc.
+ Contributed by Cygnus Support.
+ This file was put together by Ian Lance Taylor <ian@cygnus.com>.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#define OBJ_HEADER "obj-ecoff.h"
+#include "as.h"
+#include "coff/internal.h"
+#include "bfd/libcoff.h"
+#include "bfd/libecoff.h"
+
+/* Almost all of the ECOFF support is actually in ecoff.c in the main
+ gas directory. This file mostly just arranges to call that one at
+ the right times. */
+
+static int ecoff_sec_sym_ok_for_reloc PARAMS ((asection *));
+static void obj_ecoff_frob_symbol PARAMS ((symbolS *, int *));
+static void ecoff_pop_insert PARAMS ((void));
+
+/* These are the pseudo-ops we support in this file. Only those
+ relating to debugging information are supported here.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book
+ should be defined here, but are currently unsupported: .aent,
+ .bgnb, .endb, .verstamp, .vreg.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book are
+ MIPS CPU specific, and should be defined by tc-mips.c: .alias,
+ .extern, .galive, .gjaldef, .gjrlive, .livereg, .noalias, .option,
+ .rdata, .sdata, .set.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book are
+ not MIPS CPU specific, but are also not ECOFF specific. I have
+ only listed the ones which are not already in read.c. It's not
+ completely clear where these should be defined, but tc-mips.c is
+ probably the most reasonable place: .asciiz, .asm0, .endr, .err,
+ .half, .lab, .repeat, .struct, .weakext. */
+
+const pseudo_typeS obj_pseudo_table[] =
+{
+ /* COFF style debugging information. .ln is not used; .loc is used
+ instead. */
+ { "def", ecoff_directive_def, 0 },
+ { "dim", ecoff_directive_dim, 0 },
+ { "endef", ecoff_directive_endef, 0 },
+ { "file", ecoff_directive_file, 0 },
+ { "scl", ecoff_directive_scl, 0 },
+ { "size", ecoff_directive_size, 0 },
+ { "esize", ecoff_directive_size, 0 },
+ { "tag", ecoff_directive_tag, 0 },
+ { "type", ecoff_directive_type, 0 },
+ { "etype", ecoff_directive_type, 0 },
+ { "val", ecoff_directive_val, 0 },
+
+ /* ECOFF specific debugging information. */
+ { "begin", ecoff_directive_begin, 0 },
+ { "bend", ecoff_directive_bend, 0 },
+ { "end", ecoff_directive_end, 0 },
+ { "ent", ecoff_directive_ent, 0 },
+ { "fmask", ecoff_directive_fmask, 0 },
+ { "frame", ecoff_directive_frame, 0 },
+ { "loc", ecoff_directive_loc, 0 },
+ { "mask", ecoff_directive_mask, 0 },
+
+ /* Other ECOFF directives. */
+ { "extern", ecoff_directive_extern, 0 },
+
+#ifndef TC_MIPS
+ /* For TC_MIPS, tc-mips.c adds this. */
+ { "weakext", ecoff_directive_weakext, 0 },
+#endif
+
+ /* These are used on Irix. I don't know how to implement them. */
+ { "bgnb", s_ignore, 0 },
+ { "endb", s_ignore, 0 },
+ { "verstamp", s_ignore, 0 },
+
+ /* Sentinel. */
+ { NULL }
+};
+
+/* Swap out the symbols and debugging information for BFD. */
+
+void
+ecoff_frob_file ()
+{
+ const struct ecoff_debug_swap * const debug_swap
+ = &ecoff_backend (stdoutput)->debug_swap;
+ bfd_vma addr;
+ asection *sec;
+ HDRR *hdr;
+ char *buf;
+ char *set;
+
+ /* Set the section VMA values. We force the .sdata and .sbss
+ sections to the end to ensure that their VMA addresses are close
+ together so that the GP register can address both of them. We
+ put the .bss section after the .sbss section.
+
+ Also, for the Alpha, we must sort the sections, to make sure they
+ appear in the output file in the correct order. (Actually, maybe
+ this is a job for BFD. But the VMAs computed would be out of
+ whack if we computed them given our initial, random ordering.
+ It's possible that that wouldn't break things; I could do some
+ experimenting sometime and find out.
+
+ This output ordering of sections is magic, on the Alpha, at
+ least. The .lita section must come before .lit8 and .lit4,
+ otherwise the OSF/1 linker may silently trash the .lit{4,8}
+ section contents. Also, .text must preceed .rdata. These differ
+ from the order described in some parts of the DEC OSF/1 Assembly
+ Language Programmer's Guide, but that order doesn't seem to work
+ with their linker.
+
+ I don't know if section ordering on the MIPS is important. */
+
+ static const char *const names[] = {
+ /* text segment */
+ ".text", ".rdata", ".init", ".fini",
+ /* data segment */
+ ".data", ".lita", ".lit8", ".lit4", ".sdata", ".got",
+ /* bss segment */
+ ".sbss", ".bss",
+ };
+#define n_names (sizeof (names) / sizeof (names[0]))
+
+ addr = 0;
+ {
+ /* Sections that match names, order to be straightened out later. */
+ asection *secs[n_names];
+ /* Linked list of sections with non-matching names. Random ordering. */
+ asection *other_sections = 0;
+ /* Pointer to next section, since we're destroying the original
+ ordering. */
+ asection *next;
+
+ int i;
+
+ for (i = 0; i < n_names; i++)
+ secs[i] = 0;
+ for (sec = stdoutput->sections; sec != (asection *) NULL; sec = next)
+ {
+ next = sec->next;
+ for (i = 0; i < n_names; i++)
+ if (!strcmp (sec->name, names[i]))
+ {
+ secs[i] = sec;
+ break;
+ }
+ if (i == n_names)
+ {
+ bfd_set_section_vma (stdoutput, sec, addr);
+ addr += bfd_section_size (stdoutput, sec);
+ sec->next = other_sections;
+ other_sections = sec;
+ }
+ }
+ for (i = 0; i < n_names; i++)
+ if (secs[i])
+ {
+ sec = secs[i];
+ bfd_set_section_vma (stdoutput, sec, addr);
+ addr += bfd_section_size (stdoutput, sec);
+ }
+ for (i = n_names - 1; i >= 0; i--)
+ if (secs[i])
+ {
+ sec = secs[i];
+ sec->next = other_sections;
+ other_sections = sec;
+ }
+ stdoutput->sections = other_sections;
+ }
+
+ /* Build the ECOFF debugging information. */
+ assert (ecoff_data (stdoutput) != 0);
+ hdr = &ecoff_data (stdoutput)->debug_info.symbolic_header;
+ ecoff_build_debug (hdr, &buf, debug_swap);
+
+ /* Finish up the ecoff_tdata structure. */
+ set = buf;
+#define SET(ptr, count, type, size) \
+ if (hdr->count == 0) \
+ ecoff_data (stdoutput)->debug_info.ptr = (type) NULL; \
+ else \
+ { \
+ ecoff_data (stdoutput)->debug_info.ptr = (type) set; \
+ set += hdr->count * size; \
+ }
+
+ SET (line, cbLine, unsigned char *, sizeof (unsigned char));
+ SET (external_dnr, idnMax, PTR, debug_swap->external_dnr_size);
+ SET (external_pdr, ipdMax, PTR, debug_swap->external_pdr_size);
+ SET (external_sym, isymMax, PTR, debug_swap->external_sym_size);
+ SET (external_opt, ioptMax, PTR, debug_swap->external_opt_size);
+ SET (external_aux, iauxMax, union aux_ext *, sizeof (union aux_ext));
+ SET (ss, issMax, char *, sizeof (char));
+ SET (ssext, issExtMax, char *, sizeof (char));
+ SET (external_rfd, crfd, PTR, debug_swap->external_rfd_size);
+ SET (external_fdr, ifdMax, PTR, debug_swap->external_fdr_size);
+ SET (external_ext, iextMax, PTR, debug_swap->external_ext_size);
+
+#undef SET
+
+ /* Fill in the register masks. */
+ {
+ unsigned long gprmask = 0;
+ unsigned long fprmask = 0;
+ unsigned long *cprmask = NULL;
+
+#ifdef TC_MIPS
+ /* Fill in the MIPS register masks. It's probably not worth
+ setting up a generic interface for this. */
+ gprmask = mips_gprmask;
+ cprmask = mips_cprmask;
+#endif
+
+#ifdef TC_ALPHA
+ alpha_frob_ecoff_data ();
+
+ if (! bfd_ecoff_set_gp_value (stdoutput, alpha_gp_value))
+ as_fatal (_("Can't set GP value"));
+
+ gprmask = alpha_gprmask;
+ fprmask = alpha_fprmask;
+#endif
+
+ if (! bfd_ecoff_set_regmasks (stdoutput, gprmask, fprmask, cprmask))
+ as_fatal (_("Can't set register masks"));
+ }
+}
+
+/* This is called by the ECOFF code to set the external information
+ for a symbol. We just pass it on to BFD, which expects the swapped
+ information to be stored in the native field of the symbol. */
+
+void
+obj_ecoff_set_ext (sym, ext)
+ symbolS *sym;
+ EXTR *ext;
+{
+ const struct ecoff_debug_swap * const debug_swap
+ = &ecoff_backend (stdoutput)->debug_swap;
+ ecoff_symbol_type *esym;
+
+ know (bfd_asymbol_flavour (sym->bsym) == bfd_target_ecoff_flavour);
+ esym = ecoffsymbol (sym->bsym);
+ esym->local = false;
+ esym->native = xmalloc (debug_swap->external_ext_size);
+ (*debug_swap->swap_ext_out) (stdoutput, ext, esym->native);
+}
+
+static int
+ecoff_sec_sym_ok_for_reloc (sec)
+ asection *sec;
+{
+ return 1;
+}
+
+static void
+obj_ecoff_frob_symbol (sym, puntp)
+ symbolS *sym;
+ int *puntp;
+{
+ ecoff_frob_symbol (sym);
+}
+
+static void
+ecoff_pop_insert ()
+{
+ pop_insert (obj_pseudo_table);
+}
+
+const struct format_ops ecoff_format_ops =
+{
+ bfd_target_ecoff_flavour,
+ 0,
+ 1,
+ obj_ecoff_frob_symbol,
+ ecoff_frob_file,
+ 0,
+ 0, 0,
+ 0, 0,
+ 0,
+ ecoff_generate_asm_lineno,
+ ecoff_stab,
+ ecoff_sec_sym_ok_for_reloc,
+ ecoff_pop_insert,
+ ecoff_set_ext,
+ ecoff_read_begin_hook,
+ ecoff_symbol_new_hook,
+};
diff --git a/gas/config/obj-ecoff.h b/gas/config/obj-ecoff.h
new file mode 100644
index 0000000..427e619
--- /dev/null
+++ b/gas/config/obj-ecoff.h
@@ -0,0 +1,67 @@
+/* ECOFF object file format header file.
+ Copyright (C) 1993, 94, 95, 96, 97, 1999 Free Software Foundation, Inc.
+ Contributed by Cygnus Support.
+ Written by Ian Lance Taylor <ian@cygnus.com>.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#define OBJ_ECOFF 1
+
+/* Use the generic ECOFF debugging code. */
+#define ECOFF_DEBUGGING 1
+
+#define OUTPUT_FLAVOR bfd_target_ecoff_flavour
+
+#include "targ-cpu.h"
+
+#include "ecoff.h"
+
+/* For each gas symbol we keep track of which file it came from, of
+ whether we have generated an ECOFF symbol for it, and whether the
+ symbols is undefined (this last is needed to distinguish a .extern
+ symbols from a .comm symbol). */
+
+#define TARGET_SYMBOL_FIELDS \
+ struct efdr *ecoff_file; \
+ struct localsym *ecoff_symbol; \
+ valueT ecoff_extern_size;
+
+/* Modify the ECOFF symbol. */
+#define obj_frob_symbol(symp, punt) ecoff_frob_symbol (symp)
+
+/* This is used to write the symbolic data in the format that BFD
+ expects it. */
+extern void ecoff_frob_file PARAMS ((void));
+#define obj_frob_file() ecoff_frob_file ()
+
+/* We use the ECOFF functions as our hooks. */
+#define obj_read_begin_hook ecoff_read_begin_hook
+#define obj_symbol_new_hook ecoff_symbol_new_hook
+
+/* Record file switches in the ECOFF symbol table. */
+#define obj_app_file(name) ecoff_new_file (name)
+
+/* At the moment we don't want to do any stabs processing in read.c. */
+#define OBJ_PROCESS_STAB(seg, what, string, type, other, desc) \
+ ecoff_stab ((seg), (what), (string), (type), (other), (desc))
+
+#define EMIT_SECTION_SYMBOLS 0
+#define obj_sec_sym_ok_for_reloc(SEC) 1
+
+#define obj_ecoff_set_ext ecoff_set_ext
+extern void obj_ecoff_set_ext PARAMS ((struct symbol *, EXTR *));
diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c
new file mode 100644
index 0000000..f50c4b7
--- /dev/null
+++ b/gas/config/obj-elf.c
@@ -0,0 +1,1764 @@
+/* ELF object file format
+ Copyright (C) 1992, 93, 94, 95, 96, 97, 98, 1999 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#define OBJ_HEADER "obj-elf.h"
+#include "as.h"
+#include "subsegs.h"
+#include "obstack.h"
+
+#ifndef ECOFF_DEBUGGING
+#define ECOFF_DEBUGGING 0
+#else
+#define NEED_ECOFF_DEBUG
+#endif
+
+#ifdef NEED_ECOFF_DEBUG
+#include "ecoff.h"
+#endif
+
+#ifdef TC_ALPHA
+#include "elf/alpha.h"
+#endif
+
+#ifdef TC_MIPS
+#include "elf/mips.h"
+#endif
+
+#ifdef TC_PPC
+#include "elf/ppc.h"
+#endif
+
+static bfd_vma elf_s_get_size PARAMS ((symbolS *));
+static void elf_s_set_size PARAMS ((symbolS *, bfd_vma));
+static bfd_vma elf_s_get_align PARAMS ((symbolS *));
+static void elf_s_set_align PARAMS ((symbolS *, bfd_vma));
+static void elf_copy_symbol_attributes PARAMS ((symbolS *, symbolS *));
+static int elf_sec_sym_ok_for_reloc PARAMS ((asection *));
+static void adjust_stab_sections PARAMS ((bfd *, asection *, PTR));
+
+#ifdef NEED_ECOFF_DEBUG
+static boolean elf_get_extr PARAMS ((asymbol *, EXTR *));
+static void elf_set_index PARAMS ((asymbol *, bfd_size_type));
+#endif
+
+static void obj_elf_line PARAMS ((int));
+void obj_elf_version PARAMS ((int));
+static void obj_elf_size PARAMS ((int));
+static void obj_elf_type PARAMS ((int));
+static void obj_elf_ident PARAMS ((int));
+static void obj_elf_weak PARAMS ((int));
+static void obj_elf_local PARAMS ((int));
+static void obj_elf_common PARAMS ((int));
+static void obj_elf_symver PARAMS ((int));
+static void obj_elf_vtable_inherit PARAMS ((int));
+static void obj_elf_vtable_entry PARAMS ((int));
+static void obj_elf_data PARAMS ((int));
+static void obj_elf_text PARAMS ((int));
+static void obj_elf_subsection PARAMS ((int));
+
+static const pseudo_typeS elf_pseudo_table[] =
+{
+ {"comm", obj_elf_common, 0},
+ {"ident", obj_elf_ident, 0},
+ {"local", obj_elf_local, 0},
+ {"previous", obj_elf_previous, 0},
+ {"section", obj_elf_section, 0},
+ {"section.s", obj_elf_section, 0},
+ {"sect", obj_elf_section, 0},
+ {"sect.s", obj_elf_section, 0},
+ {"size", obj_elf_size, 0},
+ {"type", obj_elf_type, 0},
+ {"version", obj_elf_version, 0},
+ {"weak", obj_elf_weak, 0},
+
+ /* These are used for stabs-in-elf configurations. */
+ {"line", obj_elf_line, 0},
+
+ /* This is a GNU extension to handle symbol versions. */
+ {"symver", obj_elf_symver, 0},
+
+ /* A GNU extension to change subsection only. */
+ {"subsection", obj_elf_subsection, 0},
+
+ /* These are GNU extensions to aid in garbage collecting C++ vtables. */
+ {"vtable_inherit", obj_elf_vtable_inherit, 0},
+ {"vtable_entry", obj_elf_vtable_entry, 0},
+
+ /* These are used for dwarf. */
+ {"2byte", cons, 2},
+ {"4byte", cons, 4},
+ {"8byte", cons, 8},
+
+ /* We need to trap the section changing calls to handle .previous. */
+ {"data", obj_elf_data, 0},
+ {"text", obj_elf_text, 0},
+
+ /* End sentinel. */
+ {NULL},
+};
+
+static const pseudo_typeS ecoff_debug_pseudo_table[] =
+{
+#ifdef NEED_ECOFF_DEBUG
+ /* COFF style debugging information for ECOFF. .ln is not used; .loc
+ is used instead. */
+ { "def", ecoff_directive_def, 0 },
+ { "dim", ecoff_directive_dim, 0 },
+ { "endef", ecoff_directive_endef, 0 },
+ { "file", ecoff_directive_file, 0 },
+ { "scl", ecoff_directive_scl, 0 },
+ { "tag", ecoff_directive_tag, 0 },
+ { "val", ecoff_directive_val, 0 },
+
+ /* COFF debugging requires pseudo-ops .size and .type, but ELF
+ already has meanings for those. We use .esize and .etype
+ instead. These are only generated by gcc anyhow. */
+ { "esize", ecoff_directive_size, 0 },
+ { "etype", ecoff_directive_type, 0 },
+
+ /* ECOFF specific debugging information. */
+ { "begin", ecoff_directive_begin, 0 },
+ { "bend", ecoff_directive_bend, 0 },
+ { "end", ecoff_directive_end, 0 },
+ { "ent", ecoff_directive_ent, 0 },
+ { "fmask", ecoff_directive_fmask, 0 },
+ { "frame", ecoff_directive_frame, 0 },
+ { "loc", ecoff_directive_loc, 0 },
+ { "mask", ecoff_directive_mask, 0 },
+
+ /* Other ECOFF directives. */
+ { "extern", ecoff_directive_extern, 0 },
+
+ /* These are used on Irix. I don't know how to implement them. */
+ { "alias", s_ignore, 0 },
+ { "bgnb", s_ignore, 0 },
+ { "endb", s_ignore, 0 },
+ { "lab", s_ignore, 0 },
+ { "noalias", s_ignore, 0 },
+ { "verstamp", s_ignore, 0 },
+ { "vreg", s_ignore, 0 },
+#endif
+
+ {NULL} /* end sentinel */
+};
+
+#undef NO_RELOC
+#include "aout/aout64.h"
+
+/* This is called when the assembler starts. */
+
+void
+elf_begin ()
+{
+ /* Add symbols for the known sections to the symbol table. */
+ symbol_table_insert (section_symbol (bfd_get_section_by_name (stdoutput,
+ TEXT_SECTION_NAME)));
+ symbol_table_insert (section_symbol (bfd_get_section_by_name (stdoutput,
+ DATA_SECTION_NAME)));
+ symbol_table_insert (section_symbol (bfd_get_section_by_name (stdoutput,
+ BSS_SECTION_NAME)));
+}
+
+void
+elf_pop_insert ()
+{
+ pop_insert (elf_pseudo_table);
+ if (ECOFF_DEBUGGING)
+ pop_insert (ecoff_debug_pseudo_table);
+}
+
+static bfd_vma
+elf_s_get_size (sym)
+ symbolS *sym;
+{
+ return S_GET_SIZE (sym);
+}
+
+static void
+elf_s_set_size (sym, sz)
+ symbolS *sym;
+ bfd_vma sz;
+{
+ S_SET_SIZE (sym, sz);
+}
+
+static bfd_vma
+elf_s_get_align (sym)
+ symbolS *sym;
+{
+ return S_GET_ALIGN (sym);
+}
+
+static void
+elf_s_set_align (sym, align)
+ symbolS *sym;
+ bfd_vma align;
+{
+ S_SET_ALIGN (sym, align);
+}
+
+static void
+elf_copy_symbol_attributes (dest, src)
+ symbolS *dest, *src;
+{
+ OBJ_COPY_SYMBOL_ATTRIBUTES (dest, src);
+}
+
+static int
+elf_sec_sym_ok_for_reloc (sec)
+ asection *sec;
+{
+ return obj_sec_sym_ok_for_reloc (sec);
+}
+
+void
+elf_file_symbol (s)
+ char *s;
+{
+ symbolS *sym;
+
+ sym = symbol_new (s, absolute_section, (valueT) 0, (struct frag *) 0);
+ sym->sy_frag = &zero_address_frag;
+ sym->bsym->flags |= BSF_FILE;
+
+ if (symbol_rootP != sym)
+ {
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ symbol_insert (sym, symbol_rootP, &symbol_rootP, &symbol_lastP);
+#ifdef DEBUG
+ verify_symbol_chain (symbol_rootP, symbol_lastP);
+#endif
+ }
+
+#ifdef NEED_ECOFF_DEBUG
+ ecoff_new_file (s);
+#endif
+}
+
+static void
+obj_elf_common (ignore)
+ int ignore;
+{
+ char *name;
+ char c;
+ char *p;
+ int temp, size;
+ symbolS *symbolP;
+ int have_align;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ /* just after name is now '\0' */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("Expected comma after symbol-name"));
+ ignore_rest_of_line ();
+ return;
+ }
+ input_line_pointer++; /* skip ',' */
+ if ((temp = get_absolute_expression ()) < 0)
+ {
+ as_bad (_(".COMMon length (%d.) <0! Ignored."), temp);
+ ignore_rest_of_line ();
+ return;
+ }
+ size = temp;
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+ if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
+ {
+ as_bad (_("Ignoring attempt to re-define symbol"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (S_GET_VALUE (symbolP) != 0)
+ {
+ if (S_GET_VALUE (symbolP) != size)
+ {
+ as_warn (_("Length of .comm \"%s\" is already %ld. Not changed to %d."),
+ S_GET_NAME (symbolP), (long) S_GET_VALUE (symbolP), size);
+ }
+ }
+ know (symbolP->sy_frag == &zero_address_frag);
+ if (*input_line_pointer != ',')
+ have_align = 0;
+ else
+ {
+ have_align = 1;
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ }
+ if (! have_align || *input_line_pointer != '"')
+ {
+ if (! have_align)
+ temp = 0;
+ else
+ {
+ temp = get_absolute_expression ();
+ if (temp < 0)
+ {
+ temp = 0;
+ as_warn (_("Common alignment negative; 0 assumed"));
+ }
+ }
+ if (symbolP->local)
+ {
+ segT old_sec;
+ int old_subsec;
+ char *pfrag;
+ int align;
+
+ /* allocate_bss: */
+ old_sec = now_seg;
+ old_subsec = now_subseg;
+ if (temp)
+ {
+ /* convert to a power of 2 alignment */
+ for (align = 0; (temp & 1) == 0; temp >>= 1, ++align);
+ if (temp != 1)
+ {
+ as_bad (_("Common alignment not a power of 2"));
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ else
+ align = 0;
+ record_alignment (bss_section, align);
+ subseg_set (bss_section, 0);
+ if (align)
+ frag_align (align, 0, 0);
+ if (S_GET_SEGMENT (symbolP) == bss_section)
+ symbolP->sy_frag->fr_symbol = 0;
+ symbolP->sy_frag = frag_now;
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
+ (offsetT) size, (char *) 0);
+ *pfrag = 0;
+ S_SET_SIZE (symbolP, size);
+ S_SET_SEGMENT (symbolP, bss_section);
+ S_CLEAR_EXTERNAL (symbolP);
+ subseg_set (old_sec, old_subsec);
+ }
+ else
+ {
+ allocate_common:
+ S_SET_VALUE (symbolP, (valueT) size);
+ S_SET_ALIGN (symbolP, temp);
+ S_SET_EXTERNAL (symbolP);
+ S_SET_SEGMENT (symbolP, bfd_com_section_ptr);
+ }
+ }
+ else
+ {
+ input_line_pointer++;
+ /* @@ Some use the dot, some don't. Can we get some consistency?? */
+ if (*input_line_pointer == '.')
+ input_line_pointer++;
+ /* @@ Some say data, some say bss. */
+ if (strncmp (input_line_pointer, "bss\"", 4)
+ && strncmp (input_line_pointer, "data\"", 5))
+ {
+ while (*--input_line_pointer != '"')
+ ;
+ input_line_pointer--;
+ goto bad_common_segment;
+ }
+ while (*input_line_pointer++ != '"')
+ ;
+ goto allocate_common;
+ }
+
+ symbolP->bsym->flags |= BSF_OBJECT;
+
+ demand_empty_rest_of_line ();
+ return;
+
+ {
+ bad_common_segment:
+ p = input_line_pointer;
+ while (*p && *p != '\n')
+ p++;
+ c = *p;
+ *p = '\0';
+ as_bad (_("bad .common segment %s"), input_line_pointer + 1);
+ *p = c;
+ input_line_pointer = p;
+ ignore_rest_of_line ();
+ return;
+ }
+}
+
+static void
+obj_elf_local (ignore)
+ int ignore;
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+
+ do
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+ S_CLEAR_EXTERNAL (symbolP);
+ symbolP->local = 1;
+ if (c == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\n')
+ c = '\n';
+ }
+ }
+ while (c == ',');
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_elf_weak (ignore)
+ int ignore;
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+
+ do
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+ S_SET_WEAK (symbolP);
+ symbolP->local = 1;
+ if (c == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\n')
+ c = '\n';
+ }
+ }
+ while (c == ',');
+ demand_empty_rest_of_line ();
+}
+
+static segT previous_section;
+static int previous_subsection;
+
+/* Handle the .section pseudo-op. This code supports two different
+ syntaxes.
+
+ The first is found on Solaris, and looks like
+ .section ".sec1",#alloc,#execinstr,#write
+ Here the names after '#' are the SHF_* flags to turn on for the
+ section. I'm not sure how it determines the SHT_* type (BFD
+ doesn't really give us control over the type, anyhow).
+
+ The second format is found on UnixWare, and probably most SVR4
+ machines, and looks like
+ .section .sec1,"a",@progbits
+ The quoted string may contain any combination of a, w, x, and
+ represents the SHF_* flags to turn on for the section. The string
+ beginning with '@' can be progbits or nobits. There should be
+ other possibilities, but I don't know what they are. In any case,
+ BFD doesn't really let us set the section type. */
+
+/* Certain named sections have particular defined types, listed on p.
+ 4-19 of the ABI. */
+struct special_section
+{
+ const char *name;
+ int type;
+ int attributes;
+};
+
+static struct special_section special_sections[] =
+{
+ { ".bss", SHT_NOBITS, SHF_ALLOC + SHF_WRITE },
+ { ".comment", SHT_PROGBITS, 0 },
+ { ".data", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
+ { ".data1", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
+ { ".debug", SHT_PROGBITS, 0 },
+ { ".fini", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
+ { ".init", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
+ { ".line", SHT_PROGBITS, 0 },
+ { ".note", SHT_NOTE, 0 },
+ { ".rodata", SHT_PROGBITS, SHF_ALLOC },
+ { ".rodata1", SHT_PROGBITS, SHF_ALLOC },
+ { ".text", SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
+
+#ifdef ELF_TC_SPECIAL_SECTIONS
+ ELF_TC_SPECIAL_SECTIONS
+#endif
+
+#if 0
+ /* The following section names are special, but they can not
+ reasonably appear in assembler code. Some of the attributes are
+ processor dependent. */
+ { ".dynamic", SHT_DYNAMIC, SHF_ALLOC /* + SHF_WRITE */ },
+ { ".dynstr", SHT_STRTAB, SHF_ALLOC },
+ { ".dynsym", SHT_DYNSYM, SHF_ALLOC },
+ { ".got", SHT_PROGBITS, 0 },
+ { ".hash", SHT_HASH, SHF_ALLOC },
+ { ".interp", SHT_PROGBITS, /* SHF_ALLOC */ },
+ { ".plt", SHT_PROGBITS, 0 },
+ { ".shstrtab",SHT_STRTAB, 0 },
+ { ".strtab", SHT_STRTAB, /* SHF_ALLOC */ },
+ { ".symtab", SHT_SYMTAB, /* SHF_ALLOC */ },
+#endif
+
+ { NULL, 0, 0 }
+};
+
+void
+obj_elf_section (xxx)
+ int xxx;
+{
+ char *string;
+ int new_sec;
+ segT sec;
+ int type, attr;
+ int i;
+ flagword flags;
+ symbolS *secsym;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ if (flag_mri)
+ {
+ char mri_type;
+
+ previous_section = now_seg;
+ previous_subsection = now_subseg;
+
+ s_mri_sect (&mri_type);
+
+#ifdef md_elf_section_change_hook
+ md_elf_section_change_hook ();
+#endif
+
+ return;
+ }
+
+ /* Get name of section. */
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '"')
+ {
+ string = demand_copy_C_string (&xxx);
+ if (string == NULL)
+ {
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ else
+ {
+ char *p = input_line_pointer;
+ char c;
+ while (0 == strchr ("\n\t,; ", *p))
+ p++;
+ if (p == input_line_pointer)
+ {
+ as_warn (_("Missing section name"));
+ ignore_rest_of_line ();
+ return;
+ }
+ c = *p;
+ *p = 0;
+ string = xmalloc ((unsigned long) (p - input_line_pointer + 1));
+ strcpy (string, input_line_pointer);
+ *p = c;
+ input_line_pointer = p;
+ }
+
+ /* Switch to the section, creating it if necessary. */
+ previous_section = now_seg;
+ previous_subsection = now_subseg;
+
+ new_sec = bfd_get_section_by_name (stdoutput, string) == NULL;
+ sec = subseg_new (string, 0);
+
+ /* If this section already existed, we don't bother to change the
+ flag values. */
+ if (! new_sec)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ ++input_line_pointer;
+
+#ifdef md_elf_section_change_hook
+ md_elf_section_change_hook ();
+#endif
+
+ return;
+ }
+
+ SKIP_WHITESPACE ();
+
+ type = SHT_NULL;
+ attr = 0;
+
+ if (*input_line_pointer == ',')
+ {
+ /* Skip the comma. */
+ ++input_line_pointer;
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer == '"')
+ {
+ /* Pick up a string with a combination of a, w, x. */
+ ++input_line_pointer;
+ while (*input_line_pointer != '"')
+ {
+ switch (*input_line_pointer)
+ {
+ case 'a':
+ attr |= SHF_ALLOC;
+ break;
+ case 'w':
+ attr |= SHF_WRITE;
+ break;
+ case 'x':
+ attr |= SHF_EXECINSTR;
+ break;
+ default:
+ {
+ char *bad_msg = _("Bad .section directive: want a,w,x in string");
+#ifdef md_elf_section_letter
+ int md_attr = md_elf_section_letter (*input_line_pointer, &bad_msg);
+ if (md_attr)
+ attr |= md_attr;
+ else
+#endif
+ {
+ as_warn (bad_msg);
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ }
+ ++input_line_pointer;
+ }
+
+ /* Skip the closing quote. */
+ ++input_line_pointer;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '@' || *input_line_pointer == '%')
+ {
+ ++input_line_pointer;
+ if (strncmp (input_line_pointer, "progbits",
+ sizeof "progbits" - 1) == 0)
+ {
+ type = SHT_PROGBITS;
+ input_line_pointer += sizeof "progbits" - 1;
+ }
+ else if (strncmp (input_line_pointer, "nobits",
+ sizeof "nobits" - 1) == 0)
+ {
+ type = SHT_NOBITS;
+ input_line_pointer += sizeof "nobits" - 1;
+ }
+ else
+ {
+#ifdef md_elf_section_type
+ int md_type = md_elf_section_type (&input_line_pointer);
+ if (md_type)
+ type = md_type;
+ else
+#endif
+ {
+ as_warn (_("Unrecognized section type"));
+ ignore_rest_of_line ();
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ do
+ {
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != '#')
+ {
+ as_warn (_("Bad .section directive - character following name is not '#'"));
+ ignore_rest_of_line ();
+ return;
+ }
+ ++input_line_pointer;
+ if (strncmp (input_line_pointer, "write",
+ sizeof "write" - 1) == 0)
+ {
+ attr |= SHF_WRITE;
+ input_line_pointer += sizeof "write" - 1;
+ }
+ else if (strncmp (input_line_pointer, "alloc",
+ sizeof "alloc" - 1) == 0)
+ {
+ attr |= SHF_ALLOC;
+ input_line_pointer += sizeof "alloc" - 1;
+ }
+ else if (strncmp (input_line_pointer, "execinstr",
+ sizeof "execinstr" - 1) == 0)
+ {
+ attr |= SHF_EXECINSTR;
+ input_line_pointer += sizeof "execinstr" - 1;
+ }
+ else
+ {
+#ifdef md_elf_section_word
+ int md_attr = md_elf_section_word (&input_line_pointer);
+ if (md_attr)
+ attr |= md_attr;
+ else
+#endif
+ {
+ as_warn (_("Unrecognized section attribute"));
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ SKIP_WHITESPACE ();
+ }
+ while (*input_line_pointer++ == ',');
+ --input_line_pointer;
+ }
+ }
+
+ /* See if this is one of the special sections. */
+ for (i = 0; special_sections[i].name != NULL; i++)
+ {
+ if (string[1] == special_sections[i].name[1]
+ && strcmp (string, special_sections[i].name) == 0)
+ {
+ if (type == SHT_NULL)
+ type = special_sections[i].type;
+ else if (type != special_sections[i].type)
+ as_warn (_("Setting incorrect section type for %s"), string);
+
+ if ((attr &~ special_sections[i].attributes) != 0)
+ {
+ /* As a GNU extension, we permit a .note section to be
+ allocatable. If the linker sees an allocateable
+ .note section, it will create a PT_NOTE segment in
+ the output file. */
+ if (strcmp (string, ".note") != 0
+ || attr != SHF_ALLOC)
+ as_warn (_("Setting incorrect section attributes for %s"),
+ string);
+ }
+ attr |= special_sections[i].attributes;
+
+ break;
+ }
+ }
+
+ flags = (SEC_RELOC
+ | ((attr & SHF_WRITE) ? 0 : SEC_READONLY)
+ | ((attr & SHF_ALLOC) ? SEC_ALLOC : 0)
+ | (((attr & SHF_ALLOC) && type != SHT_NOBITS) ? SEC_LOAD : 0)
+ | ((attr & SHF_EXECINSTR) ? SEC_CODE : 0));
+ if (special_sections[i].name == NULL)
+ {
+ if (type == SHT_PROGBITS)
+ flags |= SEC_ALLOC | SEC_LOAD;
+ else if (type == SHT_NOBITS)
+ {
+ flags |= SEC_ALLOC;
+ flags &=~ SEC_LOAD;
+ }
+
+#ifdef md_elf_section_flags
+ flags = md_elf_section_flags (flags, attr, type);
+#endif
+ }
+
+ /* Prevent SEC_HAS_CONTENTS from being inadvertently set. */
+ if (type == SHT_NOBITS)
+ seg_info (sec)->bss = 1;
+
+ bfd_set_section_flags (stdoutput, sec, flags);
+
+ /* Add a symbol for this section to the symbol table. */
+ secsym = symbol_find (string);
+ if (secsym != NULL)
+ secsym->bsym = sec->symbol;
+ else
+ symbol_table_insert (section_symbol (sec));
+
+#ifdef md_elf_section_change_hook
+ md_elf_section_change_hook ();
+#endif
+
+ demand_empty_rest_of_line ();
+}
+
+/* Change to the .data section. */
+
+static void
+obj_elf_data (i)
+ int i;
+{
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ previous_section = now_seg;
+ previous_subsection = now_subseg;
+ s_data (i);
+
+#ifdef md_elf_section_change_hook
+ md_elf_section_change_hook ();
+#endif
+}
+
+/* Change to the .text section. */
+
+static void
+obj_elf_text (i)
+ int i;
+{
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ previous_section = now_seg;
+ previous_subsection = now_subseg;
+ s_text (i);
+
+#ifdef md_elf_section_change_hook
+ md_elf_section_change_hook ();
+#endif
+}
+
+static void
+obj_elf_subsection (ignore)
+ int ignore;
+{
+ register int temp;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ previous_section = now_seg;
+ previous_subsection = now_subseg;
+
+ temp = get_absolute_expression ();
+ subseg_set (now_seg, (subsegT) temp);
+ demand_empty_rest_of_line ();
+
+#ifdef md_elf_section_change_hook
+ md_elf_section_change_hook ();
+#endif
+}
+
+/* This can be called from the processor backends if they change
+ sections. */
+
+void
+obj_elf_section_change_hook ()
+{
+ previous_section = now_seg;
+ previous_subsection = now_subseg;
+}
+
+void
+obj_elf_previous (ignore)
+ int ignore;
+{
+ if (previous_section == 0)
+ {
+ as_bad (_(".previous without corresponding .section; ignored"));
+ return;
+ }
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ subseg_set (previous_section, previous_subsection);
+ previous_section = 0;
+
+#ifdef md_elf_section_change_hook
+ md_elf_section_change_hook ();
+#endif
+}
+
+static void
+obj_elf_line (ignore)
+ int ignore;
+{
+ /* Assume delimiter is part of expression. BSD4.2 as fails with
+ delightful bug, so we are not being incompatible here. */
+ new_logical_line ((char *) NULL, (int) (get_absolute_expression ()));
+ demand_empty_rest_of_line ();
+}
+
+/* This handles the .symver pseudo-op, which is used to specify a
+ symbol version. The syntax is ``.symver NAME,SYMVERNAME''.
+ SYMVERNAME may contain ELF_VER_CHR ('@') characters. This
+ pseudo-op causes the assembler to emit a symbol named SYMVERNAME
+ with the same value as the symbol NAME. */
+
+static void
+obj_elf_symver (ignore)
+ int ignore;
+{
+ char *name;
+ char c;
+ symbolS *sym;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ sym = symbol_find_or_make (name);
+
+ *input_line_pointer = c;
+
+ if (sym->sy_obj.versioned_name != NULL)
+ {
+ as_bad (_("multiple .symver directives for symbol `%s'"),
+ S_GET_NAME (sym));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after name in .symver"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ ++input_line_pointer;
+ name = input_line_pointer;
+ while (1)
+ {
+ c = get_symbol_end ();
+ if (c != ELF_VER_CHR)
+ break;
+ *input_line_pointer++ = c;
+ }
+
+ sym->sy_obj.versioned_name = xstrdup (name);
+
+ *input_line_pointer = c;
+
+ if (strchr (sym->sy_obj.versioned_name, ELF_VER_CHR) == NULL)
+ {
+ as_bad (_("missing version name in `%s' for symbol `%s'"),
+ sym->sy_obj.versioned_name, S_GET_NAME (sym));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* This handles the .vtable_inherit pseudo-op, which is used to indicate
+ to the linker the hierarchy in which a particular table resides. The
+ syntax is ".vtable_inherit CHILDNAME, PARENTNAME". */
+
+static void
+obj_elf_vtable_inherit (ignore)
+{
+ char *cname, *pname;
+ symbolS *csym, *psym;
+ char c, bad = 0;
+
+ if (*input_line_pointer == '#')
+ ++input_line_pointer;
+
+ cname = input_line_pointer;
+ c = get_symbol_end ();
+ csym = symbol_find (cname);
+
+ /* GCFIXME: should check that we don't have two .vtable_inherits for
+ the same child symbol. Also, we can currently only do this if the
+ child symbol is already exists and is placed in a fragment. */
+
+ if (csym == NULL || csym->sy_frag == NULL)
+ {
+ as_bad ("expected `%s' to have already been set for .vtable_inherit",
+ cname);
+ bad = 1;
+ }
+
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad ("expected comma after name in .vtable_inherit");
+ ignore_rest_of_line ();
+ return;
+ }
+
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer == '#')
+ ++input_line_pointer;
+
+ if (input_line_pointer[0] == '0'
+ && (input_line_pointer[1] == '\0'
+ || isspace(input_line_pointer[1])))
+ {
+ psym = section_symbol (absolute_section);
+ ++input_line_pointer;
+ }
+ else
+ {
+ pname = input_line_pointer;
+ c = get_symbol_end ();
+ psym = symbol_find_or_make (pname);
+ *input_line_pointer = c;
+ }
+
+ demand_empty_rest_of_line ();
+
+ if (bad)
+ return;
+
+ assert (csym->sy_value.X_op == O_constant);
+ fix_new (csym->sy_frag, csym->sy_value.X_add_number, 0, psym, 0, 0,
+ BFD_RELOC_VTABLE_INHERIT);
+}
+
+/* This handles the .vtable_entry pseudo-op, which is used to indicate
+ to the linker that a vtable slot was used. The syntax is
+ ".vtable_entry tablename, offset". */
+
+static void
+obj_elf_vtable_entry (ignore)
+{
+ char *name;
+ symbolS *sym;
+ offsetT offset;
+ char c;
+
+ if (*input_line_pointer == '#')
+ ++input_line_pointer;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ sym = symbol_find_or_make (name);
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad ("expected comma after name in .vtable_entry");
+ ignore_rest_of_line ();
+ return;
+ }
+
+ ++input_line_pointer;
+ if (*input_line_pointer == '#')
+ ++input_line_pointer;
+
+ offset = get_absolute_expression ();
+
+ fix_new (frag_now, frag_now_fix (), 0, sym, offset, 0,
+ BFD_RELOC_VTABLE_ENTRY);
+
+ demand_empty_rest_of_line ();
+}
+
+void
+obj_read_begin_hook ()
+{
+#ifdef NEED_ECOFF_DEBUG
+ if (ECOFF_DEBUGGING)
+ ecoff_read_begin_hook ();
+#endif
+}
+
+void
+obj_symbol_new_hook (symbolP)
+ symbolS *symbolP;
+{
+ symbolP->sy_obj.size = NULL;
+ symbolP->sy_obj.versioned_name = NULL;
+
+#ifdef NEED_ECOFF_DEBUG
+ if (ECOFF_DEBUGGING)
+ ecoff_symbol_new_hook (symbolP);
+#endif
+}
+
+void
+obj_elf_version (ignore)
+ int ignore;
+{
+ char *name;
+ unsigned int c;
+ char ch;
+ char *p;
+ asection *seg = now_seg;
+ subsegT subseg = now_subseg;
+ Elf_Internal_Note i_note;
+ Elf_External_Note e_note;
+ asection *note_secp = (asection *) NULL;
+ int i, len;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\"')
+ {
+ ++input_line_pointer; /* -> 1st char of string. */
+ name = input_line_pointer;
+
+ while (is_a_char (c = next_char_of_string ()))
+ ;
+ c = *input_line_pointer;
+ *input_line_pointer = '\0';
+ *(input_line_pointer - 1) = '\0';
+ *input_line_pointer = c;
+
+ /* create the .note section */
+
+ note_secp = subseg_new (".note", 0);
+ bfd_set_section_flags (stdoutput,
+ note_secp,
+ SEC_HAS_CONTENTS | SEC_READONLY);
+
+ /* process the version string */
+
+ len = strlen (name);
+
+ i_note.namesz = ((len + 1) + 3) & ~3; /* round this to word boundary */
+ i_note.descsz = 0; /* no description */
+ i_note.type = NT_VERSION;
+ p = frag_more (sizeof (e_note.namesz));
+ md_number_to_chars (p, (valueT) i_note.namesz, 4);
+ p = frag_more (sizeof (e_note.descsz));
+ md_number_to_chars (p, (valueT) i_note.descsz, 4);
+ p = frag_more (sizeof (e_note.type));
+ md_number_to_chars (p, (valueT) i_note.type, 4);
+
+ for (i = 0; i < len; i++)
+ {
+ ch = *(name + i);
+ {
+ FRAG_APPEND_1_CHAR (ch);
+ }
+ }
+ frag_align (2, 0, 0);
+
+ subseg_set (seg, subseg);
+ }
+ else
+ {
+ as_bad (_("Expected quoted string"));
+ }
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_elf_size (ignore)
+ int ignore;
+{
+ char *name = input_line_pointer;
+ char c = get_symbol_end ();
+ char *p;
+ expressionS exp;
+ symbolS *sym;
+
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ *p = 0;
+ as_bad (_("expected comma after name `%s' in .size directive"), name);
+ *p = c;
+ ignore_rest_of_line ();
+ return;
+ }
+ input_line_pointer++;
+ expression (&exp);
+ if (exp.X_op == O_absent)
+ {
+ as_bad (_("missing expression in .size directive"));
+ exp.X_op = O_constant;
+ exp.X_add_number = 0;
+ }
+ *p = 0;
+ sym = symbol_find_or_make (name);
+ *p = c;
+ if (exp.X_op == O_constant)
+ S_SET_SIZE (sym, exp.X_add_number);
+ else
+ {
+ sym->sy_obj.size = (expressionS *) xmalloc (sizeof (expressionS));
+ *sym->sy_obj.size = exp;
+ }
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the ELF .type pseudo-op. This sets the type of a symbol.
+ There are four syntaxes:
+
+ The first (used on Solaris) is
+ .type SYM,#function
+ The second (used on UnixWare) is
+ .type SYM,@function
+ The third (reportedly to be used on Irix 6.0) is
+ .type SYM STT_FUNC
+ The fourth (used on NetBSD/Arm and Linux/ARM) is
+ .type SYM,%function
+ */
+
+static void
+obj_elf_type (ignore)
+ int ignore;
+{
+ char *name;
+ char c;
+ int type;
+ const char *typename;
+ symbolS *sym;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ sym = symbol_find_or_make (name);
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == ',')
+ ++input_line_pointer;
+
+ SKIP_WHITESPACE ();
+ if ( *input_line_pointer == '#'
+ || *input_line_pointer == '@'
+ || *input_line_pointer == '%')
+ ++input_line_pointer;
+
+ typename = input_line_pointer;
+ c = get_symbol_end ();
+
+ type = 0;
+ if (strcmp (typename, "function") == 0
+ || strcmp (typename, "STT_FUNC") == 0)
+ type = BSF_FUNCTION;
+ else if (strcmp (typename, "object") == 0
+ || strcmp (typename, "STT_OBJECT") == 0)
+ type = BSF_OBJECT;
+ else
+ as_bad (_("ignoring unrecognized symbol type \"%s\""), typename);
+
+ *input_line_pointer = c;
+
+ sym->bsym->flags |= type;
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_elf_ident (ignore)
+ int ignore;
+{
+ static segT comment_section;
+ segT old_section = now_seg;
+ int old_subsection = now_subseg;
+
+ if (!comment_section)
+ {
+ char *p;
+ comment_section = subseg_new (".comment", 0);
+ bfd_set_section_flags (stdoutput, comment_section,
+ SEC_READONLY | SEC_HAS_CONTENTS);
+ p = frag_more (1);
+ *p = 0;
+ }
+ else
+ subseg_set (comment_section, 0);
+ stringer (1);
+ subseg_set (old_section, old_subsection);
+}
+
+#ifdef INIT_STAB_SECTION
+
+/* The first entry in a .stabs section is special. */
+
+void
+obj_elf_init_stab_section (seg)
+ segT seg;
+{
+ char *file;
+ char *p;
+ char *stabstr_name;
+ unsigned int stroff;
+
+ /* Force the section to align to a longword boundary. Without this,
+ UnixWare ar crashes. */
+ bfd_set_section_alignment (stdoutput, seg, 2);
+
+ /* Make space for this first symbol. */
+ p = frag_more (12);
+ /* Zero it out. */
+ memset (p, 0, 12);
+ as_where (&file, (unsigned int *) NULL);
+ stabstr_name = (char *) alloca (strlen (segment_name (seg)) + 4);
+ strcpy (stabstr_name, segment_name (seg));
+ strcat (stabstr_name, "str");
+ stroff = get_stab_string_offset (file, stabstr_name);
+ know (stroff == 1);
+ md_number_to_chars (p, stroff, 4);
+ seg_info (seg)->stabu.p = p;
+}
+
+#endif
+
+/* Fill in the counts in the first entry in a .stabs section. */
+
+static void
+adjust_stab_sections (abfd, sec, xxx)
+ bfd *abfd;
+ asection *sec;
+ PTR xxx;
+{
+ char *name;
+ asection *strsec;
+ char *p;
+ int strsz, nsyms;
+
+ if (strncmp (".stab", sec->name, 5))
+ return;
+ if (!strcmp ("str", sec->name + strlen (sec->name) - 3))
+ return;
+
+ name = (char *) alloca (strlen (sec->name) + 4);
+ strcpy (name, sec->name);
+ strcat (name, "str");
+ strsec = bfd_get_section_by_name (abfd, name);
+ if (strsec)
+ strsz = bfd_section_size (abfd, strsec);
+ else
+ strsz = 0;
+ nsyms = bfd_section_size (abfd, sec) / 12 - 1;
+
+ p = seg_info (sec)->stabu.p;
+ assert (p != 0);
+
+ bfd_h_put_16 (abfd, (bfd_vma) nsyms, (bfd_byte *) p + 6);
+ bfd_h_put_32 (abfd, (bfd_vma) strsz, (bfd_byte *) p + 8);
+}
+
+#ifdef NEED_ECOFF_DEBUG
+
+/* This function is called by the ECOFF code. It is supposed to
+ record the external symbol information so that the backend can
+ write it out correctly. The ELF backend doesn't actually handle
+ this at the moment, so we do it ourselves. We save the information
+ in the symbol. */
+
+void
+elf_ecoff_set_ext (sym, ext)
+ symbolS *sym;
+ struct ecoff_extr *ext;
+{
+ sym->bsym->udata.p = (PTR) ext;
+}
+
+/* This function is called by bfd_ecoff_debug_externals. It is
+ supposed to *EXT to the external symbol information, and return
+ whether the symbol should be used at all. */
+
+static boolean
+elf_get_extr (sym, ext)
+ asymbol *sym;
+ EXTR *ext;
+{
+ if (sym->udata.p == NULL)
+ return false;
+ *ext = *(EXTR *) sym->udata.p;
+ return true;
+}
+
+/* This function is called by bfd_ecoff_debug_externals. It has
+ nothing to do for ELF. */
+
+/*ARGSUSED*/
+static void
+elf_set_index (sym, indx)
+ asymbol *sym;
+ bfd_size_type indx;
+{
+}
+
+#endif /* NEED_ECOFF_DEBUG */
+
+void
+elf_frob_symbol (symp, puntp)
+ symbolS *symp;
+ int *puntp;
+{
+#ifdef NEED_ECOFF_DEBUG
+ if (ECOFF_DEBUGGING)
+ ecoff_frob_symbol (symp);
+#endif
+
+ if (symp->sy_obj.size != NULL)
+ {
+ switch (symp->sy_obj.size->X_op)
+ {
+ case O_subtract:
+ S_SET_SIZE (symp,
+ (S_GET_VALUE (symp->sy_obj.size->X_add_symbol)
+ + symp->sy_obj.size->X_add_number
+ - S_GET_VALUE (symp->sy_obj.size->X_op_symbol)));
+ break;
+ case O_constant:
+ S_SET_SIZE (symp,
+ (S_GET_VALUE (symp->sy_obj.size->X_add_symbol)
+ + symp->sy_obj.size->X_add_number));
+ break;
+ default:
+ as_bad (_(".size expression too complicated to fix up"));
+ break;
+ }
+ free (symp->sy_obj.size);
+ symp->sy_obj.size = NULL;
+ }
+
+ if (symp->sy_obj.versioned_name != NULL)
+ {
+ /* This symbol was given a new name with the .symver directive.
+
+ If this is an external reference, just rename the symbol to
+ include the version string. This will make the relocs be
+ against the correct versioned symbol.
+
+ If this is a definition, add an alias. FIXME: Using an alias
+ will permit the debugging information to refer to the right
+ symbol. However, it's not clear whether it is the best
+ approach. */
+
+ if (! S_IS_DEFINED (symp))
+ {
+ char *p;
+
+ /* Verify that the name isn't using the @@ syntax--this is
+ reserved for definitions of the default version to link
+ against. */
+ p = strchr (symp->sy_obj.versioned_name, ELF_VER_CHR);
+ know (p != NULL);
+ if (p[1] == ELF_VER_CHR)
+ {
+ as_bad (_("invalid attempt to declare external version name as default in symbol `%s'"),
+ symp->sy_obj.versioned_name);
+ *puntp = true;
+ }
+ S_SET_NAME (symp, symp->sy_obj.versioned_name);
+ }
+ else
+ {
+ symbolS *symp2;
+
+ /* FIXME: Creating a new symbol here is risky. We're in the
+ final loop over the symbol table. We can get away with
+ it only because the symbol goes to the end of the list,
+ where the loop will still see it. It would probably be
+ better to do this in obj_frob_file_before_adjust. */
+
+ symp2 = symbol_find_or_make (symp->sy_obj.versioned_name);
+
+ /* Now we act as though we saw symp2 = sym. */
+
+ S_SET_SEGMENT (symp2, S_GET_SEGMENT (symp));
+
+ /* Subtracting out the frag address here is a hack because
+ we are in the middle of the final loop. */
+ S_SET_VALUE (symp2, S_GET_VALUE (symp) - symp->sy_frag->fr_address);
+
+ symp2->sy_frag = symp->sy_frag;
+
+ /* This will copy over the size information. */
+ copy_symbol_attributes (symp2, symp);
+
+ if (S_IS_WEAK (symp))
+ S_SET_WEAK (symp2);
+
+ if (S_IS_EXTERNAL (symp))
+ S_SET_EXTERNAL (symp2);
+ }
+ }
+
+ /* Double check weak symbols. */
+ if (symp->bsym->flags & BSF_WEAK)
+ {
+ if (S_IS_COMMON (symp))
+ as_bad (_("Symbol `%s' can not be both weak and common"),
+ S_GET_NAME (symp));
+ }
+
+#ifdef TC_MIPS
+ /* The Irix 5 and 6 assemblers set the type of any common symbol and
+ any undefined non-function symbol to STT_OBJECT. We try to be
+ compatible, since newer Irix 5 and 6 linkers care. However, we
+ only set undefined symbols to be STT_OBJECT if we are on Irix,
+ because that is the only time gcc will generate the necessary
+ .global directives to mark functions. */
+
+ if (S_IS_COMMON (symp))
+ symp->bsym->flags |= BSF_OBJECT;
+
+ if (strstr (TARGET_OS, "irix") != NULL
+ && (! S_IS_DEFINED (symp) && ((symp->bsym->flags & BSF_FUNCTION) == 0)))
+ symp->bsym->flags |= BSF_OBJECT;
+#endif
+
+#ifdef TC_PPC
+ /* Frob the PowerPC, so that the symbol always has object type
+ if it is not some other type. VxWorks needs this. */
+ if ((symp->bsym->flags & (BSF_FUNCTION | BSF_FILE | BSF_SECTION_SYM)) == 0
+ && S_IS_DEFINED (symp))
+ symp->bsym->flags |= BSF_OBJECT;
+#endif
+}
+
+void
+elf_frob_file ()
+{
+ bfd_map_over_sections (stdoutput, adjust_stab_sections, (PTR) 0);
+
+#ifdef elf_tc_final_processing
+ elf_tc_final_processing ();
+#endif
+}
+
+/* It is required that we let write_relocs have the opportunity to
+ optimize away fixups before output has begun, since it is possible
+ to eliminate all fixups for a section and thus we never should
+ have generated the relocation section. */
+
+void
+elf_frob_file_after_relocs ()
+{
+#ifdef NEED_ECOFF_DEBUG
+ if (ECOFF_DEBUGGING)
+ /* Generate the ECOFF debugging information. */
+ {
+ const struct ecoff_debug_swap *debug_swap;
+ struct ecoff_debug_info debug;
+ char *buf;
+ asection *sec;
+
+ debug_swap
+ = get_elf_backend_data (stdoutput)->elf_backend_ecoff_debug_swap;
+ know (debug_swap != (const struct ecoff_debug_swap *) NULL);
+ ecoff_build_debug (&debug.symbolic_header, &buf, debug_swap);
+
+ /* Set up the pointers in debug. */
+#define SET(ptr, offset, type) \
+ debug.ptr = (type) (buf + debug.symbolic_header.offset)
+
+ SET (line, cbLineOffset, unsigned char *);
+ SET (external_dnr, cbDnOffset, PTR);
+ SET (external_pdr, cbPdOffset, PTR);
+ SET (external_sym, cbSymOffset, PTR);
+ SET (external_opt, cbOptOffset, PTR);
+ SET (external_aux, cbAuxOffset, union aux_ext *);
+ SET (ss, cbSsOffset, char *);
+ SET (external_fdr, cbFdOffset, PTR);
+ SET (external_rfd, cbRfdOffset, PTR);
+ /* ssext and external_ext are set up just below. */
+
+#undef SET
+
+ /* Set up the external symbols. */
+ debug.ssext = debug.ssext_end = NULL;
+ debug.external_ext = debug.external_ext_end = NULL;
+ if (! bfd_ecoff_debug_externals (stdoutput, &debug, debug_swap, true,
+ elf_get_extr, elf_set_index))
+ as_fatal (_("Failed to set up debugging information: %s"),
+ bfd_errmsg (bfd_get_error ()));
+
+ sec = bfd_get_section_by_name (stdoutput, ".mdebug");
+ assert (sec != NULL);
+
+ know (stdoutput->output_has_begun == false);
+
+ /* We set the size of the section, call bfd_set_section_contents
+ to force the ELF backend to allocate a file position, and then
+ write out the data. FIXME: Is this really the best way to do
+ this? */
+ sec->_raw_size = bfd_ecoff_debug_size (stdoutput, &debug, debug_swap);
+
+ if (! bfd_set_section_contents (stdoutput, sec, (PTR) NULL,
+ (file_ptr) 0, (bfd_size_type) 0))
+ as_fatal (_("Can't start writing .mdebug section: %s"),
+ bfd_errmsg (bfd_get_error ()));
+
+ know (stdoutput->output_has_begun == true);
+ know (sec->filepos != 0);
+
+ if (! bfd_ecoff_write_debug (stdoutput, &debug, debug_swap,
+ sec->filepos))
+ as_fatal (_("Could not write .mdebug section: %s"),
+ bfd_errmsg (bfd_get_error ()));
+ }
+#endif /* NEED_ECOFF_DEBUG */
+}
+
+#ifdef SCO_ELF
+
+/* Heavily plagarized from obj_elf_version. The idea is to emit the
+ SCO specific identifier in the .notes section to satisfy the SCO
+ linker.
+
+ This looks more complicated than it really is. As opposed to the
+ "obvious" solution, this should handle the cross dev cases
+ correctly. (i.e, hosting on a 64 bit big endian processor, but
+ generating SCO Elf code) Efficiency isn't a concern, as there
+ should be exactly one of these sections per object module.
+
+ SCO OpenServer 5 identifies it's ELF modules with a standard ELF
+ .note section.
+
+ int_32 namesz = 4 ; Name size
+ int_32 descsz = 12 ; Descriptive information
+ int_32 type = 1 ;
+ char name[4] = "SCO" ; Originator name ALWAYS SCO + NULL
+ int_32 version = (major ver # << 16) | version of tools ;
+ int_32 source = (tool_id << 16 ) | 1 ;
+ int_32 info = 0 ; These are set by the SCO tools, but we
+ don't know enough about the source
+ environment to set them. SCO ld currently
+ ignores them, and recommends we set them
+ to zero. */
+
+#define SCO_MAJOR_VERSION 0x1
+#define SCO_MINOR_VERSION 0x1
+
+void
+sco_id ()
+{
+
+ char *name;
+ unsigned int c;
+ char ch;
+ char *p;
+ asection *seg = now_seg;
+ subsegT subseg = now_subseg;
+ Elf_Internal_Note i_note;
+ Elf_External_Note e_note;
+ asection *note_secp = (asection *) NULL;
+ int i, len;
+
+ /* create the .note section */
+
+ note_secp = subseg_new (".note", 0);
+ bfd_set_section_flags (stdoutput,
+ note_secp,
+ SEC_HAS_CONTENTS | SEC_READONLY);
+
+ /* process the version string */
+
+ i_note.namesz = 4;
+ i_note.descsz = 12; /* 12 descriptive bytes */
+ i_note.type = NT_VERSION; /* Contains a version string */
+
+ p = frag_more (sizeof (i_note.namesz));
+ md_number_to_chars (p, (valueT) i_note.namesz, 4);
+
+ p = frag_more (sizeof (i_note.descsz));
+ md_number_to_chars (p, (valueT) i_note.descsz, 4);
+
+ p = frag_more (sizeof (i_note.type));
+ md_number_to_chars (p, (valueT) i_note.type, 4);
+
+ p = frag_more (4);
+ strcpy (p, "SCO");
+
+ /* Note: this is the version number of the ELF we're representing */
+ p = frag_more (4);
+ md_number_to_chars (p, (SCO_MAJOR_VERSION << 16) | (SCO_MINOR_VERSION), 4);
+
+ /* Here, we pick a magic number for ourselves (yes, I "registered"
+ it with SCO. The bottom bit shows that we are compat with the
+ SCO ABI. */
+ p = frag_more (4);
+ md_number_to_chars (p, 0x4c520000 | 0x0001, 4);
+
+ /* If we knew (or cared) what the source language options were, we'd
+ fill them in here. SCO has given us permission to ignore these
+ and just set them to zero. */
+ p = frag_more (4);
+ md_number_to_chars (p, 0x0000, 4);
+
+ frag_align (2, 0, 0);
+
+ /* We probably can't restore the current segment, for there likely
+ isn't one yet... */
+ if (seg && subseg)
+ subseg_set (seg, subseg);
+
+}
+
+#endif /* SCO_ELF */
+
+const struct format_ops elf_format_ops =
+{
+ bfd_target_elf_flavour,
+ 0,
+ 1,
+ elf_frob_symbol,
+ elf_frob_file,
+ elf_frob_file_after_relocs,
+ elf_s_get_size, elf_s_set_size,
+ elf_s_get_align, elf_s_set_align,
+ elf_copy_symbol_attributes,
+#ifdef NEED_ECOFF_DEBUG
+ ecoff_generate_asm_lineno,
+ ecoff_stab,
+#else
+ 0,
+ 0, /* process_stab */
+#endif
+ elf_sec_sym_ok_for_reloc,
+ elf_pop_insert,
+#ifdef NEED_ECOFF_DEBUG
+ elf_ecoff_set_ext,
+#else
+ 0,
+#endif
+ obj_read_begin_hook,
+ obj_symbol_new_hook,
+};
diff --git a/gas/config/obj-elf.h b/gas/config/obj-elf.h
new file mode 100644
index 0000000..2f4bc5f
--- /dev/null
+++ b/gas/config/obj-elf.h
@@ -0,0 +1,194 @@
+/* ELF object file format.
+ Copyright (C) 1992, 93, 94, 95, 96, 1997 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+
+/* HP PA-RISC support was contributed by the Center for Software Science
+ at the University of Utah. */
+
+#ifndef _OBJ_ELF_H
+#define _OBJ_ELF_H
+
+#define OBJ_ELF 1
+
+#define OUTPUT_FLAVOR bfd_target_elf_flavour
+
+#include <bfd.h>
+
+#define BYTES_IN_WORD 4 /* for now */
+#include "bfd/elf-bfd.h"
+
+/* Additional information we keep for each symbol. */
+
+/* FIXME: For some reason, this structure is needed both here and in
+ obj-multi.h. */
+#ifndef OBJ_SYMFIELD_TYPE
+struct elf_obj_sy
+{
+ /* Use this to keep track of .size expressions that involve
+ differences that we can't compute yet. */
+ expressionS *size;
+
+ /* The name specified by the .symver directive. */
+ char *versioned_name;
+};
+#endif
+
+#define OBJ_SYMFIELD_TYPE struct elf_obj_sy
+
+/* Symbol fields used by the ELF back end. */
+#define ELF_TARGET_SYMBOL_FIELDS int local:1;
+
+/* Don't change this; change ELF_TARGET_SYMBOL_FIELDS instead. */
+#define TARGET_SYMBOL_FIELDS ELF_TARGET_SYMBOL_FIELDS
+
+#include "targ-cpu.h"
+
+#ifndef FALSE
+#define FALSE 0
+#define TRUE !FALSE
+#endif
+
+#define obj_begin() elf_begin ()
+extern void elf_begin PARAMS ((void));
+
+/* should be conditional on address size! */
+#define elf_symbol(asymbol) ((elf_symbol_type *)(&(asymbol)->the_bfd))
+
+#define S_GET_SIZE(S) (elf_symbol ((S)->bsym)->internal_elf_sym.st_size)
+#define S_SET_SIZE(S,V) \
+ (elf_symbol((S)->bsym)->internal_elf_sym.st_size = (V))
+
+#define S_GET_ALIGN(S) (elf_symbol ((S)->bsym)->internal_elf_sym.st_value)
+#define S_SET_ALIGN(S,V) \
+ (elf_symbol ((S)->bsym)->internal_elf_sym.st_value = (V))
+
+#define S_GET_OTHER(S) (elf_symbol ((S)->bsym)->internal_elf_sym.st_other)
+#define S_SET_OTHER(S,V) \
+ (elf_symbol ((S)->bsym)->internal_elf_sym.st_other = (V))
+
+extern asection *gdb_section;
+
+#define obj_frob_file elf_frob_file
+extern void elf_frob_file PARAMS ((void));
+
+#define obj_frob_file_after_relocs elf_frob_file_after_relocs
+extern void elf_frob_file_after_relocs PARAMS ((void));
+
+#define obj_app_file elf_file_symbol
+extern void elf_file_symbol PARAMS ((char *));
+
+extern void obj_elf_section_change_hook PARAMS ((void));
+
+extern void obj_elf_section PARAMS ((int));
+extern void obj_elf_previous PARAMS ((int));
+extern void obj_elf_version PARAMS ((int));
+
+/* BFD wants to write the udata field, which is a no-no for the
+ globally defined sections. */
+#define obj_sec_sym_ok_for_reloc(SEC) ((SEC)->owner != 0)
+
+/* When setting one symbol equal to another, by default we probably
+ want them to have the same "size", whatever it means in the current
+ context. */
+#define OBJ_COPY_SYMBOL_ATTRIBUTES(DEST,SRC) \
+do \
+ { \
+ if ((SRC)->sy_obj.size) \
+ { \
+ if ((DEST)->sy_obj.size == NULL) \
+ (DEST)->sy_obj.size = \
+ (expressionS *) xmalloc (sizeof (expressionS)); \
+ *(DEST)->sy_obj.size = *(SRC)->sy_obj.size; \
+ } \
+ else \
+ { \
+ if ((DEST)->sy_obj.size != NULL) \
+ free ((DEST)->sy_obj.size); \
+ (DEST)->sy_obj.size = NULL; \
+ } \
+ S_SET_SIZE ((DEST), S_GET_SIZE (SRC)); \
+ S_SET_OTHER ((DEST), S_GET_OTHER (SRC)); \
+ } \
+while (0)
+
+/* Stabs go in a separate section. */
+#define SEPARATE_STAB_SECTIONS 1
+
+/* We need 12 bytes at the start of the section to hold some initial
+ information. */
+extern void obj_elf_init_stab_section PARAMS ((segT));
+#define INIT_STAB_SECTION(seg) obj_elf_init_stab_section (seg)
+
+#ifdef TC_ALPHA
+#define ECOFF_DEBUGGING alpha_flag_mdebug
+extern int alpha_flag_mdebug;
+#endif
+
+/* For now, always set ECOFF_DEBUGGING for a MIPS target. */
+#ifdef TC_MIPS
+#ifdef MIPS_STABS_ELF
+#define ECOFF_DEBUGGING 0
+#else
+#define ECOFF_DEBUGGING 1
+#endif /* MIPS_STABS_ELF */
+#endif /* TC_MIPS */
+
+#ifdef ECOFF_DEBUGGING
+/* If we are generating ECOFF debugging information, we need some
+ additional fields for each symbol. */
+#undef TARGET_SYMBOL_FIELDS
+#define TARGET_SYMBOL_FIELDS \
+ ELF_TARGET_SYMBOL_FIELDS \
+ struct efdr *ecoff_file; \
+ struct localsym *ecoff_symbol; \
+ valueT ecoff_extern_size;
+
+/* We smuggle stabs in ECOFF rather than using a separate section.
+ The Irix linker can not handle a separate stabs section. */
+
+#undef SEPARATE_STAB_SECTIONS
+#define SEPARATE_STAB_SECTIONS (!ECOFF_DEBUGGING)
+
+#undef INIT_STAB_SECTION
+#define INIT_STAB_SECTION(seg) \
+ ((void)(ECOFF_DEBUGGING ? 0 : (obj_elf_init_stab_section (seg), 0)))
+
+#define OBJ_PROCESS_STAB(seg, what, string, type, other, desc) \
+ if (ECOFF_DEBUGGING) \
+ ecoff_stab ((seg), (what), (string), (type), (other), (desc))
+#endif /* ECOFF_DEBUGGING */
+
+extern void elf_frob_symbol PARAMS ((struct symbol *, int *));
+#ifndef obj_frob_symbol
+#define obj_frob_symbol(symp, punt) elf_frob_symbol (symp, &punt)
+#endif
+
+extern void elf_pop_insert PARAMS ((void));
+#define obj_pop_insert() elf_pop_insert()
+
+#ifndef OBJ_MAYBE_ELF
+#define obj_ecoff_set_ext elf_ecoff_set_ext
+#ifdef ANSI_PROTOTYPES
+struct ecoff_extr;
+#endif
+extern void elf_ecoff_set_ext PARAMS ((struct symbol *, struct ecoff_extr *));
+#endif
+
+#endif /* _OBJ_ELF_H */
diff --git a/gas/config/obj-evax.c b/gas/config/obj-evax.c
new file mode 100644
index 0000000..e818c83
--- /dev/null
+++ b/gas/config/obj-evax.c
@@ -0,0 +1,83 @@
+/* obj-evax.c - EVAX (openVMS/Alpha) object file format.
+ Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+ Contributed by Klaus Kämpf (kkaempf@progis.de) of
+ proGIS Software, Aachen, Germany.
+
+ This file is part of GAS, the GNU Assembler
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA. */
+
+#define OBJ_HEADER "obj-evax.h"
+
+#include "as.h"
+
+static void s_evax_weak PARAMS ((int));
+
+const pseudo_typeS obj_pseudo_table[] =
+{
+ { "weak", s_evax_weak, 0},
+ {0, 0, 0},
+}; /* obj_pseudo_table */
+
+void obj_read_begin_hook () {}
+
+/* Handle the weak specific pseudo-op. */
+
+static void
+s_evax_weak (ignore)
+ int ignore;
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+ char *stop = NULL;
+ char stopc;
+
+ if (flag_mri)
+ stop = mri_comment_field (&stopc);
+
+ do
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+ S_SET_WEAK (symbolP);
+ if (c == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\n')
+ c = '\n';
+ }
+ }
+ while (c == ',');
+
+ if (flag_mri)
+ mri_comment_end (stop, stopc);
+
+ demand_empty_rest_of_line ();
+}
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of obj-evax.c */
diff --git a/gas/config/obj-evax.h b/gas/config/obj-evax.h
new file mode 100644
index 0000000..1d9db19
--- /dev/null
+++ b/gas/config/obj-evax.h
@@ -0,0 +1,95 @@
+/* This file is obj-evax.h
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Contributed by Klaus Kämpf (kkaempf@progis.de) of
+ proGIS Software, Aachen, Germany.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA. */
+
+/*
+ * This file is obj-evax.h and is intended to be a template for
+ * object format specific header files.
+ */
+
+/* define an obj specific macro off which target cpu back ends may key. */
+#define OBJ_EVAX 1
+
+/* include whatever target cpu is appropriate. */
+#include "targ-cpu.h"
+
+#ifdef BFD_ASSEMBLER
+#define OUTPUT_FLAVOR bfd_target_evax_flavour
+#endif
+
+/*
+ * SYMBOLS
+ */
+
+/*
+ * If your object format needs to reorder symbols, define this. When
+ * defined, symbols are kept on a doubly linked list and functions are
+ * made available for push, insert, append, and delete. If not defined,
+ * symbols are kept on a singly linked list, only the append and clear
+ * facilities are available, and they are macros.
+ */
+
+/* #define SYMBOLS_NEED_PACKPOINTERS */
+
+/* */
+typedef struct
+ {
+ void *nothing;
+ }
+obj_symbol_type; /* should be the format's symbol structure */
+
+typedef void *object_headers;
+
+#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE (0) /* your magic number */
+
+#define OBJ_EMIT_LINENO(a,b,c) /* must be *something*. This no-op's it out. */
+
+#define obj_symbol_new_hook(s) {;}
+
+#define S_SET_OTHER(S,V)
+#define S_SET_TYPE(S,T)
+#define S_SET_DESC(S,D)
+#define S_GET_OTHER(S) 0
+#define S_GET_TYPE(S) 0
+#define S_GET_DESC(S) 0
+
+#define PDSC_S_K_KIND_FP_STACK 9
+#define PDSC_S_K_KIND_FP_REGISTER 10
+#define PDSC_S_K_KIND_NULL 8
+
+#define PDSC_S_K_MIN_STACK_SIZE 32
+#define PDSC_S_K_MIN_REGISTER_SIZE 24
+#define PDSC_S_K_NULL_SIZE 16
+
+#define PDSC_S_M_BASE_REG_IS_FP 0x80 /* low byte */
+#define PDSC_S_M_NATIVE 0x10 /* high byte */
+#define PDSC_S_M_NO_JACKET 0x20 /* high byte */
+
+#define LKP_S_K_SIZE 16
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of obj-evax.h */
diff --git a/gas/config/obj-generic.c b/gas/config/obj-generic.c
new file mode 100644
index 0000000..69fc3d1
--- /dev/null
+++ b/gas/config/obj-generic.c
@@ -0,0 +1,41 @@
+/* This file is obj-generic.c and is intended to be a template for
+ object format specific source files.
+
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* These chars start a comment anywhere in a source file (except inside
+ another comment */
+const char comment_chars[] = "#";
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of obj-generic.c */
diff --git a/gas/config/obj-generic.h b/gas/config/obj-generic.h
new file mode 100644
index 0000000..dc18e43
--- /dev/null
+++ b/gas/config/obj-generic.h
@@ -0,0 +1,80 @@
+/* This file is obj-generic.h
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/*
+ * This file is obj-generic.h and is intended to be a template for
+ * object format specific header files.
+ */
+
+/* define an obj specific macro off which target cpu back ends may key. */
+#define OBJ_GENERIC 1
+
+/* include whatever target cpu is appropriate. */
+#include "targ-cpu.h"
+
+/*
+ * SYMBOLS
+ */
+
+/*
+ * If your object format needs to reorder symbols, define this. When
+ * defined, symbols are kept on a doubly linked list and functions are
+ * made available for push, insert, append, and delete. If not defined,
+ * symbols are kept on a singly linked list, only the append and clear
+ * facilities are available, and they are macros.
+ */
+
+/* #define SYMBOLS_NEED_PACKPOINTERS */
+
+/* */
+typedef struct
+ {
+ void *nothing;
+ }
+
+obj_symbol_type; /* should be the format's symbol structure */
+
+typedef void *object_headers;
+
+/* symbols have names */
+#define S_GET_NAME(s) ("foo") /* get the name of a symbolP */
+#define S_SET_NAME(s,v) ;
+/* symbols have segments */
+#define S_GET_SEGMENT(s) (SEG_UNKNOWN)
+#define S_SET_SEGMENT(s,v) ;
+/* symbols may be external */
+#define S_IS_EXTERNAL(s) (0)
+#define S_SET_EXTERNAL(s) ;
+
+/* symbols may or may not be defined */
+#define S_IS_DEFINED(s) (0)
+
+
+#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE (0) /* your magic number */
+
+#define OBJ_EMIT_LINENO(a,b,c) /* must be *something*. This no-op's it out. */
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of obj-generic.h */
diff --git a/gas/config/obj-hp300.c b/gas/config/obj-hp300.c
new file mode 100644
index 0000000..6e9cc53
--- /dev/null
+++ b/gas/config/obj-hp300.c
@@ -0,0 +1,52 @@
+/* This file is obj-hp300.h
+ Copyright (C) 1993 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "config/obj-aout.c"
+
+/* Aout file generation & utilities */
+void
+hp300_header_append (where, headers)
+ char **where;
+ object_headers *headers;
+{
+ tc_headers_hook (headers);
+
+#define DO(FIELD) \
+ { \
+ md_number_to_chars (*where, headers->header.FIELD, sizeof (headers->header.FIELD)); \
+ *where += sizeof (headers->header.FIELD); \
+ }
+
+ DO (a_info);
+ DO (a_spare1);
+ DO (a_spare2);
+ DO (a_text);
+ DO (a_data);
+ DO (a_bss);
+ DO (a_trsize);
+ DO (a_drsize);
+ DO (a_spare3);
+ DO (a_spare4);
+ DO (a_spare5);
+ DO (a_entry);
+ DO (a_spare6);
+ DO (a_spare7);
+ DO (a_syms);
+ DO (a_spare8);
+}
diff --git a/gas/config/obj-hp300.h b/gas/config/obj-hp300.h
new file mode 100644
index 0000000..ff4006b
--- /dev/null
+++ b/gas/config/obj-hp300.h
@@ -0,0 +1,71 @@
+/* This file is obj-hp300.h
+ Copyright (C) 1993 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#define __STRUCT_EXEC_OVERRIDE__
+
+struct exec_bytes
+{
+ unsigned char a_info[4]; /* a_machtype/a_magic */
+ unsigned char a_spare1[4];
+ unsigned char a_spare2[4];
+ unsigned char a_text[4]; /* length of text, in bytes */
+ unsigned char a_data[4]; /* length of data, in bytes */
+ unsigned char a_bss[4]; /* length of uninitialized data area for file, in bytes */
+ unsigned char a_trsize[4]; /* length of relocation info for text, in bytes */
+ unsigned char a_drsize[4]; /* length of relocation info for data, in bytes */
+ unsigned char a_spare3[4]; /* HP = pascal interface size */
+ unsigned char a_spare4[4]; /* HP = symbol table size */
+ unsigned char a_spare5[4]; /* HP = debug name table size */
+ unsigned char a_entry[4]; /* start address */
+ unsigned char a_spare6[4]; /* HP = source line table size */
+ unsigned char a_spare7[4]; /* HP = value table size */
+ unsigned char a_syms[4]; /* length of symbol table data in file, in bytes */
+ unsigned char a_spare8[4];
+};
+
+/* How big the "struct exec" is on disk */
+#define EXEC_BYTES_SIZE (16 * 4)
+
+struct exec
+{
+ unsigned long a_info;
+ unsigned long a_spare1;
+ unsigned long a_spare2;
+ unsigned long a_text;
+ unsigned long a_data;
+ unsigned long a_bss;
+ unsigned long a_trsize;
+ unsigned long a_drsize;
+ unsigned long a_spare3;
+ unsigned long a_spare4;
+ unsigned long a_spare5;
+ unsigned long a_entry;
+ unsigned long a_spare6;
+ unsigned long a_spare7;
+ unsigned long a_syms;
+ unsigned long a_spare8;
+};
+
+#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE (OMAGIC)
+#define AOUT_VERSION 0x02
+#define AOUT_MACHTYPE 0x0c
+#define OMAGIC 0x106
+
+#define obj_header_append hp300_header_append
+#include "config/obj-aout.h"
diff --git a/gas/config/obj-ieee.c b/gas/config/obj-ieee.c
new file mode 100644
index 0000000..30a0798
--- /dev/null
+++ b/gas/config/obj-ieee.c
@@ -0,0 +1,627 @@
+/* obj-format for ieee-695 records.
+ Copyright (C) 1991, 92, 93, 94, 95, 1997, 1998 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+
+/*
+ created by
+
+ steve chamberlain steve@cygnus.com
+ */
+
+/*
+ this will hopefully become the port through which bfd and gas talk,
+ for the moment, only ieee is known to work well.
+ */
+
+#include "bfd.h"
+#include "as.h"
+#include "subsegs.h"
+#include "output-file.h"
+#include "frags.h"
+
+bfd *abfd;
+
+/* How many addresses does the .align take? */
+static relax_addressT
+relax_align (address, alignment)
+ register relax_addressT address; /* Address now. */
+ register long alignment; /* Alignment (binary). */
+{
+ relax_addressT mask;
+ relax_addressT new_address;
+
+ mask = ~((~0) << alignment);
+ new_address = (address + mask) & (~mask);
+ return (new_address - address);
+} /* relax_align() */
+
+/* calculate the size of the frag chain and create a bfd section
+ to contain all of it */
+static void
+DEFUN (size_section, (abfd, idx),
+ bfd * abfd AND
+ unsigned int idx)
+{
+ asection *sec;
+ unsigned int size = 0;
+ fragS *frag = segment_info[idx].frag_root;
+ while (frag)
+ {
+ if (frag->fr_address != size)
+ {
+ printf (_("Out of step\n"));
+ size = frag->fr_address;
+ }
+ size += frag->fr_fix;
+ switch (frag->fr_type)
+ {
+ case rs_fill:
+ case rs_org:
+ size += frag->fr_offset * frag->fr_var;
+ break;
+ case rs_align:
+ case rs_align_code:
+ {
+ addressT off;
+
+ off = relax_align (size, frag->fr_offset);
+ if (frag->fr_subtype != 0 && off > frag->fr_subtype)
+ off = 0;
+ size += off;
+ }
+ }
+ frag = frag->fr_next;
+ }
+ if (size)
+ {
+ char *name = segment_info[idx].name;
+ if (name == (char *) NULL)
+ {
+ name = ".data";
+ }
+ segment_info[idx].user_stuff = (char *) (sec = bfd_make_section (abfd, name));
+ /* Make it output through itself */
+ sec->output_section = sec;
+ sec->flags |= SEC_HAS_CONTENTS;
+ bfd_set_section_size (abfd, sec, size);
+ }
+}
+
+/* run through a frag chain and write out the data to go with it */
+static void
+DEFUN (fill_section, (abfd, idx),
+ bfd * abfd AND
+ unsigned int idx)
+{
+ asection *sec = segment_info[idx].user_stuff;
+ if (sec)
+ {
+ fragS *frag = segment_info[idx].frag_root;
+ unsigned int offset = 0;
+ while (frag)
+ {
+ unsigned int fill_size;
+ unsigned int count;
+ switch (frag->fr_type)
+ {
+ case rs_fill:
+ case rs_align:
+ case rs_org:
+ if (frag->fr_fix)
+ {
+ bfd_set_section_contents (abfd,
+ sec,
+ frag->fr_literal,
+ frag->fr_address,
+ frag->fr_fix);
+ }
+ offset += frag->fr_fix;
+ fill_size = frag->fr_var;
+ if (fill_size)
+ {
+ unsigned int off = frag->fr_fix;
+ for (count = frag->fr_offset; count; count--)
+ {
+ bfd_set_section_contents (abfd, sec,
+ frag->fr_literal +
+ frag->fr_fix,
+ frag->fr_address + off,
+ fill_size);
+ off += fill_size;
+ }
+ }
+ break;
+ default:
+ abort ();
+ }
+ frag = frag->fr_next;
+ }
+ }
+}
+
+/* Count the relocations in a chain */
+
+static unsigned int
+DEFUN (count_entries_in_chain, (idx),
+ unsigned int idx)
+{
+ unsigned int nrelocs;
+ fixS *fixup_ptr;
+
+ /* Count the relocations */
+ fixup_ptr = segment_info[idx].fix_root;
+ nrelocs = 0;
+ while (fixup_ptr != (fixS *) NULL)
+ {
+ fixup_ptr = fixup_ptr->fx_next;
+ nrelocs++;
+ }
+ return nrelocs;
+}
+
+/* output all the relocations for a section */
+void
+DEFUN (do_relocs_for, (idx),
+ unsigned int idx)
+{
+ unsigned int nrelocs;
+ arelent **reloc_ptr_vector;
+ arelent *reloc_vector;
+ asymbol **ptrs;
+ asection *section = (asection *) (segment_info[idx].user_stuff);
+ unsigned int i;
+ fixS *from;
+ if (section)
+ {
+ nrelocs = count_entries_in_chain (idx);
+
+ reloc_ptr_vector = (arelent **) malloc ((nrelocs + 1) * sizeof (arelent *));
+ reloc_vector = (arelent *) malloc (nrelocs * sizeof (arelent));
+ ptrs = (asymbol **) malloc (nrelocs * sizeof (asymbol *));
+ from = segment_info[idx].fix_root;
+ for (i = 0; i < nrelocs; i++)
+ {
+ arelent *to = reloc_vector + i;
+ asymbol *s;
+ reloc_ptr_vector[i] = to;
+ to->howto = (reloc_howto_type *) (from->fx_r_type);
+
+#if 0 /* We can't represent complicated things in a reloc yet */
+ if (from->fx_addsy == 0 || from->fx_subsy != 0) abort();
+#endif
+
+ s = &(from->fx_addsy->sy_symbol.sy);
+ to->address = ((char *) (from->fx_frag->fr_address +
+ from->fx_where))
+ - ((char *) (&(from->fx_frag->fr_literal)));
+ to->addend = from->fx_offset;
+ /* If we know the symbol which we want to relocate to, turn
+ this reloaction into a section relative.
+
+ If this relocation is pcrelative, and we know the
+ destination, we still want to keep the relocation - since
+ the linker might relax some of the bytes, but it stops
+ being pc relative and turns into an absolute relocation. */
+ if (s)
+ {
+ if ((s->flags & BSF_UNDEFINED) == 0)
+ {
+ to->section = s->section;
+
+ /* We can refer directly to the value field here,
+ rather than using S_GET_VALUE, because this is
+ only called after do_symbols, which sets up the
+ value field. */
+ to->addend += s->value;
+
+ to->sym_ptr_ptr = 0;
+ if (to->howto->pcrel_offset)
+ {
+ /* This is a pcrel relocation, the addend should be adjusted */
+ to->addend -= to->address + 1;
+ }
+ }
+ else
+ {
+ to->section = 0;
+ *ptrs = &(from->fx_addsy->sy_symbol.sy);
+ to->sym_ptr_ptr = ptrs;
+
+ if (to->howto->pcrel_offset)
+ {
+ /* This is a pcrel relocation, the addend should be adjusted */
+ to->addend -= to->address - 1;
+ }
+ }
+
+ }
+ else
+ {
+ to->section = 0;
+ }
+
+ ptrs++;
+ from = from->fx_next;
+ }
+
+ /* attatch to the section */
+ section->orelocation = reloc_ptr_vector;
+ section->reloc_count = nrelocs;
+ section->flags |= SEC_LOAD;
+ }
+}
+
+/* do the symbols.. */
+static void
+DEFUN (do_symbols, (abfd),
+ bfd * abfd)
+{
+ extern symbolS *symbol_rootP;
+ symbolS *ptr;
+ asymbol **symbol_ptr_vec;
+ asymbol *symbol_vec;
+ unsigned int count = 0;
+ unsigned int index;
+
+
+ for (ptr = symbol_rootP;
+ ptr != (symbolS *) NULL;
+ ptr = ptr->sy_next)
+ {
+ if (SEG_NORMAL (ptr->sy_symbol.seg))
+ {
+ ptr->sy_symbol.sy.section =
+ (asection *) (segment_info[ptr->sy_symbol.seg].user_stuff);
+ S_SET_VALUE (ptr, S_GET_VALUE (ptr) + ptr->sy_frag->fr_address);
+ if (ptr->sy_symbol.sy.flags == 0)
+ {
+ ptr->sy_symbol.sy.flags = BSF_LOCAL;
+ }
+ }
+ else
+ {
+ switch (ptr->sy_symbol.seg)
+ {
+ case SEG_ABSOLUTE:
+ ptr->sy_symbol.sy.flags |= BSF_ABSOLUTE;
+ ptr->sy_symbol.sy.section = 0;
+ break;
+ case SEG_UNKNOWN:
+ ptr->sy_symbol.sy.flags = BSF_UNDEFINED;
+ ptr->sy_symbol.sy.section = 0;
+ break;
+ default:
+ abort ();
+ }
+ }
+ ptr->sy_symbol.sy.value = S_GET_VALUE (ptr);
+ count++;
+ }
+ symbol_ptr_vec = (asymbol **) malloc ((count + 1) * sizeof (asymbol *));
+
+ index = 0;
+ for (ptr = symbol_rootP;
+ ptr != (symbolS *) NULL;
+ ptr = ptr->sy_next)
+ {
+ symbol_ptr_vec[index] = &(ptr->sy_symbol.sy);
+ index++;
+ }
+ symbol_ptr_vec[index] = 0;
+ abfd->outsymbols = symbol_ptr_vec;
+ abfd->symcount = count;
+}
+
+/* The generic as->bfd converter. Other backends may have special case
+ code */
+
+void
+DEFUN_VOID (bfd_as_write_hook)
+{
+ int i;
+
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ {
+ size_section (abfd, i);
+ }
+
+
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ fill_section (abfd, i);
+
+ do_symbols (abfd);
+
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ do_relocs_for (i);
+
+}
+
+S_SET_SEGMENT (x, y)
+ symbolS *x;
+ int y;
+{
+ x->sy_symbol.seg = y;
+}
+
+S_IS_DEFINED (x)
+ symbolS *x;
+{
+ if (SEG_NORMAL (x->sy_symbol.seg))
+ {
+ return 1;
+ }
+ switch (x->sy_symbol.seg)
+ {
+ case SEG_UNKNOWN:
+ return 0;
+ default:
+ abort ();
+ }
+}
+
+S_IS_EXTERNAL (x)
+{
+ abort ();
+}
+
+S_GET_DESC (x)
+{
+ abort ();
+}
+
+S_GET_SEGMENT (x)
+ symbolS *x;
+{
+ return x->sy_symbol.seg;
+}
+
+S_SET_EXTERNAL (x)
+ symbolS *x;
+{
+ x->sy_symbol.sy.flags |= BSF_GLOBAL | BSF_EXPORT;
+}
+
+S_SET_NAME (x, y)
+ symbolS *x;
+ char *y;
+{
+ x->sy_symbol.sy.name = y;
+}
+
+S_GET_OTHER (x)
+{
+ abort ();
+}
+
+S_IS_DEBUG (x)
+{
+ abort ();
+}
+
+#ifndef segment_name
+char *
+segment_name ()
+{
+ abort ();
+}
+#endif
+
+void
+obj_read_begin_hook ()
+{
+}
+
+static void
+obj_ieee_section (ignore)
+ int ignore;
+{
+ extern char *input_line_pointer;
+ extern char is_end_of_line[];
+ char *p = input_line_pointer;
+ char *s = p;
+ int i;
+ /* Look up the name, if it doesn't exist, make it */
+ while (*p && *p != ' ' && *p != ',' && !is_end_of_line[*p])
+ {
+ p++;
+ }
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ {
+ if (segment_info[i].hadone)
+ {
+ if (strncmp (segment_info[i].name, s, p - s) == 0)
+ {
+ goto ok;
+
+ }
+ }
+ else
+ break;
+ }
+ if (i == SEG_UNKNOWN)
+ {
+ as_bad (_("too many sections"));
+ return;
+ }
+
+ segment_info[i].hadone = 1;
+ segment_info[i].name = malloc (p - s + 1);
+ memcpy (segment_info[i].name, s, p - s);
+ segment_info[i].name[p - s] = 0;
+ok:
+ subseg_set (i, 0);
+ while (!is_end_of_line[*p])
+ p++;
+ input_line_pointer = p;
+
+}
+
+
+void cons ();
+void s_ignore ();
+
+
+void s_globl ();
+const pseudo_typeS obj_pseudo_table[] =
+{
+ {"section", obj_ieee_section, 0},
+ {"data.b", cons, 1},
+ {"data.w", cons, 2},
+ {"data.l", cons, 4},
+ {"export", s_globl, 0},
+ {"option", s_ignore, 0},
+ {"end", s_ignore, 0},
+ {"import", s_ignore, 0},
+ {"sdata", stringer, 0},
+ 0,
+
+};
+
+
+
+void
+obj_symbol_new_hook (symbolP)
+ symbolS *symbolP;
+{
+ symbolP->sy_symbol.sy.the_bfd = abfd;
+}
+
+
+
+
+
+#if 1
+extern void
+DEFUN_VOID (write_object_file)
+{
+ int i;
+ struct frchain *frchain_ptr;
+ struct frag *frag_ptr;
+
+ abfd = bfd_openw (out_file_name, "ieee");
+
+ if (abfd == 0)
+ {
+ as_perror (_("FATAL: Can't create %s"), out_file_name);
+ exit (EXIT_FAILURE);
+ }
+ bfd_set_format (abfd, bfd_object);
+ bfd_set_arch_mach (abfd, bfd_arch_h8300, 0);
+ subseg_set (1, 0);
+ subseg_set (2, 0);
+ subseg_set (3, 0);
+ for (frchain_ptr = frchain_root;
+ frchain_ptr != (struct frchain *) NULL;
+ frchain_ptr = frchain_ptr->frch_next)
+ {
+ /* Run through all the sub-segments and align them up. Also close any
+ open frags. We tack a .fill onto the end of the frag chain so
+ that any .align's size can be worked by looking at the next
+ frag. */
+
+ subseg_set (frchain_ptr->frch_seg, frchain_ptr->frch_subseg);
+#ifndef SUB_SEGMENT_ALIGN
+#define SUB_SEGMENT_ALIGN(SEG) 2
+#endif
+ frag_align (SUB_SEGMENT_ALIGN (now_seg), 0, 0);
+ frag_wane (frag_now);
+ frag_now->fr_fix = 0;
+ know (frag_now->fr_next == NULL);
+ }
+
+ /* Now build one big frag chain for each segment, linked through
+ fr_next. */
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ {
+
+ fragS **prev_frag_ptr_ptr;
+ struct frchain *next_frchain_ptr;
+
+ /* struct frag **head_ptr = segment_info[i].frag_root;*/
+
+ segment_info[i].frag_root = segment_info[i].frchainP->frch_root;
+#if 0
+ /* Im not sure what this is for */
+ for (frchain_ptr = segment_info[i].frchainP->frch_root;
+ frchain_ptr != (struct frchain *) NULL;
+ frchain_ptr = frchain_ptr->frch_next)
+ {
+ *head_ptr = frchain_ptr;
+ head_ptr = &frchain_ptr->next;
+ }
+
+
+#endif
+ }
+
+ for (i = SEG_E0; i < SEG_UNKNOWN; i++)
+ {
+ relax_segment (segment_info[i].frag_root, i);
+ }
+
+ /* Now the addresses of the frags are correct within the segment */
+
+ bfd_as_write_hook ();
+ bfd_close (abfd);
+}
+
+#endif
+
+H_SET_TEXT_SIZE (a, b)
+{
+ abort ();
+}
+
+H_GET_TEXT_SIZE ()
+{
+ abort ();
+}
+
+H_SET_BSS_SIZE ()
+{
+ abort ();
+}
+
+H_SET_STRING_SIZE ()
+{
+ abort ();
+}
+
+H_SET_RELOCATION_SIZE ()
+{
+ abort ();
+}
+
+H_SET_MAGIC_NUMBER ()
+{
+ abort ();
+}
+
+H_GET_FILE_SIZE ()
+{
+ abort ();
+}
+
+H_GET_TEXT_RELOCATION_SIZE ()
+{
+ abort ();
+}
+
+/* end of obj-ieee.c */
diff --git a/gas/config/obj-ieee.h b/gas/config/obj-ieee.h
new file mode 100644
index 0000000..4a0f126
--- /dev/null
+++ b/gas/config/obj-ieee.h
@@ -0,0 +1,50 @@
+/* This file is obj-ieee.h
+
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#define BFD 1
+
+#include <bfd.h>
+
+typedef struct
+{
+ asymbol sy;
+ int seg;
+}
+
+obj_symbol_type;
+
+#define S_GET_NAME(s) (((s)->sy_symbol.sy.name))
+
+typedef struct
+ {
+ int x;
+ }
+
+object_headers;
+
+#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE 1
+
+
+int lineno_rootP;
+
+
+#define IEEE_STYLE
+
+/* end of obj-ieee.h */
diff --git a/gas/config/obj-multi.c b/gas/config/obj-multi.c
new file mode 100644
index 0000000..d115093
--- /dev/null
+++ b/gas/config/obj-multi.c
@@ -0,0 +1,4 @@
+/* foo */
+
+#include "as.h"
+
diff --git a/gas/config/obj-multi.h b/gas/config/obj-multi.h
new file mode 100644
index 0000000..fe8d98d
--- /dev/null
+++ b/gas/config/obj-multi.h
@@ -0,0 +1,50 @@
+/* hi */
+
+#include "emul.h"
+#include "targ-cpu.h"
+
+#define OUTPUT_FLAVOR (this_format->flavor)
+#define obj_frob_symbol(S,P) (this_format->frob_symbol)(S,&(P))
+#define obj_frob_file (this_format->frob_file)
+#define obj_frob_file_after_relocs (this_format->frob_file_after_relocs)
+#define obj_ecoff_set_ext (this_format->ecoff_set_ext)
+#define obj_pop_insert (this_format->pop_insert)
+#define obj_read_begin_hook() (this_format->read_begin_hook?this_format->read_begin_hook():(void)0)
+#define obj_symbol_new_hook (this_format->symbol_new_hook)
+#define obj_sec_sym_ok_for_reloc (this_format->sec_sym_ok_for_reloc)
+#define S_GET_SIZE (this_format->s_get_size)
+#define S_SET_SIZE (this_format->s_set_size)
+#define S_GET_ALIGN (this_format->s_get_align)
+#define S_SET_ALIGN (this_format->s_set_align)
+#define OBJ_COPY_SYMBOL_ATTRIBUTES (this_format->copy_symbol_attributes)
+#define OBJ_PROCESS_STAB (this_format->process_stab)
+
+#if defined (OBJ_MAYBE_ECOFF) || (defined (OBJ_MAYBE_ELF) && defined (TC_MIPS))
+#define ECOFF_DEBUGGING 1
+#endif
+
+/* FIXME: What's the story here? Why do we have to define
+ OBJ_SYMFIELD_TYPE both here and in obj-elf.h? */
+#ifdef OBJ_MAYBE_ELF
+struct elf_obj_sy
+{
+ expressionS *size;
+ char *versioned_name;
+};
+#define OBJ_SYMFIELD_TYPE struct elf_obj_sy
+#define ELF_TARGET_SYMBOL_FIELDS int local:1;
+#else
+#define ELF_TARGET_SYMBOL_FIELDS
+#endif
+
+#ifdef ECOFF_DEBUGGING
+struct efdr;
+struct localsym;
+#define ECOFF_DEBUG_TARGET_SYMBOL_FIELDS struct efdr *ecoff_file; struct localsym *ecoff_symbol; valueT ecoff_extern_size;
+#else
+#define ECOFF_DEBUG_TARGET_SYMBOL_FIELDS
+#endif
+
+#define TARGET_SYMBOL_FIELDS \
+ ELF_TARGET_SYMBOL_FIELDS \
+ ECOFF_DEBUG_TARGET_SYMBOL_FIELDS
diff --git a/gas/config/obj-som.c b/gas/config/obj-som.c
new file mode 100644
index 0000000..80af18f
--- /dev/null
+++ b/gas/config/obj-som.c
@@ -0,0 +1,307 @@
+/* SOM object file format.
+ Copyright (C) 1993, 1998 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+
+ Written by the Center for Software Science at the University of Utah
+ and by Cygnus Support. */
+
+#include "as.h"
+#include "subsegs.h"
+#include "aout/stab_gnu.h"
+#include "obstack.h"
+
+/* SOM does not need any pseudo-ops. */
+
+const pseudo_typeS obj_pseudo_table[] =
+{
+ {NULL}
+};
+
+static int version_seen = 0;
+static int copyright_seen = 0;
+static int compiler_seen = 0;
+
+/* Unused by SOM. */
+void obj_read_begin_hook () {}
+
+/* Handle a .compiler directive. This is intended to create the
+ compilation unit auxiliary header for MPE such that the linkeditor
+ can handle SOM extraction from archives. The format of the quoted
+ string is "sourcefile language version" and is delimited by blanks.*/
+
+void
+obj_som_compiler (unused)
+ int unused;
+{
+ char *buf;
+ char c;
+ char *filename;
+ char *language_name;
+ char *p;
+ char *version_id;
+
+ if (compiler_seen)
+ {
+ as_bad ("Only one .compiler pseudo-op per file!");
+ ignore_rest_of_line ();
+ return;
+ }
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\"')
+ {
+ buf = input_line_pointer;
+ ++input_line_pointer;
+ while (is_a_char (next_char_of_string ()))
+ ;
+ c = *input_line_pointer;
+ *input_line_pointer = '\000';
+ }
+ else
+ {
+ as_bad ("Expected quoted string");
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Parse the quoted string into its component parts. Skip the
+ quote. */
+ filename = buf + 1;
+ p = filename;
+ while (*p != ' ' && *p != '\000')
+ p++;
+ if (*p == '\000')
+ {
+ as_bad (".compiler directive missing language and version");
+ return;
+ }
+ *p = '\000';
+
+ language_name = ++p;
+ while (*p != ' ' && *p != '\000') p++;
+ if (*p == '\000')
+ {
+ as_bad (".compiler directive missing version");
+ return;
+ }
+ *p = '\000';
+
+ version_id = ++p;
+ while (*p != '\000') p++;
+ /* Remove the trailing quote. */
+ *(--p) = '\000';
+
+ compiler_seen = 1;
+ if (! bfd_som_attach_compilation_unit (stdoutput, filename, language_name,
+ "GNU Tools", version_id))
+ {
+ bfd_perror (stdoutput->filename);
+ as_fatal ("FATAL: Attaching compiler header %s", stdoutput->filename);
+ }
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .version directive. */
+
+void
+obj_som_version (unused)
+ int unused;
+{
+ char *version, c;
+
+ if (version_seen)
+ {
+ as_bad (_("Only one .version pseudo-op per file!"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\"')
+ {
+ version = input_line_pointer;
+ ++input_line_pointer;
+ while (is_a_char (next_char_of_string ()))
+ ;
+ c = *input_line_pointer;
+ *input_line_pointer = '\000';
+ }
+ else
+ {
+ as_bad (_("Expected quoted string"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ version_seen = 1;
+ if (bfd_som_attach_aux_hdr (stdoutput, VERSION_AUX_ID, version) == false)
+ {
+ bfd_perror (stdoutput->filename);
+ as_perror (_("FATAL: Attaching version header %s"), stdoutput->filename);
+ exit (EXIT_FAILURE);
+ }
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .copyright directive. This probably isn't complete, but
+ it's of dubious value anyway and (IMHO) not worth the time to finish.
+ If you care about copyright strings that much, you fix it. */
+
+void
+obj_som_copyright (unused)
+ int unused;
+{
+ char *copyright, c;
+
+ if (copyright_seen)
+ {
+ as_bad (_("Only one .copyright pseudo-op per file!"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\"')
+ {
+ copyright = input_line_pointer;
+ ++input_line_pointer;
+ while (is_a_char (next_char_of_string ()))
+ ;
+ c = *input_line_pointer;
+ *input_line_pointer = '\000';
+ }
+ else
+ {
+ as_bad (_("Expected quoted string"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ copyright_seen = 1;
+ if (bfd_som_attach_aux_hdr (stdoutput, COPYRIGHT_AUX_ID, copyright) == false)
+ {
+ bfd_perror (stdoutput->filename);
+ as_perror (_("FATAL: Attaching copyright header %s"), stdoutput->filename);
+ exit (EXIT_FAILURE);
+ }
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+}
+
+/* Perform any initialization necessary for stabs support.
+
+ For SOM we need to create the space which will contain the
+ two stabs subspaces. Additionally we need to set up the
+ space/subspace relationships and set space/subspace attributes
+ which BFD does not understand. */
+
+void
+obj_som_init_stab_section (seg)
+ segT seg;
+{
+ segT saved_seg = now_seg;
+ segT space;
+ subsegT saved_subseg = now_subseg;
+ char *p, *file;
+ unsigned int stroff;
+
+ /* Make the space which will contain the debug subspaces. */
+ space = bfd_make_section_old_way (stdoutput, "$GDB_DEBUG$");
+
+ /* Set SOM specific attributes for the space. In particular we set
+ the space "defined", "private", "sort_key", and "spnum" values.
+
+ Due to a bug in pxdb (called by hpux linker), the sort keys
+ of the various stabs spaces/subspaces need to be "small". We
+ reserve range 72/73 which appear to work well. */
+ obj_set_section_attributes (space, 1, 1, 72, 2);
+ bfd_set_section_alignment (stdoutput, space, 2);
+
+ /* Set the containing space for both stab sections to be $GDB_DEBUG$
+ (just created above). Also set some attributes which BFD does
+ not understand. In particular, access bits, sort keys, and load
+ quadrant. */
+ obj_set_subsection_attributes (seg, space, 0x1f, 73, 0);
+ bfd_set_section_alignment (stdoutput, seg, 2);
+
+ /* Make some space for the first special stab entry and zero the memory.
+ It contains information about the length of this file's
+ stab string and the like. Using it avoids the need to
+ relocate the stab strings.
+
+ The $GDB_STRINGS$ space will be created as a side effect of
+ the call to get_stab_string_offset. */
+ p = frag_more (12);
+ memset (p, 0, 12);
+ as_where (&file, (unsigned int *) NULL);
+ stroff = get_stab_string_offset (file, "$GDB_STRINGS$");
+ know (stroff == 1);
+ md_number_to_chars (p, stroff, 4);
+ seg_info (seg)->stabu.p = p;
+
+ /* Set the containing space for both stab sections to be $GDB_DEBUG$
+ (just created above). Also set some attributes which BFD does
+ not understand. In particular, access bits, sort keys, and load
+ quadrant. */
+ seg = bfd_get_section_by_name (stdoutput, "$GDB_STRINGS$");
+ obj_set_subsection_attributes (seg, space, 0x1f, 72, 0);
+ bfd_set_section_alignment (stdoutput, seg, 2);
+
+ subseg_set (saved_seg, saved_subseg);
+}
+
+/* Fill in the counts in the first entry in a .stabs section. */
+
+static void
+adjust_stab_sections (abfd, sec, xxx)
+ bfd *abfd;
+ asection *sec;
+ PTR xxx;
+{
+ asection *strsec;
+ char *p;
+ int strsz, nsyms;
+
+ if (strcmp ("$GDB_SYMBOLS$", sec->name))
+ return;
+
+ strsec = bfd_get_section_by_name (abfd, "$GDB_STRINGS$");
+ if (strsec)
+ strsz = bfd_section_size (abfd, strsec);
+ else
+ strsz = 0;
+ nsyms = bfd_section_size (abfd, sec) / 12 - 1;
+
+ p = seg_info (sec)->stabu.p;
+ assert (p != 0);
+
+ bfd_h_put_16 (abfd, (bfd_vma) nsyms, (bfd_byte *) p + 6);
+ bfd_h_put_32 (abfd, (bfd_vma) strsz, (bfd_byte *) p + 8);
+}
+
+/* Called late in the asssembly phase to adjust the special
+ stab entry and to set the starting address for each code subspace. */
+
+void
+som_frob_file ()
+{
+ bfd_map_over_sections (stdoutput, adjust_stab_sections, (PTR) 0);
+}
diff --git a/gas/config/obj-som.h b/gas/config/obj-som.h
new file mode 100644
index 0000000..62087b1
--- /dev/null
+++ b/gas/config/obj-som.h
@@ -0,0 +1,73 @@
+/* SOM object file format.
+ Copyright (C) 1993, 1994, 1995, 1998 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+
+ Written by the Center for Software Science at the University of Utah
+ and by Cygnus Support. */
+
+#ifndef _OBJ_SOM_H
+#define _OBJ_SOM_H
+
+#define OBJ_SOM 1
+
+#include <bfd.h>
+#include "bfd/som.h"
+#include "targ-cpu.h"
+
+#ifndef FALSE
+#define FALSE 0
+#define TRUE !FALSE
+#endif
+
+/* should be conditional on address size! */
+#define som_symbol(asymbol) ((som_symbol_type *)(&(asymbol)->the_bfd))
+
+extern void som_file_symbol PARAMS ((char *));
+extern void obj_som_version PARAMS ((int));
+extern void obj_som_init_stab_section PARAMS ((segT));
+extern void obj_som_copyright PARAMS ((int));
+extern void obj_som_compiler PARAMS ((int));
+
+#define obj_symbol_new_hook(s) {;}
+
+/* SOM has several attributes for spaces/subspaces which can not
+ be easily expressed in BFD. We use these macros to trigger calls
+ into the SOM BFD backend to set these attributes. */
+#define obj_set_section_attributes bfd_som_set_section_attributes
+#define obj_set_subsection_attributes bfd_som_set_subsection_attributes
+
+/* Likewise for symbol types. */
+#define obj_set_symbol_type bfd_som_set_symbol_type
+
+/* Stabs go in a separate sections. GDB expects to find them in sections
+ with the names $GDB_SYMBOLS$ and $GDB_STRINGS$ rather than .stab and
+ .stabstr. */
+#define SEPARATE_STAB_SECTIONS 1
+#define STAB_SECTION_NAME "$GDB_SYMBOLS$"
+#define STAB_STRING_SECTION_NAME "$GDB_STRINGS$"
+
+/* We use INIT_STAB_SECTION to record the space/subspace relationships
+ for the various debugging sections. */
+#define INIT_STAB_SECTION(seg) obj_som_init_stab_section (seg)
+
+/* We'll be updating the magic 1st stab entry once the entire assembly
+ fail has been processed. */
+#define obj_frob_file() som_frob_file()
+
+#endif /* _OBJ_SOM_H */
diff --git a/gas/config/obj-vms.c b/gas/config/obj-vms.c
new file mode 100644
index 0000000..0f08f8e
--- /dev/null
+++ b/gas/config/obj-vms.c
@@ -0,0 +1,5549 @@
+/* vms.c -- Write out a VAX/VMS object file
+ Copyright (C) 1987, 88, 92, 94, 95, 97, 1998 Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
+
+/* Written by David L. Kashtan */
+/* Modified by Eric Youngdale to write VMS debug records for program
+ variables */
+
+/* Want all of obj-vms.h (as obj-format.h, via targ-env.h, via as.h). */
+#define WANT_VMS_OBJ_DEFS
+
+#include "as.h"
+#include "config.h"
+#include "subsegs.h"
+#include "obstack.h"
+
+/* What we do if there is a goof. */
+#define error as_fatal
+
+#ifdef VMS /* These are of no use if we are cross assembling. */
+#include <fab.h> /* Define File Access Block */
+#include <nam.h> /* Define NAM Block */
+#include <xab.h> /* Define XAB - all different types*/
+extern int sys$open(), sys$close(), sys$asctim();
+#endif
+
+/*
+ * Version string of the compiler that produced the code we are
+ * assembling. (And this assembler, if we do not have compiler info.)
+ */
+char *compiler_version_string;
+
+extern int flag_hash_long_names; /* -+ */
+extern int flag_one; /* -1; compatibility with gcc 1.x */
+extern int flag_show_after_trunc; /* -H */
+extern int flag_no_hash_mixed_case; /* -h NUM */
+
+/* Flag that determines how we map names. This takes several values, and
+ * is set with the -h switch. A value of zero implies names should be
+ * upper case, and the presence of the -h switch inhibits the case hack.
+ * No -h switch at all sets vms_name_mapping to 0, and allows case hacking.
+ * A value of 2 (set with -h2) implies names should be
+ * all lower case, with no case hack. A value of 3 (set with -h3) implies
+ * that case should be preserved. */
+
+/* If the -+ switch is given, then the hash is appended to any name that is
+ * longer than 31 characters, regardless of the setting of the -h switch.
+ */
+
+char vms_name_mapping = 0;
+
+static symbolS *Entry_Point_Symbol = 0; /* Pointer to "_main" */
+
+/*
+ * We augment the "gas" symbol structure with this
+ */
+struct VMS_Symbol
+{
+ struct VMS_Symbol *Next;
+ symbolS *Symbol;
+ int Size;
+ int Psect_Index;
+ int Psect_Offset;
+};
+
+struct VMS_Symbol *VMS_Symbols = 0;
+struct VMS_Symbol *Ctors_Symbols = 0;
+struct VMS_Symbol *Dtors_Symbols = 0;
+
+/* We need this to keep track of the various input files, so that we can
+ * give the debugger the correct source line.
+ */
+
+struct input_file
+{
+ struct input_file *next;
+ struct input_file *same_file_fpnt;
+ int file_number;
+ int max_line;
+ int min_line;
+ int offset;
+ char flag;
+ char *name;
+ symbolS *spnt;
+};
+
+static struct input_file *file_root = (struct input_file *) NULL;
+
+
+/*
+ * Styles of PSECTS (program sections) that we generate; just shorthand
+ * to avoid lists of section attributes. Used by VMS_Psect_Spec().
+ */
+enum ps_type
+{
+ ps_TEXT, ps_DATA, ps_COMMON, ps_CONST, ps_CTORS, ps_DTORS
+};
+
+/*
+ * This enum is used to keep track of the various types of variables that
+ * may be present.
+ */
+
+enum advanced_type
+{
+ BASIC, POINTER, ARRAY, ENUM, STRUCT, UNION, FUNCTION, VOID, ALIAS, UNKNOWN
+};
+
+/*
+ * This structure contains the information from the stabs directives, and the
+ * information is filled in by VMS_typedef_parse. Everything that is needed
+ * to generate the debugging record for a given symbol is present here.
+ * This could be done more efficiently, using nested struct/unions, but for now
+ * I am happy that it works.
+ */
+struct VMS_DBG_Symbol
+{
+ struct VMS_DBG_Symbol *next;
+ /* description of what this is */
+ enum advanced_type advanced;
+ /* this record is for this type */
+ int dbx_type;
+ /* For advanced types this is the type referred to. I.e., the type
+ a pointer points to, or the type of object that makes up an
+ array. */
+ int type2;
+ /* Use this type when generating a variable def */
+ int VMS_type;
+ /* used for arrays - this will be present for all */
+ int index_min;
+ /* entries, but will be meaningless for non-arrays */
+ int index_max;
+ /* Size in bytes of the data type. For an array, this is the size
+ of one element in the array */
+ int data_size;
+ /* Number of the structure/union/enum - used for ref */
+ int struc_numb;
+};
+
+#define SYMTYPLST_SIZE (1<<4) /* 16; must be power of two */
+#define SYMTYP_HASH(x) ((unsigned)(x) & (SYMTYPLST_SIZE-1))
+struct VMS_DBG_Symbol *VMS_Symbol_type_list[SYMTYPLST_SIZE];
+
+/*
+ * We need this structure to keep track of forward references to
+ * struct/union/enum that have not been defined yet. When they are ultimately
+ * defined, then we can go back and generate the TIR commands to make a back
+ * reference.
+ */
+
+struct forward_ref
+{
+ struct forward_ref *next;
+ int dbx_type;
+ int struc_numb;
+ char resolved;
+};
+
+struct forward_ref *f_ref_root = (struct forward_ref *) NULL;
+
+/*
+ * This routine is used to compare the names of certain types to various
+ * fixed types that are known by the debugger.
+ */
+#define type_check(X) !strcmp (symbol_name, X)
+
+/*
+ * This variable is used to keep track of the name of the symbol we are
+ * working on while we are parsing the stabs directives.
+ */
+static const char *symbol_name;
+
+/* We use this counter to assign numbers to all of the structures, unions
+ * and enums that we define. When we actually declare a variable to the
+ * debugger, we can simply do it by number, rather than describing the
+ * whole thing each time.
+ */
+
+static structure_count = 0;
+
+/* This variable is used to indicate that we are making the last attempt to
+ parse the stabs, and that we should define as much as we can, and ignore
+ the rest */
+
+static int final_pass;
+
+/* This variable is used to keep track of the current structure number
+ * for a given variable. If this is < 0, that means that the structure
+ * has not yet been defined to the debugger. This is still cool, since
+ * the VMS object language has ways of fixing things up after the fact,
+ * so we just make a note of this, and generate fixups at the end.
+ */
+static int struct_number;
+
+/* This is used to distinguish between D_float and G_float for telling
+ the debugger about doubles. gcc outputs the same .stabs regardless
+ of whether -mg is used to select alternate doubles. */
+
+static int vax_g_doubles = 0;
+
+/* Local symbol references (used to handle N_ABS symbols; gcc does not
+ generate those, but they're possible with hand-coded assembler input)
+ are always made relative to some particular environment. If the current
+ input has any such symbols, then we expect this to get incremented
+ exactly once and end up having all of them be in environment #0. */
+
+static int Current_Environment = -1;
+
+/* Every object file must specify an module name, which is also used by
+ traceback records. Set in Write_VMS_MHD_Records(). */
+
+static char Module_Name[255+1];
+
+/*
+ * Variable descriptors are used tell the debugger the data types of certain
+ * more complicated variables (basically anything involving a structure,
+ * union, enum, array or pointer). Some non-pointer variables of the
+ * basic types that the debugger knows about do not require a variable
+ * descriptor.
+ *
+ * Since it is impossible to have a variable descriptor longer than 128
+ * bytes by virtue of the way that the VMS object language is set up,
+ * it makes not sense to make the arrays any longer than this, or worrying
+ * about dynamic sizing of the array.
+ *
+ * These are the arrays and counters that we use to build a variable
+ * descriptor.
+ */
+
+#define MAX_DEBUG_RECORD 128
+static char Local[MAX_DEBUG_RECORD]; /* buffer for variable descriptor */
+static char Asuffix[MAX_DEBUG_RECORD]; /* buffer for array descriptor */
+static int Lpnt; /* index into Local */
+static int Apoint; /* index into Asuffix */
+static char overflow; /* flag to indicate we have written too much*/
+static int total_len; /* used to calculate the total length of variable
+ descriptor plus array descriptor - used for len byte*/
+
+/* Flag if we have told user about finding global constants in the text
+ section. */
+static int gave_compiler_message = 0;
+
+
+/*
+ * Global data (Object records limited to 512 bytes by VAX-11 "C" runtime)
+ */
+static int VMS_Object_File_FD; /* File Descriptor for object file */
+static char Object_Record_Buffer[512]; /* Buffer for object file records */
+static int Object_Record_Offset;/* Offset to end of data */
+static int Current_Object_Record_Type; /* Type of record in above */
+
+/*
+ * Macros for moving data around. Must work on big-endian systems.
+ */
+#ifdef VMS /* These are more efficient for VMS->VMS systems */
+#define COPY_LONG(dest,val) ( *(long *)(dest) = (val) )
+#define COPY_SHORT(dest,val) ( *(short *)(dest) = (val) )
+#else
+#define COPY_LONG(dest,val) md_number_to_chars ((dest), (val), 4)
+#define COPY_SHORT(dest,val) md_number_to_chars ((dest), (val), 2)
+#endif
+/*
+ * Macros for placing data into the object record buffer.
+ */
+#define PUT_LONG(val) \
+ ( COPY_LONG (&Object_Record_Buffer[Object_Record_Offset], (val)), \
+ Object_Record_Offset += 4 )
+
+#define PUT_SHORT(val) \
+ ( COPY_SHORT (&Object_Record_Buffer[Object_Record_Offset], (val)), \
+ Object_Record_Offset += 2 )
+
+#define PUT_CHAR(val) ( Object_Record_Buffer[Object_Record_Offset++] = (val) )
+
+#define PUT_COUNTED_STRING(cp) do { \
+ register const char *p = (cp); \
+ PUT_CHAR ((char) strlen (p)); \
+ while (*p) PUT_CHAR (*p++); } while (0)
+
+/*
+ * Macro for determining if a Name has psect attributes attached
+ * to it.
+ */
+#define PSECT_ATTRIBUTES_STRING "$$PsectAttributes_"
+#define PSECT_ATTRIBUTES_STRING_LENGTH 18
+
+#define HAS_PSECT_ATTRIBUTES(Name) \
+ (strncmp ((*Name == '_' ? Name + 1 : Name), \
+ PSECT_ATTRIBUTES_STRING, \
+ PSECT_ATTRIBUTES_STRING_LENGTH) == 0)
+
+
+ /* in: segT out: N_TYPE bits */
+const short seg_N_TYPE[] =
+{
+ N_ABS,
+ N_TEXT,
+ N_DATA,
+ N_BSS,
+ N_UNDF, /* unknown */
+ N_UNDF, /* error */
+ N_UNDF, /* expression */
+ N_UNDF, /* debug */
+ N_UNDF, /* ntv */
+ N_UNDF, /* ptv */
+ N_REGISTER, /* register */
+};
+
+const segT N_TYPE_seg[N_TYPE + 2] =
+{ /* N_TYPE == 0x1E = 32-2 */
+ SEG_UNKNOWN, /* N_UNDF == 0 */
+ SEG_GOOF,
+ SEG_ABSOLUTE, /* N_ABS == 2 */
+ SEG_GOOF,
+ SEG_TEXT, /* N_TEXT == 4 */
+ SEG_GOOF,
+ SEG_DATA, /* N_DATA == 6 */
+ SEG_GOOF,
+ SEG_BSS, /* N_BSS == 8 */
+ SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_GOOF, SEG_GOOF, SEG_GOOF, SEG_GOOF,
+ SEG_REGISTER, /* dummy N_REGISTER for regs = 30 */
+ SEG_GOOF,
+};
+
+
+/* Local support routines which return a value. */
+
+static struct input_file *find_file PARAMS ((symbolS *));
+static struct VMS_DBG_Symbol *find_symbol PARAMS ((int));
+static symbolS *Define_Routine PARAMS ((symbolS *,int,symbolS *,int));
+
+static char *cvt_integer PARAMS ((char *,int *));
+static char *fix_name PARAMS ((char *));
+static char *get_struct_name PARAMS ((char *));
+
+static offsetT VMS_Initialized_Data_Size PARAMS ((symbolS *,unsigned));
+
+static int VMS_TBT_Source_File PARAMS ((char *,int));
+static int gen1 PARAMS ((struct VMS_DBG_Symbol *,int));
+static int forward_reference PARAMS ((char *));
+static int final_forward_reference PARAMS ((struct VMS_DBG_Symbol *));
+static int VMS_typedef_parse PARAMS ((char *));
+static int hash_string PARAMS ((const char *));
+static int VMS_Psect_Spec PARAMS ((const char *,int,enum ps_type,
+ struct VMS_Symbol *));
+
+/* Local support routines which don't directly return any value. */
+
+static void s_const PARAMS ((int));
+static void Create_VMS_Object_File PARAMS ((void));
+static void Flush_VMS_Object_Record_Buffer PARAMS ((void));
+static void Set_VMS_Object_File_Record PARAMS ((int));
+static void Close_VMS_Object_File PARAMS ((void));
+static void vms_tir_stack_psect PARAMS ((int,int,int));
+static void VMS_Store_Immediate_Data PARAMS ((const char *,int,int));
+static void VMS_Set_Data PARAMS ((int,int,int,int));
+static void VMS_Store_Struct PARAMS ((int));
+static void VMS_Def_Struct PARAMS ((int));
+static void VMS_Set_Struct PARAMS ((int));
+static void VMS_TBT_Module_Begin PARAMS ((void));
+static void VMS_TBT_Module_End PARAMS ((void));
+static void VMS_TBT_Routine_Begin PARAMS ((symbolS *,int));
+static void VMS_TBT_Routine_End PARAMS ((int,symbolS *));
+static void VMS_TBT_Block_Begin PARAMS ((symbolS *,int,char *));
+static void VMS_TBT_Block_End PARAMS ((valueT));
+static void VMS_TBT_Line_PC_Correlation PARAMS ((int,int,int,int));
+static void VMS_TBT_Source_Lines PARAMS ((int,int,int));
+static void fpush PARAMS ((int,int));
+static void rpush PARAMS ((int,int));
+static void array_suffix PARAMS ((struct VMS_DBG_Symbol *));
+static void new_forward_ref PARAMS ((int));
+static void generate_suffix PARAMS ((struct VMS_DBG_Symbol *,int));
+static void bitfield_suffix PARAMS ((struct VMS_DBG_Symbol *,int));
+static void setup_basic_type PARAMS ((struct VMS_DBG_Symbol *));
+static void VMS_DBG_record PARAMS ((struct VMS_DBG_Symbol *,int,int,char *));
+static void VMS_local_stab_Parse PARAMS ((symbolS *));
+static void VMS_stab_parse PARAMS ((symbolS *,int,int,int,int));
+static void VMS_GSYM_Parse PARAMS ((symbolS *,int));
+static void VMS_LCSYM_Parse PARAMS ((symbolS *,int));
+static void VMS_STSYM_Parse PARAMS ((symbolS *,int));
+static void VMS_RSYM_Parse PARAMS ((symbolS *,symbolS *,int));
+static void VMS_LSYM_Parse PARAMS ((void));
+static void Define_Local_Symbols PARAMS ((symbolS *,symbolS *,symbolS *,int));
+static void Write_VMS_MHD_Records PARAMS ((void));
+static void Write_VMS_EOM_Record PARAMS ((int,valueT));
+static void VMS_Case_Hack_Symbol PARAMS ((const char *,char *));
+static void VMS_Modify_Psect_Attributes PARAMS ((const char *,int *));
+static void VMS_Global_Symbol_Spec PARAMS ((const char *,int,int,int));
+static void VMS_Local_Environment_Setup PARAMS ((const char *));
+static void VMS_Emit_Globalvalues PARAMS ((unsigned,unsigned,char *));
+static void VMS_Procedure_Entry_Pt PARAMS ((char *,int,int,int));
+static void VMS_Set_Psect PARAMS ((int,int,int));
+static void VMS_Store_Repeated_Data PARAMS ((int,char *,int,int));
+static void VMS_Store_PIC_Symbol_Reference PARAMS ((symbolS *,int,
+ int,int,int,int));
+static void VMS_Fix_Indirect_Reference PARAMS ((int,int,fragS *,fragS *));
+
+/* Support code which used to be inline within vms_write_object_file. */
+static void vms_fixup_text_section PARAMS ((unsigned,struct frag *,struct frag *));
+static void synthesize_data_segment PARAMS ((unsigned,unsigned,struct frag *));
+static void vms_fixup_data_section PARAMS ((unsigned,unsigned));
+static void global_symbol_directory PARAMS ((unsigned,unsigned));
+static void local_symbols_DST PARAMS ((symbolS *,symbolS *));
+static void vms_build_DST PARAMS ((unsigned));
+static void vms_fixup_xtors_section PARAMS ((struct VMS_Symbol *, int));
+
+
+/* The following code defines the special types of pseudo-ops that we
+ use with VMS. */
+
+unsigned char const_flag = IN_DEFAULT_SECTION;
+
+static void
+s_const (arg)
+ int arg; /* 3rd field from obj_pseudo_table[]; not needed here */
+{
+ /* Since we don't need `arg', use it as our scratch variable so that
+ we won't get any "not used" warnings about it. */
+ arg = get_absolute_expression ();
+ subseg_set (SEG_DATA, (subsegT) arg);
+ const_flag = 1;
+ demand_empty_rest_of_line ();
+}
+
+const pseudo_typeS obj_pseudo_table[] =
+{
+ {"const", s_const, 0},
+ {0, 0, 0},
+}; /* obj_pseudo_table */
+
+
+/* Routine to perform RESOLVE_SYMBOL_REDEFINITION(). */
+
+int
+vms_resolve_symbol_redef (sym)
+ symbolS *sym;
+{
+ /*
+ * If the new symbol is .comm AND it has a size of zero,
+ * we ignore it (i.e. the old symbol overrides it)
+ */
+ if (SEGMENT_TO_SYMBOL_TYPE ((int) now_seg) == (N_UNDF | N_EXT)
+ && frag_now_fix () == 0)
+ {
+ as_warn (_("compiler emitted zero-size common symbol `%s' already defined"),
+ S_GET_NAME (sym));
+ return 1;
+ }
+ /*
+ * If the old symbol is .comm and it has a size of zero,
+ * we override it with the new symbol value.
+ */
+ if (S_IS_EXTERNAL (sym) && S_IS_DEFINED (sym) && S_GET_VALUE (sym) == 0)
+ {
+ as_warn (_("compiler redefined zero-size common symbol `%s'"),
+ S_GET_NAME (sym));
+ sym->sy_frag = frag_now;
+ S_SET_OTHER (sym, const_flag);
+ S_SET_VALUE (sym, frag_now_fix ());
+ /* Keep N_EXT bit. */
+ sym->sy_symbol.n_type |= SEGMENT_TO_SYMBOL_TYPE ((int) now_seg);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* `tc_frob_label' handler for colon(symbols.c), used to examine the
+ dummy label(s) gcc inserts at the beginning of each file it generates.
+ gcc 1.x put "gcc_compiled."; gcc 2.x (as of 2.7) puts "gcc2_compiled."
+ and "__gnu_language_<name>" and possibly "__vax_<type>_doubles". */
+
+void
+vms_check_for_special_label (symbolP)
+symbolS *symbolP;
+{
+ /* Special labels only occur prior to explicit section directives. */
+ if ((const_flag & IN_DEFAULT_SECTION) != 0)
+ {
+ char *sym_name = S_GET_NAME (symbolP);
+
+ if (*sym_name == '_')
+ ++sym_name;
+
+ if (!strcmp (sym_name, "__vax_g_doubles"))
+ vax_g_doubles = 1;
+#if 0 /* not necessary */
+ else if (!strcmp (sym_name, "__vax_d_doubles"))
+ vax_g_doubles = 0;
+#endif
+#if 0 /* these are potential alternatives to tc-vax.c's md_parse_options() */
+ else if (!strcmp (sym_name, "gcc_compiled."))
+ flag_one = 1;
+ else if (!strcmp (sym_name, "__gnu_language_cplusplus"))
+ flag_hash_long_names = 1;
+#endif
+ }
+ return;
+}
+
+
+void
+obj_read_begin_hook ()
+{
+ return;
+}
+
+
+void
+obj_crawl_symbol_chain (headers)
+ object_headers *headers;
+{
+ symbolS *symbolP;
+ symbolS **symbolPP;
+ int symbol_number = 0;
+
+ symbolPP = &symbol_rootP; /* -> last symbol chain link. */
+ while ((symbolP = *symbolPP) != NULL)
+ {
+ resolve_symbol_value (symbolP, 1);
+
+ /* OK, here is how we decide which symbols go out into the
+ brave new symtab. Symbols that do are:
+
+ * symbols with no name (stabd's?)
+ * symbols with debug info in their N_TYPE
+ * symbols with \1 as their 3rd character (numeric labels)
+ * "local labels" needed for PIC fixups
+
+ Symbols that don't are:
+ * symbols that are registers
+
+ All other symbols are output. We complain if a deleted
+ symbol was marked external. */
+
+ if (!S_IS_REGISTER (symbolP))
+ {
+ symbolP->sy_number = symbol_number++;
+ symbolP->sy_name_offset = 0;
+ symbolPP = &(symbol_next (symbolP));
+ }
+ else
+ {
+ if (S_IS_EXTERNAL (symbolP) || !S_IS_DEFINED (symbolP))
+ {
+ as_bad (_("Local symbol %s never defined"), S_GET_NAME (symbolP));
+ } /* oops. */
+
+ /* Unhook it from the chain. */
+ *symbolPP = symbol_next (symbolP);
+ } /* if this symbol should be in the output */
+
+ } /* for each symbol */
+
+ H_SET_STRING_SIZE (headers, string_byte_count);
+ H_SET_SYMBOL_TABLE_SIZE (headers, symbol_number);
+} /* obj_crawl_symbol_chain() */
+
+
+ /****** VMS OBJECT FILE HACKING ROUTINES *******/
+
+
+/* Create the VMS object file. */
+
+static void
+Create_VMS_Object_File ()
+{
+#if defined(eunice) || !defined(VMS)
+ VMS_Object_File_FD = creat (out_file_name, 0777, "var");
+#else /* eunice */
+ VMS_Object_File_FD = creat (out_file_name, 0, "rfm=var",
+ "ctx=bin", "mbc=16", "deq=64", "fop=tef",
+ "shr=nil");
+#endif /* eunice */
+ /* Deal with errors. */
+ if (VMS_Object_File_FD < 0)
+ as_fatal (_("Couldn't create VMS object file \"%s\""), out_file_name);
+ /* Initialize object file hacking variables. */
+ Object_Record_Offset = 0;
+ Current_Object_Record_Type = -1;
+}
+
+
+/* Flush the object record buffer to the object file. */
+
+static void
+Flush_VMS_Object_Record_Buffer ()
+{
+ /* If the buffer is empty, there's nothing to do. */
+ if (Object_Record_Offset == 0)
+ return;
+
+#ifndef VMS /* For cross-assembly purposes. */
+ {
+ char RecLen[2];
+
+ /* "Variable-length record" files have a two byte length field
+ prepended to each record. It's normally out-of-band, and native
+ VMS output will insert it automatically for this type of file.
+ When cross-assembling, we must write it explicitly. */
+ md_number_to_chars (RecLen, Object_Record_Offset, 2);
+ if (write (VMS_Object_File_FD, RecLen, 2) != 2)
+ error (_("I/O error writing VMS object file (length prefix)"));
+ /* We also need to force the actual record to be an even number of
+ bytes. For native output, that's automatic; when cross-assembling,
+ pad with a NUL byte if length is odd. Do so _after_ writing the
+ pre-padded length. Since our buffer is defined with even size,
+ an odd offset implies that it has some room left. */
+ if ((Object_Record_Offset & 1) != 0)
+ Object_Record_Buffer[Object_Record_Offset++] = '\0';
+ }
+#endif /* not VMS */
+
+ /* Write the data to the file. */
+ if (write (VMS_Object_File_FD, Object_Record_Buffer, Object_Record_Offset)
+ != Object_Record_Offset)
+ error (_("I/O error writing VMS object file"));
+
+ /* The buffer is now empty. */
+ Object_Record_Offset = 0;
+}
+
+
+/* Declare a particular type of object file record. */
+
+static void
+Set_VMS_Object_File_Record (Type)
+ int Type;
+{
+ /* If the type matches, we are done. */
+ if (Type == Current_Object_Record_Type)
+ return;
+ /* Otherwise: flush the buffer. */
+ Flush_VMS_Object_Record_Buffer ();
+ /* Remember the new type. */
+ Current_Object_Record_Type = Type;
+}
+
+
+/* Close the VMS Object file. */
+
+static void
+Close_VMS_Object_File ()
+{
+ /* Flush (should never be necessary) and reset saved record-type context. */
+ Set_VMS_Object_File_Record (-1);
+
+#ifndef VMS /* For cross-assembly purposes. */
+ {
+ char RecLen[2];
+ int minus_one = -1;
+
+ /* Write a 2 byte record-length field of -1 into the file, which
+ means end-of-block when read, hence end-of-file when occurring
+ in the file's last block. It is only needed for variable-length
+ record files transferred to VMS as fixed-length record files
+ (typical for binary FTP; NFS shouldn't need it, but it won't hurt). */
+ md_number_to_chars (RecLen, minus_one, 2);
+ write (VMS_Object_File_FD, RecLen, 2);
+ }
+#else
+ /* When written on a VMS system, the file header (cf inode) will record
+ the actual end-of-file position and no inline marker is needed. */
+#endif
+
+ close (VMS_Object_File_FD);
+}
+
+
+ /****** Text Information and Relocation routines ******/
+
+
+/* Stack Psect base followed by signed, varying-sized offset.
+ Common to several object records. */
+
+static void
+vms_tir_stack_psect (Psect_Index, Offset, Force)
+ int Psect_Index;
+ int Offset;
+ int Force;
+{
+ int psect_width, offset_width;
+
+ psect_width = ((unsigned) Psect_Index > 255) ? 2 : 1;
+ offset_width = (Force || Offset > 32767 || Offset < -32768) ? 4
+ : (Offset > 127 || Offset < -128) ? 2 : 1;
+#define Sta_P(p,o) (((o)<<1) | ((p)-1))
+ /* byte or word psect; byte, word, or longword offset */
+ switch (Sta_P(psect_width,offset_width))
+ {
+ case Sta_P(1,1): PUT_CHAR (TIR_S_C_STA_PB);
+ PUT_CHAR ((char)(unsigned char) Psect_Index);
+ PUT_CHAR ((char) Offset);
+ break;
+ case Sta_P(1,2): PUT_CHAR (TIR_S_C_STA_PW);
+ PUT_CHAR ((char)(unsigned char) Psect_Index);
+ PUT_SHORT (Offset);
+ break;
+ case Sta_P(1,4): PUT_CHAR (TIR_S_C_STA_PL);
+ PUT_CHAR ((char)(unsigned char) Psect_Index);
+ PUT_LONG (Offset);
+ break;
+ case Sta_P(2,1): PUT_CHAR (TIR_S_C_STA_WPB);
+ PUT_SHORT (Psect_Index);
+ PUT_CHAR ((char) Offset);
+ break;
+ case Sta_P(2,2): PUT_CHAR (TIR_S_C_STA_WPW);
+ PUT_SHORT (Psect_Index);
+ PUT_SHORT (Offset);
+ break;
+ case Sta_P(2,4): PUT_CHAR (TIR_S_C_STA_WPL);
+ PUT_SHORT (Psect_Index);
+ PUT_LONG (Offset);
+ break;
+ }
+#undef Sta_P
+}
+
+
+/* Store immediate data in current Psect. */
+
+static void
+VMS_Store_Immediate_Data (Pointer, Size, Record_Type)
+ const char *Pointer;
+ int Size;
+ int Record_Type;
+{
+ register int i;
+
+ Set_VMS_Object_File_Record (Record_Type);
+ /* We can only store as most 128 bytes at a time due to the way that
+ TIR commands are encoded. */
+ while (Size > 0)
+ {
+ i = (Size > 128) ? 128 : Size;
+ Size -= i;
+ /* If we cannot accommodate this record, flush the buffer. */
+ if ((Object_Record_Offset + i + 1) >= sizeof Object_Record_Buffer)
+ Flush_VMS_Object_Record_Buffer ();
+ /* If the buffer is empty we must insert record type. */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (Record_Type);
+ /* Store the count. The Store Immediate TIR command is implied by
+ a negative command byte, and the length of the immediate data
+ is abs(command_byte). So, we write the negated length value. */
+ PUT_CHAR ((char) (-i & 0xff));
+ /* Now store the data. */
+ while (--i >= 0)
+ PUT_CHAR (*Pointer++);
+ }
+ /* Flush the buffer if it is more than 75% full. */
+ if (Object_Record_Offset > (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+
+/* Make a data reference. */
+
+static void
+VMS_Set_Data (Psect_Index, Offset, Record_Type, Force)
+ int Psect_Index;
+ int Offset;
+ int Record_Type;
+ int Force;
+{
+ Set_VMS_Object_File_Record (Record_Type);
+ /* If the buffer is empty we must insert the record type. */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (Record_Type);
+ /* Stack the Psect base with its offset. */
+ vms_tir_stack_psect (Psect_Index, Offset, Force);
+ /* Set relocation base. */
+ PUT_CHAR (TIR_S_C_STO_PIDR);
+ /* Flush the buffer if it is more than 75% full. */
+ if (Object_Record_Offset > (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+
+/* Make a debugger reference to a struct, union or enum. */
+
+static void
+VMS_Store_Struct (Struct_Index)
+ int Struct_Index;
+{
+ /* We are writing a debug record. */
+ Set_VMS_Object_File_Record (OBJ_S_C_DBG);
+ /* If the buffer is empty we must insert the record type. */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (OBJ_S_C_DBG);
+ PUT_CHAR (TIR_S_C_STA_UW);
+ PUT_SHORT (Struct_Index);
+ PUT_CHAR (TIR_S_C_CTL_STKDL);
+ PUT_CHAR (TIR_S_C_STO_L);
+ /* Flush the buffer if it is more than 75% full. */
+ if (Object_Record_Offset > (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+
+/* Make a debugger reference to partially define a struct, union or enum. */
+
+static void
+VMS_Def_Struct (Struct_Index)
+ int Struct_Index;
+{
+ /* We are writing a debug record. */
+ Set_VMS_Object_File_Record (OBJ_S_C_DBG);
+ /* If the buffer is empty we must insert the record type. */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (OBJ_S_C_DBG);
+ PUT_CHAR (TIR_S_C_STA_UW);
+ PUT_SHORT (Struct_Index);
+ PUT_CHAR (TIR_S_C_CTL_DFLOC);
+ /* Flush the buffer if it is more than 75% full. */
+ if (Object_Record_Offset > (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+static void
+VMS_Set_Struct (Struct_Index)
+ int Struct_Index;
+{ /* see previous functions for comments */
+ Set_VMS_Object_File_Record (OBJ_S_C_DBG);
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (OBJ_S_C_DBG);
+ PUT_CHAR (TIR_S_C_STA_UW);
+ PUT_SHORT (Struct_Index);
+ PUT_CHAR (TIR_S_C_CTL_STLOC);
+ if (Object_Record_Offset > (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+
+ /****** Traceback Information routines ******/
+
+
+/* Write the Traceback Module Begin record. */
+
+static void
+VMS_TBT_Module_Begin ()
+{
+ register char *cp, *cp1;
+ int Size;
+ char Local[256];
+
+ /* Arrange to store the data locally (leave room for size byte). */
+ cp = &Local[1];
+ /* Begin module. */
+ *cp++ = DST_S_C_MODBEG;
+ *cp++ = 0; /* flags; not used */
+ /*
+ * Language type == "C"
+ *
+ * (FIXME: this should be based on the input...)
+ */
+ COPY_LONG (cp, DST_S_C_C);
+ cp += 4;
+ /* Store the module name. */
+ *cp++ = (char) strlen (Module_Name);
+ cp1 = Module_Name;
+ while (*cp1)
+ *cp++ = *cp1++;
+ /* Now we can store the record size. */
+ Size = (cp - Local);
+ Local[0] = Size - 1;
+ /* Put it into the object record. */
+ VMS_Store_Immediate_Data (Local, Size, OBJ_S_C_TBT);
+}
+
+
+/* Write the Traceback Module End record. */
+
+static void
+VMS_TBT_Module_End ()
+{
+ char Local[2];
+
+ /* End module. */
+ Local[0] = 1;
+ Local[1] = DST_S_C_MODEND;
+ /* Put it into the object record. */
+ VMS_Store_Immediate_Data (Local, 2, OBJ_S_C_TBT);
+}
+
+
+/* Write a Traceback Routine Begin record. */
+
+static void
+VMS_TBT_Routine_Begin (symbolP, Psect)
+ symbolS *symbolP;
+ int Psect;
+{
+ register char *cp, *cp1;
+ char *Name;
+ int Offset;
+ int Size;
+ char Local[512];
+
+ /* Strip the leading "_" from the name. */
+ Name = S_GET_NAME (symbolP);
+ if (*Name == '_')
+ Name++;
+ /* Get the text psect offset. */
+ Offset = S_GET_VALUE (symbolP);
+ /* Set the record size. */
+ Size = 1 + 1 + 4 + 1 + strlen (Name);
+ Local[0] = Size;
+ /* DST type "routine begin". */
+ Local[1] = DST_S_C_RTNBEG;
+ /* Uses CallS/CallG. */
+ Local[2] = 0;
+ /* Store the data so far. */
+ VMS_Store_Immediate_Data (Local, 3, OBJ_S_C_TBT);
+ /* Make sure we are still generating a OBJ_S_C_TBT record. */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (OBJ_S_C_TBT);
+ /* Stack the address. */
+ vms_tir_stack_psect (Psect, Offset, 0);
+ /* Store the data reference. */
+ PUT_CHAR (TIR_S_C_STO_PIDR);
+ /* Store the counted string as data. */
+ cp = Local;
+ cp1 = Name;
+ Size = strlen (cp1) + 1;
+ *cp++ = Size - 1;
+ while (*cp1)
+ *cp++ = *cp1++;
+ VMS_Store_Immediate_Data (Local, Size, OBJ_S_C_TBT);
+}
+
+
+/* Write a Traceback Routine End record.
+
+ We *must* search the symbol table to find the next routine, since the
+ assember has a way of reassembling the symbol table OUT OF ORDER Thus
+ the next routine in the symbol list is not necessarily the next one in
+ memory. For debugging to work correctly we must know the size of the
+ routine. */
+
+static void
+VMS_TBT_Routine_End (Max_Size, sp)
+ int Max_Size;
+ symbolS *sp;
+{
+ symbolS *symbolP;
+ int Size = 0x7fffffff;
+ char Local[16];
+ valueT sym_value, sp_value = S_GET_VALUE (sp);
+
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next (symbolP))
+ {
+ if (!S_IS_DEBUG (symbolP) && S_GET_TYPE (symbolP) == N_TEXT)
+ {
+ if (*S_GET_NAME (symbolP) == 'L')
+ continue;
+ sym_value = S_GET_VALUE (symbolP);
+ if (sym_value > sp_value && sym_value < Size)
+ Size = sym_value;
+
+ /*
+ * Dummy labels like "gcc_compiled." should no longer reach here.
+ */
+#if 0
+ else
+ /* check if gcc_compiled. has size of zero */
+ if (sym_value == sp_value &&
+ sp != symbolP &&
+ (!strcmp (S_GET_NAME (sp), "gcc_compiled.") ||
+ !strcmp (S_GET_NAME (sp), "gcc2_compiled.")))
+ Size = sym_value;
+#endif
+ }
+ }
+ if (Size == 0x7fffffff)
+ Size = Max_Size;
+ Size -= sp_value; /* and get the size of the routine */
+ /* Record Size. */
+ Local[0] = 6;
+ /* DST type is "routine end". */
+ Local[1] = DST_S_C_RTNEND;
+ Local[2] = 0; /* unused */
+ /* Size of routine. */
+ COPY_LONG (&Local[3], Size);
+ /* Store the record. */
+ VMS_Store_Immediate_Data (Local, 7, OBJ_S_C_TBT);
+}
+
+
+/* Write a Traceback Block Begin record. */
+
+static void
+VMS_TBT_Block_Begin (symbolP, Psect, Name)
+ symbolS *symbolP;
+ int Psect;
+ char *Name;
+{
+ register char *cp, *cp1;
+ int Offset;
+ int Size;
+ char Local[512];
+
+ /* Set the record size. */
+ Size = 1 + 1 + 4 + 1 + strlen (Name);
+ Local[0] = Size;
+ /* DST type is "begin block"; we simulate with a phony routine. */
+ Local[1] = DST_S_C_BLKBEG;
+ /* Uses CallS/CallG. */
+ Local[2] = 0;
+ /* Store the data so far. */
+ VMS_Store_Immediate_Data (Local, 3, OBJ_S_C_DBG);
+ /* Make sure we are still generating a debug record. */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (OBJ_S_C_DBG);
+ /* Now get the symbol address. */
+ PUT_CHAR (TIR_S_C_STA_WPL);
+ PUT_SHORT (Psect);
+ /* Get the text psect offset. */
+ Offset = S_GET_VALUE (symbolP);
+ PUT_LONG (Offset);
+ /* Store the data reference. */
+ PUT_CHAR (TIR_S_C_STO_PIDR);
+ /* Store the counted string as data. */
+ cp = Local;
+ cp1 = Name;
+ Size = strlen (cp1) + 1;
+ *cp++ = Size - 1;
+ while (*cp1)
+ *cp++ = *cp1++;
+ VMS_Store_Immediate_Data (Local, Size, OBJ_S_C_DBG);
+}
+
+
+/* Write a Traceback Block End record. */
+
+static void
+VMS_TBT_Block_End (Size)
+ valueT Size;
+{
+ char Local[16];
+
+ Local[0] = 6; /* record length */
+ /* DST type is "block end"; simulate with a phony end routine. */
+ Local[1] = DST_S_C_BLKEND;
+ Local[2] = 0; /* unused, must be zero */
+ COPY_LONG (&Local[3], Size);
+ VMS_Store_Immediate_Data (Local, 7, OBJ_S_C_DBG);
+}
+
+
+/* Write a Line number <-> Program Counter correlation record. */
+
+static void
+VMS_TBT_Line_PC_Correlation (Line_Number, Offset, Psect, Do_Delta)
+ int Line_Number;
+ int Offset;
+ int Psect;
+ int Do_Delta;
+{
+ register char *cp;
+ char Local[64];
+
+ if (Do_Delta == 0)
+ {
+ /*
+ * If not delta, set our PC/Line number correlation.
+ */
+ cp = &Local[1]; /* Put size in Local[0] later. */
+ /* DST type is "Line Number/PC correlation". */
+ *cp++ = DST_S_C_LINE_NUM;
+ /* Set Line number. */
+ if (Line_Number - 1 <= 255)
+ {
+ *cp++ = DST_S_C_SET_LINUM_B;
+ *cp++ = (char) (Line_Number - 1);
+ }
+ else if (Line_Number - 1 <= 65535)
+ {
+ *cp++ = DST_S_C_SET_LINE_NUM;
+ COPY_SHORT (cp, Line_Number - 1), cp += 2;
+ }
+ else
+ {
+ *cp++ = DST_S_C_SET_LINUM_L;
+ COPY_LONG (cp, Line_Number - 1), cp += 4;
+ }
+ /* Set PC. */
+ *cp++ = DST_S_C_SET_ABS_PC;
+ /* Store size now that we know it, then output the data. */
+ Local[0] = cp - &Local[1];
+ /* Account for the space that TIR_S_C_STO_PIDR will use for the PC. */
+ Local[0] += 4; /* size includes length of another longword */
+ VMS_Store_Immediate_Data (Local, cp - Local, OBJ_S_C_TBT);
+ /* Make sure we are still generating a OBJ_S_C_TBT record. */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (OBJ_S_C_TBT);
+ vms_tir_stack_psect (Psect, Offset, 0);
+ PUT_CHAR (TIR_S_C_STO_PIDR);
+ /* Do a PC offset of 0 to register the line number. */
+ Local[0] = 2;
+ Local[1] = DST_S_C_LINE_NUM;
+ Local[2] = 0; /* Increment PC by 0 and register line # */
+ VMS_Store_Immediate_Data (Local, 3, OBJ_S_C_TBT);
+ }
+ else
+ {
+ if (Do_Delta < 0)
+ {
+ /*
+ * When delta is negative, terminate the line numbers.
+ */
+ Local[0] = 1 + 1 + 4;
+ Local[1] = DST_S_C_LINE_NUM;
+ Local[2] = DST_S_C_TERM_L;
+ COPY_LONG (&Local[3], Offset);
+ VMS_Store_Immediate_Data (Local, 7, OBJ_S_C_TBT);
+ return;
+ }
+ /*
+ * Do a PC/Line delta.
+ */
+ cp = &Local[1];
+ *cp++ = DST_S_C_LINE_NUM;
+ if (Line_Number > 1)
+ {
+ /* We need to increment the line number. */
+ if (Line_Number - 1 <= 255)
+ {
+ *cp++ = DST_S_C_INCR_LINUM;
+ *cp++ = Line_Number - 1;
+ }
+ else if (Line_Number - 1 <= 65535)
+ {
+ *cp++ = DST_S_C_INCR_LINUM_W;
+ COPY_SHORT (cp, Line_Number - 1), cp += 2;
+ }
+ else
+ {
+ *cp++ = DST_S_C_INCR_LINUM_L;
+ COPY_LONG (cp, Line_Number - 1), cp += 4;
+ }
+ }
+ /*
+ * Increment the PC
+ */
+ if (Offset <= 128)
+ {
+ /* Small offsets are encoded as negative numbers, rather than the
+ usual non-negative type code followed by another data field. */
+ *cp++ = (char) -Offset;
+ }
+ else if (Offset <= 65535)
+ {
+ *cp++ = DST_S_C_DELTA_PC_W;
+ COPY_SHORT (cp, Offset), cp += 2;
+ }
+ else
+ {
+ *cp++ = DST_S_C_DELTA_PC_L;
+ COPY_LONG (cp, Offset), cp += 4;
+ }
+ /* Set size now that be know it, then output the data. */
+ Local[0] = cp - &Local[1];
+ VMS_Store_Immediate_Data (Local, cp - Local, OBJ_S_C_TBT);
+ }
+}
+
+
+/* Describe a source file to the debugger. */
+
+static int
+VMS_TBT_Source_File (Filename, ID_Number)
+ char *Filename;
+ int ID_Number;
+{
+ register char *cp;
+ int len, rfo, ffb, ebk;
+ char cdt[8];
+ char Local[512];
+#ifdef VMS /* Used for native assembly */
+ unsigned Status;
+ struct FAB fab; /* RMS file access block */
+ struct NAM nam; /* file name information */
+ struct XABDAT xabdat; /* date+time fields */
+ struct XABFHC xabfhc; /* file header characteristics */
+ char resultant_string_buffer[255 + 1];
+
+ /*
+ * Set up RMS structures:
+ */
+ /* FAB -- file access block */
+ memset ((char *) &fab, 0, sizeof fab);
+ fab.fab$b_bid = FAB$C_BID;
+ fab.fab$b_bln = (unsigned char) sizeof fab;
+ fab.fab$l_fna = Filename;
+ fab.fab$b_fns = (unsigned char) strlen (Filename);
+ fab.fab$l_nam = (char *) &nam;
+ fab.fab$l_xab = (char *) &xabdat;
+ /* NAM -- file name block */
+ memset ((char *) &nam, 0, sizeof nam);
+ nam.nam$b_bid = NAM$C_BID;
+ nam.nam$b_bln = (unsigned char) sizeof nam;
+ nam.nam$l_rsa = resultant_string_buffer;
+ nam.nam$b_rss = (unsigned char) (sizeof resultant_string_buffer - 1);
+ /* XABs -- extended attributes blocks */
+ memset ((char *) &xabdat, 0, sizeof xabdat);
+ xabdat.xab$b_cod = XAB$C_DAT;
+ xabdat.xab$b_bln = (unsigned char) sizeof xabdat;
+ xabdat.xab$l_nxt = (char *) &xabfhc;
+ memset ((char *) &xabfhc, 0, sizeof xabfhc);
+ xabfhc.xab$b_cod = XAB$C_FHC;
+ xabfhc.xab$b_bln = (unsigned char) sizeof xabfhc;
+ xabfhc.xab$l_nxt = 0;
+ /*
+ * Get the file information
+ */
+ Status = sys$open (&fab);
+ if (!(Status & 1))
+ {
+ as_tsktsk (_("Couldn't find source file \"%s\", status=%%X%x"),
+ Filename, Status);
+ return 0;
+ }
+ sys$close (&fab);
+ /* Now extract fields of interest. */
+ memcpy (cdt, (char *) &xabdat.xab$q_cdt, 8); /* creation date */
+ ebk = xabfhc.xab$l_ebk; /* end-of-file block */
+ ffb = xabfhc.xab$w_ffb; /* first free byte of last block */
+ rfo = xabfhc.xab$b_rfo; /* record format */
+ len = nam.nam$b_rsl; /* length of Filename */
+ resultant_string_buffer[len] = '\0';
+ Filename = resultant_string_buffer; /* full filename */
+#else /* Cross-assembly */
+ /* [Perhaps we ought to use actual values derived from stat() here?] */
+ memset (cdt, 0, 8); /* null VMS quadword binary time */
+ ebk = ffb = rfo = 0;
+ len = strlen (Filename);
+ if (len > 255) /* a single byte is used as count prefix */
+ {
+ Filename += (len - 255); /* tail end is more significant */
+ len = 255;
+ }
+#endif /* VMS */
+
+ cp = &Local[1]; /* fill in record length later */
+ *cp++ = DST_S_C_SOURCE; /* DST type is "source file" */
+ *cp++ = DST_S_C_SRC_FORMFEED; /* formfeeds count as source records */
+ *cp++ = DST_S_C_SRC_DECLFILE; /* declare source file */
+ know (cp == &Local[4]);
+ *cp++ = 0; /* fill in this length below */
+ *cp++ = 0; /* flags; must be zero */
+ COPY_SHORT (cp, ID_Number), cp += 2; /* file ID number */
+ memcpy (cp, cdt, 8), cp += 8; /* creation date+time */
+ COPY_LONG (cp, ebk), cp += 4; /* end-of-file block */
+ COPY_SHORT (cp, ffb), cp += 2; /* first free byte of last block */
+ *cp++ = (char) rfo; /* RMS record format */
+ /* Filename. */
+ *cp++ = (char) len;
+ while (--len >= 0)
+ *cp++ = *Filename++;
+ /* Library module name (none). */
+ *cp++ = 0;
+ /* Now that size is known, fill it in and write out the record. */
+ Local[4] = cp - &Local[5]; /* source file declaration size */
+ Local[0] = cp - &Local[1]; /* TBT record size */
+ VMS_Store_Immediate_Data (Local, cp - Local, OBJ_S_C_TBT);
+ return 1;
+}
+
+
+/* Traceback information is described in terms of lines from compiler
+ listing files, not lines from source files. We need to set up the
+ correlation between listing line numbers and source line numbers.
+ Since gcc's .stabn directives refer to the source lines, we just
+ need to describe a one-to-one correspondence. */
+
+static void
+VMS_TBT_Source_Lines (ID_Number, Starting_Line_Number, Number_Of_Lines)
+ int ID_Number;
+ int Starting_Line_Number;
+ int Number_Of_Lines;
+{
+ char *cp;
+ int chunk_limit;
+ char Local[128]; /* room enough to describe 1310700 lines... */
+
+ cp = &Local[1]; /* Put size in Local[0] later. */
+ *cp++ = DST_S_C_SOURCE; /* DST type is "source file". */
+ *cp++ = DST_S_C_SRC_SETFILE; /* Set Source File. */
+ COPY_SHORT (cp, ID_Number), cp += 2; /* File ID Number. */
+ /* Set record number and define lines. Since no longword form of
+ SRC_DEFLINES is available, we need to be able to cope with any huge
+ files a chunk at a time. It doesn't matter for tracebacks, since
+ unspecified lines are mapped one-to-one and work out right, but it
+ does matter within the debugger. Without this explicit mapping,
+ it will complain about lines not existing in the module. */
+ chunk_limit = (sizeof Local - 5) / 6;
+ if (Number_Of_Lines > 65535 * chunk_limit) /* avoid buffer overflow */
+ Number_Of_Lines = 65535 * chunk_limit;
+ while (Number_Of_Lines > 65535)
+ {
+ *cp++ = DST_S_C_SRC_SETREC_L;
+ COPY_LONG (cp, Starting_Line_Number), cp += 4;
+ *cp++ = DST_S_C_SRC_DEFLINES_W;
+ COPY_SHORT (cp, 65535), cp += 2;
+ Starting_Line_Number += 65535;
+ Number_Of_Lines -= 65535;
+ }
+ /* Set record number and define lines, normal case. */
+ if (Starting_Line_Number <= 65535)
+ {
+ *cp++ = DST_S_C_SRC_SETREC_W;
+ COPY_SHORT (cp, Starting_Line_Number), cp += 2;
+ }
+ else
+ {
+ *cp++ = DST_S_C_SRC_SETREC_L;
+ COPY_LONG (cp, Starting_Line_Number), cp += 4;
+ }
+ *cp++ = DST_S_C_SRC_DEFLINES_W;
+ COPY_SHORT (cp, Number_Of_Lines), cp += 2;
+ /* Set size now that be know it, then output the data. */
+ Local[0] = cp - &Local[1];
+ VMS_Store_Immediate_Data (Local, cp - Local, OBJ_S_C_TBT);
+}
+
+
+ /****** Debugger Information support routines ******/
+
+
+/* This routine locates a file in the list of files. If an entry does
+ not exist, one is created. For include files, a new entry is always
+ created such that inline functions can be properly debugged. */
+
+static struct input_file *
+find_file (sp)
+ symbolS *sp;
+{
+ struct input_file *same_file = 0;
+ struct input_file *fpnt, *last = 0;
+ char *sp_name;
+
+ for (fpnt = file_root; fpnt; fpnt = fpnt->next)
+ {
+ if (fpnt->spnt == sp)
+ return fpnt;
+ last = fpnt;
+ }
+ sp_name = S_GET_NAME (sp);
+ for (fpnt = file_root; fpnt; fpnt = fpnt->next)
+ {
+ if (strcmp (sp_name, fpnt->name) == 0)
+ {
+ if (fpnt->flag == 1)
+ return fpnt;
+ same_file = fpnt;
+ break;
+ }
+ }
+ fpnt = (struct input_file *) xmalloc (sizeof (struct input_file));
+ if (!file_root)
+ file_root = fpnt;
+ else
+ last->next = fpnt;
+ fpnt->next = 0;
+ fpnt->name = sp_name;
+ fpnt->min_line = 0x7fffffff;
+ fpnt->max_line = 0;
+ fpnt->offset = 0;
+ fpnt->flag = 0;
+ fpnt->file_number = 0;
+ fpnt->spnt = sp;
+ fpnt->same_file_fpnt = same_file;
+ return fpnt;
+}
+
+
+/* This routine converts a number string into an integer, and stops when
+ it sees an invalid character. The return value is the address of the
+ character just past the last character read. No error is generated. */
+
+static char *
+cvt_integer (str, rtn)
+ char *str;
+ int *rtn;
+{
+ int ival = 0, sgn = 1;
+
+ if (*str == '-')
+ sgn = -1, ++str;
+ while (*str >= '0' && *str <= '9')
+ ival = 10 * ival + *str++ - '0';
+ *rtn = sgn * ival;
+ return str;
+}
+
+
+/*
+ * The following functions and definitions are used to generate object
+ * records that will describe program variables to the VMS debugger.
+ *
+ * This file contains many of the routines needed to output debugging info
+ * into the object file that the VMS debugger needs to understand symbols.
+ * These routines are called very late in the assembly process, and thus
+ * we can be fairly lax about changing things, since the GSD and the TIR
+ * sections have already been output.
+ */
+
+
+/* This routine fixes the names that are generated by C++, ".this" is a good
+ example. The period does not work for the debugger, since it looks like
+ the syntax for a structure element, and thus it gets mightily confused.
+
+ We also use this to strip the PsectAttribute hack from the name before we
+ write a debugger record. */
+
+static char *
+fix_name (pnt)
+ char *pnt;
+{
+ char *pnt1;
+
+ /* Kill any leading "_". */
+ if (*pnt == '_')
+ pnt++;
+
+ /* Is there a Psect Attribute to skip?? */
+ if (HAS_PSECT_ATTRIBUTES (pnt))
+ {
+ /* Yes: Skip it. */
+ pnt += PSECT_ATTRIBUTES_STRING_LENGTH;
+ while (*pnt)
+ {
+ if ((pnt[0] == '$') && (pnt[1] == '$'))
+ {
+ pnt += 2;
+ break;
+ }
+ pnt++;
+ }
+ }
+
+ /* Here we fix the .this -> $this conversion. */
+ for (pnt1 = pnt; *pnt1 != 0; pnt1++)
+ if (*pnt1 == '.')
+ *pnt1 = '$';
+
+ return pnt;
+}
+
+
+/* When defining a structure, this routine is called to find the name of
+ the actual structure. It is assumed that str points to the equal sign
+ in the definition, and it moves backward until it finds the start of the
+ name. If it finds a 0, then it knows that this structure def is in the
+ outermost level, and thus symbol_name points to the symbol name. */
+
+static char *
+get_struct_name (str)
+ char *str;
+{
+ char *pnt;
+ pnt = str;
+ while ((*pnt != ':') && (*pnt != '\0'))
+ pnt--;
+ if (*pnt == '\0')
+ return (char *) symbol_name;
+ *pnt-- = '\0';
+ while ((*pnt != ';') && (*pnt != '='))
+ pnt--;
+ if (*pnt == ';')
+ return pnt + 1;
+ while ((*pnt < '0') || (*pnt > '9'))
+ pnt++;
+ while ((*pnt >= '0') && (*pnt <= '9'))
+ pnt++;
+ return pnt;
+}
+
+
+/* Search symbol list for type number dbx_type.
+ Return a pointer to struct. */
+
+static struct VMS_DBG_Symbol *
+find_symbol (dbx_type)
+ int dbx_type;
+{
+ struct VMS_DBG_Symbol *spnt;
+
+ spnt = VMS_Symbol_type_list[SYMTYP_HASH (dbx_type)];
+ while (spnt)
+ {
+ if (spnt->dbx_type == dbx_type)
+ break;
+ spnt = spnt->next;
+ }
+ if (!spnt || spnt->advanced != ALIAS)
+ return spnt;
+ return find_symbol (spnt->type2);
+}
+
+
+#if 0 /* obsolete */
+/* this routine puts info into either Local or Asuffix, depending on the sign
+ * of size. The reason is that it is easier to build the variable descriptor
+ * backwards, while the array descriptor is best built forwards. In the end
+ * they get put together, if there is not a struct/union/enum along the way
+ */
+static void
+push (value, size1)
+ int value, size1;
+{
+ if (size1 < 0)
+ {
+ size1 = -size1;
+ if (Lpnt < size1)
+ {
+ overflow = 1;
+ Lpnt = 1;
+ return;
+ }
+ Lpnt -= size1;
+ md_number_to_chars (&Local[Lpnt + 1], value, size1);
+ }
+ else
+ {
+ if (Apoint + size1 >= MAX_DEBUG_RECORD)
+ {
+ overflow = 1;
+ Apoint = MAX_DEBUG_RECORD - 1;
+ return;
+ }
+ md_number_to_chars (&Asuffix[Apoint], value, size1);
+ Apoint += size1;
+ }
+}
+#endif
+
+
+static void
+fpush (value, size)
+ int value, size;
+{
+ if (Apoint + size >= MAX_DEBUG_RECORD)
+ {
+ overflow = 1;
+ Apoint = MAX_DEBUG_RECORD - 1;
+ return;
+ }
+ if (size == 1)
+ Asuffix[Apoint++] = (char) value;
+ else
+ {
+ md_number_to_chars (&Asuffix[Apoint], value, size);
+ Apoint += size;
+ }
+}
+
+static void
+rpush (value, size)
+ int value, size;
+{
+ if (Lpnt < size)
+ {
+ overflow = 1;
+ Lpnt = 1;
+ return;
+ }
+ if (size == 1)
+ Local[Lpnt--] = (char) value;
+ else
+ {
+ Lpnt -= size;
+ md_number_to_chars (&Local[Lpnt + 1], value, size);
+ }
+}
+
+
+/* This routine generates the array descriptor for a given array. */
+
+static void
+array_suffix (spnt2)
+ struct VMS_DBG_Symbol *spnt2;
+{
+ struct VMS_DBG_Symbol *spnt;
+ struct VMS_DBG_Symbol *spnt1;
+ int rank;
+ int total_size;
+
+ rank = 0;
+ spnt = spnt2;
+ while (spnt->advanced != ARRAY)
+ {
+ spnt = find_symbol (spnt->type2);
+ if (!spnt)
+ return;
+ }
+ spnt1 = spnt;
+ total_size = 1;
+ while (spnt1->advanced == ARRAY)
+ {
+ rank++;
+ total_size *= (spnt1->index_max - spnt1->index_min + 1);
+ spnt1 = find_symbol (spnt1->type2);
+ }
+ total_size = total_size * spnt1->data_size;
+ fpush (spnt1->data_size, 2); /* element size */
+ if (spnt1->VMS_type == DBG_S_C_ADVANCED_TYPE)
+ fpush (0, 1);
+ else
+ fpush (spnt1->VMS_type, 1); /* element type */
+ fpush (DSC_K_CLASS_A, 1); /* descriptor class */
+ fpush (0, 4); /* base address */
+ fpush (0, 1); /* scale factor -- not applicable */
+ fpush (0, 1); /* digit count -- not applicable */
+ fpush (0xc0, 1); /* flags: multiplier block & bounds present */
+ fpush (rank, 1); /* number of dimensions */
+ fpush (total_size, 4);
+ fpush (0, 4); /* pointer to element [0][0]...[0] */
+ spnt1 = spnt;
+ while (spnt1->advanced == ARRAY)
+ {
+ fpush (spnt1->index_max - spnt1->index_min + 1, 4);
+ spnt1 = find_symbol (spnt1->type2);
+ }
+ spnt1 = spnt;
+ while (spnt1->advanced == ARRAY)
+ {
+ fpush (spnt1->index_min, 4);
+ fpush (spnt1->index_max, 4);
+ spnt1 = find_symbol (spnt1->type2);
+ }
+}
+
+
+/* This routine generates the start of a variable descriptor based upon
+ a struct/union/enum that has yet to be defined. We define this spot as
+ a new location, and save four bytes for the address. When the struct is
+ finally defined, then we can go back and plug in the correct address. */
+
+static void
+new_forward_ref (dbx_type)
+ int dbx_type;
+{
+ struct forward_ref *fpnt;
+ fpnt = (struct forward_ref *) xmalloc (sizeof (struct forward_ref));
+ fpnt->next = f_ref_root;
+ f_ref_root = fpnt;
+ fpnt->dbx_type = dbx_type;
+ fpnt->struc_numb = ++structure_count;
+ fpnt->resolved = 'N';
+ rpush (DST_K_TS_IND, 1); /* indirect type specification */
+ total_len = 5;
+ rpush (total_len, 2);
+ struct_number = -fpnt->struc_numb;
+}
+
+
+/* This routine generates the variable descriptor used to describe non-basic
+ variables. It calls itself recursively until it gets to the bottom of it
+ all, and then builds the descriptor backwards. It is easiest to do it
+ this way since we must periodically write length bytes, and it is easiest
+ if we know the value when it is time to write it. */
+
+static int
+gen1 (spnt, array_suffix_len)
+ struct VMS_DBG_Symbol *spnt;
+ int array_suffix_len;
+{
+ struct VMS_DBG_Symbol *spnt1;
+ int i;
+
+ switch (spnt->advanced)
+ {
+ case VOID:
+ rpush (DBG_S_C_VOID, 1);
+ total_len += 1;
+ rpush (total_len, 2);
+ return 0;
+ case BASIC:
+ case FUNCTION:
+ if (array_suffix_len == 0)
+ {
+ rpush (spnt->VMS_type, 1);
+ rpush (DBG_S_C_BASIC, 1);
+ total_len = 2;
+ rpush (total_len, 2);
+ return 1;
+ }
+ rpush (0, 4);
+ rpush (DST_K_VFLAGS_DSC, 1);
+ rpush (DST_K_TS_DSC, 1); /* descriptor type specification */
+ total_len = -2;
+ return 1;
+ case STRUCT:
+ case UNION:
+ case ENUM:
+ struct_number = spnt->struc_numb;
+ if (struct_number < 0)
+ {
+ new_forward_ref (spnt->dbx_type);
+ return 1;
+ }
+ rpush (DBG_S_C_STRUCT, 1);
+ total_len = 5;
+ rpush (total_len, 2);
+ return 1;
+ case POINTER:
+ spnt1 = find_symbol (spnt->type2);
+ i = 1;
+ if (!spnt1)
+ new_forward_ref (spnt->type2);
+ else
+ i = gen1 (spnt1, 0);
+ if (i)
+ { /* (*void) is a special case, do not put pointer suffix */
+ rpush (DBG_S_C_POINTER, 1);
+ total_len += 3;
+ rpush (total_len, 2);
+ }
+ return 1;
+ case ARRAY:
+ spnt1 = spnt;
+ while (spnt1->advanced == ARRAY)
+ {
+ spnt1 = find_symbol (spnt1->type2);
+ if (!spnt1)
+ {
+ as_tsktsk (_("debugger forward reference error, dbx type %d"),
+ spnt->type2);
+ return 0;
+ }
+ }
+/* It is too late to generate forward references, so the user gets a message.
+ * This should only happen on a compiler error */
+ (void) gen1 (spnt1, 1);
+ i = Apoint;
+ array_suffix (spnt);
+ array_suffix_len = Apoint - i;
+ switch (spnt1->advanced)
+ {
+ case BASIC:
+ case FUNCTION:
+ break;
+ default:
+ rpush (0, 2);
+ total_len += 2;
+ rpush (total_len, 2);
+ rpush (DST_K_VFLAGS_DSC, 1);
+ rpush (1, 1); /* flags: element value spec included */
+ rpush (1, 1); /* one dimension */
+ rpush (DBG_S_C_COMPLEX_ARRAY, 1);
+ }
+ total_len += array_suffix_len + 8;
+ rpush (total_len, 2);
+ break;
+ default: /* lint suppression */
+ break;
+ }
+ return 0;
+}
+
+
+/* This generates a suffix for a variable. If it is not a defined type yet,
+ then dbx_type contains the type we are expecting so we can generate a
+ forward reference. This calls gen1 to build most of the descriptor, and
+ then it puts the icing on at the end. It then dumps whatever is needed
+ to get a complete descriptor (i.e. struct reference, array suffix). */
+
+static void
+generate_suffix (spnt, dbx_type)
+ struct VMS_DBG_Symbol *spnt;
+ int dbx_type;
+{
+ static const char pvoid[6] = {
+ 5, /* record.length == 5 */
+ DST_K_TYPSPEC, /* record.type == 1 (type specification) */
+ 0, /* name.length == 0, no name follows */
+ 1, 0, /* type.length == 1 {2 bytes, little endian} */
+ DBG_S_C_VOID /* type.type == 5 (pointer to unspecified) */
+ };
+ int i;
+
+ Apoint = 0;
+ Lpnt = MAX_DEBUG_RECORD - 1;
+ total_len = 0;
+ struct_number = 0;
+ overflow = 0;
+ if (!spnt)
+ new_forward_ref (dbx_type);
+ else
+ {
+ if (spnt->VMS_type != DBG_S_C_ADVANCED_TYPE)
+ return; /* no suffix needed */
+ gen1 (spnt, 0);
+ }
+ rpush (0, 1); /* no name (len==0) */
+ rpush (DST_K_TYPSPEC, 1);
+ total_len += 4;
+ rpush (total_len, 1);
+ /* If the variable descriptor overflows the record, output a descriptor
+ for a pointer to void. */
+ if ((total_len >= MAX_DEBUG_RECORD) || overflow)
+ {
+ as_warn (_("Variable descriptor %d too complicated. Defined as `void *'."),
+ spnt->dbx_type);
+ VMS_Store_Immediate_Data (pvoid, 6, OBJ_S_C_DBG);
+ return;
+ }
+ i = 0;
+ while (Lpnt < MAX_DEBUG_RECORD - 1)
+ Local[i++] = Local[++Lpnt];
+ Lpnt = i;
+ /* we use this for reference to structure that has already been defined */
+ if (struct_number > 0)
+ {
+ VMS_Store_Immediate_Data (Local, Lpnt, OBJ_S_C_DBG);
+ Lpnt = 0;
+ VMS_Store_Struct (struct_number);
+ }
+ /* We use this for a forward reference to a structure that has yet to
+ be defined. We store four bytes of zero to make room for the actual
+ address once it is known. */
+ if (struct_number < 0)
+ {
+ struct_number = -struct_number;
+ VMS_Store_Immediate_Data (Local, Lpnt, OBJ_S_C_DBG);
+ Lpnt = 0;
+ VMS_Def_Struct (struct_number);
+ COPY_LONG (&Local[Lpnt], 0L);
+ Lpnt += 4;
+ VMS_Store_Immediate_Data (Local, Lpnt, OBJ_S_C_DBG);
+ Lpnt = 0;
+ }
+ i = 0;
+ while (i < Apoint)
+ Local[Lpnt++] = Asuffix[i++];
+ if (Lpnt != 0)
+ VMS_Store_Immediate_Data (Local, Lpnt, OBJ_S_C_DBG);
+ Lpnt = 0;
+}
+
+
+ /* "novel length" type doesn't work for simple atomic types */
+#define USE_BITSTRING_DESCRIPTOR(t) ((t)->advanced == BASIC)
+#undef SETUP_BASIC_TYPES
+
+/* This routine generates a type description for a bitfield. */
+
+static void
+bitfield_suffix (spnt, width)
+ struct VMS_DBG_Symbol *spnt;
+ int width;
+{
+ Local[Lpnt++] = 13; /* rec.len==13 */
+ Local[Lpnt++] = DST_K_TYPSPEC; /* a type specification record */
+ Local[Lpnt++] = 0; /* not named */
+ COPY_SHORT (&Local[Lpnt], 9); /* typ.len==9 */
+ Lpnt += 2;
+ Local[Lpnt++] = DST_K_TS_NOV_LENG; /* This type is a "novel length"
+ incarnation of some other type. */
+ COPY_LONG (&Local[Lpnt], width); /* size in bits == novel length */
+ Lpnt += 4;
+ VMS_Store_Immediate_Data (Local, Lpnt, OBJ_S_C_DBG);
+ Lpnt = 0;
+ /* assert( spnt->struc_numb > 0 ); */
+ VMS_Store_Struct (spnt->struc_numb); /* output 4 more bytes */
+}
+
+
+/* Formally define a builtin type, so that it can serve as the target of
+ an indirect reference. It makes bitfield_suffix() easier by avoiding
+ the need to use a forward reference for the first occurrence of each
+ type used in a bitfield. */
+
+static void
+setup_basic_type (spnt)
+ struct VMS_DBG_Symbol *spnt;
+{
+#ifdef SETUP_BASIC_TYPES
+ /* This would be very useful if "novel length" fields actually worked
+ with basic types like they do with enumerated types. However,
+ they do not, so this isn't worth doing just so that you can use
+ EXAMINE/TYPE=(__long_long_int) instead of EXAMINE/QUAD. */
+ char *p;
+#ifndef SETUP_SYNONYM_TYPES
+ /* This determines whether compatible things like `int' and `long int'
+ ought to have distinct type records rather than sharing one. */
+ struct VMS_DBG_Symbol *spnt2;
+
+ /* first check whether this type has already been seen by another name */
+ for (spnt2 = VMS_Symbol_type_list[SYMTYP_HASH (spnt->VMS_type)];
+ spnt2;
+ spnt2 = spnt2->next)
+ if (spnt2 != spnt && spnt2->VMS_type == spnt->VMS_type)
+ {
+ spnt->struc_numb = spnt2->struc_numb;
+ return;
+ }
+#endif
+
+ /* `structure number' doesn't really mean `structure'; it means an index
+ into a linker maintained set of saved locations which can be referenced
+ again later. */
+ spnt->struc_numb = ++structure_count;
+ VMS_Def_Struct (spnt->struc_numb); /* remember where this type lives */
+ /* define the simple scalar type */
+ Local[Lpnt++] = 6 + strlen (symbol_name) + 2; /* rec.len */
+ Local[Lpnt++] = DST_K_TYPSPEC; /* rec.typ==type specification */
+ Local[Lpnt++] = strlen (symbol_name) + 2;
+ Local[Lpnt++] = '_'; /* prefix name with "__" */
+ Local[Lpnt++] = '_';
+ for (p = symbol_name; *p; p++)
+ Local[Lpnt++] = *p == ' ' ? '_' : *p;
+ COPY_SHORT (&Local[Lpnt], 2); /* typ.len==2 */
+ Lpnt += 2;
+ Local[Lpnt++] = DST_K_TS_ATOM; /* typ.kind is simple type */
+ Local[Lpnt++] = spnt->VMS_type; /* typ.type */
+ VMS_Store_Immediate_Data (Local, Lpnt, OBJ_S_C_DBG);
+ Lpnt = 0;
+#endif /* SETUP_BASIC_TYPES */
+ return;
+}
+
+
+/* This routine generates a symbol definition for a C symbol for the debugger.
+ It takes a psect and offset for global symbols; if psect < 0, then this is
+ a local variable and the offset is relative to FP. In this case it can
+ be either a variable (Offset < 0) or a parameter (Offset > 0). */
+
+static void
+VMS_DBG_record (spnt, Psect, Offset, Name)
+ struct VMS_DBG_Symbol *spnt;
+ int Psect;
+ int Offset;
+ char *Name;
+{
+ char *Name_pnt;
+ int len;
+ int i = 0;
+
+ /* if there are bad characters in name, convert them */
+ Name_pnt = fix_name (Name);
+
+ len = strlen (Name_pnt);
+ if (Psect < 0)
+ { /* this is a local variable, referenced to SP */
+ Local[i++] = 7 + len;
+ Local[i++] = spnt->VMS_type;
+ Local[i++] = (Offset > 0) ? DBG_C_FUNCTION_PARAM : DBG_C_LOCAL_SYM;
+ COPY_LONG (&Local[i], Offset);
+ i += 4;
+ }
+ else
+ {
+ Local[i++] = 7 + len;
+ Local[i++] = spnt->VMS_type;
+ Local[i++] = DST_K_VALKIND_ADDR;
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ i = 0;
+ VMS_Set_Data (Psect, Offset, OBJ_S_C_DBG, 0);
+ }
+ Local[i++] = len;
+ while (*Name_pnt != '\0')
+ Local[i++] = *Name_pnt++;
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ if (spnt->VMS_type == DBG_S_C_ADVANCED_TYPE)
+ generate_suffix (spnt, 0);
+}
+
+
+/* This routine parses the stabs entries in order to make the definition
+ for the debugger of local symbols and function parameters. */
+
+static void
+VMS_local_stab_Parse (sp)
+ symbolS *sp;
+{
+ struct VMS_DBG_Symbol *spnt;
+ char *pnt;
+ char *pnt1;
+ char *str;
+ int dbx_type;
+
+ dbx_type = 0;
+ str = S_GET_NAME (sp);
+ pnt = (char *) strchr (str, ':');
+ if (!pnt)
+ return; /* no colon present */
+ pnt1 = pnt++; /* save this for later, and skip colon */
+ if (*pnt == 'c')
+ return; /* ignore static constants */
+
+/* there is one little catch that we must be aware of. Sometimes function
+ * parameters are optimized into registers, and the compiler, in its infiite
+ * wisdom outputs stabs records for *both*. In general we want to use the
+ * register if it is present, so we must search the rest of the symbols for
+ * this function to see if this parameter is assigned to a register.
+ */
+ {
+ symbolS *sp1;
+ char *str1;
+ char *pnt2;
+
+ if (*pnt == 'p')
+ {
+ for (sp1 = symbol_next (sp); sp1; sp1 = symbol_next (sp1))
+ {
+ if (!S_IS_DEBUG (sp1))
+ continue;
+ if (S_GET_RAW_TYPE (sp1) == N_FUN)
+ {
+ pnt2 = (char *) strchr (S_GET_NAME (sp1), ':') + 1;
+ if (*pnt2 == 'F' || *pnt2 == 'f')
+ break;
+ }
+ if (S_GET_RAW_TYPE (sp1) != N_RSYM)
+ continue;
+ str1 = S_GET_NAME (sp1); /* and get the name */
+ pnt2 = str;
+ while (*pnt2 != ':')
+ {
+ if (*pnt2 != *str1)
+ break;
+ pnt2++;
+ str1++;
+ }
+ if (*str1 == ':' && *pnt2 == ':')
+ return; /* they are the same! lets skip this one */
+ } /* for */
+ pnt++; /* skip p in case no register */
+ } /* if */
+ } /* p block */
+
+ pnt = cvt_integer (pnt, &dbx_type);
+ spnt = find_symbol (dbx_type);
+ if (!spnt)
+ return; /*Dunno what this is*/
+ *pnt1 = '\0';
+ VMS_DBG_record (spnt, -1, S_GET_VALUE (sp), str);
+ *pnt1 = ':'; /* and restore the string */
+ return;
+}
+
+
+/* This routine parses a stabs entry to find the information required
+ to define a variable. It is used for global and static variables.
+ Basically we need to know the address of the symbol. With older
+ versions of the compiler, const symbols are treated differently, in
+ that if they are global they are written into the text psect. The
+ global symbol entry for such a const is actually written as a program
+ entry point (Yuk!!), so if we cannot find a symbol in the list of
+ psects, we must search the entry points as well. static consts are
+ even harder, since they are never assigned a memory address. The
+ compiler passes a stab to tell us the value, but I am not sure what
+ to do with it. */
+
+static void
+VMS_stab_parse (sp, expected_type, type1, type2, Text_Psect)
+ symbolS *sp;
+ int expected_type; /* char */
+ int type1, type2, Text_Psect;
+{
+ char *pnt;
+ char *pnt1;
+ char *str;
+ symbolS *sp1;
+ struct VMS_DBG_Symbol *spnt;
+ struct VMS_Symbol *vsp;
+ int dbx_type;
+
+ dbx_type = 0;
+ str = S_GET_NAME (sp);
+ pnt = (char *) strchr (str, ':');
+ if (!pnt)
+ return; /* no colon present */
+ pnt1 = pnt; /* save this for later*/
+ pnt++;
+ if (*pnt == expected_type)
+ {
+ pnt = cvt_integer (pnt + 1, &dbx_type);
+ spnt = find_symbol (dbx_type);
+ if (!spnt)
+ return; /*Dunno what this is*/
+ /*
+ * Now we need to search the symbol table to find the psect and
+ * offset for this variable.
+ */
+ *pnt1 = '\0';
+ vsp = VMS_Symbols;
+ while (vsp)
+ {
+ pnt = S_GET_NAME (vsp->Symbol);
+ if (pnt && *pnt++ == '_'
+ /* make sure name is the same and symbol type matches */
+ && strcmp (pnt, str) == 0
+ && (S_GET_RAW_TYPE (vsp->Symbol) == type1
+ || S_GET_RAW_TYPE (vsp->Symbol) == type2))
+ break;
+ vsp = vsp->Next;
+ }
+ if (vsp)
+ {
+ VMS_DBG_record (spnt, vsp->Psect_Index, vsp->Psect_Offset, str);
+ *pnt1 = ':'; /* and restore the string */
+ return;
+ }
+ /* The symbol was not in the symbol list, but it may be an
+ "entry point" if it was a constant. */
+ for (sp1 = symbol_rootP; sp1; sp1 = symbol_next (sp1))
+ {
+ /*
+ * Dispatch on STAB type
+ */
+ if (S_IS_DEBUG (sp1) || (S_GET_TYPE (sp1) != N_TEXT))
+ continue;
+ pnt = S_GET_NAME (sp1);
+ if (*pnt == '_')
+ pnt++;
+ if (strcmp (pnt, str) == 0)
+ {
+ if (!gave_compiler_message && expected_type == 'G')
+ {
+ char *long_const_msg = _("\
+***Warning - the assembly code generated by the compiler has placed \n\
+ global constant(s) in the text psect. These will not be available to \n\
+ other modules, since this is not the correct way to handle this. You \n\
+ have two options: 1) get a patched compiler that does not put global \n\
+ constants in the text psect, or 2) remove the 'const' keyword from \n\
+ definitions of global variables in your source module(s). Don't say \n\
+ I didn't warn you! \n");
+
+ as_tsktsk (long_const_msg);
+ gave_compiler_message = 1;
+ }
+ VMS_DBG_record (spnt,
+ Text_Psect,
+ S_GET_VALUE (sp1),
+ str);
+ *pnt1 = ':';
+ /* fool assembler to not output this as a routine in the TBT */
+ pnt1 = S_GET_NAME (sp1);
+ *pnt1 = 'L';
+ S_SET_NAME (sp1, pnt1);
+ return;
+ }
+ }
+ }
+ *pnt1 = ':'; /* and restore the string */
+ return;
+}
+
+
+/* Simpler interfaces into VMS_stab_parse(). */
+
+static void
+VMS_GSYM_Parse (sp, Text_Psect)
+ symbolS *sp;
+ int Text_Psect;
+{ /* Global variables */
+ VMS_stab_parse (sp, 'G', (N_UNDF | N_EXT), (N_DATA | N_EXT), Text_Psect);
+}
+
+static void
+VMS_LCSYM_Parse (sp, Text_Psect)
+ symbolS *sp;
+ int Text_Psect;
+{ /* Static symbols - uninitialized */
+ VMS_stab_parse (sp, 'S', N_BSS, -1, Text_Psect);
+}
+
+static void
+VMS_STSYM_Parse (sp, Text_Psect)
+ symbolS *sp;
+ int Text_Psect;
+{ /* Static symbols - initialized */
+ VMS_stab_parse (sp, 'S', N_DATA, -1, Text_Psect);
+}
+
+
+/* For register symbols, we must figure out what range of addresses
+ within the psect are valid. We will use the brackets in the stab
+ directives to give us guidance as to the PC range that this variable
+ is in scope. I am still not completely comfortable with this but
+ as I learn more, I seem to get a better handle on what is going on.
+ Caveat Emptor. */
+
+static void
+VMS_RSYM_Parse (sp, Current_Routine, Text_Psect)
+ symbolS *sp, *Current_Routine;
+ int Text_Psect;
+{
+ symbolS *symbolP;
+ struct VMS_DBG_Symbol *spnt;
+ char *pnt;
+ char *pnt1;
+ char *str;
+ int dbx_type;
+ int len;
+ int i = 0;
+ int bcnt = 0;
+ int Min_Offset = -1; /* min PC of validity */
+ int Max_Offset = 0; /* max PC of validity */
+
+ for (symbolP = sp; symbolP; symbolP = symbol_next (symbolP))
+ {
+ /*
+ * Dispatch on STAB type
+ */
+ switch (S_GET_RAW_TYPE (symbolP))
+ {
+ case N_LBRAC:
+ if (bcnt++ == 0)
+ Min_Offset = S_GET_VALUE (symbolP);
+ break;
+ case N_RBRAC:
+ if (--bcnt == 0)
+ Max_Offset = S_GET_VALUE (symbolP) - 1;
+ break;
+ }
+ if ((Min_Offset != -1) && (bcnt == 0))
+ break;
+ if (S_GET_RAW_TYPE (symbolP) == N_FUN)
+ {
+ pnt = (char *) strchr (S_GET_NAME (symbolP), ':') + 1;
+ if (*pnt == 'F' || *pnt == 'f') break;
+ }
+ }
+
+ /* Check to see that the addresses were defined. If not, then there
+ were no brackets in the function, and we must try to search for
+ the next function. Since functions can be in any order, we should
+ search all of the symbol list to find the correct ending address. */
+ if (Min_Offset == -1)
+ {
+ int Max_Source_Offset;
+ int This_Offset;
+
+ Min_Offset = S_GET_VALUE (sp);
+ Max_Source_Offset = Min_Offset; /* just in case no N_SLINEs found */
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next (symbolP))
+ switch (S_GET_RAW_TYPE (symbolP))
+ {
+ case N_TEXT | N_EXT:
+ This_Offset = S_GET_VALUE (symbolP);
+ if (This_Offset > Min_Offset && This_Offset < Max_Offset)
+ Max_Offset = This_Offset;
+ break;
+ case N_SLINE:
+ This_Offset = S_GET_VALUE (symbolP);
+ if (This_Offset > Max_Source_Offset)
+ Max_Source_Offset = This_Offset;
+ break;
+ }
+ /* If this is the last routine, then we use the PC of the last source
+ line as a marker of the max PC for which this reg is valid. */
+ if (Max_Offset == 0x7fffffff)
+ Max_Offset = Max_Source_Offset;
+ }
+
+ dbx_type = 0;
+ str = S_GET_NAME (sp);
+ if ((pnt = (char *) strchr (str, ':')) == 0)
+ return; /* no colon present */
+ pnt1 = pnt; /* save this for later*/
+ pnt++;
+ if (*pnt != 'r')
+ return;
+ pnt = cvt_integer (pnt + 1, &dbx_type);
+ spnt = find_symbol (dbx_type);
+ if (!spnt)
+ return; /*Dunno what this is yet*/
+ *pnt1 = '\0';
+ pnt = fix_name (S_GET_NAME (sp)); /* if there are bad characters in name, convert them */
+ len = strlen (pnt);
+ Local[i++] = 25 + len;
+ Local[i++] = spnt->VMS_type;
+ Local[i++] = DST_K_VFLAGS_TVS; /* trailing value specified */
+ COPY_LONG (&Local[i], 1 + len); /* relative offset, beyond name */
+ i += 4;
+ Local[i++] = len; /* name length (ascic prefix) */
+ while (*pnt != '\0')
+ Local[i++] = *pnt++;
+ Local[i++] = DST_K_VS_FOLLOWS; /* value specification follows */
+ COPY_SHORT (&Local[i], 15); /* length of rest of record */
+ i += 2;
+ Local[i++] = DST_K_VS_ALLOC_SPLIT; /* split lifetime */
+ Local[i++] = 1; /* one binding follows */
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ i = 0;
+ VMS_Set_Data (Text_Psect, Min_Offset, OBJ_S_C_DBG, 1);
+ VMS_Set_Data (Text_Psect, Max_Offset, OBJ_S_C_DBG, 1);
+ Local[i++] = DST_K_VALKIND_REG; /* nested value spec */
+ COPY_LONG (&Local[i], S_GET_VALUE (sp));
+ i += 4;
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ *pnt1 = ':';
+ if (spnt->VMS_type == DBG_S_C_ADVANCED_TYPE)
+ generate_suffix (spnt, 0);
+}
+
+
+/* This function examines a structure definition, checking all of the elements
+ to make sure that all of them are fully defined. The only thing that we
+ kick out are arrays of undefined structs, since we do not know how big
+ they are. All others we can handle with a normal forward reference. */
+
+static int
+forward_reference (pnt)
+ char *pnt;
+{
+ struct VMS_DBG_Symbol *spnt, *spnt1;
+ int i;
+
+ pnt = cvt_integer (pnt + 1, &i);
+ if (*pnt == ';')
+ return 0; /* no forward references */
+ do
+ {
+ pnt = (char *) strchr (pnt, ':');
+ pnt = cvt_integer (pnt + 1, &i);
+ spnt = find_symbol (i);
+ while (spnt && (spnt->advanced == POINTER || spnt->advanced == ARRAY))
+ {
+ spnt1 = find_symbol (spnt->type2);
+ if (spnt->advanced == ARRAY && !spnt1)
+ return 1;
+ spnt = spnt1;
+ }
+ pnt = cvt_integer (pnt + 1, &i);
+ pnt = cvt_integer (pnt + 1, &i);
+ } while (*++pnt != ';');
+ return 0; /* no forward refences found */
+}
+
+
+/* Used to check a single element of a structure on the final pass. */
+
+static int
+final_forward_reference (spnt)
+ struct VMS_DBG_Symbol *spnt;
+{
+ struct VMS_DBG_Symbol *spnt1;
+
+ while (spnt && (spnt->advanced == POINTER || spnt->advanced == ARRAY))
+ {
+ spnt1 = find_symbol (spnt->type2);
+ if (spnt->advanced == ARRAY && !spnt1)
+ return 1;
+ spnt = spnt1;
+ }
+ return 0; /* no forward refences found */
+}
+
+
+/* This routine parses the stabs directives to find any definitions of dbx
+ type numbers. It makes a note of all of them, creating a structure
+ element of VMS_DBG_Symbol that describes it. This also generates the
+ info for the debugger that describes the struct/union/enum, so that
+ further references to these data types will be by number
+
+ We have to process pointers right away, since there can be references
+ to them later in the same stabs directive. We cannot have forward
+ references to pointers, (but we can have a forward reference to a
+ pointer to a structure/enum/union) and this is why we process them
+ immediately. After we process the pointer, then we search for defs
+ that are nested even deeper.
+
+ 8/15/92: We have to process arrays right away too, because there can
+ be multiple references to identical array types in one structure
+ definition, and only the first one has the definition. */
+
+static int
+VMS_typedef_parse (str)
+ char *str;
+{
+ char *pnt;
+ char *pnt1;
+ const char *pnt2;
+ int i;
+ int dtype;
+ struct forward_ref *fpnt;
+ int i1, i2, i3, len;
+ struct VMS_DBG_Symbol *spnt;
+ struct VMS_DBG_Symbol *spnt1;
+
+ /* check for any nested def's */
+ pnt = (char *) strchr (str + 1, '=');
+ if (pnt && str[1] != '*' && (str[1] != 'a' || str[2] != 'r')
+ && VMS_typedef_parse (pnt) == 1)
+ return 1;
+ /* now find dbx_type of entry */
+ pnt = str - 1;
+ if (*pnt == 'c')
+ { /* check for static constants */
+ *str = '\0'; /* for now we ignore them */
+ return 0;
+ }
+ while ((*pnt <= '9') && (*pnt >= '0'))
+ pnt--;
+ pnt++; /* and get back to the number */
+ cvt_integer (pnt, &i1);
+ spnt = find_symbol (i1);
+ /* first see if this has been defined already, due to forward reference */
+ if (!spnt)
+ {
+ i2 = SYMTYP_HASH (i1);
+ spnt = (struct VMS_DBG_Symbol *) xmalloc (sizeof (struct VMS_DBG_Symbol));
+ spnt->next = VMS_Symbol_type_list[i2];
+ VMS_Symbol_type_list[i2] = spnt;
+ spnt->dbx_type = i1; /* and save the type */
+ spnt->type2 = spnt->VMS_type = spnt->data_size = 0;
+ spnt->index_min = spnt->index_max = spnt->struc_numb = 0;
+ }
+ /*
+ * For structs and unions, do a partial parse, otherwise we sometimes get
+ * circular definitions that are impossible to resolve. We read enough
+ * info so that any reference to this type has enough info to be resolved.
+ */
+ pnt = str + 1; /* point to character past equal sign */
+ if (*pnt >= '0' && *pnt <= '9')
+ {
+ if (type_check ("void"))
+ { /* this is the void symbol */
+ *str = '\0';
+ spnt->advanced = VOID;
+ return 0;
+ }
+ if (type_check ("unknown type"))
+ {
+ *str = '\0';
+ spnt->advanced = UNKNOWN;
+ return 0;
+ }
+ pnt1 = cvt_integer (pnt, &i1);
+ if (i1 != spnt->dbx_type)
+ {
+ spnt->advanced = ALIAS;
+ spnt->type2 = i1;
+ strcpy (str, pnt1);
+ return 0;
+ }
+ as_tsktsk (_("debugginer output: %d is an unknown untyped variable."),
+ spnt->dbx_type);
+ return 1; /* do not know what this is */
+ }
+
+ pnt = str + 1; /* point to character past equal sign */
+ switch (*pnt)
+ {
+ case 'r':
+ spnt->advanced = BASIC;
+ if (type_check ("int"))
+ {
+ spnt->VMS_type = DBG_S_C_SLINT;
+ spnt->data_size = 4;
+ }
+ else if (type_check ("long int"))
+ {
+ spnt->VMS_type = DBG_S_C_SLINT;
+ spnt->data_size = 4;
+ }
+ else if (type_check ("unsigned int"))
+ {
+ spnt->VMS_type = DBG_S_C_ULINT;
+ spnt->data_size = 4;
+ }
+ else if (type_check ("long unsigned int"))
+ {
+ spnt->VMS_type = DBG_S_C_ULINT;
+ spnt->data_size = 4;
+ }
+ else if (type_check ("short int"))
+ {
+ spnt->VMS_type = DBG_S_C_SSINT;
+ spnt->data_size = 2;
+ }
+ else if (type_check ("short unsigned int"))
+ {
+ spnt->VMS_type = DBG_S_C_USINT;
+ spnt->data_size = 2;
+ }
+ else if (type_check ("char"))
+ {
+ spnt->VMS_type = DBG_S_C_SCHAR;
+ spnt->data_size = 1;
+ }
+ else if (type_check ("signed char"))
+ {
+ spnt->VMS_type = DBG_S_C_SCHAR;
+ spnt->data_size = 1;
+ }
+ else if (type_check ("unsigned char"))
+ {
+ spnt->VMS_type = DBG_S_C_UCHAR;
+ spnt->data_size = 1;
+ }
+ else if (type_check ("float"))
+ {
+ spnt->VMS_type = DBG_S_C_REAL4;
+ spnt->data_size = 4;
+ }
+ else if (type_check ("double"))
+ {
+ spnt->VMS_type = vax_g_doubles ? DBG_S_C_REAL8_G : DBG_S_C_REAL8;
+ spnt->data_size = 8;
+ }
+ else if (type_check ("long double"))
+ {
+ /* same as double, at least for now */
+ spnt->VMS_type = vax_g_doubles ? DBG_S_C_REAL8_G : DBG_S_C_REAL8;
+ spnt->data_size = 8;
+ }
+ else if (type_check ("long long int"))
+ {
+ spnt->VMS_type = DBG_S_C_SQUAD; /* signed quadword */
+ spnt->data_size = 8;
+ }
+ else if (type_check ("long long unsigned int"))
+ {
+ spnt->VMS_type = DBG_S_C_UQUAD; /* unsigned quadword */
+ spnt->data_size = 8;
+ }
+ else if (type_check ("complex float"))
+ {
+ spnt->VMS_type = DBG_S_C_COMPLX4;
+ spnt->data_size = 2 * 4;
+ }
+ else if (type_check ("complex double"))
+ {
+ spnt->VMS_type = vax_g_doubles ? DBG_S_C_COMPLX8_G : DBG_S_C_COMPLX8;
+ spnt->data_size = 2 * 8;
+ }
+ else if (type_check ("complex long double"))
+ {
+ /* same as complex double, at least for now */
+ spnt->VMS_type = vax_g_doubles ? DBG_S_C_COMPLX8_G : DBG_S_C_COMPLX8;
+ spnt->data_size = 2 * 8;
+ }
+ else
+ {
+ /* [pr]
+ * Shouldn't get here, but if we do, something
+ * more substantial ought to be done...
+ */
+ spnt->VMS_type = 0;
+ spnt->data_size = 0;
+ }
+ if (spnt->VMS_type != 0)
+ setup_basic_type (spnt);
+ pnt1 = (char *) strchr (str, ';') + 1;
+ break;
+ case 's':
+ case 'u':
+ spnt->advanced = (*pnt == 's') ? STRUCT : UNION;
+ spnt->VMS_type = DBG_S_C_ADVANCED_TYPE;
+ pnt1 = cvt_integer (pnt + 1, &spnt->data_size);
+ if (!final_pass && forward_reference (pnt))
+ {
+ spnt->struc_numb = -1;
+ return 1;
+ }
+ spnt->struc_numb = ++structure_count;
+ pnt1--;
+ pnt = get_struct_name (str);
+ VMS_Def_Struct (spnt->struc_numb);
+ i = 0;
+ for (fpnt = f_ref_root; fpnt; fpnt = fpnt->next)
+ if (fpnt->dbx_type == spnt->dbx_type)
+ {
+ fpnt->resolved = 'Y';
+ VMS_Set_Struct (fpnt->struc_numb);
+ VMS_Store_Struct (spnt->struc_numb);
+ i++;
+ }
+ if (i > 0)
+ VMS_Set_Struct (spnt->struc_numb);
+ i = 0;
+ Local[i++] = 11 + strlen (pnt);
+ Local[i++] = DBG_S_C_STRUCT_START;
+ Local[i++] = DST_K_VFLAGS_NOVAL; /* structure definition only */
+ COPY_LONG (&Local[i], 0L); /* hence value is unused */
+ i += 4;
+ Local[i++] = strlen (pnt);
+ pnt2 = pnt;
+ while (*pnt2 != '\0')
+ Local[i++] = *pnt2++;
+ i2 = spnt->data_size * 8; /* number of bits */
+ COPY_LONG (&Local[i], i2);
+ i += 4;
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ i = 0;
+ if (pnt != symbol_name)
+ {
+ pnt += strlen (pnt);
+ *pnt = ':';
+ } /* replace colon for later */
+ while (*++pnt1 != ';')
+ {
+ pnt = (char *) strchr (pnt1, ':');
+ *pnt = '\0';
+ pnt2 = pnt1;
+ pnt1 = cvt_integer (pnt + 1, &dtype);
+ pnt1 = cvt_integer (pnt1 + 1, &i2);
+ pnt1 = cvt_integer (pnt1 + 1, &i3);
+ spnt1 = find_symbol (dtype);
+ len = strlen (pnt2);
+ if (spnt1 && (spnt1->advanced == BASIC || spnt1->advanced == ENUM)
+ && ((i3 != spnt1->data_size * 8) || (i2 % 8 != 0)))
+ { /* bitfield */
+ if (USE_BITSTRING_DESCRIPTOR (spnt1))
+ {
+ /* This uses a type descriptor, which doesn't work if
+ the enclosing structure has been placed in a register.
+ Also, enum bitfields degenerate to simple integers. */
+ int unsigned_type = (spnt1->VMS_type == DBG_S_C_ULINT
+ || spnt1->VMS_type == DBG_S_C_USINT
+ || spnt1->VMS_type == DBG_S_C_UCHAR
+ || spnt1->VMS_type == DBG_S_C_UQUAD
+ || spnt1->advanced == ENUM); /* (approximate) */
+ Apoint = 0;
+ fpush (19 + len, 1);
+ fpush (unsigned_type ? DBG_S_C_UBITU : DBG_S_C_SBITU, 1);
+ fpush (DST_K_VFLAGS_DSC, 1); /* specified by descriptor */
+ fpush (1 + len, 4); /* relative offset to descriptor */
+ fpush (len, 1); /* length byte (ascic prefix) */
+ while (*pnt2 != '\0') /* name bytes */
+ fpush (*pnt2++, 1);
+ fpush (i3, 2); /* dsc length == size of bitfield */
+ /* dsc type == un?signed bitfield */
+ fpush (unsigned_type ? DBG_S_C_UBITU : DBG_S_C_SBITU, 1);
+ fpush (DSC_K_CLASS_UBS, 1); /* dsc class == unaligned bitstring */
+ fpush (0x00, 4); /* dsc pointer == zeroes */
+ fpush (i2, 4); /* start position */
+ VMS_Store_Immediate_Data (Asuffix, Apoint, OBJ_S_C_DBG);
+ Apoint = 0;
+ }
+ else
+ {
+ /* Use a "novel length" type specification, which works
+ right for register structures and for enum bitfields
+ but results in larger object modules. */
+ Local[i++] = 7 + len;
+ Local[i++] = DBG_S_C_ADVANCED_TYPE; /* type spec follows */
+ Local[i++] = DBG_S_C_STRUCT_ITEM; /* value is a bit offset */
+ COPY_LONG (&Local[i], i2); /* bit offset */
+ i += 4;
+ Local[i++] = strlen (pnt2);
+ while (*pnt2 != '\0')
+ Local[i++] = *pnt2++;
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ i = 0;
+ bitfield_suffix (spnt1, i3);
+ }
+ }
+ else
+ { /* not a bitfield */
+ /* check if this is a forward reference */
+ if (final_pass && final_forward_reference (spnt1))
+ {
+ as_tsktsk (_("debugger output: structure element `%s' has undefined type"),
+ pnt2);
+ continue;
+ }
+ Local[i++] = 7 + len;
+ Local[i++] = spnt1 ? spnt1->VMS_type : DBG_S_C_ADVANCED_TYPE;
+ Local[i++] = DBG_S_C_STRUCT_ITEM;
+ COPY_LONG (&Local[i], i2); /* bit offset */
+ i += 4;
+ Local[i++] = strlen (pnt2);
+ while (*pnt2 != '\0')
+ Local[i++] = *pnt2++;
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ i = 0;
+ if (!spnt1)
+ generate_suffix (spnt1, dtype);
+ else if (spnt1->VMS_type == DBG_S_C_ADVANCED_TYPE)
+ generate_suffix (spnt1, 0);
+ }
+ }
+ pnt1++;
+ Local[i++] = 0x01; /* length byte */
+ Local[i++] = DBG_S_C_STRUCT_END;
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ i = 0;
+ break;
+ case 'e':
+ spnt->advanced = ENUM;
+ spnt->VMS_type = DBG_S_C_ADVANCED_TYPE;
+ spnt->struc_numb = ++structure_count;
+ spnt->data_size = 4;
+ VMS_Def_Struct (spnt->struc_numb);
+ i = 0;
+ for (fpnt = f_ref_root; fpnt; fpnt = fpnt->next)
+ if (fpnt->dbx_type == spnt->dbx_type)
+ {
+ fpnt->resolved = 'Y';
+ VMS_Set_Struct (fpnt->struc_numb);
+ VMS_Store_Struct (spnt->struc_numb);
+ i++;
+ }
+ if (i > 0)
+ VMS_Set_Struct (spnt->struc_numb);
+ i = 0;
+ len = strlen (symbol_name);
+ Local[i++] = 3 + len;
+ Local[i++] = DBG_S_C_ENUM_START;
+ Local[i++] = 4 * 8; /* enum values are 32 bits */
+ Local[i++] = len;
+ pnt2 = symbol_name;
+ while (*pnt2 != '\0')
+ Local[i++] = *pnt2++;
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ i = 0;
+ while (*++pnt != ';')
+ {
+ pnt1 = (char *) strchr (pnt, ':');
+ *pnt1++ = '\0';
+ pnt1 = cvt_integer (pnt1, &i1);
+ len = strlen (pnt);
+ Local[i++] = 7 + len;
+ Local[i++] = DBG_S_C_ENUM_ITEM;
+ Local[i++] = DST_K_VALKIND_LITERAL;
+ COPY_LONG (&Local[i], i1);
+ i += 4;
+ Local[i++] = len;
+ pnt2 = pnt;
+ while (*pnt != '\0')
+ Local[i++] = *pnt++;
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ i = 0;
+ pnt = pnt1; /* Skip final semicolon */
+ }
+ Local[i++] = 0x01; /* len byte */
+ Local[i++] = DBG_S_C_ENUM_END;
+ VMS_Store_Immediate_Data (Local, i, OBJ_S_C_DBG);
+ i = 0;
+ pnt1 = pnt + 1;
+ break;
+ case 'a':
+ spnt->advanced = ARRAY;
+ spnt->VMS_type = DBG_S_C_ADVANCED_TYPE;
+ pnt = (char *) strchr (pnt, ';');
+ if (!pnt)
+ return 1;
+ pnt1 = cvt_integer (pnt + 1, &spnt->index_min);
+ pnt1 = cvt_integer (pnt1 + 1, &spnt->index_max);
+ pnt1 = cvt_integer (pnt1 + 1, &spnt->type2);
+ pnt = (char *) strchr (str + 1, '=');
+ if (pnt && VMS_typedef_parse (pnt) == 1)
+ return 1;
+ break;
+ case 'f':
+ spnt->advanced = FUNCTION;
+ spnt->VMS_type = DBG_S_C_FUNCTION_ADDR;
+ /* this masquerades as a basic type*/
+ spnt->data_size = 4;
+ pnt1 = cvt_integer (pnt + 1, &spnt->type2);
+ break;
+ case '*':
+ spnt->advanced = POINTER;
+ spnt->VMS_type = DBG_S_C_ADVANCED_TYPE;
+ spnt->data_size = 4;
+ pnt1 = cvt_integer (pnt + 1, &spnt->type2);
+ pnt = (char *) strchr (str + 1, '=');
+ if (pnt && VMS_typedef_parse (pnt) == 1)
+ return 1;
+ break;
+ default:
+ spnt->advanced = UNKNOWN;
+ spnt->VMS_type = 0;
+ as_tsktsk (_("debugger output: %d is an unknown type of variable."),
+ spnt->dbx_type);
+ return 1; /* unable to decipher */
+ }
+ /* This removes the evidence of the definition so that the outer levels
+ of parsing do not have to worry about it. */
+ pnt = str;
+ while (*pnt1 != '\0')
+ *pnt++ = *pnt1++;
+ *pnt = '\0';
+ return 0;
+}
+
+
+/* This is the root routine that parses the stabs entries for definitions.
+ it calls VMS_typedef_parse, which can in turn call itself. We need to
+ be careful, since sometimes there are forward references to other symbol
+ types, and these cannot be resolved until we have completed the parse.
+
+ Also check and see if we are using continuation stabs, if we are, then
+ paste together the entire contents of the stab before we pass it to
+ VMS_typedef_parse. */
+
+static void
+VMS_LSYM_Parse ()
+{
+ char *pnt;
+ char *pnt1;
+ char *pnt2;
+ char *str;
+ char *parse_buffer = 0;
+ char fixit[10];
+ int incomplete, pass, incom1;
+ struct forward_ref *fpnt;
+ symbolS *sp;
+
+ pass = 0;
+ final_pass = 0;
+ incomplete = 0;
+ do
+ {
+ incom1 = incomplete;
+ incomplete = 0;
+ for (sp = symbol_rootP; sp; sp = symbol_next (sp))
+ {
+ /*
+ * Deal with STAB symbols
+ */
+ if (S_IS_DEBUG (sp))
+ {
+ /*
+ * Dispatch on STAB type
+ */
+ switch (S_GET_RAW_TYPE (sp))
+ {
+ case N_GSYM:
+ case N_LCSYM:
+ case N_STSYM:
+ case N_PSYM:
+ case N_RSYM:
+ case N_LSYM:
+ case N_FUN: /*sometimes these contain typedefs*/
+ str = S_GET_NAME (sp);
+ symbol_name = str;
+ pnt = str + strlen (str) - 1;
+ if (*pnt == '?') /* Continuation stab. */
+ {
+ symbolS *spnext;
+ int tlen = 0;
+
+ spnext = sp;
+ do {
+ tlen += strlen (str) - 1;
+ spnext = symbol_next (spnext);
+ str = S_GET_NAME (spnext);
+ pnt = str + strlen (str) - 1;
+ } while (*pnt == '?');
+ tlen += strlen (str);
+ parse_buffer = (char *) xmalloc (tlen + 1);
+ strcpy (parse_buffer, S_GET_NAME (sp));
+ pnt2 = parse_buffer + strlen (parse_buffer) - 1;
+ *pnt2 = '\0';
+ spnext = sp;
+ do {
+ spnext = symbol_next (spnext);
+ str = S_GET_NAME (spnext);
+ strcat (pnt2, str);
+ pnt2 += strlen (str) - 1;
+ *str = '\0'; /* Erase this string */
+ /* S_SET_NAME (spnext, str); */
+ if (*pnt2 != '?') break;
+ *pnt2 = '\0';
+ } while (1);
+ str = parse_buffer;
+ symbol_name = str;
+ }
+ if ((pnt = (char *) strchr (str, ':')) != 0)
+ {
+ *pnt = '\0';
+ pnt1 = pnt + 1;
+ if ((pnt2 = (char *) strchr (pnt1, '=')) != 0)
+ incomplete += VMS_typedef_parse (pnt2);
+ if (parse_buffer)
+ {
+ /* At this point the parse buffer should just
+ contain name:nn. If it does not, then we
+ are in real trouble. Anyway, this is always
+ shorter than the original line. */
+ pnt2 = S_GET_NAME (sp);
+ strcpy (pnt2, parse_buffer);
+ /* S_SET_NAME (sp, pnt2); */
+ free (parse_buffer), parse_buffer = 0;
+ }
+ *pnt = ':'; /* put back colon to restore dbx_type */
+ }
+ break;
+ } /*switch*/
+ } /* if */
+ } /*for*/
+ pass++;
+ /*
+ * Make one last pass, if needed, and define whatever we can
+ * that is left.
+ */
+ if (final_pass == 0 && incomplete == incom1)
+ {
+ final_pass = 1;
+ incom1++; /* Force one last pass through */
+ }
+ } while (incomplete != 0 && incomplete != incom1);
+ /* repeat until all refs resolved if possible */
+/* if (pass > 1) printf (" Required %d passes\n", pass); */
+ if (incomplete != 0)
+ {
+ as_tsktsk (_("debugger output: Unable to resolve %d circular references."),
+ incomplete);
+ }
+ fpnt = f_ref_root;
+ symbol_name = "\0";
+ while (fpnt)
+ {
+ if (fpnt->resolved != 'Y')
+ {
+ if (find_symbol (fpnt->dbx_type))
+ {
+ as_tsktsk (_("debugger forward reference error, dbx type %d"),
+ fpnt->dbx_type);
+ break;
+ }
+ fixit[0] = 0;
+ sprintf (&fixit[1], "%d=s4;", fpnt->dbx_type);
+ pnt2 = (char *) strchr (&fixit[1], '=');
+ VMS_typedef_parse (pnt2);
+ }
+ fpnt = fpnt->next;
+ }
+}
+
+
+static void
+Define_Local_Symbols (s0P, s2P, Current_Routine, Text_Psect)
+ symbolS *s0P, *s2P;
+ symbolS *Current_Routine;
+ int Text_Psect;
+{
+ symbolS *s1P; /* each symbol from s0P .. s2P (exclusive) */
+
+ for (s1P = symbol_next (s0P); s1P != s2P; s1P = symbol_next (s1P))
+ {
+ if (!s1P)
+ break; /* and return */
+ if (S_GET_RAW_TYPE (s1P) == N_FUN)
+ {
+ char *pnt = (char *) strchr (S_GET_NAME (s1P), ':') + 1;
+ if (*pnt == 'F' || *pnt == 'f') break;
+ }
+ if (!S_IS_DEBUG (s1P))
+ continue;
+ /*
+ * Dispatch on STAB type
+ */
+ switch (S_GET_RAW_TYPE (s1P))
+ {
+ default:
+ continue; /* not left or right brace */
+
+ case N_LSYM:
+ case N_PSYM:
+ VMS_local_stab_Parse (s1P);
+ break;
+
+ case N_RSYM:
+ VMS_RSYM_Parse (s1P, Current_Routine, Text_Psect);
+ break;
+ } /*switch*/
+ } /* for */
+}
+
+
+/* This function crawls the symbol chain searching for local symbols that
+ need to be described to the debugger. When we enter a new scope with
+ a "{", it creates a new "block", which helps the debugger keep track
+ of which scope we are currently in. */
+
+static symbolS *
+Define_Routine (s0P, Level, Current_Routine, Text_Psect)
+ symbolS *s0P;
+ int Level;
+ symbolS *Current_Routine;
+ int Text_Psect;
+{
+ symbolS *s1P;
+ valueT Offset;
+ int rcount = 0;
+
+ for (s1P = symbol_next (s0P); s1P != 0; s1P = symbol_next (s1P))
+ {
+ if (S_GET_RAW_TYPE (s1P) == N_FUN)
+ {
+ char *pnt = (char *) strchr (S_GET_NAME (s1P), ':') + 1;
+ if (*pnt == 'F' || *pnt == 'f') break;
+ }
+ if (!S_IS_DEBUG (s1P))
+ continue;
+ /*
+ * Dispatch on STAB type
+ */
+ switch (S_GET_RAW_TYPE (s1P))
+ {
+ default:
+ continue; /* not left or right brace */
+
+ case N_LBRAC:
+ if (Level != 0)
+ {
+ char str[10];
+ sprintf (str, "$%d", rcount++);
+ VMS_TBT_Block_Begin (s1P, Text_Psect, str);
+ }
+ Offset = S_GET_VALUE (s1P); /* side-effect: fully resolve symbol */
+ Define_Local_Symbols (s0P, s1P, Current_Routine, Text_Psect);
+ s1P = Define_Routine (s1P, Level + 1, Current_Routine, Text_Psect);
+ if (Level != 0)
+ VMS_TBT_Block_End (S_GET_VALUE (s1P) - Offset);
+ s0P = s1P;
+ break;
+
+ case N_RBRAC:
+ return s1P;
+ } /*switch*/
+ } /* for */
+
+ /* We end up here if there were no brackets in this function.
+ Define everything. */
+ Define_Local_Symbols (s0P, (symbolS *)0, Current_Routine, Text_Psect);
+ return s1P;
+}
+
+
+#ifndef VMS
+#include <sys/types.h>
+#include <time.h>
+static void get_VMS_time_on_unix PARAMS ((char *));
+
+/* Manufacture a VMS-like time string on a Unix based system. */
+static void
+get_VMS_time_on_unix (Now)
+ char *Now;
+{
+ char *pnt;
+ time_t timeb;
+
+ time (&timeb);
+ pnt = ctime (&timeb);
+ pnt[3] = 0;
+ pnt[7] = 0;
+ pnt[10] = 0;
+ pnt[16] = 0;
+ pnt[24] = 0;
+ sprintf (Now, "%2s-%3s-%s %s", pnt + 8, pnt + 4, pnt + 20, pnt + 11);
+}
+#endif /* not VMS */
+
+
+/* Write the MHD (Module Header) records. */
+
+static void
+Write_VMS_MHD_Records ()
+{
+ register const char *cp;
+ register char *cp1;
+ register int i;
+#ifdef VMS
+ struct { unsigned short len, mbz; char *ptr; } Descriptor;
+#endif
+ char Now[17+1];
+
+ /* We are writing a module header record. */
+ Set_VMS_Object_File_Record (OBJ_S_C_HDR);
+ /*
+ * ***************************
+ * *MAIN MODULE HEADER RECORD*
+ * ***************************
+ */
+ /* Store record type and header type. */
+ PUT_CHAR (OBJ_S_C_HDR);
+ PUT_CHAR (MHD_S_C_MHD);
+ /* Structure level is 0. */
+ PUT_CHAR (OBJ_S_C_STRLVL);
+ /* Maximum record size is size of the object record buffer. */
+ PUT_SHORT (sizeof (Object_Record_Buffer));
+
+ /*
+ * FIXME: module name and version should be user
+ * specifiable via `.ident' and/or `#pragma ident'.
+ */
+
+ /* Get module name (the FILENAME part of the object file). */
+ cp = out_file_name;
+ cp1 = Module_Name;
+ while (*cp)
+ {
+ if (*cp == ']' || *cp == '>' || *cp == ':' || *cp == '/')
+ {
+ cp1 = Module_Name;
+ cp++;
+ continue;
+ }
+ *cp1++ = islower (*cp) ? toupper (*cp++) : *cp++;
+ }
+ *cp1 = '\0';
+
+ /* Limit it to 31 characters and store in the object record. */
+ while (--cp1 >= Module_Name)
+ if (*cp1 == '.')
+ *cp1 = '\0';
+ if (strlen (Module_Name) > 31)
+ {
+ if (flag_hash_long_names)
+ as_tsktsk (_("Module name truncated: %s\n"), Module_Name);
+ Module_Name[31] = '\0';
+ }
+ PUT_COUNTED_STRING (Module_Name);
+ /* Module Version is "V1.0". */
+ PUT_COUNTED_STRING ("V1.0");
+ /* Creation time is "now" (17 chars of time string): "dd-MMM-yyyy hh:mm". */
+#ifndef VMS
+ get_VMS_time_on_unix (Now);
+#else /* VMS */
+ Descriptor.len = sizeof Now - 1;
+ Descriptor.mbz = 0; /* type & class unspecified */
+ Descriptor.ptr = Now;
+ (void) sys$asctim ((unsigned short *)0, &Descriptor, (long *)0, 0);
+#endif /* VMS */
+ for (i = 0; i < 17; i++)
+ PUT_CHAR (Now[i]);
+ /* Patch time is "never" (17 zeros). */
+ for (i = 0; i < 17; i++)
+ PUT_CHAR (0);
+ /* Force this to be a separate output record. */
+ Flush_VMS_Object_Record_Buffer ();
+
+ /*
+ * *************************
+ * *LANGUAGE PROCESSOR NAME*
+ * *************************
+ */
+ /* Store record type and header type. */
+ PUT_CHAR (OBJ_S_C_HDR);
+ PUT_CHAR (MHD_S_C_LNM);
+ /*
+ * Store language processor name and version (not a counted string!).
+ *
+ * This is normally supplied by the gcc driver for the command line
+ * which invokes gas. If absent, we fall back to gas's version.
+ */
+ cp = compiler_version_string;
+ if (cp == 0)
+ {
+ cp = "GNU AS V";
+ while (*cp)
+ PUT_CHAR (*cp++);
+ cp = VERSION;
+ }
+ while (*cp >= ' ')
+ PUT_CHAR (*cp++);
+ /* Force this to be a separate output record. */
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+
+/* Write the EOM (End Of Module) record. */
+
+static void
+Write_VMS_EOM_Record (Psect, Offset)
+ int Psect;
+ valueT Offset;
+{
+ /*
+ * We are writing an end-of-module record
+ * (this assumes that the entry point will always be in a psect
+ * represented by a single byte, which is the case for code in
+ * Text_Psect==0)
+ */
+ Set_VMS_Object_File_Record (OBJ_S_C_EOM);
+ PUT_CHAR (OBJ_S_C_EOM); /* Record type. */
+ PUT_CHAR (0); /* Error severity level (we ignore it). */
+ /*
+ * Store the entry point, if it exists
+ */
+ if (Psect >= 0)
+ {
+ PUT_CHAR (Psect);
+ PUT_LONG (Offset);
+ }
+ /* Flush the record; this will be our final output. */
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+
+/* this hash routine borrowed from GNU-EMACS, and strengthened slightly ERY*/
+
+static int
+hash_string (ptr)
+ const char *ptr;
+{
+ register const unsigned char *p = (unsigned char *) ptr;
+ register const unsigned char *end = p + strlen (ptr);
+ register unsigned char c;
+ register int hash = 0;
+
+ while (p != end)
+ {
+ c = *p++;
+ hash = ((hash << 3) + (hash << 15) + (hash >> 28) + c);
+ }
+ return hash;
+}
+
+/*
+ * Generate a Case-Hacked VMS symbol name (limited to 31 chars)
+ */
+static void
+VMS_Case_Hack_Symbol (In, Out)
+ register const char *In;
+ register char *Out;
+{
+ long int init;
+ long int result;
+ char *pnt = 0;
+ char *new_name;
+ const char *old_name;
+ register int i;
+ int destructor = 0; /*hack to allow for case sens in a destructor*/
+ int truncate = 0;
+ int Case_Hack_Bits = 0;
+ int Saw_Dollar = 0;
+ static char Hex_Table[16] =
+ {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+ /*
+ * Kill any leading "_"
+ */
+ if ((In[0] == '_') && ((In[1] > '9') || (In[1] < '0')))
+ In++;
+
+ new_name = Out; /* save this for later*/
+
+#if barfoo /* Dead code */
+ if ((In[0] == '_') && (In[1] == '$') && (In[2] == '_'))
+ destructor = 1;
+#endif
+
+ /* We may need to truncate the symbol, save the hash for later*/
+ result = (strlen (In) > 23) ? hash_string (In) : 0;
+ /*
+ * Is there a Psect Attribute to skip??
+ */
+ if (HAS_PSECT_ATTRIBUTES (In))
+ {
+ /*
+ * Yes: Skip it
+ */
+ In += PSECT_ATTRIBUTES_STRING_LENGTH;
+ while (*In)
+ {
+ if ((In[0] == '$') && (In[1] == '$'))
+ {
+ In += 2;
+ break;
+ }
+ In++;
+ }
+ }
+
+ old_name = In;
+/* if (strlen (In) > 31 && flag_hash_long_names)
+ as_tsktsk ("Symbol name truncated: %s\n", In); */
+ /*
+ * Do the case conversion
+ */
+ i = 23; /* Maximum of 23 chars */
+ while (*In && (--i >= 0))
+ {
+ Case_Hack_Bits <<= 1;
+ if (*In == '$')
+ Saw_Dollar = 1;
+ if ((destructor == 1) && (i == 21))
+ Saw_Dollar = 0;
+ switch (vms_name_mapping)
+ {
+ case 0:
+ if (isupper (*In)) {
+ *Out++ = *In++;
+ Case_Hack_Bits |= 1;
+ } else {
+ *Out++ = islower (*In) ? toupper (*In++) : *In++;
+ }
+ break;
+ case 3: *Out++ = *In++;
+ break;
+ case 2:
+ if (islower (*In)) {
+ *Out++ = *In++;
+ } else {
+ *Out++ = isupper (*In) ? tolower (*In++) : *In++;
+ }
+ break;
+ }
+ }
+ /*
+ * If we saw a dollar sign, we don't do case hacking
+ */
+ if (flag_no_hash_mixed_case || Saw_Dollar)
+ Case_Hack_Bits = 0;
+
+ /*
+ * If we have more than 23 characters and everything is lowercase
+ * we can insert the full 31 characters
+ */
+ if (*In)
+ {
+ /*
+ * We have more than 23 characters
+ * If we must add the case hack, then we have truncated the str
+ */
+ pnt = Out;
+ truncate = 1;
+ if (Case_Hack_Bits == 0)
+ {
+ /*
+ * And so far they are all lower case:
+ * Check up to 8 more characters
+ * and ensure that they are lowercase
+ */
+ for (i = 0; (In[i] != 0) && (i < 8); i++)
+ if (isupper (In[i]) && !Saw_Dollar && !flag_no_hash_mixed_case)
+ break;
+
+ if (In[i] == 0)
+ truncate = 0;
+
+ if ((i == 8) || (In[i] == 0))
+ {
+ /*
+ * They are: Copy up to 31 characters
+ * to the output string
+ */
+ i = 8;
+ while ((--i >= 0) && (*In))
+ switch (vms_name_mapping){
+ case 0: *Out++ = islower (*In) ? toupper (*In++) : *In++;
+ break;
+ case 3: *Out++ = *In++;
+ break;
+ case 2: *Out++ = isupper (*In) ? tolower (*In++) : *In++;
+ break;
+ }
+ }
+ }
+ }
+ /*
+ * If there were any uppercase characters in the name we
+ * take on the case hacking string
+ */
+
+ /* Old behavior for regular GNU-C compiler */
+ if (!flag_hash_long_names)
+ truncate = 0;
+ if ((Case_Hack_Bits != 0) || (truncate == 1))
+ {
+ if (truncate == 0)
+ {
+ *Out++ = '_';
+ for (i = 0; i < 6; i++)
+ {
+ *Out++ = Hex_Table[Case_Hack_Bits & 0xf];
+ Case_Hack_Bits >>= 4;
+ }
+ *Out++ = 'X';
+ }
+ else
+ {
+ Out = pnt; /*Cut back to 23 characters maximum */
+ *Out++ = '_';
+ for (i = 0; i < 7; i++)
+ {
+ init = result & 0x01f;
+ *Out++ = (init < 10) ? ('0' + init) : ('A' + init - 10);
+ result = result >> 5;
+ }
+ }
+ } /*Case Hack */
+ /*
+ * Done
+ */
+ *Out = 0;
+ if (truncate == 1 && flag_hash_long_names && flag_show_after_trunc)
+ as_tsktsk (_("Symbol %s replaced by %s\n"), old_name, new_name);
+}
+
+
+/*
+ * Scan a symbol name for a psect attribute specification
+ */
+#define GLOBALSYMBOL_BIT 0x10000
+#define GLOBALVALUE_BIT 0x20000
+
+
+static void
+VMS_Modify_Psect_Attributes (Name, Attribute_Pointer)
+ const char *Name;
+ int *Attribute_Pointer;
+{
+ register int i;
+ register const char *cp;
+ int Negate;
+ static const struct
+ {
+ const char *Name;
+ int Value;
+ } Attributes[] =
+ {
+ {"PIC", GPS_S_M_PIC},
+ {"LIB", GPS_S_M_LIB},
+ {"OVR", GPS_S_M_OVR},
+ {"REL", GPS_S_M_REL},
+ {"GBL", GPS_S_M_GBL},
+ {"SHR", GPS_S_M_SHR},
+ {"EXE", GPS_S_M_EXE},
+ {"RD", GPS_S_M_RD},
+ {"WRT", GPS_S_M_WRT},
+ {"VEC", GPS_S_M_VEC},
+ {"GLOBALSYMBOL", GLOBALSYMBOL_BIT},
+ {"GLOBALVALUE", GLOBALVALUE_BIT},
+ {0, 0}
+ };
+
+ /*
+ * Kill leading "_"
+ */
+ if (*Name == '_')
+ Name++;
+ /*
+ * Check for a PSECT attribute list
+ */
+ if (!HAS_PSECT_ATTRIBUTES (Name))
+ return; /* If not, return */
+ /*
+ * Skip the attribute list indicator
+ */
+ Name += PSECT_ATTRIBUTES_STRING_LENGTH;
+ /*
+ * Process the attributes ("_" separated, "$" terminated)
+ */
+ while (*Name != '$')
+ {
+ /*
+ * Assume not negating
+ */
+ Negate = 0;
+ /*
+ * Check for "NO"
+ */
+ if ((Name[0] == 'N') && (Name[1] == 'O'))
+ {
+ /*
+ * We are negating (and skip the NO)
+ */
+ Negate = 1;
+ Name += 2;
+ }
+ /*
+ * Find the token delimiter
+ */
+ cp = Name;
+ while (*cp && (*cp != '_') && (*cp != '$'))
+ cp++;
+ /*
+ * Look for the token in the attribute list
+ */
+ for (i = 0; Attributes[i].Name; i++)
+ {
+ /*
+ * If the strings match, set/clear the attr.
+ */
+ if (strncmp (Name, Attributes[i].Name, cp - Name) == 0)
+ {
+ /*
+ * Set or clear
+ */
+ if (Negate)
+ *Attribute_Pointer &=
+ ~Attributes[i].Value;
+ else
+ *Attribute_Pointer |=
+ Attributes[i].Value;
+ /*
+ * Done
+ */
+ break;
+ }
+ }
+ /*
+ * Now skip the attribute
+ */
+ Name = cp;
+ if (*Name == '_')
+ Name++;
+ }
+}
+
+
+#define GBLSYM_REF 0
+#define GBLSYM_DEF 1
+#define GBLSYM_VAL 2
+#define GBLSYM_LCL 4 /* not GBL after all... */
+#define GBLSYM_WEAK 8
+
+/*
+ * Define a global symbol (or possibly a local one).
+ */
+static void
+VMS_Global_Symbol_Spec (Name, Psect_Number, Psect_Offset, Flags)
+ const char *Name;
+ int Psect_Number;
+ int Psect_Offset;
+ int Flags;
+{
+ char Local[32];
+
+ /*
+ * We are writing a GSD record
+ */
+ Set_VMS_Object_File_Record (OBJ_S_C_GSD);
+ /*
+ * If the buffer is empty we must insert the GSD record type
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (OBJ_S_C_GSD);
+ /*
+ * We are writing a Global (or local) symbol definition subrecord.
+ */
+ PUT_CHAR ((Flags & GBLSYM_LCL) != 0 ? GSD_S_C_LSY :
+ ((unsigned) Psect_Number <= 255) ? GSD_S_C_SYM : GSD_S_C_SYMW);
+ /*
+ * Data type is undefined
+ */
+ PUT_CHAR (0);
+ /*
+ * Switch on Definition/Reference
+ */
+ if ((Flags & GBLSYM_DEF) == 0)
+ {
+ /*
+ * Reference
+ */
+ PUT_SHORT (((Flags & GBLSYM_VAL) == 0) ? GSY_S_M_REL : 0);
+ if ((Flags & GBLSYM_LCL) != 0) /* local symbols have extra field */
+ PUT_SHORT (Current_Environment);
+ }
+ else
+ {
+ int sym_flags;
+
+ /*
+ * Definition
+ *[ assert (LSY_S_M_DEF == GSY_S_M_DEF && LSY_S_M_REL == GSY_S_M_REL); ]
+ */
+ sym_flags = GSY_S_M_DEF;
+ if (Flags & GBLSYM_WEAK)
+ sym_flags |= GSY_S_M_WEAK;
+ if ((Flags & GBLSYM_VAL) == 0)
+ sym_flags |= GSY_S_M_REL;
+ PUT_SHORT (sym_flags);
+ if ((Flags & GBLSYM_LCL) != 0) /* local symbols have extra field */
+ PUT_SHORT (Current_Environment);
+ /*
+ * Psect Number
+ */
+ if ((Flags & GBLSYM_LCL) == 0 && (unsigned) Psect_Number <= 255)
+ PUT_CHAR (Psect_Number);
+ else
+ PUT_SHORT (Psect_Number);
+ /*
+ * Offset
+ */
+ PUT_LONG (Psect_Offset);
+ }
+ /*
+ * Finally, the global symbol name
+ */
+ VMS_Case_Hack_Symbol (Name, Local);
+ PUT_COUNTED_STRING (Local);
+ /*
+ * Flush the buffer if it is more than 75% full
+ */
+ if (Object_Record_Offset > (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+/*
+ * Define an environment to support local symbol references.
+ * This is just to mollify the linker; we don't actually do
+ * anything useful with it.
+ */
+static void
+VMS_Local_Environment_Setup (Env_Name)
+ const char *Env_Name;
+{
+ /* We are writing a GSD record. */
+ Set_VMS_Object_File_Record (OBJ_S_C_GSD);
+ /* If the buffer is empty we must insert the GSD record type. */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (OBJ_S_C_GSD);
+ /* We are writing an ENV subrecord. */
+ PUT_CHAR (GSD_S_C_ENV);
+
+ ++Current_Environment; /* index of environment being defined */
+
+ /* ENV$W_FLAGS: we are defining the next environment. It's not nested. */
+ PUT_SHORT (ENV_S_M_DEF);
+ /* ENV$W_ENVINDX: index is always 0 for non-nested definitions. */
+ PUT_SHORT (0);
+
+ /* ENV$B_NAMLNG + ENV$T_NAME: environment name in ASCIC format. */
+ if (!Env_Name) Env_Name = "";
+ PUT_COUNTED_STRING ((char *)Env_Name);
+
+ /* Flush the buffer if it is more than 75% full. */
+ if (Object_Record_Offset > (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+
+/*
+ * Define a psect
+ */
+static int
+VMS_Psect_Spec (Name, Size, Type, vsp)
+ const char *Name;
+ int Size;
+ enum ps_type Type;
+ struct VMS_Symbol *vsp;
+{
+ char Local[32];
+ int Psect_Attributes;
+
+ /*
+ * Generate the appropriate PSECT flags given the PSECT type
+ */
+ switch (Type)
+ {
+ case ps_TEXT:
+ /* Text psects are PIC,noOVR,REL,noGBL,SHR,EXE,RD,noWRT. */
+ Psect_Attributes = (GPS_S_M_PIC|GPS_S_M_REL|GPS_S_M_SHR|GPS_S_M_EXE
+ |GPS_S_M_RD);
+ break;
+ case ps_DATA:
+ /* Data psects are PIC,noOVR,REL,noGBL,noSHR,noEXE,RD,WRT. */
+ Psect_Attributes = (GPS_S_M_PIC|GPS_S_M_REL|GPS_S_M_RD|GPS_S_M_WRT);
+ break;
+ case ps_COMMON:
+ /* Common block psects are: PIC,OVR,REL,GBL,noSHR,noEXE,RD,WRT. */
+ Psect_Attributes = (GPS_S_M_PIC|GPS_S_M_OVR|GPS_S_M_REL|GPS_S_M_GBL
+ |GPS_S_M_RD|GPS_S_M_WRT);
+ break;
+ case ps_CONST:
+ /* Const data psects are: PIC,OVR,REL,GBL,noSHR,noEXE,RD,noWRT. */
+ Psect_Attributes = (GPS_S_M_PIC|GPS_S_M_OVR|GPS_S_M_REL|GPS_S_M_GBL
+ |GPS_S_M_RD);
+ break;
+ case ps_CTORS:
+ /* Ctor psects are PIC,noOVR,REL,GBL,noSHR,noEXE,RD,noWRT. */
+ Psect_Attributes = (GPS_S_M_PIC|GPS_S_M_REL|GPS_S_M_GBL|GPS_S_M_RD);
+ break;
+ case ps_DTORS:
+ /* Dtor psects are PIC,noOVR,REL,GBL,noSHR,noEXE,RD,noWRT. */
+ Psect_Attributes = (GPS_S_M_PIC|GPS_S_M_REL|GPS_S_M_GBL|GPS_S_M_RD);
+ break;
+ default:
+ /* impossible */
+ error (_("Unknown VMS psect type (%ld)"), (long) Type);
+ break;
+ }
+ /*
+ * Modify the psect attributes according to any attribute string
+ */
+ if (vsp && S_GET_TYPE (vsp->Symbol) == N_ABS)
+ Psect_Attributes |= GLOBALVALUE_BIT;
+ else if (HAS_PSECT_ATTRIBUTES (Name))
+ VMS_Modify_Psect_Attributes (Name, &Psect_Attributes);
+ /*
+ * Check for globalref/def/val.
+ */
+ if ((Psect_Attributes & GLOBALVALUE_BIT) != 0)
+ {
+ /*
+ * globalvalue symbols were generated before. This code
+ * prevents unsightly psect buildup, and makes sure that
+ * fixup references are emitted correctly.
+ */
+ vsp->Psect_Index = -1; /* to catch errors */
+ S_SET_TYPE (vsp->Symbol, N_UNDF); /* make refs work */
+ return 1; /* decrement psect counter */
+ }
+
+ if ((Psect_Attributes & GLOBALSYMBOL_BIT) != 0)
+ {
+ switch (S_GET_RAW_TYPE (vsp->Symbol))
+ {
+ case N_UNDF | N_EXT:
+ VMS_Global_Symbol_Spec (Name, vsp->Psect_Index,
+ vsp->Psect_Offset, GBLSYM_REF);
+ vsp->Psect_Index = -1;
+ S_SET_TYPE (vsp->Symbol, N_UNDF);
+ return 1; /* return and indicate no psect */
+ case N_DATA | N_EXT:
+ VMS_Global_Symbol_Spec (Name, vsp->Psect_Index,
+ vsp->Psect_Offset, GBLSYM_DEF);
+ /* In this case we still generate the psect */
+ break;
+ default:
+ as_fatal (_("Globalsymbol attribute for symbol %s was unexpected."),
+ Name);
+ break;
+ } /* switch */
+ }
+
+ Psect_Attributes &= 0xffff; /* clear out the globalref/def stuff */
+ /*
+ * We are writing a GSD record
+ */
+ Set_VMS_Object_File_Record (OBJ_S_C_GSD);
+ /*
+ * If the buffer is empty we must insert the GSD record type
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (OBJ_S_C_GSD);
+ /*
+ * We are writing a PSECT definition subrecord
+ */
+ PUT_CHAR (GSD_S_C_PSC);
+ /*
+ * Psects are always LONGWORD aligned
+ */
+ PUT_CHAR (2);
+ /*
+ * Specify the psect attributes
+ */
+ PUT_SHORT (Psect_Attributes);
+ /*
+ * Specify the allocation
+ */
+ PUT_LONG (Size);
+ /*
+ * Finally, the psect name
+ */
+ VMS_Case_Hack_Symbol (Name, Local);
+ PUT_COUNTED_STRING (Local);
+ /*
+ * Flush the buffer if it is more than 75% full
+ */
+ if (Object_Record_Offset > (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+ return 0;
+}
+
+
+/* Given the pointer to a symbol we calculate how big the data at the
+ symbol is. We do this by looking for the next symbol (local or global)
+ which will indicate the start of another datum. */
+
+static offsetT
+VMS_Initialized_Data_Size (s0P, End_Of_Data)
+ register symbolS *s0P;
+ unsigned End_Of_Data;
+{
+ symbolS *s1P;
+ valueT s0P_val = S_GET_VALUE (s0P), s1P_val,
+ nearest_val = (valueT) End_Of_Data;
+
+ /* Find the nearest symbol what follows this one. */
+ for (s1P = symbol_rootP; s1P; s1P = symbol_next (s1P))
+ {
+ /* The data type must match. */
+ if (S_GET_TYPE (s1P) != N_DATA)
+ continue;
+ s1P_val = S_GET_VALUE (s1P);
+ if (s1P_val > s0P_val && s1P_val < nearest_val)
+ nearest_val = s1P_val;
+ }
+ /* Calculate its size. */
+ return (offsetT) (nearest_val - s0P_val);
+}
+
+
+/* Check symbol names for the Psect hack with a globalvalue, and then
+ generate globalvalues for those that have it. */
+
+static void
+VMS_Emit_Globalvalues (text_siz, data_siz, Data_Segment)
+ unsigned text_siz;
+ unsigned data_siz;
+ char *Data_Segment;
+{
+ register symbolS *sp;
+ char *stripped_name, *Name;
+ int Size;
+ int Psect_Attributes;
+ int globalvalue;
+ int typ, abstyp;
+
+ /*
+ * Scan the symbol table for globalvalues, and emit def/ref when
+ * required. These will be caught again later and converted to
+ * N_UNDF
+ */
+ for (sp = symbol_rootP; sp; sp = sp->sy_next)
+ {
+ typ = S_GET_RAW_TYPE (sp);
+ abstyp = ((typ & ~N_EXT) == N_ABS);
+ /*
+ * See if this is something we want to look at.
+ */
+ if (!abstyp &&
+ typ != (N_DATA | N_EXT) &&
+ typ != (N_UNDF | N_EXT))
+ continue;
+ /*
+ * See if this has globalvalue specification.
+ */
+ Name = S_GET_NAME (sp);
+
+ if (abstyp)
+ {
+ stripped_name = 0;
+ Psect_Attributes = GLOBALVALUE_BIT;
+ }
+ else if (HAS_PSECT_ATTRIBUTES (Name))
+ {
+ stripped_name = (char *) xmalloc (strlen (Name) + 1);
+ strcpy (stripped_name, Name);
+ Psect_Attributes = 0;
+ VMS_Modify_Psect_Attributes (stripped_name, &Psect_Attributes);
+ }
+ else
+ continue;
+
+ if ((Psect_Attributes & GLOBALVALUE_BIT) != 0)
+ {
+ switch (typ)
+ {
+ case N_ABS:
+ /* Local symbol references will want
+ to have an environment defined. */
+ if (Current_Environment < 0)
+ VMS_Local_Environment_Setup (".N_ABS");
+ VMS_Global_Symbol_Spec (Name, 0,
+ S_GET_VALUE (sp),
+ GBLSYM_DEF|GBLSYM_VAL|GBLSYM_LCL);
+ break;
+ case N_ABS | N_EXT:
+ VMS_Global_Symbol_Spec (Name, 0,
+ S_GET_VALUE (sp),
+ GBLSYM_DEF|GBLSYM_VAL);
+ break;
+ case N_UNDF | N_EXT:
+ VMS_Global_Symbol_Spec (stripped_name, 0, 0, GBLSYM_VAL);
+ break;
+ case N_DATA | N_EXT:
+ Size = VMS_Initialized_Data_Size (sp, text_siz + data_siz);
+ if (Size > 4)
+ error (_("Invalid data type for globalvalue"));
+ globalvalue = md_chars_to_number (Data_Segment +
+ S_GET_VALUE (sp) - text_siz , Size);
+ /* Three times for good luck. The linker seems to get confused
+ if there are fewer than three */
+ VMS_Global_Symbol_Spec (stripped_name, 0, 0, GBLSYM_VAL);
+ VMS_Global_Symbol_Spec (stripped_name, 0, globalvalue,
+ GBLSYM_DEF|GBLSYM_VAL);
+ VMS_Global_Symbol_Spec (stripped_name, 0, globalvalue,
+ GBLSYM_DEF|GBLSYM_VAL);
+ break;
+ default:
+ as_warn (_("Invalid globalvalue of %s"), stripped_name);
+ break;
+ } /* switch */
+ } /* if */
+ if (stripped_name) free (stripped_name); /* clean up */
+ } /* for */
+
+}
+
+
+/*
+ * Define a procedure entry pt/mask
+ */
+static void
+VMS_Procedure_Entry_Pt (Name, Psect_Number, Psect_Offset, Entry_Mask)
+ char *Name;
+ int Psect_Number;
+ int Psect_Offset;
+ int Entry_Mask;
+{
+ char Local[32];
+
+ /*
+ * We are writing a GSD record
+ */
+ Set_VMS_Object_File_Record (OBJ_S_C_GSD);
+ /*
+ * If the buffer is empty we must insert the GSD record type
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (OBJ_S_C_GSD);
+ /*
+ * We are writing a Procedure Entry Pt/Mask subrecord
+ */
+ PUT_CHAR (((unsigned) Psect_Number <= 255) ? GSD_S_C_EPM : GSD_S_C_EPMW);
+ /*
+ * Data type is undefined
+ */
+ PUT_CHAR (0);
+ /*
+ * Flags = "RELOCATABLE" and "DEFINED"
+ */
+ PUT_SHORT (GSY_S_M_DEF | GSY_S_M_REL);
+ /*
+ * Psect Number
+ */
+ if ((unsigned) Psect_Number <= 255)
+ PUT_CHAR (Psect_Number);
+ else
+ PUT_SHORT (Psect_Number);
+ /*
+ * Offset
+ */
+ PUT_LONG (Psect_Offset);
+ /*
+ * Entry mask
+ */
+ PUT_SHORT (Entry_Mask);
+ /*
+ * Finally, the global symbol name
+ */
+ VMS_Case_Hack_Symbol (Name, Local);
+ PUT_COUNTED_STRING (Local);
+ /*
+ * Flush the buffer if it is more than 75% full
+ */
+ if (Object_Record_Offset > (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+
+/*
+ * Set the current location counter to a particular Psect and Offset
+ */
+static void
+VMS_Set_Psect (Psect_Index, Offset, Record_Type)
+ int Psect_Index;
+ int Offset;
+ int Record_Type;
+{
+ /*
+ * We are writing a "Record_Type" record
+ */
+ Set_VMS_Object_File_Record (Record_Type);
+ /*
+ * If the buffer is empty we must insert the record type
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (Record_Type);
+ /*
+ * Stack the Psect base + Offset
+ */
+ vms_tir_stack_psect (Psect_Index, Offset, 0);
+ /*
+ * Set relocation base
+ */
+ PUT_CHAR (TIR_S_C_CTL_SETRB);
+ /*
+ * Flush the buffer if it is more than 75% full
+ */
+ if (Object_Record_Offset > (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+
+/*
+ * Store repeated immediate data in current Psect
+ */
+static void
+VMS_Store_Repeated_Data (Repeat_Count, Pointer, Size, Record_Type)
+ int Repeat_Count;
+ register char *Pointer;
+ int Size;
+ int Record_Type;
+{
+
+ /*
+ * Ignore zero bytes/words/longwords
+ */
+ switch (Size)
+ {
+ case 4:
+ if (Pointer[3] != 0 || Pointer[2] != 0) break;
+ /* else FALLTHRU */
+ case 2:
+ if (Pointer[1] != 0) break;
+ /* else FALLTHRU */
+ case 1:
+ if (Pointer[0] != 0) break;
+ /* zero value */
+ return;
+ default:
+ break;
+ }
+ /*
+ * If the data is too big for a TIR_S_C_STO_RIVB sub-record
+ * then we do it manually
+ */
+ if (Size > 255)
+ {
+ while (--Repeat_Count >= 0)
+ VMS_Store_Immediate_Data (Pointer, Size, Record_Type);
+ return;
+ }
+ /*
+ * We are writing a "Record_Type" record
+ */
+ Set_VMS_Object_File_Record (Record_Type);
+ /*
+ * If the buffer is empty we must insert record type
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (Record_Type);
+ /*
+ * Stack the repeat count
+ */
+ PUT_CHAR (TIR_S_C_STA_LW);
+ PUT_LONG (Repeat_Count);
+ /*
+ * And now the command and its data
+ */
+ PUT_CHAR (TIR_S_C_STO_RIVB);
+ PUT_CHAR (Size);
+ while (--Size >= 0)
+ PUT_CHAR (*Pointer++);
+ /*
+ * Flush the buffer if it is more than 75% full
+ */
+ if (Object_Record_Offset > (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+
+/*
+ * Store a Position Independent Reference
+ */
+static void
+VMS_Store_PIC_Symbol_Reference (Symbol, Offset, PC_Relative,
+ Psect, Psect_Offset, Record_Type)
+ symbolS *Symbol;
+ int Offset;
+ int PC_Relative;
+ int Psect;
+ int Psect_Offset;
+ int Record_Type;
+{
+ register struct VMS_Symbol *vsp = Symbol->sy_obj;
+ char Local[32];
+ int local_sym = 0;
+
+ /*
+ * We are writing a "Record_Type" record
+ */
+ Set_VMS_Object_File_Record (Record_Type);
+ /*
+ * If the buffer is empty we must insert record type
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (Record_Type);
+ /*
+ * Set to the appropriate offset in the Psect.
+ * For a Code reference we need to fix the operand
+ * specifier as well, so back up 1 byte;
+ * for a Data reference we just store HERE.
+ */
+ VMS_Set_Psect (Psect,
+ PC_Relative ? Psect_Offset - 1 : Psect_Offset,
+ Record_Type);
+ /*
+ * Make sure we are still generating a "Record Type" record
+ */
+ if (Object_Record_Offset == 0)
+ PUT_CHAR (Record_Type);
+ /*
+ * Dispatch on symbol type (so we can stack its value)
+ */
+ switch (S_GET_RAW_TYPE (Symbol))
+ {
+ /*
+ * Global symbol
+ */
+ case N_ABS:
+ local_sym = 1;
+ /*FALLTHRU*/
+ case N_ABS | N_EXT:
+#ifdef NOT_VAX_11_C_COMPATIBLE
+ case N_UNDF | N_EXT:
+ case N_DATA | N_EXT:
+#endif /* NOT_VAX_11_C_COMPATIBLE */
+ case N_UNDF:
+ case N_TEXT | N_EXT:
+ /*
+ * Get the symbol name (case hacked)
+ */
+ VMS_Case_Hack_Symbol (S_GET_NAME (Symbol), Local);
+ /*
+ * Stack the global symbol value
+ */
+ if (!local_sym)
+ {
+ PUT_CHAR (TIR_S_C_STA_GBL);
+ }
+ else
+ {
+ /* Local symbols have an extra field. */
+ PUT_CHAR (TIR_S_C_STA_LSY);
+ PUT_SHORT (Current_Environment);
+ }
+ PUT_COUNTED_STRING (Local);
+ if (Offset)
+ {
+ /*
+ * Stack the longword offset
+ */
+ PUT_CHAR (TIR_S_C_STA_LW);
+ PUT_LONG (Offset);
+ /*
+ * Add the two, leaving the result on the stack
+ */
+ PUT_CHAR (TIR_S_C_OPR_ADD);
+ }
+ break;
+ /*
+ * Uninitialized local data
+ */
+ case N_BSS:
+ /*
+ * Stack the Psect (+offset)
+ */
+ vms_tir_stack_psect (vsp->Psect_Index,
+ vsp->Psect_Offset + Offset,
+ 0);
+ break;
+ /*
+ * Local text
+ */
+ case N_TEXT:
+ /*
+ * Stack the Psect (+offset)
+ */
+ vms_tir_stack_psect (vsp->Psect_Index,
+ S_GET_VALUE (Symbol) + Offset,
+ 0);
+ break;
+ /*
+ * Initialized local or global data
+ */
+ case N_DATA:
+#ifndef NOT_VAX_11_C_COMPATIBLE
+ case N_UNDF | N_EXT:
+ case N_DATA | N_EXT:
+#endif /* NOT_VAX_11_C_COMPATIBLE */
+ /*
+ * Stack the Psect (+offset)
+ */
+ vms_tir_stack_psect (vsp->Psect_Index,
+ vsp->Psect_Offset + Offset,
+ 0);
+ break;
+ }
+ /*
+ * Store either a code or data reference
+ */
+ PUT_CHAR (PC_Relative ? TIR_S_C_STO_PICR : TIR_S_C_STO_PIDR);
+ /*
+ * Flush the buffer if it is more than 75% full
+ */
+ if (Object_Record_Offset > (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+}
+
+
+/*
+ * Check in the text area for an indirect pc-relative reference
+ * and fix it up with addressing mode 0xff [PC indirect]
+ *
+ * THIS SHOULD BE REPLACED BY THE USE OF TIR_S_C_STO_PIRR IN THE
+ * PIC CODE GENERATING FIXUP ROUTINE.
+ */
+static void
+VMS_Fix_Indirect_Reference (Text_Psect, Offset, fragP, text_frag_root)
+ int Text_Psect;
+ int Offset;
+ register fragS *fragP;
+ fragS *text_frag_root;
+{
+ /*
+ * The addressing mode byte is 1 byte before the address
+ */
+ Offset--;
+ /*
+ * Is it in THIS frag??
+ */
+ if ((Offset < fragP->fr_address) ||
+ (Offset >= (fragP->fr_address + fragP->fr_fix)))
+ {
+ /*
+ * We need to search for the fragment containing this
+ * Offset
+ */
+ for (fragP = text_frag_root; fragP; fragP = fragP->fr_next)
+ {
+ if ((Offset >= fragP->fr_address) &&
+ (Offset < (fragP->fr_address + fragP->fr_fix)))
+ break;
+ }
+ /*
+ * If we couldn't find the frag, things are BAD!!
+ */
+ if (fragP == 0)
+ error (_("Couldn't find fixup fragment when checking for indirect reference"));
+ }
+ /*
+ * Check for indirect PC relative addressing mode
+ */
+ if (fragP->fr_literal[Offset - fragP->fr_address] == (char) 0xff)
+ {
+ static char Address_Mode = (char) 0xff;
+
+ /*
+ * Yes: Store the indirect mode back into the image
+ * to fix up the damage done by STO_PICR
+ */
+ VMS_Set_Psect (Text_Psect, Offset, OBJ_S_C_TIR);
+ VMS_Store_Immediate_Data (&Address_Mode, 1, OBJ_S_C_TIR);
+ }
+}
+
+
+/*
+ * If the procedure "main()" exists we have to add the instruction
+ * "jsb c$main_args" at the beginning to be compatible with VAX-11 "C".
+ *
+ * FIXME: the macro name `HACK_DEC_C_STARTUP' should be renamed
+ * to `HACK_VAXCRTL_STARTUP' because Digital's compiler
+ * named "DEC C" uses run-time library "DECC$SHR", but this
+ * startup code is for "VAXCRTL", the library for Digital's
+ * older "VAX C". Also, this extra code isn't needed for
+ * supporting gcc because it already generates the VAXCRTL
+ * startup call when compiling main(). The reference to
+ * `flag_hash_long_names' looks very suspicious too;
+ * probably an old-style command line option was inadvertently
+ * overloaded here, then blindly converted into the new one.
+ */
+void
+vms_check_for_main ()
+{
+ register symbolS *symbolP;
+#ifdef HACK_DEC_C_STARTUP /* JF */
+ register struct frchain *frchainP;
+ register fragS *fragP;
+ register fragS **prev_fragPP;
+ register struct fix *fixP;
+ register fragS *New_Frag;
+ int i;
+#endif /* HACK_DEC_C_STARTUP */
+
+ symbolP = (symbolS *) symbol_find ("_main");
+ if (symbolP && !S_IS_DEBUG (symbolP) &&
+ S_IS_EXTERNAL (symbolP) && (S_GET_TYPE (symbolP) == N_TEXT))
+ {
+#ifdef HACK_DEC_C_STARTUP
+ if (!flag_hash_long_names)
+ {
+#endif
+ /*
+ * Remember the entry point symbol
+ */
+ Entry_Point_Symbol = symbolP;
+#ifdef HACK_DEC_C_STARTUP
+ }
+ else
+ {
+ /*
+ * Scan all the fragment chains for the one with "_main"
+ * (Actually we know the fragment from the symbol, but we need
+ * the previous fragment so we can change its pointer)
+ */
+ frchainP = frchain_root;
+ while (frchainP)
+ {
+ /*
+ * Scan all the fragments in this chain, remembering
+ * the "previous fragment"
+ */
+ prev_fragPP = &frchainP->frch_root;
+ fragP = frchainP->frch_root;
+ while (fragP && (fragP != frchainP->frch_last))
+ {
+ /*
+ * Is this the fragment?
+ */
+ if (fragP == symbolP->sy_frag)
+ {
+ /*
+ * Yes: Modify the fragment by replacing
+ * it with a new fragment.
+ */
+ New_Frag = (fragS *)
+ xmalloc (sizeof (*New_Frag) +
+ fragP->fr_fix +
+ fragP->fr_var +
+ 5);
+ /*
+ * The fragments are the same except
+ * that the "fixed" area is larger
+ */
+ *New_Frag = *fragP;
+ New_Frag->fr_fix += 6;
+ /*
+ * Copy the literal data opening a hole
+ * 2 bytes after "_main" (i.e. just after
+ * the entry mask). Into which we place
+ * the JSB instruction.
+ */
+ New_Frag->fr_literal[0] = fragP->fr_literal[0];
+ New_Frag->fr_literal[1] = fragP->fr_literal[1];
+ New_Frag->fr_literal[2] = 0x16; /* Jsb */
+ New_Frag->fr_literal[3] = 0xef;
+ New_Frag->fr_literal[4] = 0;
+ New_Frag->fr_literal[5] = 0;
+ New_Frag->fr_literal[6] = 0;
+ New_Frag->fr_literal[7] = 0;
+ for (i = 2; i < fragP->fr_fix + fragP->fr_var; i++)
+ New_Frag->fr_literal[i + 6] =
+ fragP->fr_literal[i];
+ /*
+ * Now replace the old fragment with the
+ * newly generated one.
+ */
+ *prev_fragPP = New_Frag;
+ /*
+ * Remember the entry point symbol
+ */
+ Entry_Point_Symbol = symbolP;
+ /*
+ * Scan the text area fixup structures
+ * as offsets in the fragment may have
+ * changed
+ */
+ for (fixP = text_fix_root; fixP; fixP = fixP->fx_next)
+ {
+ /*
+ * Look for references to this
+ * fragment.
+ */
+ if (fixP->fx_frag == fragP)
+ {
+ /*
+ * Change the fragment
+ * pointer
+ */
+ fixP->fx_frag = New_Frag;
+ /*
+ * If the offset is after
+ * the entry mask we need
+ * to account for the JSB
+ * instruction we just
+ * inserted.
+ */
+ if (fixP->fx_where >= 2)
+ fixP->fx_where += 6;
+ }
+ }
+ /*
+ * Scan the symbols as offsets in the
+ * fragment may have changed
+ */
+ for (symbolP = symbol_rootP;
+ symbolP;
+ symbolP = symbol_next (symbolP))
+ {
+ /*
+ * Look for references to this
+ * fragment.
+ */
+ if (symbolP->sy_frag == fragP)
+ {
+ /*
+ * Change the fragment
+ * pointer
+ */
+ symbolP->sy_frag = New_Frag;
+ /*
+ * If the offset is after
+ * the entry mask we need
+ * to account for the JSB
+ * instruction we just
+ * inserted.
+ */
+ if (S_GET_VALUE (symbolP) >= 2)
+ S_SET_VALUE (symbolP,
+ S_GET_VALUE (symbolP) + 6);
+ }
+ }
+ /*
+ * Make a symbol reference to
+ * "_c$main_args" so we can get
+ * its address inserted into the
+ * JSB instruction.
+ */
+ symbolP = (symbolS *) xmalloc (sizeof (*symbolP));
+ S_SET_NAME (symbolP, "_C$MAIN_ARGS");
+ S_SET_TYPE (symbolP, N_UNDF);
+ S_SET_OTHER (symbolP, 0);
+ S_SET_DESC (symbolP, 0);
+ S_SET_VALUE (symbolP, 0);
+ symbolP->sy_name_offset = 0;
+ symbolP->sy_number = 0;
+ symbolP->sy_obj = 0;
+ symbolP->sy_frag = New_Frag;
+ symbolP->sy_resolved = 0;
+ symbolP->sy_resolving = 0;
+ /* this actually inserts at the beginning of the list */
+ symbol_append (symbol_rootP, symbolP,
+ &symbol_rootP, &symbol_lastP);
+
+ symbol_rootP = symbolP;
+ /*
+ * Generate a text fixup structure
+ * to get "_c$main_args" stored into the
+ * JSB instruction.
+ */
+ fixP = (struct fix *) xmalloc (sizeof (*fixP));
+ fixP->fx_frag = New_Frag;
+ fixP->fx_where = 4;
+ fixP->fx_addsy = symbolP;
+ fixP->fx_subsy = 0;
+ fixP->fx_offset = 0;
+ fixP->fx_size = 4;
+ fixP->fx_pcrel = 1;
+ fixP->fx_next = text_fix_root;
+ text_fix_root = fixP;
+ /*
+ * Now make sure we exit from the loop
+ */
+ frchainP = 0;
+ break;
+ }
+ /*
+ * Try the next fragment
+ */
+ prev_fragPP = &fragP->fr_next;
+ fragP = fragP->fr_next;
+ }
+ /*
+ * Try the next fragment chain
+ */
+ if (frchainP)
+ frchainP = frchainP->frch_next;
+ }
+ }
+#endif /* HACK_DEC_C_STARTUP */
+ }
+}
+
+
+/*
+ * Beginning of vms_write_object_file().
+ */
+
+static
+struct vms_obj_state {
+
+ /* Next program section index to use. */
+ int psect_number;
+
+ /* Psect index for code. Always ends up #0. */
+ int text_psect;
+
+ /* Psect index for initialized static variables. */
+ int data_psect;
+
+ /* Psect index for uninitialized static variables. */
+ int bss_psect;
+
+ /* Psect index for static constructors. */
+ int ctors_psect;
+
+ /* Psect index for static destructors. */
+ int dtors_psect;
+
+ /* Number of bytes used for local symbol data. */
+ int local_initd_data_size;
+
+ /* Dynamic buffer for initialized data. */
+ char *data_segment;
+
+} vms_obj_state;
+
+#define Psect_Number vms_obj_state.psect_number
+#define Text_Psect vms_obj_state.text_psect
+#define Data_Psect vms_obj_state.data_psect
+#define Bss_Psect vms_obj_state.bss_psect
+#define Ctors_Psect vms_obj_state.ctors_psect
+#define Dtors_Psect vms_obj_state.dtors_psect
+#define Local_Initd_Data_Size vms_obj_state.local_initd_data_size
+#define Data_Segment vms_obj_state.data_segment
+
+
+#define IS_GXX_VTABLE(symP) (strncmp (S_GET_NAME (symP), "__vt.", 5) == 0)
+#define IS_GXX_XTOR(symP) (strncmp (S_GET_NAME (symP), "__GLOBAL_.", 10) == 0)
+#define XTOR_SIZE 4
+
+
+/* Perform text segment fixups. */
+
+static void
+vms_fixup_text_section (text_siz, text_frag_root, data_frag_root)
+ unsigned text_siz;
+ struct frag *text_frag_root;
+ struct frag *data_frag_root;
+{
+ register fragS *fragP;
+ register struct fix *fixP;
+ offsetT dif;
+
+ /* Scan the text fragments. */
+ for (fragP = text_frag_root; fragP; fragP = fragP->fr_next)
+ {
+ /* Stop if we get to the data fragments. */
+ if (fragP == data_frag_root)
+ break;
+ /* Ignore fragments with no data. */
+ if ((fragP->fr_fix == 0) && (fragP->fr_var == 0))
+ continue;
+ /* Go the the appropriate offset in the Text Psect. */
+ VMS_Set_Psect (Text_Psect, fragP->fr_address, OBJ_S_C_TIR);
+ /* Store the "fixed" part. */
+ if (fragP->fr_fix)
+ VMS_Store_Immediate_Data (fragP->fr_literal,
+ fragP->fr_fix,
+ OBJ_S_C_TIR);
+ /* Store the "variable" part. */
+ if (fragP->fr_var && fragP->fr_offset)
+ VMS_Store_Repeated_Data (fragP->fr_offset,
+ fragP->fr_literal + fragP->fr_fix,
+ fragP->fr_var,
+ OBJ_S_C_TIR);
+ } /* text frag loop */
+
+ /*
+ * Now we go through the text segment fixups and generate
+ * TIR records to fix up addresses within the Text Psect.
+ */
+ for (fixP = text_fix_root; fixP; fixP = fixP->fx_next)
+ {
+ /* We DO handle the case of "Symbol - Symbol" as
+ long as it is in the same segment. */
+ if (fixP->fx_subsy && fixP->fx_addsy)
+ {
+ /* They need to be in the same segment. */
+ if (S_GET_RAW_TYPE (fixP->fx_subsy) !=
+ S_GET_RAW_TYPE (fixP->fx_addsy))
+ error (_("Fixup data addsy and subsy don't have the same type"));
+ /* And they need to be in one that we can check the psect on. */
+ if ((S_GET_TYPE (fixP->fx_addsy) != N_DATA) &&
+ (S_GET_TYPE (fixP->fx_addsy) != N_TEXT))
+ error (_("Fixup data addsy and subsy don't have an appropriate type"));
+ /* This had better not be PC relative! */
+ if (fixP->fx_pcrel)
+ error (_("Fixup data is erroneously \"pcrel\""));
+ /* Subtract their values to get the difference. */
+ dif = S_GET_VALUE (fixP->fx_addsy) - S_GET_VALUE (fixP->fx_subsy);
+ md_number_to_chars (Local, (valueT)dif, fixP->fx_size);
+ /* Now generate the fixup object records;
+ set the psect and store the data. */
+ VMS_Set_Psect (Text_Psect,
+ fixP->fx_where + fixP->fx_frag->fr_address,
+ OBJ_S_C_TIR);
+ VMS_Store_Immediate_Data (Local,
+ fixP->fx_size,
+ OBJ_S_C_TIR);
+ continue; /* done with this fixup */
+ } /* if fx_subsy && fx_addsy */
+ /* Size will HAVE to be "long". */
+ if (fixP->fx_size != 4)
+ error (_("Fixup datum is not a longword"));
+ /* Symbol must be "added" (if it is ever
+ subtracted we can fix this assumption). */
+ if (fixP->fx_addsy == 0)
+ error (_("Fixup datum is not \"fixP->fx_addsy\""));
+ /* Store the symbol value in a PIC fashion. */
+ VMS_Store_PIC_Symbol_Reference (fixP->fx_addsy,
+ fixP->fx_offset,
+ fixP->fx_pcrel,
+ Text_Psect,
+ fixP->fx_where + fixP->fx_frag->fr_address,
+ OBJ_S_C_TIR);
+ /*
+ * Check for indirect address reference, which has to be fixed up
+ * (as the linker will screw it up with TIR_S_C_STO_PICR)...
+ */
+ if (fixP->fx_pcrel)
+ VMS_Fix_Indirect_Reference (Text_Psect,
+ fixP->fx_where + fixP->fx_frag->fr_address,
+ fixP->fx_frag,
+ text_frag_root);
+ } /* text fix loop */
+}
+
+
+/* Create a buffer holding the data segment. */
+
+static void
+synthesize_data_segment (data_siz, text_siz, data_frag_root)
+ unsigned data_siz, text_siz;
+ struct frag *data_frag_root;
+{
+ register fragS *fragP;
+ char *fill_literal;
+ long fill_size, count, i;
+
+ /* Allocate the data segment. */
+ Data_Segment = (char *) xmalloc (data_siz);
+ /* Run through the data fragments, filling in the segment. */
+ for (fragP = data_frag_root; fragP; fragP = fragP->fr_next)
+ {
+ i = fragP->fr_address - text_siz;
+ if (fragP->fr_fix)
+ memcpy (Data_Segment + i, fragP->fr_literal, fragP->fr_fix);
+ i += fragP->fr_fix;
+
+ if ((fill_size = fragP->fr_var) != 0)
+ {
+ fill_literal = fragP->fr_literal + fragP->fr_fix;
+ for (count = fragP->fr_offset; count; count--)
+ {
+ memcpy (Data_Segment + i, fill_literal, fill_size);
+ i += fill_size;
+ }
+ }
+ } /* data frag loop */
+
+ return;
+}
+
+
+/* Perform data segment fixups. */
+
+static void
+vms_fixup_data_section (data_siz, text_siz)
+ unsigned data_siz, text_siz;
+{
+ register struct VMS_Symbol *vsp;
+ register struct fix *fixP;
+ register symbolS *sp;
+ addressT fr_address;
+ offsetT dif;
+ valueT val;
+
+ /* Run through all the data symbols and store the data. */
+ for (vsp = VMS_Symbols; vsp; vsp = vsp->Next)
+ {
+ /* Ignore anything other than data symbols. */
+ if (S_GET_TYPE (vsp->Symbol) != N_DATA)
+ continue;
+ /* Set the Psect + Offset. */
+ VMS_Set_Psect (vsp->Psect_Index,
+ vsp->Psect_Offset,
+ OBJ_S_C_TIR);
+ /* Store the data. */
+ val = S_GET_VALUE (vsp->Symbol);
+ VMS_Store_Immediate_Data (Data_Segment + val - text_siz,
+ vsp->Size,
+ OBJ_S_C_TIR);
+ } /* N_DATA symbol loop */
+
+ /*
+ * Now we go through the data segment fixups and generate
+ * TIR records to fix up addresses within the Data Psects.
+ */
+ for (fixP = data_fix_root; fixP; fixP = fixP->fx_next)
+ {
+ /* Find the symbol for the containing datum. */
+ for (vsp = VMS_Symbols; vsp; vsp = vsp->Next)
+ {
+ /* Only bother with Data symbols. */
+ sp = vsp->Symbol;
+ if (S_GET_TYPE (sp) != N_DATA)
+ continue;
+ /* Ignore symbol if After fixup. */
+ val = S_GET_VALUE (sp);
+ fr_address = fixP->fx_frag->fr_address;
+ if (val > fixP->fx_where + fr_address)
+ continue;
+ /* See if the datum is here. */
+ if (val + vsp->Size <= fixP->fx_where + fr_address)
+ continue;
+ /* We DO handle the case of "Symbol - Symbol" as
+ long as it is in the same segment. */
+ if (fixP->fx_subsy && fixP->fx_addsy)
+ {
+ /* They need to be in the same segment. */
+ if (S_GET_RAW_TYPE (fixP->fx_subsy) !=
+ S_GET_RAW_TYPE (fixP->fx_addsy))
+ error (_("Fixup data addsy and subsy don't have the same type"));
+ /* And they need to be in one that we can check the psect on. */
+ if ((S_GET_TYPE (fixP->fx_addsy) != N_DATA) &&
+ (S_GET_TYPE (fixP->fx_addsy) != N_TEXT))
+ error (_("Fixup data addsy and subsy don't have an appropriate type"));
+ /* This had better not be PC relative! */
+ if (fixP->fx_pcrel)
+ error (_("Fixup data is erroneously \"pcrel\""));
+ /* Subtract their values to get the difference. */
+ dif = S_GET_VALUE (fixP->fx_addsy) - S_GET_VALUE (fixP->fx_subsy);
+ md_number_to_chars (Local, (valueT)dif, fixP->fx_size);
+ /*
+ * Now generate the fixup object records;
+ * set the psect and store the data.
+ */
+ VMS_Set_Psect (vsp->Psect_Index,
+ fr_address + fixP->fx_where
+ - val + vsp->Psect_Offset,
+ OBJ_S_C_TIR);
+ VMS_Store_Immediate_Data (Local,
+ fixP->fx_size,
+ OBJ_S_C_TIR);
+ break; /* done with this fixup */
+ }
+ /* Size will HAVE to be "long". */
+ if (fixP->fx_size != 4)
+ error (_("Fixup datum is not a longword"));
+ /* Symbol must be "added" (if it is ever
+ subtracted we can fix this assumption). */
+ if (fixP->fx_addsy == 0)
+ error (_("Fixup datum is not \"fixP->fx_addsy\""));
+ /* Store the symbol value in a PIC fashion. */
+ VMS_Store_PIC_Symbol_Reference (fixP->fx_addsy,
+ fixP->fx_offset,
+ fixP->fx_pcrel,
+ vsp->Psect_Index,
+ fr_address + fixP->fx_where
+ - val + vsp->Psect_Offset,
+ OBJ_S_C_TIR);
+ /* Done with this fixup. */
+ break;
+ } /* vms_symbol loop */
+
+ } /* data fix loop */
+}
+
+/* Perform ctors/dtors segment fixups. */
+
+static void
+vms_fixup_xtors_section (symbols, sect_no)
+ struct VMS_Symbol *symbols;
+ int sect_no;
+{
+ register struct VMS_Symbol *vsp;
+
+ /* Run through all the symbols and store the data. */
+ for (vsp = symbols; vsp; vsp = vsp->Next)
+ {
+ register symbolS *sp;
+
+ /* Set relocation base. */
+ VMS_Set_Psect (vsp->Psect_Index, vsp->Psect_Offset, OBJ_S_C_TIR);
+
+ sp = vsp->Symbol;
+ /* Stack the Psect base with its offset. */
+ VMS_Set_Data (Text_Psect, S_GET_VALUE (sp), OBJ_S_C_TIR, 0);
+ }
+ /* Flush the buffer if it is more than 75% full. */
+ if (Object_Record_Offset > (sizeof (Object_Record_Buffer) * 3 / 4))
+ Flush_VMS_Object_Record_Buffer ();
+
+ return;
+}
+
+
+/* Define symbols for the linker. */
+
+static void
+global_symbol_directory (text_siz, data_siz)
+ unsigned text_siz, data_siz;
+{
+ register fragS *fragP;
+ register symbolS *sp;
+ register struct VMS_Symbol *vsp;
+ int Globalref, define_as_global_symbol;
+
+#if 0
+ /* The g++ compiler does not write out external references to
+ vtables correctly. Check for this and holler if we see it
+ happening. If that compiler bug is ever fixed we can remove
+ this.
+
+ (Jun'95: gcc 2.7.0's cc1plus still exhibits this behavior.)
+
+ This was reportedly fixed as of June 2, 1998. */
+
+ for (sp = symbol_rootP; sp; sp = symbol_next (sp))
+ if (S_GET_RAW_TYPE (sp) == N_UNDF && IS_GXX_VTABLE (sp))
+ {
+ S_SET_TYPE (sp, N_UNDF | N_EXT);
+ S_SET_OTHER (sp, 1);
+ as_warn (_("g++ wrote an extern reference to `%s' as a routine.\nI will fix it, but I hope that it was note really a routine."),
+ S_GET_NAME (sp));
+ }
+#endif
+
+ /*
+ * Now scan the symbols and emit the appropriate GSD records
+ */
+ for (sp = symbol_rootP; sp; sp = symbol_next (sp))
+ {
+ define_as_global_symbol = 0;
+ vsp = 0;
+ /* Dispatch on symbol type. */
+ switch (S_GET_RAW_TYPE (sp))
+ {
+
+ /* Global uninitialized data. */
+ case N_UNDF | N_EXT:
+ /* Make a VMS data symbol entry. */
+ vsp = (struct VMS_Symbol *) xmalloc (sizeof *vsp);
+ vsp->Symbol = sp;
+ vsp->Size = S_GET_VALUE (sp);
+ vsp->Psect_Index = Psect_Number++;
+ vsp->Psect_Offset = 0;
+ vsp->Next = VMS_Symbols;
+ VMS_Symbols = vsp;
+ sp->sy_obj = vsp;
+ /* Make the psect for this data. */
+ Globalref = VMS_Psect_Spec (S_GET_NAME (sp),
+ vsp->Size,
+ S_GET_OTHER (sp) ? ps_CONST : ps_COMMON,
+ vsp);
+ if (Globalref)
+ Psect_Number--;
+#ifdef NOT_VAX_11_C_COMPATIBLE
+ define_as_global_symbol = 1;
+#else
+ /* See if this is an external vtable. We want to help the
+ linker find these things in libraries, so we make a symbol
+ reference. This is not compatible with VAX-C usage for
+ variables, but since vtables are only used internally by
+ g++, we can get away with this hack. */
+ define_as_global_symbol = IS_GXX_VTABLE (sp);
+#endif
+ break;
+
+ /* Local uninitialized data. */
+ case N_BSS:
+ /* Make a VMS data symbol entry. */
+ vsp = (struct VMS_Symbol *) xmalloc (sizeof *vsp);
+ vsp->Symbol = sp;
+ vsp->Size = 0;
+ vsp->Psect_Index = Bss_Psect;
+ vsp->Psect_Offset = S_GET_VALUE (sp) - bss_address_frag.fr_address;
+ vsp->Next = VMS_Symbols;
+ VMS_Symbols = vsp;
+ sp->sy_obj = vsp;
+ break;
+
+ /* Global initialized data. */
+ case N_DATA | N_EXT:
+ /* Make a VMS data symbol entry. */
+ vsp = (struct VMS_Symbol *) xmalloc (sizeof *vsp);
+ vsp->Symbol = sp;
+ vsp->Size = VMS_Initialized_Data_Size (sp, text_siz + data_siz);
+ vsp->Psect_Index = Psect_Number++;
+ vsp->Psect_Offset = 0;
+ vsp->Next = VMS_Symbols;
+ VMS_Symbols = vsp;
+ sp->sy_obj = vsp;
+ /* Make its psect. */
+ Globalref = VMS_Psect_Spec (S_GET_NAME (sp),
+ vsp->Size,
+ S_GET_OTHER (sp) ? ps_CONST : ps_COMMON,
+ vsp);
+ if (Globalref)
+ Psect_Number--;
+#ifdef NOT_VAX_11_C_COMPATIBLE
+ define_as_global_symbol = 1;
+#else
+ /* See N_UNDF|N_EXT above for explanation. */
+ define_as_global_symbol = IS_GXX_VTABLE (sp);
+#endif
+ break;
+
+ /* Local initialized data. */
+ case N_DATA:
+ {
+ char *sym_name = S_GET_NAME (sp);
+
+ /* Always suppress local numeric labels. */
+ if (sym_name && strcmp (sym_name, FAKE_LABEL_NAME) == 0)
+ break;
+
+ /* Make a VMS data symbol entry. */
+ vsp = (struct VMS_Symbol *) xmalloc (sizeof *vsp);
+ vsp->Symbol = sp;
+ vsp->Size = VMS_Initialized_Data_Size (sp, text_siz + data_siz);
+ vsp->Psect_Index = Data_Psect;
+ vsp->Psect_Offset = Local_Initd_Data_Size;
+ Local_Initd_Data_Size += vsp->Size;
+ vsp->Next = VMS_Symbols;
+ VMS_Symbols = vsp;
+ sp->sy_obj = vsp;
+ }
+ break;
+
+ /* Global Text definition. */
+ case N_TEXT | N_EXT:
+ {
+
+ if (IS_GXX_XTOR (sp))
+ {
+ vsp = (struct VMS_Symbol *) xmalloc (sizeof *vsp);
+ vsp->Symbol = sp;
+ vsp->Size = XTOR_SIZE;
+ sp->sy_obj = vsp;
+ switch ((S_GET_NAME (sp))[10])
+ {
+ case 'I':
+ vsp->Psect_Index = Ctors_Psect;
+ vsp->Psect_Offset = (Ctors_Symbols==0)?0:(Ctors_Symbols->Psect_Offset+XTOR_SIZE);
+ vsp->Next = Ctors_Symbols;
+ Ctors_Symbols = vsp;
+ break;
+ case 'D':
+ vsp->Psect_Index = Dtors_Psect;
+ vsp->Psect_Offset = (Dtors_Symbols==0)?0:(Dtors_Symbols->Psect_Offset+XTOR_SIZE);
+ vsp->Next = Dtors_Symbols;
+ Dtors_Symbols = vsp;
+ break;
+ case 'G':
+ as_warn (_("Can't handle global xtors symbols yet."));
+ break;
+ default:
+ as_warn (_("Unknown %s"), S_GET_NAME (sp));
+ break;
+ }
+ }
+ else
+ {
+ unsigned short Entry_Mask;
+
+ /* Get the entry mask. */
+ fragP = sp->sy_frag;
+ /* First frag might be empty if we're generating listings.
+ So skip empty rs_fill frags. */
+ while (fragP && fragP->fr_type == rs_fill && fragP->fr_fix == 0)
+ fragP = fragP->fr_next;
+
+ /* If first frag doesn't contain the data, what do we do?
+ If it's possibly smaller than two bytes, that would
+ imply that the entry mask is not stored where we're
+ expecting it.
+
+ If you can find a test case that triggers this, report
+ it (and tell me what the entry mask field ought to be),
+ and I'll try to fix it. KR */
+ if (fragP->fr_fix < 2)
+ abort ();
+
+ Entry_Mask = (fragP->fr_literal[0] & 0x00ff) |
+ ((fragP->fr_literal[1] & 0x00ff) << 8);
+ /* Define the procedure entry point. */
+ VMS_Procedure_Entry_Pt (S_GET_NAME (sp),
+ Text_Psect,
+ S_GET_VALUE (sp),
+ Entry_Mask);
+ }
+ break;
+ }
+
+ /* Local Text definition. */
+ case N_TEXT:
+ /* Make a VMS data symbol entry. */
+ if (Text_Psect != -1)
+ {
+ vsp = (struct VMS_Symbol *) xmalloc (sizeof *vsp);
+ vsp->Symbol = sp;
+ vsp->Size = 0;
+ vsp->Psect_Index = Text_Psect;
+ vsp->Psect_Offset = S_GET_VALUE (sp);
+ vsp->Next = VMS_Symbols;
+ VMS_Symbols = vsp;
+ sp->sy_obj = vsp;
+ }
+ break;
+
+ /* Global Reference. */
+ case N_UNDF:
+ /* Make a GSD global symbol reference record. */
+ VMS_Global_Symbol_Spec (S_GET_NAME (sp),
+ 0,
+ 0,
+ GBLSYM_REF);
+ break;
+
+ /* Absolute symbol. */
+ case N_ABS:
+ case N_ABS | N_EXT:
+ /* gcc doesn't generate these;
+ VMS_Emit_Globalvalue handles them though. */
+ vsp = (struct VMS_Symbol *) xmalloc (sizeof *vsp);
+ vsp->Symbol = sp;
+ vsp->Size = 4; /* always assume 32 bits */
+ vsp->Psect_Index = 0;
+ vsp->Psect_Offset = S_GET_VALUE (sp);
+ vsp->Next = VMS_Symbols;
+ VMS_Symbols = vsp;
+ sp->sy_obj = vsp;
+ break;
+
+ /* Anything else. */
+ default:
+ /* Ignore STAB symbols, including .stabs emitted by g++. */
+ if (S_IS_DEBUG (sp) || (S_GET_TYPE (sp) == 22))
+ break;
+ /*
+ * Error otherwise.
+ */
+ as_tsktsk (_("unhandled stab type %d"), S_GET_TYPE (sp));
+ break;
+ }
+
+ /* Global symbols have different linkage than external variables. */
+ if (define_as_global_symbol)
+ VMS_Global_Symbol_Spec (S_GET_NAME (sp),
+ vsp->Psect_Index,
+ 0,
+ GBLSYM_DEF);
+ }
+
+ return;
+}
+
+
+/* Output debugger symbol table information for symbols which
+ are local to a specific routine. */
+
+static void
+local_symbols_DST (s0P, Current_Routine)
+ symbolS *s0P, *Current_Routine;
+{
+ symbolS *s1P;
+ char *s0P_name, *pnt0, *pnt1;
+
+ s0P_name = S_GET_NAME (s0P);
+ if (*s0P_name++ != '_')
+ return;
+
+ for (s1P = Current_Routine; s1P; s1P = symbol_next (s1P))
+ {
+#if 0 /* redundant; RAW_TYPE != N_FUN suffices */
+ if (!S_IS_DEBUG (s1P))
+ continue;
+#endif
+ if (S_GET_RAW_TYPE (s1P) != N_FUN)
+ continue;
+ pnt0 = s0P_name;
+ pnt1 = S_GET_NAME (s1P);
+ /* We assume the two strings are never exactly equal... */
+ while (*pnt0++ == *pnt1++)
+ {
+ }
+ /* Found it if s0P name is exhausted and s1P name has ":F" or ":f" next.
+ Note: both pointers have advanced one past the non-matching char. */
+ if ((*pnt1 == 'F' || *pnt1 == 'f') && *--pnt1 == ':' && *--pnt0 == '\0')
+ {
+ Define_Routine (s1P, 0, Current_Routine, Text_Psect);
+ return;
+ }
+ }
+}
+
+
+/* Construct and output the debug symbol table. */
+
+static void
+vms_build_DST (text_siz)
+ unsigned text_siz;
+{
+ register symbolS *symbolP;
+ symbolS *Current_Routine = 0;
+ struct input_file *Cur_File = 0;
+ offsetT Cur_Offset = -1;
+ int Cur_Line_Number = 0;
+ int File_Number = 0;
+ int Debugger_Offset = 0;
+ int file_available;
+ int dsc;
+ offsetT val;
+
+ /* Write the Traceback Begin Module record. */
+ VMS_TBT_Module_Begin ();
+
+ /*
+ * Output debugging info for global variables and static variables
+ * that are not specific to one routine. We also need to examine
+ * all stabs directives, to find the definitions to all of the
+ * advanced data types, and this is done by VMS_LSYM_Parse. This
+ * needs to be done before any definitions are output to the object
+ * file, since there can be forward references in the stabs
+ * directives. When through with parsing, the text of the stabs
+ * directive is altered, with the definitions removed, so that later
+ * passes will see directives as they would be written if the type
+ * were already defined.
+ *
+ * We also look for files and include files, and make a list of
+ * them. We examine the source file numbers to establish the actual
+ * lines that code was generated from, and then generate offsets.
+ */
+ VMS_LSYM_Parse ();
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next (symbolP))
+ {
+ /* Only deal with STAB symbols here. */
+ if (!S_IS_DEBUG (symbolP))
+ continue;
+ /*
+ * Dispatch on STAB type.
+ */
+ switch (S_GET_RAW_TYPE (symbolP))
+ {
+ case N_SLINE:
+ dsc = S_GET_DESC (symbolP);
+ if (dsc > Cur_File->max_line)
+ Cur_File->max_line = dsc;
+ if (dsc < Cur_File->min_line)
+ Cur_File->min_line = dsc;
+ break;
+ case N_SO:
+ Cur_File = find_file (symbolP);
+ Cur_File->flag = 1;
+ Cur_File->min_line = 1;
+ break;
+ case N_SOL:
+ Cur_File = find_file (symbolP);
+ break;
+ case N_GSYM:
+ VMS_GSYM_Parse (symbolP, Text_Psect);
+ break;
+ case N_LCSYM:
+ VMS_LCSYM_Parse (symbolP, Text_Psect);
+ break;
+ case N_FUN: /* For static constant symbols */
+ case N_STSYM:
+ VMS_STSYM_Parse (symbolP, Text_Psect);
+ break;
+ default:
+ break;
+ } /* switch */
+ } /* for */
+
+ /*
+ * Now we take a quick sweep through the files and assign offsets
+ * to each one. This will essentially be the starting line number to
+ * the debugger for each file. Output the info for the debugger to
+ * specify the files, and then tell it how many lines to use.
+ */
+ for (Cur_File = file_root; Cur_File; Cur_File = Cur_File->next)
+ {
+ if (Cur_File->max_line == 0)
+ continue;
+ if ((strncmp (Cur_File->name, "GNU_GXX_INCLUDE:", 16) == 0) &&
+ !flag_debug)
+ continue;
+ if ((strncmp (Cur_File->name, "GNU_CC_INCLUDE:", 15) == 0) &&
+ !flag_debug)
+ continue;
+ /* show a few extra lines at the start of the region selected */
+ if (Cur_File->min_line > 2)
+ Cur_File->min_line -= 2;
+ Cur_File->offset = Debugger_Offset - Cur_File->min_line + 1;
+ Debugger_Offset += Cur_File->max_line - Cur_File->min_line + 1;
+ if (Cur_File->same_file_fpnt)
+ {
+ Cur_File->file_number = Cur_File->same_file_fpnt->file_number;
+ }
+ else
+ {
+ Cur_File->file_number = ++File_Number;
+ file_available = VMS_TBT_Source_File (Cur_File->name,
+ Cur_File->file_number);
+ if (!file_available)
+ {
+ Cur_File->file_number = 0;
+ File_Number--;
+ continue;
+ }
+ }
+ VMS_TBT_Source_Lines (Cur_File->file_number,
+ Cur_File->min_line,
+ Cur_File->max_line - Cur_File->min_line + 1);
+ } /* for */
+ Cur_File = (struct input_file *) NULL;
+
+ /*
+ * Scan the symbols and write out the routines
+ * (this makes the assumption that symbols are in
+ * order of ascending text segment offset)
+ */
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next (symbolP))
+ {
+ /*
+ * Deal with text symbols.
+ */
+ if (!S_IS_DEBUG (symbolP) && S_GET_TYPE (symbolP) == N_TEXT)
+ {
+ /*
+ * Ignore symbols starting with "L", as they are local symbols.
+ */
+ if (*S_GET_NAME (symbolP) == 'L')
+ continue;
+ /*
+ * If there is a routine start defined, terminate it.
+ */
+ if (Current_Routine)
+ VMS_TBT_Routine_End (text_siz, Current_Routine);
+
+ /*
+ * Check for & skip dummy labels like "gcc_compiled.".
+ * They're identified by the IN_DEFAULT_SECTION flag.
+ */
+ if ((S_GET_OTHER (symbolP) & IN_DEFAULT_SECTION) != 0 &&
+ S_GET_VALUE (symbolP) == 0)
+ continue;
+ /*
+ * Store the routine begin traceback info.
+ */
+ VMS_TBT_Routine_Begin (symbolP, Text_Psect);
+ Current_Routine = symbolP;
+ /*
+ * Define symbols local to this routine.
+ */
+ local_symbols_DST (symbolP, Current_Routine);
+ /*
+ * Done
+ */
+ continue;
+
+ }
+ /*
+ * Deal with STAB symbols.
+ */
+ else if (S_IS_DEBUG (symbolP))
+ {
+ /*
+ * Dispatch on STAB type.
+ */
+ switch (S_GET_RAW_TYPE (symbolP))
+ {
+ /*
+ * Line number
+ */
+ case N_SLINE:
+ /* Offset the line into the correct portion of the file. */
+ if (Cur_File->file_number == 0)
+ break;
+ val = S_GET_VALUE (symbolP);
+ /* Sometimes the same offset gets several source lines
+ assigned to it. We should be selective about which
+ lines we allow, we should prefer lines that are in
+ the main source file when debugging inline functions. */
+ if (val == Cur_Offset && Cur_File->file_number != 1)
+ break;
+
+ /* calculate actual debugger source line */
+ dsc = S_GET_DESC (symbolP) + Cur_File->offset;
+ S_SET_DESC (symbolP, dsc);
+ /*
+ * Define PC/Line correlation.
+ */
+ if (Cur_Offset == -1)
+ {
+ /*
+ * First N_SLINE; set up initial correlation.
+ */
+ VMS_TBT_Line_PC_Correlation (dsc,
+ val,
+ Text_Psect,
+ 0);
+ }
+ else if ((dsc - Cur_Line_Number) <= 0)
+ {
+ /*
+ * Line delta is not +ve, we need to close the line and
+ * start a new PC/Line correlation.
+ */
+ VMS_TBT_Line_PC_Correlation (0,
+ val - Cur_Offset,
+ 0,
+ -1);
+ VMS_TBT_Line_PC_Correlation (dsc,
+ val,
+ Text_Psect,
+ 0);
+ }
+ else
+ {
+ /*
+ * Line delta is +ve, all is well.
+ */
+ VMS_TBT_Line_PC_Correlation (dsc - Cur_Line_Number,
+ val - Cur_Offset,
+ 0,
+ 1);
+ }
+ /* Update the current line/PC info. */
+ Cur_Line_Number = dsc;
+ Cur_Offset = val;
+ break;
+
+ /*
+ * Source file
+ */
+ case N_SO:
+ /* Remember that we had a source file and emit
+ the source file debugger record. */
+ Cur_File = find_file (symbolP);
+ break;
+
+ case N_SOL:
+ /* We need to make sure that we are really in the actual
+ source file when we compute the maximum line number.
+ Otherwise the debugger gets really confused. */
+ Cur_File = find_file (symbolP);
+ break;
+
+ default:
+ break;
+ } /* switch */
+ } /* if (IS_DEBUG) */
+ } /* for */
+
+ /*
+ * If there is a routine start defined, terminate it
+ * (and the line numbers).
+ */
+ if (Current_Routine)
+ {
+ /* Terminate the line numbers. */
+ VMS_TBT_Line_PC_Correlation (0,
+ text_siz - S_GET_VALUE (Current_Routine),
+ 0,
+ -1);
+ /* Terminate the routine. */
+ VMS_TBT_Routine_End (text_siz, Current_Routine);
+ }
+
+ /* Write the Traceback End Module TBT record. */
+ VMS_TBT_Module_End ();
+}
+
+
+/* Write a VAX/VMS object file (everything else has been done!). */
+
+void
+vms_write_object_file (text_siz, data_siz, bss_siz, text_frag_root,
+ data_frag_root)
+ unsigned text_siz;
+ unsigned data_siz;
+ unsigned bss_siz;
+ fragS *text_frag_root;
+ fragS *data_frag_root;
+{
+ register struct VMS_Symbol *vsp;
+
+ /*
+ * Initialize program section indices; values get updated later.
+ */
+ Psect_Number = 0; /* next Psect Index to use */
+ Text_Psect = -1; /* Text Psect Index */
+ Data_Psect = -2; /* Data Psect Index JF: Was -1 */
+ Bss_Psect = -3; /* Bss Psect Index JF: Was -1 */
+ Ctors_Psect = -4; /* Ctors Psect Index */
+ Dtors_Psect = -5; /* Dtors Psect Index */
+ /* Initialize other state variables. */
+ Data_Segment = 0;
+ Local_Initd_Data_Size = 0;
+
+ /*
+ * Create the actual output file and populate it with required
+ * "module header" information.
+ */
+ Create_VMS_Object_File ();
+ Write_VMS_MHD_Records ();
+
+ /*
+ * Create the Data segment:
+ *
+ * Since this is REALLY hard to do any other way,
+ * we actually manufacture the data segment and
+ * then store the appropriate values out of it.
+ * We need to generate this early, so that globalvalues
+ * can be properly emitted.
+ */
+ if (data_siz > 0)
+ synthesize_data_segment (data_siz, text_siz, data_frag_root);
+
+
+ /******* Global Symbol Directory *******/
+
+ /*
+ * Emit globalvalues now. We must do this before the text psect is
+ * defined, or we will get linker warnings about multiply defined
+ * symbols. All of the globalvalues "reference" psect 0, although
+ * it really does not have anything to do with it.
+ */
+ VMS_Emit_Globalvalues (text_siz, data_siz, Data_Segment);
+ /*
+ * Define the Text Psect
+ */
+ Text_Psect = Psect_Number++;
+ VMS_Psect_Spec ("$code", text_siz, ps_TEXT, 0);
+ /*
+ * Define the BSS Psect
+ */
+ if (bss_siz > 0)
+ {
+ Bss_Psect = Psect_Number++;
+ VMS_Psect_Spec ("$uninitialized_data", bss_siz, ps_DATA, 0);
+ }
+ /*
+ * Define symbols to the linker.
+ */
+ global_symbol_directory (text_siz, data_siz);
+ /*
+ * Define the Data Psect
+ */
+ if (data_siz > 0 && Local_Initd_Data_Size > 0)
+ {
+ Data_Psect = Psect_Number++;
+ VMS_Psect_Spec ("$data", Local_Initd_Data_Size, ps_DATA, 0);
+ /*
+ * Local initialized data (N_DATA) symbols need to be updated to the
+ * proper value of Data_Psect now that it's actually been defined.
+ * (A dummy value was used in global_symbol_directory() above.)
+ */
+ for (vsp = VMS_Symbols; vsp; vsp = vsp->Next)
+ if (vsp->Psect_Index < 0 && S_GET_RAW_TYPE (vsp->Symbol) == N_DATA)
+ vsp->Psect_Index = Data_Psect;
+ }
+
+
+ if (Ctors_Symbols != 0)
+ {
+ char *ps_name = "$ctors";
+ Ctors_Psect = Psect_Number++;
+ VMS_Psect_Spec (ps_name, Ctors_Symbols->Psect_Offset + XTOR_SIZE,
+ ps_CTORS, 0);
+ VMS_Global_Symbol_Spec (ps_name, Ctors_Psect,
+ 0, GBLSYM_DEF|GBLSYM_WEAK);
+ for (vsp = Ctors_Symbols; vsp; vsp = vsp->Next)
+ vsp->Psect_Index = Ctors_Psect;
+ }
+
+ if (Dtors_Symbols != 0)
+ {
+ char *ps_name = "$dtors";
+ Dtors_Psect = Psect_Number++;
+ VMS_Psect_Spec (ps_name, Dtors_Symbols->Psect_Offset + XTOR_SIZE,
+ ps_DTORS, 0);
+ VMS_Global_Symbol_Spec (ps_name, Dtors_Psect,
+ 0, GBLSYM_DEF|GBLSYM_WEAK);
+ for (vsp = Dtors_Symbols; vsp; vsp = vsp->Next)
+ vsp->Psect_Index = Dtors_Psect;
+ }
+
+ /******* Text Information and Relocation Records *******/
+
+ /*
+ * Write the text segment data
+ */
+ if (text_siz > 0)
+ vms_fixup_text_section (text_siz, text_frag_root, data_frag_root);
+ /*
+ * Write the data segment data, then discard it.
+ */
+ if (data_siz > 0)
+ {
+ vms_fixup_data_section (data_siz, text_siz);
+ free (Data_Segment), Data_Segment = 0;
+ }
+
+ if (Ctors_Symbols != 0)
+ {
+ vms_fixup_xtors_section (Ctors_Symbols, Ctors_Psect);
+ }
+
+ if (Dtors_Symbols != 0)
+ {
+ vms_fixup_xtors_section (Dtors_Symbols, Dtors_Psect);
+ }
+
+
+ /******* Debugger Symbol Table Records *******/
+
+ vms_build_DST (text_siz);
+
+
+ /******* Wrap things up *******/
+
+ /*
+ * Write the End Of Module record
+ */
+ if (Entry_Point_Symbol)
+ Write_VMS_EOM_Record (Text_Psect, S_GET_VALUE (Entry_Point_Symbol));
+ else
+ Write_VMS_EOM_Record (-1, (valueT) 0);
+
+ /*
+ * All done, close the object file
+ */
+ Close_VMS_Object_File ();
+}
+
+/* end of obj-vms.c */
diff --git a/gas/config/obj-vms.h b/gas/config/obj-vms.h
new file mode 100644
index 0000000..ac0c1fb
--- /dev/null
+++ b/gas/config/obj-vms.h
@@ -0,0 +1,552 @@
+/* VMS object file format
+ Copyright (C) 1989, 90, 91, 92, 93, 94, 95, 96, 1997
+ Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2,
+or (at your option) any later version.
+
+GAS is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
+
+/* Tag to validate a.out object file format processing */
+#define OBJ_VMS 1
+
+#include "targ-cpu.h"
+
+#define LONGWORD_ALIGNMENT 2
+
+/* This macro controls subsection alignment within a section.
+ *
+ * Under VAX/VMS, the linker (and PSECT specifications)
+ * take care of correctly aligning the segments.
+ * Doing the alignment here (on initialized data) can
+ * mess up the calculation of global data PSECT sizes.
+ */
+#define SUB_SEGMENT_ALIGN(SEG) \
+ (((SEG) == data_section) ? 0 : LONGWORD_ALIGNMENT)
+
+/* This flag is used to remember whether we are in the const or the
+ data section. By and large they are identical, but we set a no-write
+ bit for psects in the const section. */
+
+extern unsigned char const_flag;
+
+/* This is overloaded onto const_flag, for convenience. It's used to flag
+ dummy labels like "gcc2_compiled." which occur before the first .text
+ or .data section directive. */
+
+#define IN_DEFAULT_SECTION 0x80
+
+/* These are defined in obj-vms.c. */
+extern const short seg_N_TYPE[];
+extern const segT N_TYPE_seg[];
+
+#undef NO_RELOC
+enum reloc_type
+ {
+ NO_RELOC, RELOC_32
+ };
+
+#define N_BADMAG(x) (0)
+#define N_TXTOFF(x) ( sizeof(struct exec) )
+#define N_DATOFF(x) ( N_TXTOFF(x) + (x).a_text )
+#define N_TROFF(x) ( N_DATOFF(x) + (x).a_data )
+#define N_DROFF(x) ( N_TROFF(x) + (x).a_trsize )
+#define N_SYMOFF(x) ( N_DROFF(x) + (x).a_drsize )
+#define N_STROFF(x) ( N_SYMOFF(x) + (x).a_syms )
+
+/* We use this copy of the exec header for VMS. We do not actually use it, but
+ what we actually do is let gas fill in the relevant slots, and when we get
+ around to writing an obj file, we just pick out what we need. */
+
+struct exec
+{
+ unsigned long a_text; /* length of text, in bytes */
+ unsigned long a_data; /* length of data, in bytes */
+ unsigned long a_bss; /* length of uninitialized data area for file, in bytes */
+ unsigned long a_trsize; /* length of relocation info for text, in bytes */
+ unsigned long a_drsize; /* length of relocation info for data, in bytes */
+ unsigned long a_entry; /* start address */
+ unsigned long a_syms; /* length of symbol table data in file, in bytes */
+};
+
+typedef struct
+ {
+ struct exec header; /* a.out header */
+ long string_table_size; /* names + '\0' + sizeof(int) */
+ }
+object_headers;
+
+/* A single entry in the symbol table
+ * (this started as a clone of bout.h's nlist, but much was unneeded).
+ */
+struct nlist
+ {
+ char *n_name;
+ unsigned char n_type; /* See below */
+ unsigned char n_other; /* used for const_flag and "default section" */
+ unsigned : 16; /* padding for alignment */
+ int n_desc; /* source line number for N_SLINE stabs */
+ };
+
+/* Legal values of n_type (see aout/stab.def for the majority of the codes).
+ */
+#define N_UNDF 0 /* Undefined symbol */
+#define N_ABS 2 /* Absolute symbol */
+#define N_TEXT 4 /* Text symbol */
+#define N_DATA 6 /* Data symbol */
+#define N_BSS 8 /* BSS symbol */
+#define N_FN 31 /* Filename symbol */
+
+#define N_EXT 1 /* External symbol (OR'd in with one of above) */
+#define N_TYPE 036 /* Mask for all the type bits */
+
+#define N_STAB 0340 /* Mask for all bits used for SDB entries */
+
+#include "aout/stab_gnu.h"
+
+/* SYMBOL TABLE */
+/* Symbol table entry data type */
+
+typedef struct nlist obj_symbol_type; /* Symbol table entry */
+
+/* Symbol table macros and constants */
+
+#define OBJ_SYMFIELD_TYPE struct VMS_Symbol *
+
+/*
+ * Macros to extract information from a symbol table entry.
+ * This syntaxic indirection allows independence regarding a.out or coff.
+ * The argument (s) of all these macros is a pointer to a symbol table entry.
+ */
+
+/* True if the symbol is external */
+#define S_IS_EXTERNAL(s) ((s)->sy_symbol.n_type & N_EXT)
+
+/* True if symbol has been defined, ie is in N_{TEXT,DATA,BSS,ABS} or N_EXT */
+#define S_IS_DEFINED(s) (S_GET_TYPE(s) != N_UNDF)
+
+#define S_IS_COMMON(s) (S_GET_TYPE(s) == N_UNDF && S_GET_VALUE(s) != 0)
+
+#define S_IS_REGISTER(s) ((s)->sy_symbol.n_type == N_REGISTER)
+
+/* True if a debug special symbol entry */
+#define S_IS_DEBUG(s) ((s)->sy_symbol.n_type & N_STAB)
+/* True if a symbol is local symbol name */
+/* A symbol name whose name begin with ^A is a gas internal pseudo symbol
+ nameless symbols come from .stab directives. */
+#define S_IS_LOCAL(s) (S_GET_NAME(s) && \
+ !S_IS_DEBUG(s) && \
+ (strchr(S_GET_NAME(s), '\001') != 0 || \
+ strchr(S_GET_NAME(s), '\002') != 0 || \
+ (S_LOCAL_NAME(s) && !flag_keep_locals)))
+/* True if a symbol is not defined in this file */
+#define S_IS_EXTERN(s) ((s)->sy_symbol.n_type & N_EXT)
+/* True if the symbol has been generated because of a .stabd directive */
+#define S_IS_STABD(s) (S_GET_NAME(s) == (char *)0)
+
+/* Accessors */
+/* The name of the symbol */
+#define S_GET_NAME(s) ((s)->sy_symbol.n_name)
+/* The pointer to the string table */
+#define S_GET_OFFSET(s) ((s)->sy_name_offset)
+/* The raw type of the symbol */
+#define S_GET_RAW_TYPE(s) ((s)->sy_symbol.n_type)
+/* The type of the symbol */
+#define S_GET_TYPE(s) ((s)->sy_symbol.n_type & N_TYPE)
+/* The numeric value of the segment */
+#define S_GET_SEGMENT(s) (N_TYPE_seg[S_GET_TYPE(s)])
+/* The n_other expression value */
+#define S_GET_OTHER(s) ((s)->sy_symbol.n_other)
+/* The n_desc expression value */
+#define S_GET_DESC(s) ((s)->sy_symbol.n_desc)
+
+/* Modifiers */
+/* Assume that a symbol cannot be simultaneously in more than on segment */
+/* set segment */
+#define S_SET_SEGMENT(s,seg) ((s)->sy_symbol.n_type &= ~N_TYPE,(s)->sy_symbol.n_type|=SEGMENT_TO_SYMBOL_TYPE(seg))
+/* The symbol is external */
+#define S_SET_EXTERNAL(s) ((s)->sy_symbol.n_type |= N_EXT)
+/* The symbol is not external */
+#define S_CLEAR_EXTERNAL(s) ((s)->sy_symbol.n_type &= ~N_EXT)
+/* Set the name of the symbol */
+#define S_SET_NAME(s,v) ((s)->sy_symbol.n_name = (v))
+/* Set the offset in the string table */
+#define S_SET_OFFSET(s,v) ((s)->sy_name_offset = (v))
+/* Set the n_other expression value */
+#define S_SET_OTHER(s,v) ((s)->sy_symbol.n_other = (v))
+/* Set the n_desc expression value */
+#define S_SET_DESC(s,v) ((s)->sy_symbol.n_desc = (v))
+/* Set the n_type expression value */
+#define S_SET_TYPE(s,v) ((s)->sy_symbol.n_type = (v))
+
+
+/* File header macro and type definition */
+
+#define H_GET_TEXT_SIZE(h) ((h)->header.a_text)
+#define H_GET_DATA_SIZE(h) ((h)->header.a_data)
+#define H_GET_BSS_SIZE(h) ((h)->header.a_bss)
+
+#define H_SET_TEXT_SIZE(h,v) ((h)->header.a_text = md_section_align(SEG_TEXT, (v)))
+#define H_SET_DATA_SIZE(h,v) ((h)->header.a_data = md_section_align(SEG_DATA, (v)))
+#define H_SET_BSS_SIZE(h,v) ((h)->header.a_bss = md_section_align(SEG_BSS, (v)))
+
+#define H_SET_STRING_SIZE(h,v) ((h)->string_table_size = (v))
+#define H_SET_SYMBOL_TABLE_SIZE(h,v) ((h)->header.a_syms = (v) * \
+ sizeof(struct nlist))
+
+/* line numbering stuff. */
+#define OBJ_EMIT_LINENO(a, b, c) {;}
+
+#define obj_symbol_new_hook(s) {;}
+
+/* Force structure tags into scope so that their use in prototypes
+ will never be their first occurance. */
+struct fix;
+struct symbol;
+struct frag;
+
+/* obj-vms routines visible to the rest of gas. */
+
+extern void tc_aout_fix_to_chars PARAMS ((char *,struct fix *,relax_addressT));
+
+extern int vms_resolve_symbol_redef PARAMS ((struct symbol *));
+#define RESOLVE_SYMBOL_REDEFINITION(X) vms_resolve_symbol_redef(X)
+
+/* Compiler-generated label "__vax_g_doubles" is used to augment .stabs. */
+extern void vms_check_for_special_label PARAMS ((struct symbol *));
+#define obj_frob_label(X) vms_check_for_special_label(X)
+
+extern void vms_check_for_main PARAMS ((void));
+
+extern void vms_write_object_file PARAMS ((unsigned,unsigned,unsigned,
+ struct frag *,struct frag *));
+
+/* VMS executables are nothing like a.out, but the VMS port of gcc uses
+ a.out format stabs which obj-vms.c then translates. */
+
+#define AOUT_STABS
+
+
+#ifdef WANT_VMS_OBJ_DEFS
+
+/* The rest of this file contains definitions for constants used within
+ the actual VMS object file. We do not use a $ in the symbols (as per
+ usual VMS convention) since System V gags on it. */
+
+#define OBJ_S_C_HDR 0
+#define OBJ_S_C_HDR_MHD 0
+#define OBJ_S_C_HDR_LNM 1
+#define OBJ_S_C_HDR_SRC 2
+#define OBJ_S_C_HDR_TTL 3
+#define OBJ_S_C_HDR_CPR 4
+#define OBJ_S_C_HDR_MTC 5
+#define OBJ_S_C_HDR_GTX 6
+#define OBJ_S_C_GSD 1
+#define OBJ_S_C_GSD_PSC 0
+#define OBJ_S_C_GSD_SYM 1
+#define OBJ_S_C_GSD_EPM 2
+#define OBJ_S_C_GSD_PRO 3
+#define OBJ_S_C_GSD_SYMW 4
+#define OBJ_S_C_GSD_EPMW 5
+#define OBJ_S_C_GSD_PROW 6
+#define OBJ_S_C_GSD_IDC 7
+#define OBJ_S_C_GSD_ENV 8
+#define OBJ_S_C_GSD_LSY 9
+#define OBJ_S_C_GSD_LEPM 10
+#define OBJ_S_C_GSD_LPRO 11
+#define OBJ_S_C_GSD_SPSC 12
+#define OBJ_S_C_TIR 2
+#define OBJ_S_C_EOM 3
+#define OBJ_S_C_DBG 4
+#define OBJ_S_C_TBT 5
+#define OBJ_S_C_LNK 6
+#define OBJ_S_C_EOMW 7
+#define OBJ_S_C_MAXRECTYP 7
+#define OBJ_S_K_SUBTYP 1
+#define OBJ_S_C_SUBTYP 1
+#define OBJ_S_C_MAXRECSIZ 2048
+#define OBJ_S_C_STRLVL 0
+#define OBJ_S_C_SYMSIZ 31
+#define OBJ_S_C_STOREPLIM -1
+#define OBJ_S_C_PSCALILIM 9
+
+#define MHD_S_C_MHD 0
+#define MHD_S_C_LNM 1
+#define MHD_S_C_SRC 2
+#define MHD_S_C_TTL 3
+#define MHD_S_C_CPR 4
+#define MHD_S_C_MTC 5
+#define MHD_S_C_GTX 6
+#define MHD_S_C_MAXHDRTYP 6
+
+#define GSD_S_K_ENTRIES 1
+#define GSD_S_C_ENTRIES 1
+#define GSD_S_C_PSC 0
+#define GSD_S_C_SYM 1
+#define GSD_S_C_EPM 2
+#define GSD_S_C_PRO 3
+#define GSD_S_C_SYMW 4
+#define GSD_S_C_EPMW 5
+#define GSD_S_C_PROW 6
+#define GSD_S_C_IDC 7
+#define GSD_S_C_ENV 8
+#define GSD_S_C_LSY 9
+#define GSD_S_C_LEPM 10
+#define GSD_S_C_LPRO 11
+#define GSD_S_C_SPSC 12
+#define GSD_S_C_SYMV 13
+#define GSD_S_C_EPMV 14
+#define GSD_S_C_PROV 15
+#define GSD_S_C_MAXRECTYP 15
+
+#define GSY_S_M_WEAK 1
+#define GSY_S_M_DEF 2
+#define GSY_S_M_UNI 4
+#define GSY_S_M_REL 8
+
+#define LSY_S_M_DEF 2
+#define LSY_S_M_REL 8
+
+#define ENV_S_M_DEF 1
+#define ENV_S_M_NESTED 2
+
+#define GPS_S_M_PIC 1
+#define GPS_S_M_LIB 2
+#define GPS_S_M_OVR 4
+#define GPS_S_M_REL 8
+#define GPS_S_M_GBL 16
+#define GPS_S_M_SHR 32
+#define GPS_S_M_EXE 64
+#define GPS_S_M_RD 128
+#define GPS_S_M_WRT 256
+#define GPS_S_M_VEC 512
+#define GPS_S_K_NAME 9
+#define GPS_S_C_NAME 9
+
+#define TIR_S_C_STA_GBL 0
+#define TIR_S_C_STA_SB 1
+#define TIR_S_C_STA_SW 2
+#define TIR_S_C_STA_LW 3
+#define TIR_S_C_STA_PB 4
+#define TIR_S_C_STA_PW 5
+#define TIR_S_C_STA_PL 6
+#define TIR_S_C_STA_UB 7
+#define TIR_S_C_STA_UW 8
+#define TIR_S_C_STA_BFI 9
+#define TIR_S_C_STA_WFI 10
+#define TIR_S_C_STA_LFI 11
+#define TIR_S_C_STA_EPM 12
+#define TIR_S_C_STA_CKARG 13
+#define TIR_S_C_STA_WPB 14
+#define TIR_S_C_STA_WPW 15
+#define TIR_S_C_STA_WPL 16
+#define TIR_S_C_STA_LSY 17
+#define TIR_S_C_STA_LIT 18
+#define TIR_S_C_STA_LEPM 19
+#define TIR_S_C_MAXSTACOD 19
+#define TIR_S_C_MINSTOCOD 20
+#define TIR_S_C_STO_SB 20
+#define TIR_S_C_STO_SW 21
+#define TIR_S_C_STO_L 22
+#define TIR_S_C_STO_BD 23
+#define TIR_S_C_STO_WD 24
+#define TIR_S_C_STO_LD 25
+#define TIR_S_C_STO_LI 26
+#define TIR_S_C_STO_PIDR 27
+#define TIR_S_C_STO_PICR 28
+#define TIR_S_C_STO_RSB 29
+#define TIR_S_C_STO_RSW 30
+#define TIR_S_C_STO_RL 31
+#define TIR_S_C_STO_VPS 32
+#define TIR_S_C_STO_USB 33
+#define TIR_S_C_STO_USW 34
+#define TIR_S_C_STO_RUB 35
+#define TIR_S_C_STO_RUW 36
+#define TIR_S_C_STO_B 37
+#define TIR_S_C_STO_W 38
+#define TIR_S_C_STO_RB 39
+#define TIR_S_C_STO_RW 40
+#define TIR_S_C_STO_RIVB 41
+#define TIR_S_C_STO_PIRR 42
+#define TIR_S_C_MAXSTOCOD 42
+#define TIR_S_C_MINOPRCOD 50
+#define TIR_S_C_OPR_NOP 50
+#define TIR_S_C_OPR_ADD 51
+#define TIR_S_C_OPR_SUB 52
+#define TIR_S_C_OPR_MUL 53
+#define TIR_S_C_OPR_DIV 54
+#define TIR_S_C_OPR_AND 55
+#define TIR_S_C_OPR_IOR 56
+#define TIR_S_C_OPR_EOR 57
+#define TIR_S_C_OPR_NEG 58
+#define TIR_S_C_OPR_COM 59
+#define TIR_S_C_OPR_INSV 60
+#define TIR_S_C_OPR_ASH 61
+#define TIR_S_C_OPR_USH 62
+#define TIR_S_C_OPR_ROT 63
+#define TIR_S_C_OPR_SEL 64
+#define TIR_S_C_OPR_REDEF 65
+#define TIR_S_C_OPR_DFLIT 66
+#define TIR_S_C_MAXOPRCOD 66
+#define TIR_S_C_MINCTLCOD 80
+#define TIR_S_C_CTL_SETRB 80
+#define TIR_S_C_CTL_AUGRB 81
+#define TIR_S_C_CTL_DFLOC 82
+#define TIR_S_C_CTL_STLOC 83
+#define TIR_S_C_CTL_STKDL 84
+#define TIR_S_C_MAXCTLCOD 84
+
+/*
+ * Debugger symbol definitions: These are done by hand, as no
+ * machine-readable version seems
+ * to be available.
+ */
+#define DST_S_C_C 7 /* Language == "C" */
+#define DST_S_C_CXX 15 /* Language == "C++" */
+#define DST_S_C_VERSION 153
+#define DST_S_C_SOURCE 155 /* Source file */
+#define DST_S_C_PROLOG 162
+#define DST_S_C_BLKBEG 176 /* Beginning of block */
+#define DST_S_C_BLKEND 177 /* End of block */
+#define DST_S_C_ENTRY 181
+#define DST_S_C_PSECT 184
+#define DST_S_C_LINE_NUM 185 /* Line Number */
+#define DST_S_C_LBLORLIT 186
+#define DST_S_C_LABEL 187
+#define DST_S_C_MODBEG 188 /* Beginning of module */
+#define DST_S_C_MODEND 189 /* End of module */
+#define DST_S_C_RTNBEG 190 /* Beginning of routine */
+#define DST_S_C_RTNEND 191 /* End of routine */
+#define DST_S_C_DELTA_PC_W 1 /* Incr PC */
+#define DST_S_C_INCR_LINUM 2 /* Incr Line # */
+#define DST_S_C_INCR_LINUM_W 3 /* Incr Line # */
+#define DST_S_C_SET_LINUM_INCR 4
+#define DST_S_C_SET_LINUM_INCR_W 5
+#define DST_S_C_RESET_LINUM_INCR 6
+#define DST_S_C_BEG_STMT_MODE 7
+#define DST_S_C_END_STMT_MODE 8
+#define DST_S_C_SET_LINE_NUM 9 /* Set Line # */
+#define DST_S_C_SET_PC 10
+#define DST_S_C_SET_PC_W 11
+#define DST_S_C_SET_PC_L 12
+#define DST_S_C_SET_STMTNUM 13
+#define DST_S_C_TERM 14 /* End of lines */
+#define DST_S_C_TERM_W 15 /* End of lines */
+#define DST_S_C_SET_ABS_PC 16 /* Set PC */
+#define DST_S_C_DELTA_PC_L 17 /* Incr PC */
+#define DST_S_C_INCR_LINUM_L 18 /* Incr Line # */
+#define DST_S_C_SET_LINUM_B 19 /* Set Line # */
+#define DST_S_C_SET_LINUM_L 20 /* Set Line # */
+#define DST_S_C_TERM_L 21 /* End of lines */
+/* these are used with DST_S_C_SOURCE */
+#define DST_S_C_SRC_DECLFILE 1 /* Declare source file */
+#define DST_S_C_SRC_SETFILE 2 /* Set source file */
+#define DST_S_C_SRC_SETREC_L 3 /* Set record, longword value */
+#define DST_S_C_SRC_SETREC_W 4 /* Set record, word value */
+#define DST_S_C_SRC_DEFLINES_W 10 /* # of line, word counter */
+#define DST_S_C_SRC_DEFLINES_B 11 /* # of line, byte counter */
+#define DST_S_C_SRC_FORMFEED 16 /* ^L counts as a record */
+/* the following are the codes for the various data types. Anything not on
+ * the list is included under 'advanced_type'
+ */
+#define DBG_S_C_UCHAR 0x02
+#define DBG_S_C_USINT 0x03
+#define DBG_S_C_ULINT 0x04
+#define DBG_S_C_UQUAD 0x05
+#define DBG_S_C_SCHAR 0x06
+#define DBG_S_C_SSINT 0x07
+#define DBG_S_C_SLINT 0x08
+#define DBG_S_C_SQUAD 0x09
+#define DBG_S_C_REAL4 0x0a
+#define DBG_S_C_REAL8 0x0b /* D_float double */
+#define DBG_S_C_COMPLX4 0x0c /* 2xF_float complex float */
+#define DBG_S_C_COMPLX8 0x0d /* 2xD_float complex double */
+#define DBG_S_C_REAL8_G 0x1b /* G_float double */
+#define DBG_S_C_COMPLX8_G 0x1d /* 2xG_float complex double */
+#define DBG_S_C_FUNCTION_ADDR 0x17
+#define DBG_S_C_ADVANCED_TYPE 0xa3
+/* Some of these are just for future reference. [pr]
+ */
+#define DBG_S_C_UBITA 0x01 /* unsigned, aligned bit field */
+#define DBG_S_C_UBITU 0x22 /* unsigned, unaligned bit field */
+#define DBG_S_C_SBITA 0x29 /* signed, aligned bit field */
+#define DBG_S_C_SBITU 0x2a /* signed, unaligned bit field */
+#define DBG_S_C_CSTRING 0x2e /* asciz ('\0' terminated) string */
+#define DBG_S_C_WCHAR 0x38 /* wchar_t */
+/* These are descriptor class codes.
+ */
+#define DSC_K_CLASS_S 0x01 /* static (fixed length) */
+#define DSC_K_CLASS_D 0x02 /* dynamic string (not via malloc!) */
+#define DSC_K_CLASS_A 0x04 /* array */
+#define DSC_K_CLASS_UBS 0x0d /* unaligned bit string */
+/* These are the codes that are used to generate the definitions of struct
+ * union and enum records
+ */
+#define DBG_S_C_ENUM_ITEM 0xa4
+#define DBG_S_C_ENUM_START 0xa5
+#define DBG_S_C_ENUM_END 0xa6
+#define DBG_S_C_STRUCT_ITEM DST_K_VFLAGS_BITOFFS /* 0xff */
+#define DBG_S_C_STRUCT_START 0xab
+#define DBG_S_C_STRUCT_END 0xac
+#define DST_K_TYPSPEC 0xaf /* type specification */
+/* These codes are used in the generation of the symbol definition records
+ */
+#define DST_K_VFLAGS_NOVAL 0x80 /* struct definition only */
+#define DST_K_VFLAGS_DSC 0xfa /* descriptor used */
+#define DST_K_VFLAGS_TVS 0xfb /* trailing value specified */
+#define DST_K_VS_FOLLOWS 0xfd /* value spec follows */
+#define DST_K_VFLAGS_BITOFFS 0xff /* value contains bit offset */
+#define DST_K_VALKIND_LITERAL 0
+#define DST_K_VALKIND_ADDR 1
+#define DST_K_VALKIND_DESC 2
+#define DST_K_VALKIND_REG 3
+#define DST_K_REG_VAX_AP 0x0c /* R12 */
+#define DST_K_REG_VAX_FP 0x0d /* R13 */
+#define DST_K_REG_VAX_SP 0x0e /* R14 */
+#define DST_V_VALKIND 0 /* offset of valkind field */
+#define DST_V_INDIRECT 2 /* offset to indirect bit */
+#define DST_V_DISP 3 /* offset to displacement bit */
+#define DST_V_REGNUM 4 /* offset to register number */
+#define DST_M_INDIRECT (1<<DST_V_INDIRECT)
+#define DST_M_DISP (1<<DST_V_DISP)
+#define DBG_C_FUNCTION_PARAM /* 0xc9 */ \
+ (DST_K_VALKIND_ADDR|DST_M_DISP|(DST_K_REG_VAX_AP<<DST_V_REGNUM))
+#define DBG_C_LOCAL_SYM /* 0xd9 */ \
+ (DST_K_VALKIND_ADDR|DST_M_DISP|(DST_K_REG_VAX_FP<<DST_V_REGNUM))
+/* Kinds of value specifications
+ */
+#define DST_K_VS_ALLOC_SPLIT 3 /* split lifetime */
+/* Kinds of type specifications
+ */
+#define DST_K_TS_ATOM 0x01 /* atomic type specification */
+#define DST_K_TS_DSC 0x02 /* descriptor type spec */
+#define DST_K_TS_IND 0x03 /* indirect type specification */
+#define DST_K_TS_TPTR 0x04 /* typed pointer type spec */
+#define DST_K_TS_PTR 0x05 /* pointer type spec */
+#define DST_K_TS_ARRAY 0x07 /* array type spec */
+#define DST_K_TS_NOV_LENG 0x0e /* novel length type spec */
+/* These are the codes that are used in the suffix records to determine the
+ * actual data type
+ */
+#define DBG_S_C_BASIC DST_K_TS_ATOM
+#define DBG_S_C_BASIC_ARRAY DST_K_TS_DSC
+#define DBG_S_C_STRUCT DST_K_TS_IND
+#define DBG_S_C_POINTER DST_K_TS_TPTR
+#define DBG_S_C_VOID DST_K_TS_PTR
+#define DBG_S_C_COMPLEX_ARRAY DST_K_TS_ARRAY
+
+#endif /* WANT_VMS_OBJ_DEFS */
+
+/* end of obj-vms.h */
diff --git a/gas/config/tc-a29k.c b/gas/config/tc-a29k.c
new file mode 100644
index 0000000..600fec5
--- /dev/null
+++ b/gas/config/tc-a29k.c
@@ -0,0 +1,1296 @@
+/* tc-a29k.c -- Assemble for the AMD 29000.
+ Copyright (C) 1989, 90, 91, 92, 93, 94, 95, 1998
+ Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+/* John Gilmore has reorganized this module somewhat, to make it easier
+ to convert it to new machines' assemblers as desired. There was too
+ much bloody rewriting required before. There still probably is. */
+
+#include <ctype.h>
+#include "as.h"
+
+#include "opcode/a29k.h"
+
+/* Make it easier to clone this machine desc into another one. */
+#define machine_opcode a29k_opcode
+#define machine_opcodes a29k_opcodes
+#define machine_ip a29k_ip
+#define machine_it a29k_it
+
+#define IMMEDIATE_BIT 0x01000000 /* Turns RB into Immediate */
+#define ABSOLUTE_BIT 0x01000000 /* Turns PC-relative to Absolute */
+#define CE_BIT 0x00800000 /* Coprocessor enable in LOAD */
+#define UI_BIT 0x00000080 /* Unsigned integer in CONVERT */
+
+/* handle of the OPCODE hash table */
+static struct hash_control *op_hash = NULL;
+
+struct machine_it
+ {
+ char *error;
+ unsigned long opcode;
+ struct nlist *nlistp;
+ expressionS exp;
+ int pcrel;
+ int reloc_offset; /* Offset of reloc within insn */
+
+ int reloc;
+ }
+the_insn;
+
+static void machine_ip PARAMS ((char *str));
+/* static void print_insn PARAMS ((struct machine_it *insn)); */
+#ifndef OBJ_COFF
+static void s_data1 PARAMS ((void));
+static void s_use PARAMS ((int));
+#endif
+
+const pseudo_typeS
+md_pseudo_table[] =
+{
+ {"align", s_align_bytes, 4},
+ {"block", s_space, 0},
+ {"cputype", s_ignore, 0}, /* CPU as 29000 or 29050 */
+ {"reg", s_lsym, 0}, /* Register equate, same as equ */
+ {"space", s_ignore, 0}, /* Listing control */
+ {"sect", s_ignore, 0}, /* Creation of coff sections */
+#ifndef OBJ_COFF
+ /* We can do this right with coff. */
+ {"use", s_use, 0},
+#endif
+ {"word", cons, 4},
+ {NULL, 0, 0},
+};
+
+#if defined(BFD_HEADERS)
+#ifdef RELSZ
+const int md_reloc_size = RELSZ; /* Coff headers */
+#else
+const int md_reloc_size = 12; /* something else headers */
+#endif
+#else
+const int md_reloc_size = 12; /* Not bfdized*/
+#endif
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful */
+const char comment_chars[] = ";";
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that comments like this one will always work */
+const char line_comment_chars[] = "#";
+
+/* We needed an unused char for line separation to work around the
+ lack of macros, using sed and such. */
+const char line_separator_chars[] = "@";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c. Ideally it shouldn't have to know about it at
+ all, but nothing is ideal around here. */
+
+/*
+ * anull bit - causes the branch delay slot instructions to not be executed
+ */
+#define ANNUL (1 << 29)
+
+#ifndef OBJ_COFF
+
+static void
+s_use (ignore)
+ int ignore;
+{
+ if (strncmp (input_line_pointer, ".text", 5) == 0)
+ {
+ input_line_pointer += 5;
+ s_text (0);
+ return;
+ }
+ if (strncmp (input_line_pointer, ".data", 5) == 0)
+ {
+ input_line_pointer += 5;
+ s_data (0);
+ return;
+ }
+ if (strncmp (input_line_pointer, ".data1", 6) == 0)
+ {
+ input_line_pointer += 6;
+ s_data1 ();
+ return;
+ }
+ /* Literals can't go in the text segment because you can't read from
+ instruction memory on some 29k's. So, into initialized data. */
+ if (strncmp (input_line_pointer, ".lit", 4) == 0)
+ {
+ input_line_pointer += 4;
+ subseg_set (SEG_DATA, 200);
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ as_bad (_("Unknown segment type"));
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_data1 ()
+{
+ subseg_set (SEG_DATA, 1);
+ demand_empty_rest_of_line ();
+}
+
+#endif /* OBJ_COFF */
+
+/* Install symbol definition that maps REGNAME to REGNO.
+ FIXME-SOON: These are not recognized in mixed case. */
+
+static void
+insert_sreg (regname, regnum)
+ char *regname;
+ int regnum;
+{
+ /* FIXME-SOON, put something in these syms so they won't be output
+ to the symbol table of the resulting object file. */
+
+ /* Must be large enough to hold the names of the special registers. */
+ char buf[80];
+ int i;
+
+ symbol_table_insert (symbol_new (regname, SEG_REGISTER, (valueT) regnum,
+ &zero_address_frag));
+ for (i = 0; regname[i]; i++)
+ buf[i] = islower (regname[i]) ? toupper (regname[i]) : regname[i];
+ buf[i] = '\0';
+
+ symbol_table_insert (symbol_new (buf, SEG_REGISTER, (valueT) regnum,
+ &zero_address_frag));
+}
+
+/* Install symbol definitions for assorted special registers.
+ See ASM29K Ref page 2-9. */
+
+void
+define_some_regs ()
+{
+#define SREG 256
+
+ /* Protected special-purpose register names */
+ insert_sreg ("vab", SREG + 0);
+ insert_sreg ("ops", SREG + 1);
+ insert_sreg ("cps", SREG + 2);
+ insert_sreg ("cfg", SREG + 3);
+ insert_sreg ("cha", SREG + 4);
+ insert_sreg ("chd", SREG + 5);
+ insert_sreg ("chc", SREG + 6);
+ insert_sreg ("rbp", SREG + 7);
+ insert_sreg ("tmc", SREG + 8);
+ insert_sreg ("tmr", SREG + 9);
+ insert_sreg ("pc0", SREG + 10);
+ insert_sreg ("pc1", SREG + 11);
+ insert_sreg ("pc2", SREG + 12);
+ insert_sreg ("mmu", SREG + 13);
+ insert_sreg ("lru", SREG + 14);
+
+ /* Additional protected special-purpose registers for the 29050 */
+ insert_sreg ("rsn", SREG + 15);
+ insert_sreg ("rma0", SREG + 16);
+ insert_sreg ("rmc0", SREG + 17);
+ insert_sreg ("rma1", SREG + 18);
+ insert_sreg ("rmc1", SREG + 19);
+ insert_sreg ("spc0", SREG + 20);
+ insert_sreg ("spc1", SREG + 21);
+ insert_sreg ("spc2", SREG + 22);
+ insert_sreg ("iba0", SREG + 23);
+ insert_sreg ("ibc0", SREG + 24);
+ insert_sreg ("iba1", SREG + 25);
+ insert_sreg ("ibc1", SREG + 26);
+
+ /* Additional registers for the 29040. */
+ insert_sreg ("dba", SREG + 27);
+ insert_sreg ("dbc", SREG + 28);
+ insert_sreg ("cir", SREG + 29);
+ insert_sreg ("cdr", SREG + 30);
+
+ /* Unprotected special-purpose register names */
+ insert_sreg ("ipc", SREG + 128);
+ insert_sreg ("ipa", SREG + 129);
+ insert_sreg ("ipb", SREG + 130);
+ insert_sreg ("q", SREG + 131);
+ insert_sreg ("alu", SREG + 132);
+ insert_sreg ("bp", SREG + 133);
+ insert_sreg ("fc", SREG + 134);
+ insert_sreg ("cr", SREG + 135);
+ insert_sreg ("fpe", SREG + 160);
+ insert_sreg ("inte", SREG + 161);
+ insert_sreg ("fps", SREG + 162);
+ /* "", SREG+163); Reserved */
+ insert_sreg ("exop", SREG + 164);
+}
+
+/* This function is called once, at assembler startup time. It should
+ set up all the tables, etc., that the MD part of the assembler will
+ need. */
+void
+md_begin ()
+{
+ register const char *retval = NULL;
+ int lose = 0;
+ register int skipnext = 0;
+ register unsigned int i;
+ register char *strend, *strend2;
+
+ /* Hash up all the opcodes for fast use later. */
+
+ op_hash = hash_new ();
+
+ for (i = 0; i < num_opcodes; i++)
+ {
+ const char *name = machine_opcodes[i].name;
+
+ if (skipnext)
+ {
+ skipnext = 0;
+ continue;
+ }
+
+ /* Hack to avoid multiple opcode entries. We pre-locate all the
+ variations (b/i field and P/A field) and handle them. */
+
+ if (!strcmp (name, machine_opcodes[i + 1].name))
+ {
+ if ((machine_opcodes[i].opcode & 0x01000000) != 0
+ || (machine_opcodes[i + 1].opcode & 0x01000000) == 0
+ || ((machine_opcodes[i].opcode | 0x01000000)
+ != machine_opcodes[i + 1].opcode))
+ goto bad_table;
+ strend = machine_opcodes[i].args + strlen (machine_opcodes[i].args) - 1;
+ strend2 = machine_opcodes[i + 1].args + strlen (machine_opcodes[i + 1].args) - 1;
+ switch (*strend)
+ {
+ case 'b':
+ if (*strend2 != 'i')
+ goto bad_table;
+ break;
+ case 'P':
+ if (*strend2 != 'A')
+ goto bad_table;
+ break;
+ default:
+ bad_table:
+ fprintf (stderr, "internal error: can't handle opcode %s\n",
+ name);
+ lose = 1;
+ }
+
+ /* OK, this is an i/b or A/P pair. We skip the
+ higher-valued one, and let the code for operand checking
+ handle OR-ing in the bit. */
+ skipnext = 1;
+ }
+
+ retval = hash_insert (op_hash, name, (PTR) &machine_opcodes[i]);
+ if (retval != NULL)
+ {
+ fprintf (stderr, "internal error: can't hash `%s': %s\n",
+ machine_opcodes[i].name, retval);
+ lose = 1;
+ }
+ }
+
+ if (lose)
+ as_fatal (_("Broken assembler. No assembly attempted."));
+
+ define_some_regs ();
+}
+
+/* Assemble a single instruction. Its label has already been handled
+ by the generic front end. We just parse opcode and operands, and
+ produce the bytes of data and relocation. */
+
+void
+md_assemble (str)
+ char *str;
+{
+ char *toP;
+
+ know (str);
+ machine_ip (str);
+ toP = frag_more (4);
+ /* put out the opcode */
+ md_number_to_chars (toP, the_insn.opcode, 4);
+
+ /* put out the symbol-dependent stuff */
+ if (the_insn.reloc != NO_RELOC)
+ {
+ fix_new_exp (frag_now,
+ (toP - frag_now->fr_literal + the_insn.reloc_offset),
+ 4, /* size */
+ &the_insn.exp,
+ the_insn.pcrel,
+ the_insn.reloc);
+ }
+}
+
+char *
+parse_operand (s, operandp, opt)
+ char *s;
+ expressionS *operandp;
+ int opt;
+{
+ char *save = input_line_pointer;
+ char *new;
+
+ input_line_pointer = s;
+ expression (operandp);
+ if (operandp->X_op == O_absent && ! opt)
+ as_bad (_("missing operand"));
+ new = input_line_pointer;
+ input_line_pointer = save;
+ return new;
+}
+
+/* Instruction parsing. Takes a string containing the opcode.
+ Operands are at input_line_pointer. Output is in the_insn.
+ Warnings or errors are generated. */
+
+static void
+machine_ip (str)
+ char *str;
+{
+ char *s;
+ const char *args;
+ struct machine_opcode *insn;
+ char *argsStart;
+ unsigned long opcode;
+ expressionS the_operand;
+ expressionS *operand = &the_operand;
+ unsigned int reg;
+
+ /* Must handle `div0' opcode. */
+ s = str;
+ if (isalpha (*s))
+ for (; isalnum (*s); ++s)
+ if (isupper (*s))
+ *s = tolower (*s);
+
+ switch (*s)
+ {
+ case '\0':
+ break;
+
+ case ' ': /* FIXME-SOMEDAY more whitespace */
+ *s++ = '\0';
+ break;
+
+ default:
+ as_bad (_("Unknown opcode: `%s'"), str);
+ return;
+ }
+ if ((insn = (struct machine_opcode *) hash_find (op_hash, str)) == NULL)
+ {
+ as_bad (_("Unknown opcode `%s'."), str);
+ return;
+ }
+ argsStart = s;
+ opcode = insn->opcode;
+ memset (&the_insn, '\0', sizeof (the_insn));
+ the_insn.reloc = NO_RELOC;
+
+ /* Build the opcode, checking as we go to make sure that the
+ operands match.
+
+ If an operand matches, we modify the_insn or opcode appropriately,
+ and do a "continue". If an operand fails to match, we "break". */
+
+ if (insn->args[0] != '\0')
+ {
+ /* Prime the pump. */
+ s = parse_operand (s, operand, insn->args[0] == 'I');
+ }
+
+ for (args = insn->args;; ++args)
+ {
+ switch (*args)
+ {
+
+ case '\0': /* end of args */
+ if (*s == '\0')
+ {
+ /* We are truly done. */
+ the_insn.opcode = opcode;
+ return;
+ }
+ as_bad (_("Too many operands: %s"), s);
+ break;
+
+ case ',': /* Must match a comma */
+ if (*s++ == ',')
+ {
+ /* Parse next operand. */
+ s = parse_operand (s, operand, args[1] == 'I');
+ continue;
+ }
+ break;
+
+ case 'v': /* Trap numbers (immediate field) */
+ if (operand->X_op == O_constant)
+ {
+ if (operand->X_add_number < 256)
+ {
+ opcode |= (operand->X_add_number << 16);
+ continue;
+ }
+ else
+ {
+ as_bad (_("Immediate value of %ld is too large"),
+ (long) operand->X_add_number);
+ continue;
+ }
+ }
+ the_insn.reloc = RELOC_8;
+ the_insn.reloc_offset = 1; /* BIG-ENDIAN Byte 1 of insn */
+ the_insn.exp = *operand;
+ continue;
+
+ case 'b': /* A general register or 8-bit immediate */
+ case 'i':
+ /* We treat the two cases identically since we mashed
+ them together in the opcode table. */
+ if (operand->X_op == O_register)
+ goto general_reg;
+
+ /* Make sure the 'i' case really exists. */
+ if ((insn->opcode | IMMEDIATE_BIT) != (insn + 1)->opcode)
+ break;
+
+ opcode |= IMMEDIATE_BIT;
+ if (operand->X_op == O_constant)
+ {
+ if (operand->X_add_number < 256)
+ {
+ opcode |= operand->X_add_number;
+ continue;
+ }
+ else
+ {
+ as_bad (_("Immediate value of %ld is too large"),
+ (long) operand->X_add_number);
+ continue;
+ }
+ }
+ the_insn.reloc = RELOC_8;
+ the_insn.reloc_offset = 3; /* BIG-ENDIAN Byte 3 of insn */
+ the_insn.exp = *operand;
+ continue;
+
+ case 'a': /* next operand must be a register */
+ case 'c':
+ general_reg:
+ /* lrNNN or grNNN or %%expr or a user-def register name */
+ if (operand->X_op != O_register)
+ break; /* Only registers */
+ know (operand->X_add_symbol == 0);
+ know (operand->X_op_symbol == 0);
+ reg = operand->X_add_number;
+ if (reg >= SREG)
+ break; /* No special registers */
+
+ /* Got the register, now figure out where it goes in the
+ opcode. */
+ switch (*args)
+ {
+ case 'a':
+ opcode |= reg << 8;
+ continue;
+
+ case 'b':
+ case 'i':
+ opcode |= reg;
+ continue;
+
+ case 'c':
+ opcode |= reg << 16;
+ continue;
+ }
+ as_fatal (_("failed sanity check."));
+ break;
+
+ case 'x': /* 16 bit constant, zero-extended */
+ case 'X': /* 16 bit constant, one-extended */
+ if (operand->X_op == O_constant)
+ {
+ opcode |= (operand->X_add_number & 0xFF) << 0 |
+ ((operand->X_add_number & 0xFF00) << 8);
+ continue;
+ }
+ the_insn.reloc = RELOC_CONST;
+ the_insn.exp = *operand;
+ continue;
+
+ case 'h':
+ if (operand->X_op == O_constant)
+ {
+ opcode |= (operand->X_add_number & 0x00FF0000) >> 16 |
+ (((unsigned long) operand->X_add_number
+ /* avoid sign ext */ & 0xFF000000) >> 8);
+ continue;
+ }
+ the_insn.reloc = RELOC_CONSTH;
+ the_insn.exp = *operand;
+ continue;
+
+ case 'P': /* PC-relative jump address */
+ case 'A': /* Absolute jump address */
+ /* These two are treated together since we folded the
+ opcode table entries together. */
+ if (operand->X_op == O_constant)
+ {
+ /* Make sure the 'A' case really exists. */
+ if ((insn->opcode | ABSOLUTE_BIT) != (insn + 1)->opcode)
+ break;
+ {
+ bfd_vma v, mask;
+ mask = 0x1ffff;
+ v = operand->X_add_number & ~ mask;
+ if (v)
+ as_bad ("call/jmp target out of range");
+ }
+ opcode |= ABSOLUTE_BIT |
+ (operand->X_add_number & 0x0003FC00) << 6 |
+ ((operand->X_add_number & 0x000003FC) >> 2);
+ continue;
+ }
+ the_insn.reloc = RELOC_JUMPTARG;
+ the_insn.exp = *operand;
+ the_insn.pcrel = 1; /* Assume PC-relative jump */
+ /* FIXME-SOON, Do we figure out whether abs later, after
+ know sym val? */
+ continue;
+
+ case 'e': /* Coprocessor enable bit for LOAD/STORE insn */
+ if (operand->X_op == O_constant)
+ {
+ if (operand->X_add_number == 0)
+ continue;
+ if (operand->X_add_number == 1)
+ {
+ opcode |= CE_BIT;
+ continue;
+ }
+ }
+ break;
+
+ case 'n': /* Control bits for LOAD/STORE instructions */
+ if (operand->X_op == O_constant &&
+ operand->X_add_number < 128)
+ {
+ opcode |= (operand->X_add_number << 16);
+ continue;
+ }
+ break;
+
+ case 's': /* Special register number */
+ if (operand->X_op != O_register)
+ break; /* Only registers */
+ if (operand->X_add_number < SREG)
+ break; /* Not a special register */
+ opcode |= (operand->X_add_number & 0xFF) << 8;
+ continue;
+
+ case 'u': /* UI bit of CONVERT */
+ if (operand->X_op == O_constant)
+ {
+ if (operand->X_add_number == 0)
+ continue;
+ if (operand->X_add_number == 1)
+ {
+ opcode |= UI_BIT;
+ continue;
+ }
+ }
+ break;
+
+ case 'r': /* RND bits of CONVERT */
+ if (operand->X_op == O_constant &&
+ operand->X_add_number < 8)
+ {
+ opcode |= operand->X_add_number << 4;
+ continue;
+ }
+ break;
+
+ case 'I': /* ID bits of INV and IRETINV. */
+ /* This operand is optional. */
+ if (operand->X_op == O_absent)
+ continue;
+ else if (operand->X_op == O_constant
+ && operand->X_add_number < 4)
+ {
+ opcode |= operand->X_add_number << 16;
+ continue;
+ }
+ break;
+
+ case 'd': /* FD bits of CONVERT */
+ if (operand->X_op == O_constant &&
+ operand->X_add_number < 4)
+ {
+ opcode |= operand->X_add_number << 2;
+ continue;
+ }
+ break;
+
+
+ case 'f': /* FS bits of CONVERT */
+ if (operand->X_op == O_constant &&
+ operand->X_add_number < 4)
+ {
+ opcode |= operand->X_add_number << 0;
+ continue;
+ }
+ break;
+
+ case 'C':
+ if (operand->X_op == O_constant &&
+ operand->X_add_number < 4)
+ {
+ opcode |= operand->X_add_number << 16;
+ continue;
+ }
+ break;
+
+ case 'F':
+ if (operand->X_op == O_constant &&
+ operand->X_add_number < 16)
+ {
+ opcode |= operand->X_add_number << 18;
+ continue;
+ }
+ break;
+
+ default:
+ BAD_CASE (*args);
+ }
+ /* Types or values of args don't match. */
+ as_bad ("Invalid operands");
+ return;
+ }
+}
+
+/* This is identical to the md_atof in m68k.c. I think this is right,
+ but I'm not sure.
+
+ Turn a string in input_line_pointer into a floating point constant
+ of type type, and store the appropriate bytes in *litP. The number
+ of LITTLENUMS emitted is stored in *sizeP . An error message is
+ returned, or NULL on OK. */
+
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+char *
+md_atof (type, litP, sizeP)
+ char type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+
+ switch (type)
+ {
+
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP = 0;
+ return "Bad call to MD_ATOF()";
+ }
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+ *sizeP = prec * sizeof (LITTLENUM_TYPE);
+ for (wordP = words; prec--;)
+ {
+ md_number_to_chars (litP, (valueT) (*wordP++), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ return 0;
+}
+
+/*
+ * Write out big-endian.
+ */
+void
+md_number_to_chars (buf, val, n)
+ char *buf;
+ valueT val;
+ int n;
+{
+ number_to_chars_bigendian (buf, val, n);
+}
+
+void
+md_apply_fix (fixP, val)
+ fixS *fixP;
+ long val;
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ fixP->fx_addnumber = val; /* Remember value for emit_reloc */
+
+
+ know (fixP->fx_size == 4);
+ know (fixP->fx_r_type < NO_RELOC);
+
+ /* This is a hack. There should be a better way to handle this. */
+ if (fixP->fx_r_type == RELOC_WDISP30 && fixP->fx_addsy)
+ {
+ val += fixP->fx_where + fixP->fx_frag->fr_address;
+ }
+
+ switch (fixP->fx_r_type)
+ {
+
+ case RELOC_32:
+ buf[0] = val >> 24;
+ buf[1] = val >> 16;
+ buf[2] = val >> 8;
+ buf[3] = val;
+ break;
+
+ case RELOC_8:
+ buf[0] = val;
+ break;
+
+ case RELOC_WDISP30:
+ val = (val >>= 2) + 1;
+ buf[0] |= (val >> 24) & 0x3f;
+ buf[1] = (val >> 16);
+ buf[2] = val >> 8;
+ buf[3] = val;
+ break;
+
+ case RELOC_HI22:
+ buf[1] |= (val >> 26) & 0x3f;
+ buf[2] = val >> 18;
+ buf[3] = val >> 10;
+ break;
+
+ case RELOC_LO10:
+ buf[2] |= (val >> 8) & 0x03;
+ buf[3] = val;
+ break;
+
+ case RELOC_BASE13:
+ buf[2] |= (val >> 8) & 0x1f;
+ buf[3] = val;
+ break;
+
+ case RELOC_WDISP22:
+ val = (val >>= 2) + 1;
+ /* FALLTHROUGH */
+ case RELOC_BASE22:
+ buf[1] |= (val >> 16) & 0x3f;
+ buf[2] = val >> 8;
+ buf[3] = val;
+ break;
+
+ case RELOC_JUMPTARG: /* 00XX00XX pattern in a word */
+ if (!fixP->fx_done)
+ {
+ /* The linker tries to support both AMD and old GNU style
+ R_IREL relocs. That means that if the addend is exactly
+ the negative of the address within the section, the
+ linker will not handle it correctly. */
+ if (fixP->fx_pcrel
+ && val != 0
+ && val == - (fixP->fx_frag->fr_address + fixP->fx_where))
+ as_bad_where
+ (fixP->fx_file, fixP->fx_line,
+ "the linker will not handle this relocation correctly");
+ }
+ else if (fixP->fx_pcrel)
+ {
+ long v = val >> 17;
+ if (v != 0 && v != -1)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ "call/jmp target out of range");
+ }
+ else
+ /* this case was supposed to be handled in machine_ip */
+ abort ();
+ buf[1] = val >> 10; /* Holds bits 0003FFFC of address */
+ buf[3] = val >> 2;
+ break;
+
+ case RELOC_CONST: /* 00XX00XX pattern in a word */
+ buf[1] = val >> 8; /* Holds bits 0000XXXX */
+ buf[3] = val;
+ break;
+
+ case RELOC_CONSTH: /* 00XX00XX pattern in a word */
+ buf[1] = val >> 24; /* Holds bits XXXX0000 */
+ buf[3] = val >> 16;
+ break;
+
+ case NO_RELOC:
+ default:
+ as_bad (_("bad relocation type: 0x%02x"), fixP->fx_r_type);
+ break;
+ }
+}
+
+#ifdef OBJ_COFF
+short
+tc_coff_fix2rtype (fixP)
+ fixS *fixP;
+{
+
+ switch (fixP->fx_r_type)
+ {
+ case RELOC_32:
+ return (R_WORD);
+ case RELOC_8:
+ return (R_BYTE);
+ case RELOC_CONST:
+ return (R_ILOHALF);
+ case RELOC_CONSTH:
+ return (R_IHIHALF);
+ case RELOC_JUMPTARG:
+ return (R_IREL);
+ default:
+ printf (_("need %o3\n"), fixP->fx_r_type);
+ abort ();
+ } /* switch on type */
+
+ return (0);
+}
+
+#endif /* OBJ_COFF */
+
+/* should never be called for 29k */
+void
+md_convert_frag (headers, seg, fragP)
+ object_headers *headers;
+ segT seg;
+ register fragS *fragP;
+{
+ as_fatal (_("a29k_convert_frag\n"));
+}
+
+/* should never be called for a29k */
+int
+md_estimate_size_before_relax (fragP, segtype)
+ register fragS *fragP;
+ segT segtype;
+{
+ as_fatal (_("a29k_estimate_size_before_relax\n"));
+ return 0;
+}
+
+#if 0
+/* for debugging only */
+static void
+print_insn (insn)
+ struct machine_it *insn;
+{
+ char *Reloc[] =
+ {
+ "RELOC_8",
+ "RELOC_16",
+ "RELOC_32",
+ "RELOC_DISP8",
+ "RELOC_DISP16",
+ "RELOC_DISP32",
+ "RELOC_WDISP30",
+ "RELOC_WDISP22",
+ "RELOC_HI22",
+ "RELOC_22",
+ "RELOC_13",
+ "RELOC_LO10",
+ "RELOC_SFA_BASE",
+ "RELOC_SFA_OFF13",
+ "RELOC_BASE10",
+ "RELOC_BASE13",
+ "RELOC_BASE22",
+ "RELOC_PC10",
+ "RELOC_PC22",
+ "RELOC_JMP_TBL",
+ "RELOC_SEGOFF16",
+ "RELOC_GLOB_DAT",
+ "RELOC_JMP_SLOT",
+ "RELOC_RELATIVE",
+ "NO_RELOC"
+ };
+
+ if (insn->error)
+ {
+ fprintf (stderr, "ERROR: %s\n");
+ }
+ fprintf (stderr, "opcode=0x%08x\n", insn->opcode);
+ fprintf (stderr, "reloc = %s\n", Reloc[insn->reloc]);
+ fprintf (stderr, "exp = {\n");
+ fprintf (stderr, "\t\tX_add_symbol = %s\n",
+ insn->exp.X_add_symbol ?
+ (S_GET_NAME (insn->exp.X_add_symbol) ?
+ S_GET_NAME (insn->exp.X_add_symbol) : "???") : "0");
+ fprintf (stderr, "\t\tX_op_symbol = %s\n",
+ insn->exp.X_op_symbol ?
+ (S_GET_NAME (insn->exp.X_op_symbol) ?
+ S_GET_NAME (insn->exp.X_op_symbol) : "???") : "0");
+ fprintf (stderr, "\t\tX_add_number = %d\n",
+ insn->exp.X_add_number);
+ fprintf (stderr, "}\n");
+}
+
+#endif
+
+/* Translate internal representation of relocation info to target format.
+
+ On sparc/29k: first 4 bytes are normal unsigned long address, next three
+ bytes are index, most sig. byte first. Byte 7 is broken up with
+ bit 7 as external, bits 6 & 5 unused, and the lower
+ five bits as relocation type. Next 4 bytes are long addend. */
+/* Thanx and a tip of the hat to Michael Bloom, mb@ttidca.tti.com */
+
+#ifdef OBJ_AOUT
+
+void
+tc_aout_fix_to_chars (where, fixP, segment_address_in_file)
+ char *where;
+ fixS *fixP;
+ relax_addressT segment_address_in_file;
+{
+ long r_symbolnum;
+
+ know (fixP->fx_r_type < NO_RELOC);
+ know (fixP->fx_addsy != NULL);
+
+ md_number_to_chars (where,
+ fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file,
+ 4);
+
+ r_symbolnum = (S_IS_DEFINED (fixP->fx_addsy)
+ ? S_GET_TYPE (fixP->fx_addsy)
+ : fixP->fx_addsy->sy_number);
+
+ where[4] = (r_symbolnum >> 16) & 0x0ff;
+ where[5] = (r_symbolnum >> 8) & 0x0ff;
+ where[6] = r_symbolnum & 0x0ff;
+ where[7] = (((!S_IS_DEFINED (fixP->fx_addsy)) << 7) & 0x80) | (0 & 0x60) | (fixP->fx_r_type & 0x1F);
+ /* Also easy */
+ md_number_to_chars (&where[8], fixP->fx_addnumber, 4);
+}
+
+#endif /* OBJ_AOUT */
+
+CONST char *md_shortopts = "";
+struct option md_longopts[] = {
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ return 0;
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+}
+
+/* This is called when a line is unrecognized. This is used to handle
+ definitions of a29k style local labels. */
+
+int
+a29k_unrecognized_line (c)
+ int c;
+{
+ int lab;
+ char *s;
+
+ if (c != '$'
+ || ! isdigit ((unsigned char) input_line_pointer[0]))
+ return 0;
+
+ s = input_line_pointer;
+
+ lab = 0;
+ while (isdigit ((unsigned char) *s))
+ {
+ lab = lab * 10 + *s - '0';
+ ++s;
+ }
+
+ if (*s != ':')
+ {
+ /* Not a label definition. */
+ return 0;
+ }
+
+ if (dollar_label_defined (lab))
+ {
+ as_bad (_("label \"$%d\" redefined"), lab);
+ return 0;
+ }
+
+ define_dollar_label (lab);
+ colon (dollar_label_name (lab, 0));
+ input_line_pointer = s + 1;
+
+ return 1;
+}
+
+/* Default the values of symbols known that should be "predefined". We
+ don't bother to predefine them unless you actually use one, since there
+ are a lot of them. */
+
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ long regnum;
+ char testbuf[5 + /*SLOP*/ 5];
+
+ if (name[0] == 'g' || name[0] == 'G'
+ || name[0] == 'l' || name[0] == 'L'
+ || name[0] == 's' || name[0] == 'S')
+ {
+ /* Perhaps a global or local register name */
+ if (name[1] == 'r' || name[1] == 'R')
+ {
+ long maxreg;
+
+ /* Parse the number, make sure it has no extra zeroes or
+ trailing chars. */
+ regnum = atol (&name[2]);
+
+ if (name[0] == 's' || name[0] == 'S')
+ maxreg = 255;
+ else
+ maxreg = 127;
+ if (regnum > maxreg)
+ return NULL;
+
+ sprintf (testbuf, "%ld", regnum);
+ if (strcmp (testbuf, &name[2]) != 0)
+ return NULL; /* gr007 or lr7foo or whatever */
+
+ /* We have a wiener! Define and return a new symbol for it. */
+ if (name[0] == 'l' || name[0] == 'L')
+ regnum += 128;
+ else if (name[0] == 's' || name[0] == 'S')
+ regnum += SREG;
+ return (symbol_new (name, SEG_REGISTER, (valueT) regnum,
+ &zero_address_frag));
+ }
+ }
+
+ return NULL;
+}
+
+/* Parse an operand that is machine-specific. */
+
+void
+md_operand (expressionP)
+ expressionS *expressionP;
+{
+
+ if (input_line_pointer[0] == '%' && input_line_pointer[1] == '%')
+ {
+ /* We have a numeric register expression. No biggy. */
+ input_line_pointer += 2; /* Skip %% */
+ (void) expression (expressionP);
+ if (expressionP->X_op != O_constant
+ || expressionP->X_add_number > 255)
+ as_bad (_("Invalid expression after %%%%\n"));
+ expressionP->X_op = O_register;
+ }
+ else if (input_line_pointer[0] == '&')
+ {
+ /* We are taking the 'address' of a register...this one is not
+ in the manual, but it *is* in traps/fpsymbol.h! What they
+ seem to want is the register number, as an absolute number. */
+ input_line_pointer++; /* Skip & */
+ (void) expression (expressionP);
+ if (expressionP->X_op != O_register)
+ as_bad (_("Invalid register in & expression"));
+ else
+ expressionP->X_op = O_constant;
+ }
+ else if (input_line_pointer[0] == '$'
+ && isdigit ((unsigned char) input_line_pointer[1]))
+ {
+ long lab;
+ char *name;
+ symbolS *sym;
+
+ /* This is a local label. */
+ ++input_line_pointer;
+ lab = (long) get_absolute_expression ();
+ if (dollar_label_defined (lab))
+ {
+ name = dollar_label_name (lab, 0);
+ sym = symbol_find (name);
+ }
+ else
+ {
+ name = dollar_label_name (lab, 1);
+ sym = symbol_find_or_make (name);
+ }
+
+ expressionP->X_op = O_symbol;
+ expressionP->X_add_symbol = sym;
+ expressionP->X_add_number = 0;
+ }
+ else if (input_line_pointer[0] == '$')
+ {
+ char *s;
+ char type;
+ int fieldnum, fieldlimit;
+ LITTLENUM_TYPE floatbuf[8];
+
+ /* $float(), $doubleN(), or $extendN() convert floating values
+ to integers. */
+
+ s = input_line_pointer;
+
+ ++s;
+
+ fieldnum = 0;
+ if (strncmp (s, "double", sizeof "double" - 1) == 0)
+ {
+ s += sizeof "double" - 1;
+ type = 'd';
+ fieldlimit = 2;
+ }
+ else if (strncmp (s, "float", sizeof "float" - 1) == 0)
+ {
+ s += sizeof "float" - 1;
+ type = 'f';
+ fieldlimit = 1;
+ }
+ else if (strncmp (s, "extend", sizeof "extend" - 1) == 0)
+ {
+ s += sizeof "extend" - 1;
+ type = 'x';
+ fieldlimit = 4;
+ }
+ else
+ {
+ return;
+ }
+
+ if (isdigit (*s))
+ {
+ fieldnum = *s - '0';
+ ++s;
+ }
+ if (fieldnum >= fieldlimit)
+ return;
+
+ SKIP_WHITESPACE ();
+ if (*s != '(')
+ return;
+ ++s;
+ SKIP_WHITESPACE ();
+
+ s = atof_ieee (s, type, floatbuf);
+ if (s == NULL)
+ return;
+ s = s;
+
+ SKIP_WHITESPACE ();
+ if (*s != ')')
+ return;
+ ++s;
+ SKIP_WHITESPACE ();
+
+ input_line_pointer = s;
+ expressionP->X_op = O_constant;
+ expressionP->X_unsigned = 1;
+ expressionP->X_add_number = ((floatbuf[fieldnum * 2]
+ << LITTLENUM_NUMBER_OF_BITS)
+ + floatbuf[fieldnum * 2 + 1]);
+ }
+}
+
+/* Round up a section size to the appropriate boundary. */
+valueT
+md_section_align (segment, size)
+ segT segment;
+ valueT size;
+{
+ return size; /* Byte alignment is fine */
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the 29000, they're relative to the address of the instruction,
+ which we have set up as the address of the fixup too. */
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ return fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+/* end of tc-a29k.c */
diff --git a/gas/config/tc-a29k.h b/gas/config/tc-a29k.h
new file mode 100644
index 0000000..b60f605
--- /dev/null
+++ b/gas/config/tc-a29k.h
@@ -0,0 +1,54 @@
+/* tc-a29k.h -- Assemble for the AMD 29000.
+ Copyright (C) 1989, 90, 91, 92, 93, 95, 1998 Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
+
+#define TC_A29K
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+#define WORKING_DOT_WORD
+
+#define LEX_DOLLAR 1
+
+#define tc_unrecognized_line(c) a29k_unrecognized_line (c)
+extern int a29k_unrecognized_line PARAMS ((int));
+
+#define tc_headers_hook(a) ; /* not used */
+#define tc_headers_hook(a) ; /* not used */
+#define tc_crawl_symbol_chain(a) ; /* not used */
+#define tc_coff_symbol_emit_hook(a) ; /* not used */
+
+#define AOUT_MACHTYPE 101
+#define TC_COFF_FIX2RTYPE(fix_ptr) tc_coff_fix2rtype(fix_ptr)
+#define BFD_ARCH bfd_arch_a29k
+#define COFF_MAGIC SIPFBOMAGIC
+/* Should the reloc be output ?
+ on the 29k, this is true only if there is a symbol attatched.
+ on the h8, this is allways true, since no fixup is done
+*/
+#define TC_COUNT_RELOC(x) (x->fx_addsy)
+#define TC_CONS_RELOC RELOC_32
+
+#define COFF_FLAGS F_AR32W
+#define reloc_type int
+#define NEED_FX_R_TYPE
+
+#define ZERO_BASED_SEGMENTS
+
+/* end of tc-a29k.h */
diff --git a/gas/config/tc-alpha.c b/gas/config/tc-alpha.c
new file mode 100644
index 0000000..568617f
--- /dev/null
+++ b/gas/config/tc-alpha.c
@@ -0,0 +1,4762 @@
+/* tc-alpha.c - Processor-specific code for the DEC Alpha AXP CPU.
+ Copyright (C) 1989, 93-98, 1999 Free Software Foundation, Inc.
+ Contributed by Carnegie Mellon University, 1993.
+ Written by Alessandro Forin, based on earlier gas-1.38 target CPU files.
+ Modified by Ken Raeburn for gas-2.x and ECOFF support.
+ Modified by Richard Henderson for ELF support.
+ Modified by Klaus K"ampf for EVAX (openVMS/Alpha) support.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+/*
+ * Mach Operating System
+ * Copyright (c) 1993 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include "as.h"
+#include "subsegs.h"
+#include "ecoff.h"
+
+#include "opcode/alpha.h"
+
+#ifdef OBJ_ELF
+#include "elf/alpha.h"
+#endif
+
+#include <ctype.h>
+
+
+/* Local types */
+
+#define MAX_INSN_FIXUPS 2
+#define MAX_INSN_ARGS 5
+
+struct alpha_fixup
+{
+ expressionS exp;
+ bfd_reloc_code_real_type reloc;
+};
+
+struct alpha_insn
+{
+ unsigned insn;
+ int nfixups;
+ struct alpha_fixup fixups[MAX_INSN_FIXUPS];
+};
+
+enum alpha_macro_arg
+{
+ MACRO_EOA = 1, MACRO_IR, MACRO_PIR, MACRO_CPIR, MACRO_FPR, MACRO_EXP
+};
+
+struct alpha_macro
+{
+ const char *name;
+ void (*emit) PARAMS ((const expressionS *, int, const PTR));
+ const PTR arg;
+ enum alpha_macro_arg argsets[16];
+};
+
+/* Two extra symbols we want to see in our input. This is a blatent
+ misuse of the expressionS.X_op field. */
+
+#define O_pregister (O_max+1) /* O_register, but in parentheses */
+#define O_cpregister (O_pregister+1) /* + a leading comma */
+
+/* Macros for extracting the type and number of encoded register tokens */
+
+#define is_ir_num(x) (((x) & 32) == 0)
+#define is_fpr_num(x) (((x) & 32) != 0)
+#define regno(x) ((x) & 31)
+
+/* Something odd inherited from the old assembler */
+
+#define note_gpreg(R) (alpha_gprmask |= (1 << (R)))
+#define note_fpreg(R) (alpha_fprmask |= (1 << (R)))
+
+/* Predicates for 16- and 32-bit ranges */
+/* XXX: The non-shift version appears to trigger a compiler bug when
+ cross-assembling from x86 w/ gcc 2.7.2. */
+
+#if 1
+#define range_signed_16(x) \
+ (((offsetT)(x) >> 15) == 0 || ((offsetT)(x) >> 15) == -1)
+#define range_signed_32(x) \
+ (((offsetT)(x) >> 31) == 0 || ((offsetT)(x) >> 31) == -1)
+#else
+#define range_signed_16(x) ((offsetT)(x) >= -(offsetT)0x8000 && \
+ (offsetT)(x) <= (offsetT)0x7FFF)
+#define range_signed_32(x) ((offsetT)(x) >= -(offsetT)0x80000000 && \
+ (offsetT)(x) <= (offsetT)0x7FFFFFFF)
+#endif
+
+/* Macros for sign extending from 16- and 32-bits. */
+/* XXX: The cast macros will work on all the systems that I care about,
+ but really a predicate should be found to use the non-cast forms. */
+
+#if 1
+#define sign_extend_16(x) ((short)(x))
+#define sign_extend_32(x) ((int)(x))
+#else
+#define sign_extend_16(x) ((offsetT)(((x) & 0xFFFF) ^ 0x8000) - 0x8000)
+#define sign_extend_32(x) ((offsetT)(((x) & 0xFFFFFFFF) \
+ ^ 0x80000000) - 0x80000000)
+#endif
+
+/* Macros to build tokens */
+
+#define set_tok_reg(t, r) (memset(&(t), 0, sizeof(t)), \
+ (t).X_op = O_register, \
+ (t).X_add_number = (r))
+#define set_tok_preg(t, r) (memset(&(t), 0, sizeof(t)), \
+ (t).X_op = O_pregister, \
+ (t).X_add_number = (r))
+#define set_tok_cpreg(t, r) (memset(&(t), 0, sizeof(t)), \
+ (t).X_op = O_cpregister, \
+ (t).X_add_number = (r))
+#define set_tok_freg(t, r) (memset(&(t), 0, sizeof(t)), \
+ (t).X_op = O_register, \
+ (t).X_add_number = (r)+32)
+#define set_tok_sym(t, s, a) (memset(&(t), 0, sizeof(t)), \
+ (t).X_op = O_symbol, \
+ (t).X_add_symbol = (s), \
+ (t).X_add_number = (a))
+#define set_tok_const(t, n) (memset(&(t), 0, sizeof(t)), \
+ (t).X_op = O_constant, \
+ (t).X_add_number = (n))
+
+
+/* Prototypes for all local functions */
+
+static int tokenize_arguments PARAMS ((char *, expressionS *, int));
+static const struct alpha_opcode *find_opcode_match
+ PARAMS ((const struct alpha_opcode *, const expressionS *, int *, int *));
+static const struct alpha_macro *find_macro_match
+ PARAMS ((const struct alpha_macro *, const expressionS *, int *));
+static unsigned insert_operand
+ PARAMS ((unsigned, const struct alpha_operand *, offsetT, char *, unsigned));
+static void assemble_insn
+ PARAMS ((const struct alpha_opcode *, const expressionS *, int,
+ struct alpha_insn *));
+static void emit_insn PARAMS ((struct alpha_insn *));
+static void assemble_tokens_to_insn
+ PARAMS ((const char *, const expressionS *, int, struct alpha_insn *));
+static void assemble_tokens
+ PARAMS ((const char *, const expressionS *, int, int));
+
+static int load_expression
+ PARAMS ((int, const expressionS *, int *, expressionS *));
+
+static void emit_ldgp PARAMS ((const expressionS *, int, const PTR));
+static void emit_division PARAMS ((const expressionS *, int, const PTR));
+static void emit_lda PARAMS ((const expressionS *, int, const PTR));
+static void emit_ldah PARAMS ((const expressionS *, int, const PTR));
+static void emit_ir_load PARAMS ((const expressionS *, int, const PTR));
+static void emit_loadstore PARAMS ((const expressionS *, int, const PTR));
+static void emit_jsrjmp PARAMS ((const expressionS *, int, const PTR));
+static void emit_ldX PARAMS ((const expressionS *, int, const PTR));
+static void emit_ldXu PARAMS ((const expressionS *, int, const PTR));
+static void emit_uldX PARAMS ((const expressionS *, int, const PTR));
+static void emit_uldXu PARAMS ((const expressionS *, int, const PTR));
+static void emit_ldil PARAMS ((const expressionS *, int, const PTR));
+static void emit_stX PARAMS ((const expressionS *, int, const PTR));
+static void emit_ustX PARAMS ((const expressionS *, int, const PTR));
+static void emit_sextX PARAMS ((const expressionS *, int, const PTR));
+static void emit_retjcr PARAMS ((const expressionS *, int, const PTR));
+
+static void s_alpha_text PARAMS ((int));
+static void s_alpha_data PARAMS ((int));
+#ifndef OBJ_ELF
+static void s_alpha_comm PARAMS ((int));
+static void s_alpha_rdata PARAMS ((int));
+#endif
+#ifdef OBJ_ECOFF
+static void s_alpha_sdata PARAMS ((int));
+#endif
+#ifdef OBJ_ELF
+static void s_alpha_section PARAMS ((int));
+static void s_alpha_ent PARAMS ((int));
+static void s_alpha_end PARAMS ((int));
+static void s_alpha_mask PARAMS ((int));
+static void s_alpha_frame PARAMS ((int));
+static void s_alpha_prologue PARAMS ((int));
+static void s_alpha_coff_wrapper PARAMS ((int));
+#endif
+#ifdef OBJ_EVAX
+static void s_alpha_section PARAMS ((int));
+#endif
+static void s_alpha_gprel32 PARAMS ((int));
+static void s_alpha_float_cons PARAMS ((int));
+static void s_alpha_proc PARAMS ((int));
+static void s_alpha_set PARAMS ((int));
+static void s_alpha_base PARAMS ((int));
+static void s_alpha_align PARAMS ((int));
+static void s_alpha_stringer PARAMS ((int));
+static void s_alpha_space PARAMS ((int));
+
+static void create_literal_section PARAMS ((const char *, segT *, symbolS **));
+#ifndef OBJ_ELF
+static void select_gp_value PARAMS ((void));
+#endif
+static void alpha_align PARAMS ((int, char *, symbolS *, int));
+
+
+/* Generic assembler global variables which must be defined by all
+ targets. */
+
+/* Characters which always start a comment. */
+const char comment_chars[] = "#";
+
+/* Characters which start a comment at the beginning of a line. */
+const char line_comment_chars[] = "#";
+
+/* Characters which may be used to separate multiple commands on a
+ single line. */
+const char line_separator_chars[] = ";";
+
+/* Characters which are used to indicate an exponent in a floating
+ point number. */
+const char EXP_CHARS[] = "eE";
+
+/* Characters which mean that a number is a floating point constant,
+ as in 0d1.0. */
+#if 0
+const char FLT_CHARS[] = "dD";
+#else
+/* XXX: Do all of these really get used on the alpha?? */
+char FLT_CHARS[] = "rRsSfFdDxXpP";
+#endif
+
+#ifdef OBJ_EVAX
+const char *md_shortopts = "Fm:g+1h:HG:";
+#else
+const char *md_shortopts = "Fm:gG:";
+#endif
+
+struct option md_longopts[] = {
+#define OPTION_32ADDR (OPTION_MD_BASE)
+ { "32addr", no_argument, NULL, OPTION_32ADDR },
+#define OPTION_RELAX (OPTION_32ADDR+1)
+ { "relax", no_argument, NULL, OPTION_RELAX },
+#ifdef OBJ_ELF
+#define OPTION_MDEBUG (OPTION_RELAX+1)
+#define OPTION_NO_MDEBUG (OPTION_MDEBUG+1)
+ { "mdebug", no_argument, NULL, OPTION_MDEBUG },
+ { "no-mdebug", no_argument, NULL, OPTION_NO_MDEBUG },
+#endif
+ { NULL, no_argument, NULL, 0 }
+};
+
+size_t md_longopts_size = sizeof(md_longopts);
+
+
+#ifdef OBJ_EVAX
+#define AXP_REG_R0 0
+#define AXP_REG_R16 16
+#define AXP_REG_R17 17
+#undef AXP_REG_T9
+#define AXP_REG_T9 22
+#undef AXP_REG_T10
+#define AXP_REG_T10 23
+#undef AXP_REG_T11
+#define AXP_REG_T11 24
+#undef AXP_REG_T12
+#define AXP_REG_T12 25
+#define AXP_REG_AI 25
+#undef AXP_REG_FP
+#define AXP_REG_FP 29
+
+#undef AXP_REG_GP
+#define AXP_REG_GP AXP_REG_PV
+#endif /* OBJ_EVAX */
+
+/* The cpu for which we are generating code */
+static unsigned alpha_target = AXP_OPCODE_BASE;
+static const char *alpha_target_name = "<all>";
+
+/* The hash table of instruction opcodes */
+static struct hash_control *alpha_opcode_hash;
+
+/* The hash table of macro opcodes */
+static struct hash_control *alpha_macro_hash;
+
+#ifdef OBJ_ECOFF
+/* The $gp relocation symbol */
+static symbolS *alpha_gp_symbol;
+
+/* XXX: what is this, and why is it exported? */
+valueT alpha_gp_value;
+#endif
+
+/* The current $gp register */
+static int alpha_gp_register = AXP_REG_GP;
+
+/* A table of the register symbols */
+static symbolS *alpha_register_table[64];
+
+/* Constant sections, or sections of constants */
+#ifdef OBJ_ECOFF
+static segT alpha_lita_section;
+static segT alpha_lit4_section;
+#endif
+#ifdef OBJ_EVAX
+static segT alpha_link_section;
+static segT alpha_ctors_section;
+static segT alpha_dtors_section;
+#endif
+static segT alpha_lit8_section;
+
+/* Symbols referring to said sections. */
+#ifdef OBJ_ECOFF
+static symbolS *alpha_lita_symbol;
+static symbolS *alpha_lit4_symbol;
+#endif
+#ifdef OBJ_EVAX
+static symbolS *alpha_link_symbol;
+static symbolS *alpha_ctors_symbol;
+static symbolS *alpha_dtors_symbol;
+#endif
+static symbolS *alpha_lit8_symbol;
+
+/* Literal for .litX+0x8000 within .lita */
+#ifdef OBJ_ECOFF
+static offsetT alpha_lit4_literal;
+static offsetT alpha_lit8_literal;
+#endif
+
+/* The active .ent symbol. */
+#ifdef OBJ_ELF
+static symbolS *alpha_cur_ent_sym;
+#endif
+
+/* Is the assembler not allowed to use $at? */
+static int alpha_noat_on = 0;
+
+/* Are macros enabled? */
+static int alpha_macros_on = 1;
+
+/* Are floats disabled? */
+static int alpha_nofloats_on = 0;
+
+/* Are addresses 32 bit? */
+static int alpha_addr32_on = 0;
+
+/* Symbol labelling the current insn. When the Alpha gas sees
+ foo:
+ .quad 0
+ and the section happens to not be on an eight byte boundary, it
+ will align both the symbol and the .quad to an eight byte boundary. */
+static symbolS *alpha_insn_label;
+
+/* Whether we should automatically align data generation pseudo-ops.
+ .align 0 will turn this off. */
+static int alpha_auto_align_on = 1;
+
+/* The known current alignment of the current section. */
+static int alpha_current_align;
+
+/* These are exported to ECOFF code. */
+unsigned long alpha_gprmask, alpha_fprmask;
+
+/* Whether the debugging option was seen. */
+static int alpha_debug;
+
+#ifdef OBJ_ELF
+/* Whether we are emitting an mdebug section. */
+int alpha_flag_mdebug = 1;
+#endif
+
+/* Don't fully resolve relocations, allowing code movement in the linker. */
+static int alpha_flag_relax;
+
+/* What value to give to bfd_set_gp_size. */
+static int g_switch_value = 8;
+
+#ifdef OBJ_EVAX
+/* Collect information about current procedure here. */
+static struct {
+ symbolS *symbol; /* proc pdesc symbol */
+ int pdsckind;
+ int framereg; /* register for frame pointer */
+ int framesize; /* size of frame */
+ int rsa_offset;
+ int ra_save;
+ int fp_save;
+ long imask;
+ long fmask;
+ int type;
+ int prologue;
+} alpha_evax_proc;
+
+static int alpha_flag_hash_long_names = 0; /* -+ */
+static int alpha_flag_show_after_trunc = 0; /* -H */
+
+/* If the -+ switch is given, then a hash is appended to any name that is
+ * longer than 64 characters, else longer symbol names are truncated.
+ */
+
+#endif
+
+/* A table of CPU names and opcode sets. */
+
+static const struct cpu_type
+{
+ const char *name;
+ unsigned flags;
+} cpu_types[] =
+{
+ /* Ad hoc convention: cpu number gets palcode, process code doesn't.
+ This supports usage under DU 4.0b that does ".arch ev4", and
+ usage in MILO that does -m21064. Probably something more
+ specific like -m21064-pal should be used, but oh well. */
+
+ { "21064", AXP_OPCODE_BASE|AXP_OPCODE_EV4 },
+ { "21064a", AXP_OPCODE_BASE|AXP_OPCODE_EV4 },
+ { "21066", AXP_OPCODE_BASE|AXP_OPCODE_EV4 },
+ { "21068", AXP_OPCODE_BASE|AXP_OPCODE_EV4 },
+ { "21164", AXP_OPCODE_BASE|AXP_OPCODE_EV5 },
+ { "21164a", AXP_OPCODE_BASE|AXP_OPCODE_EV5|AXP_OPCODE_BWX },
+ { "21164pc", (AXP_OPCODE_BASE|AXP_OPCODE_EV5|AXP_OPCODE_BWX
+ |AXP_OPCODE_MAX) },
+ { "21264", (AXP_OPCODE_BASE|AXP_OPCODE_EV6|AXP_OPCODE_BWX
+ |AXP_OPCODE_MAX|AXP_OPCODE_CIX) },
+
+ { "ev4", AXP_OPCODE_BASE },
+ { "ev45", AXP_OPCODE_BASE },
+ { "lca45", AXP_OPCODE_BASE },
+ { "ev5", AXP_OPCODE_BASE },
+ { "ev56", AXP_OPCODE_BASE|AXP_OPCODE_BWX },
+ { "pca56", AXP_OPCODE_BASE|AXP_OPCODE_BWX|AXP_OPCODE_MAX },
+ { "ev6", AXP_OPCODE_BASE|AXP_OPCODE_BWX|AXP_OPCODE_MAX|AXP_OPCODE_CIX },
+
+ { "all", AXP_OPCODE_BASE },
+ { 0 }
+};
+
+/* The macro table */
+
+static const struct alpha_macro alpha_macros[] = {
+/* Load/Store macros */
+ { "lda", emit_lda, NULL,
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "ldah", emit_ldah, NULL,
+ { MACRO_IR, MACRO_EXP, MACRO_EOA } },
+
+ { "ldl", emit_ir_load, "ldl",
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "ldl_l", emit_ir_load, "ldl_l",
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "ldq", emit_ir_load, "ldq",
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "ldq_l", emit_ir_load, "ldq_l",
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "ldq_u", emit_ir_load, "ldq_u",
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "ldf", emit_loadstore, "ldf",
+ { MACRO_FPR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_FPR, MACRO_EXP, MACRO_EOA } },
+ { "ldg", emit_loadstore, "ldg",
+ { MACRO_FPR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_FPR, MACRO_EXP, MACRO_EOA } },
+ { "lds", emit_loadstore, "lds",
+ { MACRO_FPR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_FPR, MACRO_EXP, MACRO_EOA } },
+ { "ldt", emit_loadstore, "ldt",
+ { MACRO_FPR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_FPR, MACRO_EXP, MACRO_EOA } },
+
+ { "ldb", emit_ldX, (PTR)0,
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "ldbu", emit_ldXu, (PTR)0,
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "ldw", emit_ldX, (PTR)1,
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "ldwu", emit_ldXu, (PTR)1,
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+
+ { "uldw", emit_uldX, (PTR)1,
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "uldwu", emit_uldXu, (PTR)1,
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "uldl", emit_uldX, (PTR)2,
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "uldlu", emit_uldXu, (PTR)2,
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "uldq", emit_uldXu, (PTR)3,
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+
+ { "ldgp", emit_ldgp, NULL,
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA } },
+
+ { "ldi", emit_lda, NULL,
+ { MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "ldil", emit_ldil, NULL,
+ { MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "ldiq", emit_lda, NULL,
+ { MACRO_IR, MACRO_EXP, MACRO_EOA } },
+#if 0
+ { "ldif" emit_ldiq, NULL,
+ { MACRO_FPR, MACRO_EXP, MACRO_EOA } },
+ { "ldid" emit_ldiq, NULL,
+ { MACRO_FPR, MACRO_EXP, MACRO_EOA } },
+ { "ldig" emit_ldiq, NULL,
+ { MACRO_FPR, MACRO_EXP, MACRO_EOA } },
+ { "ldis" emit_ldiq, NULL,
+ { MACRO_FPR, MACRO_EXP, MACRO_EOA } },
+ { "ldit" emit_ldiq, NULL,
+ { MACRO_FPR, MACRO_EXP, MACRO_EOA } },
+#endif
+
+ { "stl", emit_loadstore, "stl",
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "stl_c", emit_loadstore, "stl_c",
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "stq", emit_loadstore, "stq",
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "stq_c", emit_loadstore, "stq_c",
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "stq_u", emit_loadstore, "stq_u",
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "stf", emit_loadstore, "stf",
+ { MACRO_FPR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_FPR, MACRO_EXP, MACRO_EOA } },
+ { "stg", emit_loadstore, "stg",
+ { MACRO_FPR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_FPR, MACRO_EXP, MACRO_EOA } },
+ { "sts", emit_loadstore, "sts",
+ { MACRO_FPR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_FPR, MACRO_EXP, MACRO_EOA } },
+ { "stt", emit_loadstore, "stt",
+ { MACRO_FPR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_FPR, MACRO_EXP, MACRO_EOA } },
+
+ { "stb", emit_stX, (PTR)0,
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "stw", emit_stX, (PTR)1,
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "ustw", emit_ustX, (PTR)1,
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "ustl", emit_ustX, (PTR)2,
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "ustq", emit_ustX, (PTR)3,
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA } },
+
+/* Arithmetic macros */
+#if 0
+ { "absl" emit_absl, 1, { IR } },
+ { "absl" emit_absl, 2, { IR, IR } },
+ { "absl" emit_absl, 2, { EXP, IR } },
+ { "absq" emit_absq, 1, { IR } },
+ { "absq" emit_absq, 2, { IR, IR } },
+ { "absq" emit_absq, 2, { EXP, IR } },
+#endif
+
+ { "sextb", emit_sextX, (PTR)0,
+ { MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EOA,
+ /* MACRO_EXP, MACRO_IR, MACRO_EOA */ } },
+ { "sextw", emit_sextX, (PTR)1,
+ { MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EOA,
+ /* MACRO_EXP, MACRO_IR, MACRO_EOA */ } },
+
+ { "divl", emit_division, "__divl",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "divlu", emit_division, "__divlu",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "divq", emit_division, "__divq",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "divqu", emit_division, "__divqu",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "reml", emit_division, "__reml",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "remlu", emit_division, "__remlu",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "remq", emit_division, "__remq",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "remqu", emit_division, "__remqu",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+
+ { "jsr", emit_jsrjmp, "jsr",
+ { MACRO_PIR, MACRO_EXP, MACRO_EOA,
+ MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA,
+ MACRO_EXP, MACRO_EOA } },
+ { "jmp", emit_jsrjmp, "jmp",
+ { MACRO_PIR, MACRO_EXP, MACRO_EOA,
+ MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA,
+ MACRO_EXP, MACRO_EOA } },
+ { "ret", emit_retjcr, "ret",
+ { MACRO_IR, MACRO_EXP, MACRO_EOA,
+ MACRO_IR, MACRO_EOA,
+ MACRO_PIR, MACRO_EXP, MACRO_EOA,
+ MACRO_PIR, MACRO_EOA,
+ MACRO_EXP, MACRO_EOA,
+ MACRO_EOA } },
+ { "jcr", emit_retjcr, "jcr",
+ { MACRO_IR, MACRO_EXP, MACRO_EOA,
+ MACRO_IR, MACRO_EOA,
+ MACRO_PIR, MACRO_EXP, MACRO_EOA,
+ MACRO_PIR, MACRO_EOA,
+ MACRO_EXP, MACRO_EOA,
+ MACRO_EOA } },
+ { "jsr_coroutine", emit_retjcr, "jcr",
+ { MACRO_IR, MACRO_EXP, MACRO_EOA,
+ MACRO_IR, MACRO_EOA,
+ MACRO_PIR, MACRO_EXP, MACRO_EOA,
+ MACRO_PIR, MACRO_EOA,
+ MACRO_EXP, MACRO_EOA,
+ MACRO_EOA } },
+};
+
+static const int alpha_num_macros
+ = sizeof(alpha_macros) / sizeof(*alpha_macros);
+
+/* Public interface functions */
+
+/* This function is called once, at assembler startup time. It sets
+ up all the tables, etc. that the MD part of the assembler will
+ need, that can be determined before arguments are parsed. */
+
+void
+md_begin ()
+{
+ unsigned int i;
+
+ /* Create the opcode hash table */
+
+ alpha_opcode_hash = hash_new ();
+ for (i = 0; i < alpha_num_opcodes; )
+ {
+ const char *name, *retval, *slash;
+
+ name = alpha_opcodes[i].name;
+ retval = hash_insert (alpha_opcode_hash, name, (PTR)&alpha_opcodes[i]);
+ if (retval)
+ as_fatal (_("internal error: can't hash opcode `%s': %s"), name, retval);
+
+ /* Some opcodes include modifiers of various sorts with a "/mod"
+ syntax, like the architecture manual suggests. However, for
+ use with gcc at least, we also need access to those same opcodes
+ without the "/". */
+
+ if ((slash = strchr (name, '/')) != NULL)
+ {
+ char *p = xmalloc (strlen (name));
+ memcpy (p, name, slash - name);
+ strcpy (p + (slash - name), slash + 1);
+
+ (void)hash_insert(alpha_opcode_hash, p, (PTR)&alpha_opcodes[i]);
+ /* Ignore failures -- the opcode table does duplicate some
+ variants in different forms, like "hw_stq" and "hw_st/q". */
+ }
+
+ while (++i < alpha_num_opcodes
+ && (alpha_opcodes[i].name == name
+ || !strcmp (alpha_opcodes[i].name, name)))
+ continue;
+ }
+
+ /* Create the macro hash table */
+
+ alpha_macro_hash = hash_new ();
+ for (i = 0; i < alpha_num_macros; )
+ {
+ const char *name, *retval;
+
+ name = alpha_macros[i].name;
+ retval = hash_insert (alpha_macro_hash, name, (PTR)&alpha_macros[i]);
+ if (retval)
+ as_fatal (_("internal error: can't hash macro `%s': %s"), name, retval);
+
+ while (++i < alpha_num_macros
+ && (alpha_macros[i].name == name
+ || !strcmp (alpha_macros[i].name, name)))
+ continue;
+ }
+
+ /* Construct symbols for each of the registers */
+
+ for (i = 0; i < 32; ++i)
+ {
+ char name[4];
+ sprintf(name, "$%d", i);
+ alpha_register_table[i] = symbol_create(name, reg_section, i,
+ &zero_address_frag);
+ }
+ for (; i < 64; ++i)
+ {
+ char name[5];
+ sprintf(name, "$f%d", i-32);
+ alpha_register_table[i] = symbol_create(name, reg_section, i,
+ &zero_address_frag);
+ }
+
+ /* Create the special symbols and sections we'll be using */
+
+ /* So .sbss will get used for tiny objects. */
+ bfd_set_gp_size (stdoutput, g_switch_value);
+
+#ifdef OBJ_ECOFF
+ create_literal_section (".lita", &alpha_lita_section, &alpha_lita_symbol);
+
+ /* For handling the GP, create a symbol that won't be output in the
+ symbol table. We'll edit it out of relocs later. */
+ alpha_gp_symbol = symbol_create ("<GP value>", alpha_lita_section, 0x8000,
+ &zero_address_frag);
+#endif
+
+#ifdef OBJ_EVAX
+ create_literal_section (".link", &alpha_link_section, &alpha_link_symbol);
+#endif
+
+#ifdef OBJ_ELF
+ if (ECOFF_DEBUGGING)
+ {
+ segT sec = subseg_new(".mdebug", (subsegT)0);
+ bfd_set_section_flags(stdoutput, sec, SEC_HAS_CONTENTS|SEC_READONLY);
+ bfd_set_section_alignment(stdoutput, sec, 3);
+ }
+#endif /* OBJ_ELF */
+
+ subseg_set(text_section, 0);
+}
+
+/* The public interface to the instruction assembler. */
+
+void
+md_assemble (str)
+ char *str;
+{
+ char opname[32]; /* current maximum is 13 */
+ expressionS tok[MAX_INSN_ARGS];
+ int ntok, opnamelen, trunclen;
+
+ /* split off the opcode */
+ opnamelen = strspn (str, "abcdefghijklmnopqrstuvwxyz_/468");
+ trunclen = (opnamelen < sizeof (opname) - 1
+ ? opnamelen
+ : sizeof (opname) - 1);
+ memcpy (opname, str, trunclen);
+ opname[trunclen] = '\0';
+
+ /* tokenize the rest of the line */
+ if ((ntok = tokenize_arguments (str + opnamelen, tok, MAX_INSN_ARGS)) < 0)
+ {
+ as_bad (_("syntax error"));
+ return;
+ }
+
+ /* finish it off */
+ assemble_tokens (opname, tok, ntok, alpha_macros_on);
+}
+
+/* Round up a section's size to the appropriate boundary. */
+
+valueT
+md_section_align (seg, size)
+ segT seg;
+ valueT size;
+{
+ int align = bfd_get_section_alignment(stdoutput, seg);
+ valueT mask = ((valueT)1 << align) - 1;
+
+ return (size + mask) & ~mask;
+}
+
+/* Turn a string in input_line_pointer into a floating point constant
+ of type type, and store the appropriate bytes in *litP. The number
+ of LITTLENUMS emitted is stored in *sizeP. An error message is
+ returned, or NULL on OK. */
+
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+extern char *vax_md_atof PARAMS ((int, char *, int *));
+
+char *
+md_atof (type, litP, sizeP)
+ char type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+
+ switch (type)
+ {
+ /* VAX floats */
+ case 'G':
+ /* VAX md_atof doesn't like "G" for some reason. */
+ type = 'g';
+ case 'F':
+ case 'D':
+ return vax_md_atof (type, litP, sizeP);
+
+ /* IEEE floats */
+ case 'f':
+ prec = 2;
+ break;
+
+ case 'd':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("Bad call to MD_ATOF()");
+ }
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+ *sizeP = prec * sizeof (LITTLENUM_TYPE);
+
+ for (wordP = words + prec - 1; prec--;)
+ {
+ md_number_to_chars (litP, (long) (*wordP--), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+
+ return 0;
+}
+
+/* Take care of the target-specific command-line options. */
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ switch (c)
+ {
+ case 'F':
+ alpha_nofloats_on = 1;
+ break;
+
+ case OPTION_32ADDR:
+ alpha_addr32_on = 1;
+ break;
+
+ case 'g':
+ alpha_debug = 1;
+ break;
+
+ case 'G':
+ g_switch_value = atoi(arg);
+ break;
+
+ case 'm':
+ {
+ const struct cpu_type *p;
+ for (p = cpu_types; p->name; ++p)
+ if (strcmp(arg, p->name) == 0)
+ {
+ alpha_target_name = p->name, alpha_target = p->flags;
+ goto found;
+ }
+ as_warn(_("Unknown CPU identifier `%s'"), arg);
+ found:;
+ }
+ break;
+
+#ifdef OBJ_EVAX
+ case '+': /* For g++. Hash any name > 63 chars long. */
+ alpha_flag_hash_long_names = 1;
+ break;
+
+ case 'H': /* Show new symbol after hash truncation */
+ alpha_flag_show_after_trunc = 1;
+ break;
+
+ case 'h': /* for gnu-c/vax compatibility. */
+ break;
+#endif
+
+ case OPTION_RELAX:
+ alpha_flag_relax = 1;
+ break;
+
+#ifdef OBJ_ELF
+ case OPTION_MDEBUG:
+ alpha_flag_mdebug = 1;
+ break;
+ case OPTION_NO_MDEBUG:
+ alpha_flag_mdebug = 0;
+ break;
+#endif
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Print a description of the command-line options that we accept. */
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ fputs(_("\
+Alpha options:\n\
+-32addr treat addresses as 32-bit values\n\
+-F lack floating point instructions support\n\
+-mev4 | -mev45 | -mev5 | -mev56 | -mpca56 | -mev6 | -mall\n\
+ specify variant of Alpha architecture\n\
+-m21064 | -m21066 | -m21164 | -m21164a | -m21164pc | -m21264\n\
+ these variants include PALcode opcodes\n"),
+ stream);
+#ifdef OBJ_EVAX
+ fputs (_("\
+VMS options:\n\
+-+ hash encode (don't truncate) names longer than 64 characters\n\
+-H show new symbol after hash truncation\n"),
+ stream);
+#endif
+}
+
+/* Decide from what point a pc-relative relocation is relative to,
+ relative to the pc-relative fixup. Er, relatively speaking. */
+
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_ALPHA_GPDISP:
+ case BFD_RELOC_ALPHA_GPDISP_HI16:
+ case BFD_RELOC_ALPHA_GPDISP_LO16:
+ return addr;
+ default:
+ return fixP->fx_size + addr;
+ }
+}
+
+/* Attempt to simplify or even eliminate a fixup. The return value is
+ ignored; perhaps it was once meaningful, but now it is historical.
+ To indicate that a fixup has been eliminated, set fixP->fx_done.
+
+ For ELF, here it is that we transform the GPDISP_HI16 reloc we used
+ internally into the GPDISP reloc used externally. We had to do
+ this so that we'd have the GPDISP_LO16 reloc as a tag to compute
+ the distance to the "lda" instruction for setting the addend to
+ GPDISP. */
+
+int
+md_apply_fix (fixP, valueP)
+ fixS *fixP;
+ valueT *valueP;
+{
+ char * const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
+ valueT value = *valueP;
+ unsigned image, size;
+
+ switch (fixP->fx_r_type)
+ {
+ /* The GPDISP relocations are processed internally with a symbol
+ referring to the current function; we need to drop in a value
+ which, when added to the address of the start of the function,
+ gives the desired GP. */
+ case BFD_RELOC_ALPHA_GPDISP_HI16:
+ {
+ fixS *next = fixP->fx_next;
+ assert (next->fx_r_type == BFD_RELOC_ALPHA_GPDISP_LO16);
+
+ fixP->fx_offset = (next->fx_frag->fr_address + next->fx_where
+ - fixP->fx_frag->fr_address - fixP->fx_where);
+
+ value = (value - sign_extend_16 (value)) >> 16;
+ }
+#ifdef OBJ_ELF
+ fixP->fx_r_type = BFD_RELOC_ALPHA_GPDISP;
+#endif
+ goto do_reloc_gp;
+
+ case BFD_RELOC_ALPHA_GPDISP_LO16:
+ value = sign_extend_16 (value);
+ fixP->fx_offset = 0;
+#ifdef OBJ_ELF
+ fixP->fx_done = 1;
+#endif
+
+ do_reloc_gp:
+ fixP->fx_addsy = section_symbol (absolute_section);
+ md_number_to_chars (fixpos, value, 2);
+ break;
+
+ case BFD_RELOC_16:
+ if (fixP->fx_pcrel)
+ fixP->fx_r_type = BFD_RELOC_16_PCREL;
+ size = 2;
+ goto do_reloc_xx;
+ case BFD_RELOC_32:
+ if (fixP->fx_pcrel)
+ fixP->fx_r_type = BFD_RELOC_32_PCREL;
+ size = 4;
+ goto do_reloc_xx;
+ case BFD_RELOC_64:
+ if (fixP->fx_pcrel)
+ fixP->fx_r_type = BFD_RELOC_64_PCREL;
+ size = 8;
+ do_reloc_xx:
+ if (fixP->fx_pcrel == 0 && fixP->fx_addsy == 0)
+ {
+ md_number_to_chars (fixpos, value, size);
+ goto done;
+ }
+ return 1;
+
+#ifdef OBJ_ECOFF
+ case BFD_RELOC_GPREL32:
+ assert (fixP->fx_subsy == alpha_gp_symbol);
+ fixP->fx_subsy = 0;
+ /* FIXME: inherited this obliviousness of `value' -- why? */
+ md_number_to_chars (fixpos, -alpha_gp_value, 4);
+ break;
+#endif
+#ifdef OBJ_ELF
+ case BFD_RELOC_GPREL32:
+ return 1;
+#endif
+
+ case BFD_RELOC_23_PCREL_S2:
+ if (fixP->fx_pcrel == 0 && fixP->fx_addsy == 0)
+ {
+ image = bfd_getl32(fixpos);
+ image = (image & ~0x1FFFFF) | ((value >> 2) & 0x1FFFFF);
+ goto write_done;
+ }
+ return 1;
+
+ case BFD_RELOC_ALPHA_HINT:
+ if (fixP->fx_pcrel == 0 && fixP->fx_addsy == 0)
+ {
+ image = bfd_getl32(fixpos);
+ image = (image & ~0x3FFF) | ((value >> 2) & 0x3FFF);
+ goto write_done;
+ }
+ return 1;
+
+#ifdef OBJ_ECOFF
+ case BFD_RELOC_ALPHA_LITERAL:
+ md_number_to_chars (fixpos, value, 2);
+ return 1;
+
+ case BFD_RELOC_ALPHA_LITUSE:
+ return 1;
+#endif
+#ifdef OBJ_ELF
+ case BFD_RELOC_ALPHA_ELF_LITERAL:
+ case BFD_RELOC_ALPHA_LITUSE:
+ return 1;
+#endif
+#ifdef OBJ_EVAX
+ case BFD_RELOC_ALPHA_LINKAGE:
+ case BFD_RELOC_ALPHA_CODEADDR:
+ return 1;
+#endif
+
+ default:
+ {
+ const struct alpha_operand *operand;
+
+ if ((int)fixP->fx_r_type >= 0)
+ as_fatal (_("unhandled relocation type %s"),
+ bfd_get_reloc_code_name (fixP->fx_r_type));
+
+ assert (-(int)fixP->fx_r_type < alpha_num_operands);
+ operand = &alpha_operands[-(int)fixP->fx_r_type];
+
+ /* The rest of these fixups only exist internally during symbol
+ resolution and have no representation in the object file.
+ Therefore they must be completely resolved as constants. */
+
+ if (fixP->fx_addsy != 0
+ && fixP->fx_addsy->bsym->section != absolute_section)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("non-absolute expression in constant field"));
+
+ image = bfd_getl32(fixpos);
+ image = insert_operand(image, operand, (offsetT)value,
+ fixP->fx_file, fixP->fx_line);
+ }
+ goto write_done;
+ }
+
+ if (fixP->fx_addsy != 0 || fixP->fx_pcrel != 0)
+ return 1;
+ else
+ {
+ as_warn_where(fixP->fx_file, fixP->fx_line,
+ _("type %d reloc done?\n"), (int)fixP->fx_r_type);
+ goto done;
+ }
+
+write_done:
+ md_number_to_chars(fixpos, image, 4);
+
+done:
+ fixP->fx_done = 1;
+ return 0;
+}
+
+/*
+ * Look for a register name in the given symbol.
+ */
+
+symbolS *
+md_undefined_symbol(name)
+ char *name;
+{
+ if (*name == '$')
+ {
+ int is_float = 0, num;
+
+ switch (*++name)
+ {
+ case 'f':
+ if (name[1] == 'p' && name[2] == '\0')
+ return alpha_register_table[AXP_REG_FP];
+ is_float = 32;
+ /* FALLTHRU */
+
+ case 'r':
+ if (!isdigit(*++name))
+ break;
+ /* FALLTHRU */
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (name[1] == '\0')
+ num = name[0] - '0';
+ else if (name[0] != '0' && isdigit(name[1]) && name[2] == '\0')
+ {
+ num = (name[0] - '0') * 10 + name[1] - '0';
+ if (num >= 32)
+ break;
+ }
+ else
+ break;
+
+ if (!alpha_noat_on && num == AXP_REG_AT)
+ as_warn(_("Used $at without \".set noat\""));
+ return alpha_register_table[num + is_float];
+
+ case 'a':
+ if (name[1] == 't' && name[2] == '\0')
+ {
+ if (!alpha_noat_on)
+ as_warn(_("Used $at without \".set noat\""));
+ return alpha_register_table[AXP_REG_AT];
+ }
+ break;
+
+ case 'g':
+ if (name[1] == 'p' && name[2] == '\0')
+ return alpha_register_table[alpha_gp_register];
+ break;
+
+ case 's':
+ if (name[1] == 'p' && name[2] == '\0')
+ return alpha_register_table[AXP_REG_SP];
+ break;
+ }
+ }
+ return NULL;
+}
+
+#ifdef OBJ_ECOFF
+/* @@@ Magic ECOFF bits. */
+
+void
+alpha_frob_ecoff_data ()
+{
+ select_gp_value ();
+ /* $zero and $f31 are read-only */
+ alpha_gprmask &= ~1;
+ alpha_fprmask &= ~1;
+}
+#endif
+
+/* Hook to remember a recently defined label so that the auto-align
+ code can adjust the symbol after we know what alignment will be
+ required. */
+
+void
+alpha_define_label (sym)
+ symbolS *sym;
+{
+ alpha_insn_label = sym;
+}
+
+/* Return true if we must always emit a reloc for a type and false if
+ there is some hope of resolving it a assembly time. */
+
+int
+alpha_force_relocation (f)
+ fixS *f;
+{
+ if (alpha_flag_relax)
+ return 1;
+
+ switch (f->fx_r_type)
+ {
+ case BFD_RELOC_ALPHA_GPDISP_HI16:
+ case BFD_RELOC_ALPHA_GPDISP_LO16:
+ case BFD_RELOC_ALPHA_GPDISP:
+#ifdef OBJ_ECOFF
+ case BFD_RELOC_ALPHA_LITERAL:
+#endif
+#ifdef OBJ_ELF
+ case BFD_RELOC_ALPHA_ELF_LITERAL:
+#endif
+ case BFD_RELOC_ALPHA_LITUSE:
+ case BFD_RELOC_GPREL32:
+#ifdef OBJ_EVAX
+ case BFD_RELOC_ALPHA_LINKAGE:
+ case BFD_RELOC_ALPHA_CODEADDR:
+#endif
+ return 1;
+
+ case BFD_RELOC_23_PCREL_S2:
+ case BFD_RELOC_32:
+ case BFD_RELOC_64:
+ case BFD_RELOC_ALPHA_HINT:
+ return 0;
+
+ default:
+ assert((int)f->fx_r_type < 0 && -(int)f->fx_r_type < alpha_num_operands);
+ return 0;
+ }
+}
+
+/* Return true if we can partially resolve a relocation now. */
+
+int
+alpha_fix_adjustable (f)
+ fixS *f;
+{
+#ifdef OBJ_ELF
+ /* Prevent all adjustments to global symbols */
+ if (S_IS_EXTERN (f->fx_addsy) || S_IS_WEAK (f->fx_addsy))
+ return 0;
+#endif
+
+ /* Are there any relocation types for which we must generate a reloc
+ but we can adjust the values contained within it? */
+ switch (f->fx_r_type)
+ {
+ case BFD_RELOC_ALPHA_GPDISP_HI16:
+ case BFD_RELOC_ALPHA_GPDISP_LO16:
+ case BFD_RELOC_ALPHA_GPDISP:
+ return 0;
+
+#ifdef OBJ_ECOFF
+ case BFD_RELOC_ALPHA_LITERAL:
+#endif
+#ifdef OBJ_ELF
+ case BFD_RELOC_ALPHA_ELF_LITERAL:
+#endif
+#ifdef OBJ_EVAX
+ case BFD_RELOC_ALPHA_LINKAGE:
+ case BFD_RELOC_ALPHA_CODEADDR:
+#endif
+ return 1;
+
+ case BFD_RELOC_ALPHA_LITUSE:
+ return 0;
+
+ case BFD_RELOC_GPREL32:
+ case BFD_RELOC_23_PCREL_S2:
+ case BFD_RELOC_32:
+ case BFD_RELOC_64:
+ case BFD_RELOC_ALPHA_HINT:
+ return 1;
+
+ default:
+ assert ((int)f->fx_r_type < 0
+ && - (int)f->fx_r_type < alpha_num_operands);
+ return 1;
+ }
+ /*NOTREACHED*/
+}
+
+/* Generate the BFD reloc to be stuck in the object file from the
+ fixup used internally in the assembler. */
+
+arelent *
+tc_gen_reloc (sec, fixp)
+ asection *sec;
+ fixS *fixp;
+{
+ arelent *reloc;
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ /* Make sure none of our internal relocations make it this far.
+ They'd better have been fully resolved by this point. */
+ assert ((int)fixp->fx_r_type > 0);
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("cannot represent `%s' relocation in object file"),
+ bfd_get_reloc_code_name (fixp->fx_r_type));
+ return NULL;
+ }
+
+ if (!fixp->fx_pcrel != !reloc->howto->pc_relative)
+ {
+ as_fatal (_("internal error? cannot generate `%s' relocation"),
+ bfd_get_reloc_code_name (fixp->fx_r_type));
+ }
+ assert (!fixp->fx_pcrel == !reloc->howto->pc_relative);
+
+#ifdef OBJ_ECOFF
+ if (fixp->fx_r_type == BFD_RELOC_ALPHA_LITERAL)
+ {
+ /* fake out bfd_perform_relocation. sigh */
+ reloc->addend = -alpha_gp_value;
+ }
+ else
+#endif
+ {
+ reloc->addend = fixp->fx_offset;
+#ifdef OBJ_ELF
+ /*
+ * Ohhh, this is ugly. The problem is that if this is a local global
+ * symbol, the relocation will entirely be performed at link time, not
+ * at assembly time. bfd_perform_reloc doesn't know about this sort
+ * of thing, and as a result we need to fake it out here.
+ */
+ if ((S_IS_EXTERN (fixp->fx_addsy) || S_IS_WEAK (fixp->fx_addsy))
+ && !S_IS_COMMON(fixp->fx_addsy))
+ reloc->addend -= fixp->fx_addsy->bsym->value;
+#endif
+ }
+
+ return reloc;
+}
+
+/* Parse a register name off of the input_line and return a register
+ number. Gets md_undefined_symbol above to do the register name
+ matching for us.
+
+ Only called as a part of processing the ECOFF .frame directive. */
+
+int
+tc_get_register (frame)
+ int frame;
+{
+ int framereg = AXP_REG_SP;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '$')
+ {
+ char *s = input_line_pointer;
+ char c = get_symbol_end ();
+ symbolS *sym = md_undefined_symbol (s);
+
+ *strchr(s, '\0') = c;
+ if (sym && (framereg = S_GET_VALUE (sym)) <= 31)
+ goto found;
+ }
+ as_warn (_("frame reg expected, using $%d."), framereg);
+
+found:
+ note_gpreg (framereg);
+ return framereg;
+}
+
+/* This is called before the symbol table is processed. In order to
+ work with gcc when using mips-tfile, we must keep all local labels.
+ However, in other cases, we want to discard them. If we were
+ called with -g, but we didn't see any debugging information, it may
+ mean that gcc is smuggling debugging information through to
+ mips-tfile, in which case we must generate all local labels. */
+
+#ifdef OBJ_ECOFF
+
+void
+alpha_frob_file_before_adjust ()
+{
+ if (alpha_debug != 0
+ && ! ecoff_debugging_seen)
+ flag_keep_locals = 1;
+}
+
+#endif /* OBJ_ECOFF */
+
+/* Parse the arguments to an opcode. */
+
+static int
+tokenize_arguments (str, tok, ntok)
+ char *str;
+ expressionS tok[];
+ int ntok;
+{
+ expressionS *end_tok = tok + ntok;
+ char *old_input_line_pointer;
+ int saw_comma = 0, saw_arg = 0;
+
+ memset (tok, 0, sizeof (*tok) * ntok);
+
+ /* Save and restore input_line_pointer around this function */
+ old_input_line_pointer = input_line_pointer;
+ input_line_pointer = str;
+
+ while (tok < end_tok && *input_line_pointer)
+ {
+ SKIP_WHITESPACE ();
+ switch (*input_line_pointer)
+ {
+ case '\0':
+ goto fini;
+
+ case ',':
+ ++input_line_pointer;
+ if (saw_comma || !saw_arg)
+ goto err;
+ saw_comma = 1;
+ break;
+
+ case '(':
+ {
+ char *hold = input_line_pointer++;
+
+ /* First try for parenthesized register ... */
+ expression (tok);
+ if (*input_line_pointer == ')' && tok->X_op == O_register)
+ {
+ tok->X_op = (saw_comma ? O_cpregister : O_pregister);
+ saw_comma = 0;
+ saw_arg = 1;
+ ++input_line_pointer;
+ ++tok;
+ break;
+ }
+
+ /* ... then fall through to plain expression */
+ input_line_pointer = hold;
+ }
+
+ default:
+ if (saw_arg && !saw_comma)
+ goto err;
+ expression (tok);
+ if (tok->X_op == O_illegal || tok->X_op == O_absent)
+ goto err;
+
+ saw_comma = 0;
+ saw_arg = 1;
+ ++tok;
+ break;
+ }
+ }
+
+fini:
+ if (saw_comma)
+ goto err;
+ input_line_pointer = old_input_line_pointer;
+ return ntok - (end_tok - tok);
+
+err:
+ input_line_pointer = old_input_line_pointer;
+ return -1;
+}
+
+/* Search forward through all variants of an opcode looking for a
+ syntax match. */
+
+static const struct alpha_opcode *
+find_opcode_match(first_opcode, tok, pntok, pcpumatch)
+ const struct alpha_opcode *first_opcode;
+ const expressionS *tok;
+ int *pntok;
+ int *pcpumatch;
+{
+ const struct alpha_opcode *opcode = first_opcode;
+ int ntok = *pntok;
+ int got_cpu_match = 0;
+
+ do
+ {
+ const unsigned char *opidx;
+ int tokidx = 0;
+
+ /* Don't match opcodes that don't exist on this architecture */
+ if (!(opcode->flags & alpha_target))
+ goto match_failed;
+
+ got_cpu_match = 1;
+
+ for (opidx = opcode->operands; *opidx; ++opidx)
+ {
+ const struct alpha_operand *operand = &alpha_operands[*opidx];
+
+ /* only take input from real operands */
+ if (operand->flags & AXP_OPERAND_FAKE)
+ continue;
+
+ /* when we expect input, make sure we have it */
+ if (tokidx >= ntok)
+ {
+ if ((operand->flags & AXP_OPERAND_OPTIONAL_MASK) == 0)
+ goto match_failed;
+ continue;
+ }
+
+ /* match operand type with expression type */
+ switch (operand->flags & AXP_OPERAND_TYPECHECK_MASK)
+ {
+ case AXP_OPERAND_IR:
+ if (tok[tokidx].X_op != O_register
+ || !is_ir_num(tok[tokidx].X_add_number))
+ goto match_failed;
+ break;
+ case AXP_OPERAND_FPR:
+ if (tok[tokidx].X_op != O_register
+ || !is_fpr_num(tok[tokidx].X_add_number))
+ goto match_failed;
+ break;
+ case AXP_OPERAND_IR|AXP_OPERAND_PARENS:
+ if (tok[tokidx].X_op != O_pregister
+ || !is_ir_num(tok[tokidx].X_add_number))
+ goto match_failed;
+ break;
+ case AXP_OPERAND_IR|AXP_OPERAND_PARENS|AXP_OPERAND_COMMA:
+ if (tok[tokidx].X_op != O_cpregister
+ || !is_ir_num(tok[tokidx].X_add_number))
+ goto match_failed;
+ break;
+
+ case AXP_OPERAND_RELATIVE:
+ case AXP_OPERAND_SIGNED:
+ case AXP_OPERAND_UNSIGNED:
+ switch (tok[tokidx].X_op)
+ {
+ case O_illegal:
+ case O_absent:
+ case O_register:
+ case O_pregister:
+ case O_cpregister:
+ goto match_failed;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ /* everything else should have been fake */
+ abort();
+ }
+ ++tokidx;
+ }
+
+ /* possible match -- did we use all of our input? */
+ if (tokidx == ntok)
+ {
+ *pntok = ntok;
+ return opcode;
+ }
+
+ match_failed:;
+ }
+ while (++opcode-alpha_opcodes < alpha_num_opcodes
+ && !strcmp(opcode->name, first_opcode->name));
+
+ if (*pcpumatch)
+ *pcpumatch = got_cpu_match;
+
+ return NULL;
+}
+
+/* Search forward through all variants of a macro looking for a syntax
+ match. */
+
+static const struct alpha_macro *
+find_macro_match(first_macro, tok, pntok)
+ const struct alpha_macro *first_macro;
+ const expressionS *tok;
+ int *pntok;
+{
+ const struct alpha_macro *macro = first_macro;
+ int ntok = *pntok;
+
+ do
+ {
+ const enum alpha_macro_arg *arg = macro->argsets;
+ int tokidx = 0;
+
+ while (*arg)
+ {
+ switch (*arg)
+ {
+ case MACRO_EOA:
+ if (tokidx == ntok)
+ return macro;
+ else
+ tokidx = 0;
+ break;
+
+ case MACRO_IR:
+ if (tokidx >= ntok || tok[tokidx].X_op != O_register
+ || !is_ir_num(tok[tokidx].X_add_number))
+ goto match_failed;
+ ++tokidx;
+ break;
+ case MACRO_PIR:
+ if (tokidx >= ntok || tok[tokidx].X_op != O_pregister
+ || !is_ir_num(tok[tokidx].X_add_number))
+ goto match_failed;
+ ++tokidx;
+ break;
+ case MACRO_CPIR:
+ if (tokidx >= ntok || tok[tokidx].X_op != O_cpregister
+ || !is_ir_num(tok[tokidx].X_add_number))
+ goto match_failed;
+ ++tokidx;
+ break;
+ case MACRO_FPR:
+ if (tokidx >= ntok || tok[tokidx].X_op != O_register
+ || !is_fpr_num(tok[tokidx].X_add_number))
+ goto match_failed;
+ ++tokidx;
+ break;
+
+ case MACRO_EXP:
+ if (tokidx >= ntok)
+ goto match_failed;
+ switch (tok[tokidx].X_op)
+ {
+ case O_illegal:
+ case O_absent:
+ case O_register:
+ case O_pregister:
+ case O_cpregister:
+ goto match_failed;
+
+ default:
+ break;
+ }
+ ++tokidx;
+ break;
+
+ match_failed:
+ while (*arg != MACRO_EOA)
+ ++arg;
+ tokidx = 0;
+ break;
+ }
+ ++arg;
+ }
+ }
+ while (++macro-alpha_macros < alpha_num_macros
+ && !strcmp(macro->name, first_macro->name));
+
+ return NULL;
+}
+
+/* Insert an operand value into an instruction. */
+
+static unsigned
+insert_operand(insn, operand, val, file, line)
+ unsigned insn;
+ const struct alpha_operand *operand;
+ offsetT val;
+ char *file;
+ unsigned line;
+{
+ if (operand->bits != 32 && !(operand->flags & AXP_OPERAND_NOOVERFLOW))
+ {
+ offsetT min, max;
+
+ if (operand->flags & AXP_OPERAND_SIGNED)
+ {
+ max = (1 << (operand->bits - 1)) - 1;
+ min = -(1 << (operand->bits - 1));
+ }
+ else
+ {
+ max = (1 << operand->bits) - 1;
+ min = 0;
+ }
+
+ if (val < min || val > max)
+ {
+ const char *err =
+ _("operand out of range (%s not between %d and %d)");
+ char buf[sizeof (val) * 3 + 2];
+
+ sprint_value(buf, val);
+ if (file)
+ as_warn_where(file, line, err, buf, min, max);
+ else
+ as_warn(err, buf, min, max);
+ }
+ }
+
+ if (operand->insert)
+ {
+ const char *errmsg = NULL;
+
+ insn = (*operand->insert) (insn, val, &errmsg);
+ if (errmsg)
+ as_warn (errmsg);
+ }
+ else
+ insn |= ((val & ((1 << operand->bits) - 1)) << operand->shift);
+
+ return insn;
+}
+
+/*
+ * Turn an opcode description and a set of arguments into
+ * an instruction and a fixup.
+ */
+
+static void
+assemble_insn(opcode, tok, ntok, insn)
+ const struct alpha_opcode *opcode;
+ const expressionS *tok;
+ int ntok;
+ struct alpha_insn *insn;
+{
+ const unsigned char *argidx;
+ unsigned image;
+ int tokidx = 0;
+
+ memset (insn, 0, sizeof (*insn));
+ image = opcode->opcode;
+
+ for (argidx = opcode->operands; *argidx; ++argidx)
+ {
+ const struct alpha_operand *operand = &alpha_operands[*argidx];
+ const expressionS *t;
+
+ if (operand->flags & AXP_OPERAND_FAKE)
+ {
+ /* fake operands take no value and generate no fixup */
+ image = insert_operand(image, operand, 0, NULL, 0);
+ continue;
+ }
+
+ if (tokidx >= ntok)
+ {
+ switch (operand->flags & AXP_OPERAND_OPTIONAL_MASK)
+ {
+ case AXP_OPERAND_DEFAULT_FIRST:
+ t = &tok[0];
+ break;
+ case AXP_OPERAND_DEFAULT_SECOND:
+ t = &tok[1];
+ break;
+ case AXP_OPERAND_DEFAULT_ZERO:
+ {
+ static const expressionS zero_exp = { 0, 0, 0, O_constant, 1 };
+ t = &zero_exp;
+ }
+ break;
+ default:
+ abort();
+ }
+ }
+ else
+ t = &tok[tokidx++];
+
+ switch (t->X_op)
+ {
+ case O_register:
+ case O_pregister:
+ case O_cpregister:
+ image = insert_operand(image, operand, regno(t->X_add_number),
+ NULL, 0);
+ break;
+
+ case O_constant:
+ image = insert_operand(image, operand, t->X_add_number, NULL, 0);
+ break;
+
+ default:
+ {
+ struct alpha_fixup *fixup;
+
+ if (insn->nfixups >= MAX_INSN_FIXUPS)
+ as_fatal(_("too many fixups"));
+
+ fixup = &insn->fixups[insn->nfixups++];
+
+ fixup->exp = *t;
+ fixup->reloc = operand->default_reloc;
+ }
+ break;
+ }
+ }
+
+ insn->insn = image;
+}
+
+/*
+ * Actually output an instruction with its fixup.
+ */
+
+static void
+emit_insn (insn)
+ struct alpha_insn *insn;
+{
+ char *f;
+ int i;
+
+ /* Take care of alignment duties */
+ if (alpha_auto_align_on && alpha_current_align < 2)
+ alpha_align (2, (char *) NULL, alpha_insn_label, 0);
+ if (alpha_current_align > 2)
+ alpha_current_align = 2;
+ alpha_insn_label = NULL;
+
+ /* Write out the instruction. */
+ f = frag_more (4);
+ md_number_to_chars (f, insn->insn, 4);
+
+ /* Apply the fixups in order */
+ for (i = 0; i < insn->nfixups; ++i)
+ {
+ const struct alpha_operand *operand;
+ struct alpha_fixup *fixup = &insn->fixups[i];
+ int size, pcrel;
+ fixS *fixP;
+
+ /* Some fixups are only used internally and so have no howto */
+ if ((int)fixup->reloc < 0)
+ {
+ operand = &alpha_operands[-(int)fixup->reloc];
+ size = 4;
+ pcrel = ((operand->flags & AXP_OPERAND_RELATIVE) != 0);
+ }
+#ifdef OBJ_ELF
+ /* These relocation types are only used internally. */
+ else if (fixup->reloc == BFD_RELOC_ALPHA_GPDISP_HI16
+ || fixup->reloc == BFD_RELOC_ALPHA_GPDISP_LO16)
+ {
+ size = 2, pcrel = 0;
+ }
+#endif
+ else
+ {
+ reloc_howto_type *reloc_howto
+ = bfd_reloc_type_lookup (stdoutput, fixup->reloc);
+ assert (reloc_howto);
+
+ size = bfd_get_reloc_size (reloc_howto);
+ pcrel = reloc_howto->pc_relative;
+ }
+ assert (size >= 1 && size <= 4);
+
+ fixP = fix_new_exp (frag_now, f - frag_now->fr_literal, size,
+ &fixup->exp, pcrel, fixup->reloc);
+
+ /* Turn off complaints that the addend is too large for some fixups */
+ switch (fixup->reloc)
+ {
+ case BFD_RELOC_ALPHA_GPDISP_LO16:
+#ifdef OBJ_ECOFF
+ case BFD_RELOC_ALPHA_LITERAL:
+#endif
+#ifdef OBJ_ELF
+ case BFD_RELOC_ALPHA_ELF_LITERAL:
+#endif
+ case BFD_RELOC_GPREL32:
+ fixP->fx_no_overflow = 1;
+ break;
+
+ default:
+ if ((int)fixup->reloc < 0)
+ {
+ if (operand->flags & AXP_OPERAND_NOOVERFLOW)
+ fixP->fx_no_overflow = 1;
+ }
+ break;
+ }
+ }
+}
+
+/* Given an opcode name and a pre-tokenized set of arguments, assemble
+ the insn, but do not emit it.
+
+ Note that this implies no macros allowed, since we can't store more
+ than one insn in an insn structure. */
+
+static void
+assemble_tokens_to_insn(opname, tok, ntok, insn)
+ const char *opname;
+ const expressionS *tok;
+ int ntok;
+ struct alpha_insn *insn;
+{
+ const struct alpha_opcode *opcode;
+
+ /* search opcodes */
+ opcode = (const struct alpha_opcode *) hash_find (alpha_opcode_hash, opname);
+ if (opcode)
+ {
+ int cpumatch;
+ opcode = find_opcode_match (opcode, tok, &ntok, &cpumatch);
+ if (opcode)
+ {
+ assemble_insn (opcode, tok, ntok, insn);
+ return;
+ }
+ else if (cpumatch)
+ as_bad (_("inappropriate arguments for opcode `%s'"), opname);
+ else
+ as_bad (_("opcode `%s' not supported for target %s"), opname,
+ alpha_target_name);
+ }
+ else
+ as_bad (_("unknown opcode `%s'"), opname);
+}
+
+/* Given an opcode name and a pre-tokenized set of arguments, take the
+ opcode all the way through emission. */
+
+static void
+assemble_tokens (opname, tok, ntok, local_macros_on)
+ const char *opname;
+ const expressionS *tok;
+ int ntok;
+ int local_macros_on;
+{
+ int found_something = 0;
+ const struct alpha_opcode *opcode;
+ const struct alpha_macro *macro;
+ int cpumatch = 1;
+
+ /* search macros */
+ if (local_macros_on)
+ {
+ macro = ((const struct alpha_macro *)
+ hash_find (alpha_macro_hash, opname));
+ if (macro)
+ {
+ found_something = 1;
+ macro = find_macro_match (macro, tok, &ntok);
+ if (macro)
+ {
+ (*macro->emit) (tok, ntok, macro->arg);
+ return;
+ }
+ }
+ }
+
+ /* search opcodes */
+ opcode = (const struct alpha_opcode *) hash_find (alpha_opcode_hash, opname);
+ if (opcode)
+ {
+ found_something = 1;
+ opcode = find_opcode_match (opcode, tok, &ntok, &cpumatch);
+ if (opcode)
+ {
+ struct alpha_insn insn;
+ assemble_insn (opcode, tok, ntok, &insn);
+ emit_insn (&insn);
+ return;
+ }
+ }
+
+ if (found_something)
+ if (cpumatch)
+ as_bad (_("inappropriate arguments for opcode `%s'"), opname);
+ else
+ as_bad (_("opcode `%s' not supported for target %s"), opname,
+ alpha_target_name);
+ else
+ as_bad (_("unknown opcode `%s'"), opname);
+}
+
+
+/* Some instruction sets indexed by lg(size) */
+static const char * const sextX_op[] = { "sextb", "sextw", "sextl", NULL };
+static const char * const insXl_op[] = { "insbl", "inswl", "insll", "insql" };
+static const char * const insXh_op[] = { NULL, "inswh", "inslh", "insqh" };
+static const char * const extXl_op[] = { "extbl", "extwl", "extll", "extql" };
+static const char * const extXh_op[] = { NULL, "extwh", "extlh", "extqh" };
+static const char * const mskXl_op[] = { "mskbl", "mskwl", "mskll", "mskql" };
+static const char * const mskXh_op[] = { NULL, "mskwh", "msklh", "mskqh" };
+static const char * const stX_op[] = { "stb", "stw", "stl", "stq" };
+static const char * const ldX_op[] = { "ldb", "ldw", "ldll", "ldq" };
+static const char * const ldXu_op[] = { "ldbu", "ldwu", NULL, NULL };
+
+/* Implement the ldgp macro. */
+
+static void
+emit_ldgp (tok, ntok, unused)
+ const expressionS *tok;
+ int ntok;
+ const PTR unused;
+{
+#ifdef OBJ_AOUT
+FIXME
+#endif
+#if defined(OBJ_ECOFF) || defined(OBJ_ELF)
+ /* from "ldgp r1,n(r2)", generate "ldah r1,X(R2); lda r1,Y(r1)"
+ with appropriate constants and relocations. */
+ struct alpha_insn insn;
+ expressionS newtok[3];
+ expressionS addend;
+
+ /* We're going to need this symbol in md_apply_fix(). */
+ (void) section_symbol (absolute_section);
+
+#ifdef OBJ_ECOFF
+ if (regno (tok[2].X_add_number) == AXP_REG_PV)
+ ecoff_set_gp_prolog_size (0);
+#endif
+
+ newtok[0] = tok[0];
+ set_tok_const (newtok[1], 0);
+ newtok[2] = tok[2];
+
+ assemble_tokens_to_insn ("ldah", newtok, 3, &insn);
+
+ addend = tok[1];
+
+#ifdef OBJ_ECOFF
+ if (addend.X_op != O_constant)
+ as_bad (_("can not resolve expression"));
+ addend.X_op = O_symbol;
+ addend.X_add_symbol = alpha_gp_symbol;
+#endif
+
+ insn.nfixups = 1;
+ insn.fixups[0].exp = addend;
+ insn.fixups[0].reloc = BFD_RELOC_ALPHA_GPDISP_HI16;
+
+ emit_insn (&insn);
+
+ set_tok_preg (newtok[2], tok[0].X_add_number);
+
+ assemble_tokens_to_insn ("lda", newtok, 3, &insn);
+
+#ifdef OBJ_ECOFF
+ addend.X_add_number += 4;
+#endif
+
+ insn.nfixups = 1;
+ insn.fixups[0].exp = addend;
+ insn.fixups[0].reloc = BFD_RELOC_ALPHA_GPDISP_LO16;
+
+ emit_insn (&insn);
+#endif /* OBJ_ECOFF || OBJ_ELF */
+}
+
+#ifdef OBJ_EVAX
+
+/* Add symbol+addend to link pool.
+ Return offset from basesym to entry in link pool.
+
+ Add new fixup only if offset isn't 16bit. */
+
+valueT
+add_to_link_pool (basesym, sym, addend)
+ symbolS *basesym;
+ symbolS *sym;
+ offsetT addend;
+{
+ segT current_section = now_seg;
+ int current_subsec = now_subseg;
+ valueT offset;
+ bfd_reloc_code_real_type reloc_type;
+ char *p;
+ segment_info_type *seginfo = seg_info (alpha_link_section);
+ fixS *fixp;
+
+ offset = -basesym->sy_obj;
+
+ /* @@ This assumes all entries in a given section will be of the same
+ size... Probably correct, but unwise to rely on. */
+ /* This must always be called with the same subsegment. */
+
+ if (seginfo->frchainP)
+ for (fixp = seginfo->frchainP->fix_root;
+ fixp != (fixS *) NULL;
+ fixp = fixp->fx_next, offset += 8)
+ {
+ if (fixp->fx_addsy == sym && fixp->fx_offset == addend)
+ {
+ if (range_signed_16 (offset))
+ {
+ return offset;
+ }
+ }
+ }
+
+ /* Not found in 16bit signed range. */
+
+ subseg_set (alpha_link_section, 0);
+ p = frag_more (8);
+ memset (p, 0, 8);
+
+ fix_new (frag_now, p - frag_now->fr_literal, 8, sym, addend, 0,
+ BFD_RELOC_64);
+
+ subseg_set (current_section, current_subsec);
+ seginfo->literal_pool_size += 8;
+ return offset;
+}
+
+#endif /* OBJ_EVAX */
+
+/* Load a (partial) expression into a target register.
+
+ If poffset is not null, after the call it will either contain
+ O_constant 0, or a 16-bit offset appropriate for any MEM format
+ instruction. In addition, pbasereg will be modified to point to
+ the base register to use in that MEM format instruction.
+
+ In any case, *pbasereg should contain a base register to add to the
+ expression. This will normally be either AXP_REG_ZERO or
+ alpha_gp_register. Symbol addresses will always be loaded via $gp,
+ so "foo($0)" is interpreted as adding the address of foo to $0;
+ i.e. "ldq $targ, LIT($gp); addq $targ, $0, $targ". Odd, perhaps,
+ but this is what OSF/1 does.
+
+ Finally, the return value is true if the calling macro may emit a
+ LITUSE reloc if otherwise appropriate. */
+
+static int
+load_expression (targreg, exp, pbasereg, poffset)
+ int targreg;
+ const expressionS *exp;
+ int *pbasereg;
+ expressionS *poffset;
+{
+ int emit_lituse = 0;
+ offsetT addend = exp->X_add_number;
+ int basereg = *pbasereg;
+ struct alpha_insn insn;
+ expressionS newtok[3];
+
+ switch (exp->X_op)
+ {
+ case O_symbol:
+ {
+#ifdef OBJ_ECOFF
+ offsetT lit;
+
+ /* attempt to reduce .lit load by splitting the offset from
+ its symbol when possible, but don't create a situation in
+ which we'd fail. */
+ if (!range_signed_32 (addend) &&
+ (alpha_noat_on || targreg == AXP_REG_AT))
+ {
+ lit = add_to_literal_pool (exp->X_add_symbol, addend,
+ alpha_lita_section, 8);
+ addend = 0;
+ }
+ else
+ {
+ lit = add_to_literal_pool (exp->X_add_symbol, 0,
+ alpha_lita_section, 8);
+ }
+
+ if (lit >= 0x8000)
+ as_fatal (_("overflow in literal (.lita) table"));
+
+ /* emit "ldq r, lit(gp)" */
+
+ if (basereg != alpha_gp_register && targreg == basereg)
+ {
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+ if (targreg == AXP_REG_AT)
+ as_bad (_("macro requires $at while $at in use"));
+
+ set_tok_reg (newtok[0], AXP_REG_AT);
+ }
+ else
+ set_tok_reg (newtok[0], targreg);
+ set_tok_sym (newtok[1], alpha_lita_symbol, lit);
+ set_tok_preg (newtok[2], alpha_gp_register);
+
+ assemble_tokens_to_insn ("ldq", newtok, 3, &insn);
+
+ assert (insn.nfixups == 1);
+ insn.fixups[0].reloc = BFD_RELOC_ALPHA_LITERAL;
+#endif /* OBJ_ECOFF */
+#ifdef OBJ_ELF
+ /* emit "ldq r, gotoff(gp)" */
+
+ if (basereg != alpha_gp_register && targreg == basereg)
+ {
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+ if (targreg == AXP_REG_AT)
+ as_bad (_("macro requires $at while $at in use"));
+
+ set_tok_reg (newtok[0], AXP_REG_AT);
+ }
+ else
+ set_tok_reg (newtok[0], targreg);
+
+ /* XXX: Disable this .got minimizing optimization so that we can get
+ better instruction offset knowledge in the compiler. This happens
+ very infrequently anyway. */
+ if (1 || (!range_signed_32 (addend)
+ && (alpha_noat_on || targreg == AXP_REG_AT)))
+ {
+ newtok[1] = *exp;
+ addend = 0;
+ }
+ else
+ {
+ set_tok_sym (newtok[1], exp->X_add_symbol, 0);
+ }
+
+ set_tok_preg (newtok[2], alpha_gp_register);
+
+ assemble_tokens_to_insn ("ldq", newtok, 3, &insn);
+
+ assert (insn.nfixups == 1);
+ insn.fixups[0].reloc = BFD_RELOC_ALPHA_ELF_LITERAL;
+#endif /* OBJ_ELF */
+#ifdef OBJ_EVAX
+ offsetT link;
+
+ /* Find symbol or symbol pointer in link section. */
+
+ if (exp->X_add_symbol == alpha_evax_proc.symbol)
+ {
+ if (range_signed_16 (addend))
+ {
+ set_tok_reg (newtok[0], targreg);
+ set_tok_const (newtok[1], addend);
+ set_tok_preg (newtok[2], basereg);
+ assemble_tokens_to_insn ("lda", newtok, 3, &insn);
+ addend = 0;
+ }
+ else
+ {
+ set_tok_reg (newtok[0], targreg);
+ set_tok_const (newtok[1], 0);
+ set_tok_preg (newtok[2], basereg);
+ assemble_tokens_to_insn ("lda", newtok, 3, &insn);
+ }
+ }
+ else
+ {
+ if (!range_signed_32 (addend))
+ {
+ link = add_to_link_pool (alpha_evax_proc.symbol,
+ exp->X_add_symbol, addend);
+ addend = 0;
+ }
+ else
+ {
+ link = add_to_link_pool (alpha_evax_proc.symbol,
+ exp->X_add_symbol, 0);
+ }
+ set_tok_reg (newtok[0], targreg);
+ set_tok_const (newtok[1], link);
+ set_tok_preg (newtok[2], basereg);
+ assemble_tokens_to_insn ("ldq", newtok, 3, &insn);
+ }
+#endif /* OBJ_EVAX */
+
+ emit_insn(&insn);
+
+#ifndef OBJ_EVAX
+ emit_lituse = 1;
+
+ if (basereg != alpha_gp_register && basereg != AXP_REG_ZERO)
+ {
+ /* emit "addq r, base, r" */
+
+ set_tok_reg (newtok[1], basereg);
+ set_tok_reg (newtok[2], targreg);
+ assemble_tokens ("addq", newtok, 3, 0);
+ }
+#endif
+
+ basereg = targreg;
+ }
+ break;
+
+ case O_constant:
+ break;
+
+ case O_subtract:
+ /* Assume that this difference expression will be resolved to an
+ absolute value and that that value will fit in 16 bits. */
+
+ set_tok_reg (newtok[0], targreg);
+ newtok[1] = *exp;
+ set_tok_preg (newtok[2], basereg);
+ assemble_tokens ("lda", newtok, 3, 0);
+
+ if (poffset)
+ set_tok_const (*poffset, 0);
+ return 0;
+
+ case O_big:
+ if (exp->X_add_number > 0)
+ as_bad (_("bignum invalid; zero assumed"));
+ else
+ as_bad (_("floating point number invalid; zero assumed"));
+ addend = 0;
+ break;
+
+ default:
+ as_bad (_("can't handle expression"));
+ addend = 0;
+ break;
+ }
+
+ if (!range_signed_32 (addend))
+ {
+ offsetT lit;
+
+ /* for 64-bit addends, just put it in the literal pool */
+
+#ifdef OBJ_EVAX
+ /* emit "ldq targreg, lit(basereg)" */
+ lit = add_to_link_pool (alpha_evax_proc.symbol,
+ section_symbol (absolute_section), addend);
+ set_tok_reg (newtok[0], targreg);
+ set_tok_const (newtok[1], lit);
+ set_tok_preg (newtok[2], alpha_gp_register);
+ assemble_tokens ("ldq", newtok, 3, 0);
+#else
+
+ if (alpha_lit8_section == NULL)
+ {
+ create_literal_section (".lit8",
+ &alpha_lit8_section,
+ &alpha_lit8_symbol);
+
+#ifdef OBJ_ECOFF
+ alpha_lit8_literal = add_to_literal_pool (alpha_lit8_symbol, 0x8000,
+ alpha_lita_section, 8);
+ if (alpha_lit8_literal >= 0x8000)
+ as_fatal (_("overflow in literal (.lita) table"));
+#endif
+ }
+
+ lit = add_to_literal_pool (NULL, addend, alpha_lit8_section, 8) - 0x8000;
+ if (lit >= 0x8000)
+ as_fatal (_("overflow in literal (.lit8) table"));
+
+ /* emit "lda litreg, .lit8+0x8000" */
+
+ if (targreg == basereg)
+ {
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+ if (targreg == AXP_REG_AT)
+ as_bad (_("macro requires $at while $at in use"));
+
+ set_tok_reg (newtok[0], AXP_REG_AT);
+ }
+ else
+ set_tok_reg (newtok[0], targreg);
+#ifdef OBJ_ECOFF
+ set_tok_sym (newtok[1], alpha_lita_symbol, alpha_lit8_literal);
+#endif
+#ifdef OBJ_ELF
+ set_tok_sym (newtok[1], alpha_lit8_symbol, 0x8000);
+#endif
+ set_tok_preg (newtok[2], alpha_gp_register);
+
+ assemble_tokens_to_insn ("ldq", newtok, 3, &insn);
+
+ assert (insn.nfixups == 1);
+#ifdef OBJ_ECOFF
+ insn.fixups[0].reloc = BFD_RELOC_ALPHA_LITERAL;
+#endif
+#ifdef OBJ_ELF
+ insn.fixups[0].reloc = BFD_RELOC_ALPHA_ELF_LITERAL;
+#endif
+
+ emit_insn (&insn);
+
+ /* emit "ldq litreg, lit(litreg)" */
+
+ set_tok_const (newtok[1], lit);
+ set_tok_preg (newtok[2], newtok[0].X_add_number);
+
+ assemble_tokens_to_insn ("ldq", newtok, 3, &insn);
+
+ assert (insn.nfixups < MAX_INSN_FIXUPS);
+ if (insn.nfixups > 0)
+ {
+ memmove (&insn.fixups[1], &insn.fixups[0],
+ sizeof(struct alpha_fixup) * insn.nfixups);
+ }
+ insn.nfixups++;
+ insn.fixups[0].reloc = BFD_RELOC_ALPHA_LITUSE;
+ insn.fixups[0].exp.X_op = O_constant;
+ insn.fixups[0].exp.X_add_number = 1;
+ emit_lituse = 0;
+
+ emit_insn (&insn);
+
+ /* emit "addq litreg, base, target" */
+
+ if (basereg != AXP_REG_ZERO)
+ {
+ set_tok_reg (newtok[1], basereg);
+ set_tok_reg (newtok[2], targreg);
+ assemble_tokens ("addq", newtok, 3, 0);
+ }
+#endif /* !OBJ_EVAX */
+
+ if (poffset)
+ set_tok_const (*poffset, 0);
+ *pbasereg = targreg;
+ }
+ else
+ {
+ offsetT low, high, extra, tmp;
+
+ /* for 32-bit operands, break up the addend */
+
+ low = sign_extend_16 (addend);
+ tmp = addend - low;
+ high = sign_extend_16 (tmp >> 16);
+
+ if (tmp - (high << 16))
+ {
+ extra = 0x4000;
+ tmp -= 0x40000000;
+ high = sign_extend_16 (tmp >> 16);
+ }
+ else
+ extra = 0;
+
+ set_tok_reg (newtok[0], targreg);
+ set_tok_preg (newtok[2], basereg);
+
+ if (extra)
+ {
+ /* emit "ldah r, extra(r) */
+ set_tok_const (newtok[1], extra);
+ assemble_tokens ("ldah", newtok, 3, 0);
+ set_tok_preg (newtok[2], basereg = targreg);
+ }
+
+ if (high)
+ {
+ /* emit "ldah r, high(r) */
+ set_tok_const (newtok[1], high);
+ assemble_tokens ("ldah", newtok, 3, 0);
+ basereg = targreg;
+ set_tok_preg (newtok[2], basereg);
+ }
+
+ if ((low && !poffset) || (!poffset && basereg != targreg))
+ {
+ /* emit "lda r, low(base)" */
+ set_tok_const (newtok[1], low);
+ assemble_tokens ("lda", newtok, 3, 0);
+ basereg = targreg;
+ low = 0;
+ }
+
+ if (poffset)
+ set_tok_const (*poffset, low);
+ *pbasereg = basereg;
+ }
+
+ return emit_lituse;
+}
+
+/* The lda macro differs from the lda instruction in that it handles
+ most simple expressions, particualrly symbol address loads and
+ large constants. */
+
+static void
+emit_lda (tok, ntok, unused)
+ const expressionS *tok;
+ int ntok;
+ const PTR unused;
+{
+ int basereg;
+
+ if (ntok == 2)
+ basereg = (tok[1].X_op == O_constant ? AXP_REG_ZERO : alpha_gp_register);
+ else
+ basereg = tok[2].X_add_number;
+
+ (void) load_expression (tok[0].X_add_number, &tok[1], &basereg, NULL);
+}
+
+/* The ldah macro differs from the ldah instruction in that it has $31
+ as an implied base register. */
+
+static void
+emit_ldah (tok, ntok, unused)
+ const expressionS *tok;
+ int ntok;
+ const PTR unused;
+{
+ expressionS newtok[3];
+
+ newtok[0] = tok[0];
+ newtok[1] = tok[1];
+ set_tok_preg (newtok[2], AXP_REG_ZERO);
+
+ assemble_tokens ("ldah", newtok, 3, 0);
+}
+
+/* Handle all "simple" integer register loads -- ldq, ldq_l, ldq_u,
+ etc. They differ from the real instructions in that they do simple
+ expressions like the lda macro. */
+
+static void
+emit_ir_load (tok, ntok, opname)
+ const expressionS *tok;
+ int ntok;
+ const PTR opname;
+{
+ int basereg, lituse;
+ expressionS newtok[3];
+ struct alpha_insn insn;
+
+ if (ntok == 2)
+ basereg = (tok[1].X_op == O_constant ? AXP_REG_ZERO : alpha_gp_register);
+ else
+ basereg = tok[2].X_add_number;
+
+ lituse = load_expression (tok[0].X_add_number, &tok[1], &basereg,
+ &newtok[1]);
+
+ newtok[0] = tok[0];
+ set_tok_preg (newtok[2], basereg);
+
+ assemble_tokens_to_insn ((const char *)opname, newtok, 3, &insn);
+
+ if (lituse)
+ {
+ assert (insn.nfixups < MAX_INSN_FIXUPS);
+ if (insn.nfixups > 0)
+ {
+ memmove (&insn.fixups[1], &insn.fixups[0],
+ sizeof(struct alpha_fixup) * insn.nfixups);
+ }
+ insn.nfixups++;
+ insn.fixups[0].reloc = BFD_RELOC_ALPHA_LITUSE;
+ insn.fixups[0].exp.X_op = O_constant;
+ insn.fixups[0].exp.X_add_number = 1;
+ }
+
+ emit_insn (&insn);
+}
+
+/* Handle fp register loads, and both integer and fp register stores.
+ Again, we handle simple expressions. */
+
+static void
+emit_loadstore (tok, ntok, opname)
+ const expressionS *tok;
+ int ntok;
+ const PTR opname;
+{
+ int basereg, lituse;
+ expressionS newtok[3];
+ struct alpha_insn insn;
+
+ if (ntok == 2)
+ basereg = (tok[1].X_op == O_constant ? AXP_REG_ZERO : alpha_gp_register);
+ else
+ basereg = tok[2].X_add_number;
+
+ if (tok[1].X_op != O_constant || !range_signed_16(tok[1].X_add_number))
+ {
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+
+ lituse = load_expression (AXP_REG_AT, &tok[1], &basereg, &newtok[1]);
+ }
+ else
+ {
+ newtok[1] = tok[1];
+ lituse = 0;
+ }
+
+ newtok[0] = tok[0];
+ set_tok_preg (newtok[2], basereg);
+
+ assemble_tokens_to_insn ((const char *)opname, newtok, 3, &insn);
+
+ if (lituse)
+ {
+ assert (insn.nfixups < MAX_INSN_FIXUPS);
+ if (insn.nfixups > 0)
+ {
+ memmove (&insn.fixups[1], &insn.fixups[0],
+ sizeof(struct alpha_fixup) * insn.nfixups);
+ }
+ insn.nfixups++;
+ insn.fixups[0].reloc = BFD_RELOC_ALPHA_LITUSE;
+ insn.fixups[0].exp.X_op = O_constant;
+ insn.fixups[0].exp.X_add_number = 1;
+ }
+
+ emit_insn (&insn);
+}
+
+/* Load a half-word or byte as an unsigned value. */
+
+static void
+emit_ldXu (tok, ntok, vlgsize)
+ const expressionS *tok;
+ int ntok;
+ const PTR vlgsize;
+{
+ if (alpha_target & AXP_OPCODE_BWX)
+ emit_ir_load (tok, ntok, ldXu_op[(long)vlgsize]);
+ else
+ {
+ expressionS newtok[3];
+
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+
+ /* emit "lda $at, exp" */
+
+ memcpy (newtok, tok, sizeof (expressionS) * ntok);
+ newtok[0].X_add_number = AXP_REG_AT;
+ assemble_tokens ("lda", newtok, ntok, 1);
+
+ /* emit "ldq_u targ, 0($at)" */
+
+ newtok[0] = tok[0];
+ set_tok_const (newtok[1], 0);
+ set_tok_preg (newtok[2], AXP_REG_AT);
+ assemble_tokens ("ldq_u", newtok, 3, 1);
+
+ /* emit "extXl targ, $at, targ" */
+
+ set_tok_reg (newtok[1], AXP_REG_AT);
+ newtok[2] = newtok[0];
+ assemble_tokens (extXl_op[(long)vlgsize], newtok, 3, 1);
+ }
+}
+
+/* Load a half-word or byte as a signed value. */
+
+static void
+emit_ldX (tok, ntok, vlgsize)
+ const expressionS *tok;
+ int ntok;
+ const PTR vlgsize;
+{
+ emit_ldXu (tok, ntok, vlgsize);
+ assemble_tokens (sextX_op[(long)vlgsize], tok, 1, 1);
+}
+
+/* Load an integral value from an unaligned address as an unsigned
+ value. */
+
+static void
+emit_uldXu (tok, ntok, vlgsize)
+ const expressionS *tok;
+ int ntok;
+ const PTR vlgsize;
+{
+ long lgsize = (long)vlgsize;
+ expressionS newtok[3];
+
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+
+ /* emit "lda $at, exp" */
+
+ memcpy (newtok, tok, sizeof (expressionS) * ntok);
+ newtok[0].X_add_number = AXP_REG_AT;
+ assemble_tokens ("lda", newtok, ntok, 1);
+
+ /* emit "ldq_u $t9, 0($at)" */
+
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_const (newtok[1], 0);
+ set_tok_preg (newtok[2], AXP_REG_AT);
+ assemble_tokens ("ldq_u", newtok, 3, 1);
+
+ /* emit "ldq_u $t10, size-1($at)" */
+
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_const (newtok[1], (1<<lgsize)-1);
+ assemble_tokens ("ldq_u", newtok, 3, 1);
+
+ /* emit "extXl $t9, $at, $t9" */
+
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_reg (newtok[1], AXP_REG_AT);
+ set_tok_reg (newtok[2], AXP_REG_T9);
+ assemble_tokens (extXl_op[lgsize], newtok, 3, 1);
+
+ /* emit "extXh $t10, $at, $t10" */
+
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_reg (newtok[2], AXP_REG_T10);
+ assemble_tokens (extXh_op[lgsize], newtok, 3, 1);
+
+ /* emit "or $t9, $t10, targ" */
+
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_reg (newtok[1], AXP_REG_T10);
+ newtok[2] = tok[0];
+ assemble_tokens ("or", newtok, 3, 1);
+}
+
+/* Load an integral value from an unaligned address as a signed value.
+ Note that quads should get funneled to the unsigned load since we
+ don't have to do the sign extension. */
+
+static void
+emit_uldX (tok, ntok, vlgsize)
+ const expressionS *tok;
+ int ntok;
+ const PTR vlgsize;
+{
+ emit_uldXu (tok, ntok, vlgsize);
+ assemble_tokens (sextX_op[(long)vlgsize], tok, 1, 1);
+}
+
+/* Implement the ldil macro. */
+
+static void
+emit_ldil (tok, ntok, unused)
+ const expressionS *tok;
+ int ntok;
+ const PTR unused;
+{
+ expressionS newtok[2];
+
+ memcpy (newtok, tok, sizeof(newtok));
+ newtok[1].X_add_number = sign_extend_32 (tok[1].X_add_number);
+
+ assemble_tokens ("lda", newtok, ntok, 1);
+}
+
+/* Store a half-word or byte. */
+
+static void
+emit_stX (tok, ntok, vlgsize)
+ const expressionS *tok;
+ int ntok;
+ const PTR vlgsize;
+{
+ int lgsize = (int)(long)vlgsize;
+
+ if (alpha_target & AXP_OPCODE_BWX)
+ emit_loadstore (tok, ntok, stX_op[lgsize]);
+ else
+ {
+ expressionS newtok[3];
+
+ if (alpha_noat_on)
+ as_bad(_("macro requires $at register while noat in effect"));
+
+ /* emit "lda $at, exp" */
+
+ memcpy (newtok, tok, sizeof (expressionS) * ntok);
+ newtok[0].X_add_number = AXP_REG_AT;
+ assemble_tokens ("lda", newtok, ntok, 1);
+
+ /* emit "ldq_u $t9, 0($at)" */
+
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_const (newtok[1], 0);
+ set_tok_preg (newtok[2], AXP_REG_AT);
+ assemble_tokens ("ldq_u", newtok, 3, 1);
+
+ /* emit "insXl src, $at, $t10" */
+
+ newtok[0] = tok[0];
+ set_tok_reg (newtok[1], AXP_REG_AT);
+ set_tok_reg (newtok[2], AXP_REG_T10);
+ assemble_tokens (insXl_op[lgsize], newtok, 3, 1);
+
+ /* emit "mskXl $t9, $at, $t9" */
+
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ newtok[2] = newtok[0];
+ assemble_tokens (mskXl_op[lgsize], newtok, 3, 1);
+
+ /* emit "or $t9, $t10, $t9" */
+
+ set_tok_reg (newtok[1], AXP_REG_T10);
+ assemble_tokens ("or", newtok, 3, 1);
+
+ /* emit "stq_u $t9, 0($at) */
+
+ set_tok_const (newtok[1], 0);
+ set_tok_preg (newtok[2], AXP_REG_AT);
+ assemble_tokens ("stq_u", newtok, 3, 1);
+ }
+}
+
+/* Store an integer to an unaligned address. */
+
+static void
+emit_ustX (tok, ntok, vlgsize)
+ const expressionS *tok;
+ int ntok;
+ const PTR vlgsize;
+{
+ int lgsize = (int)(long)vlgsize;
+ expressionS newtok[3];
+
+ /* emit "lda $at, exp" */
+
+ memcpy (newtok, tok, sizeof (expressionS) * ntok);
+ newtok[0].X_add_number = AXP_REG_AT;
+ assemble_tokens ("lda", newtok, ntok, 1);
+
+ /* emit "ldq_u $9, 0($at)" */
+
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_const (newtok[1], 0);
+ set_tok_preg (newtok[2], AXP_REG_AT);
+ assemble_tokens ("ldq_u", newtok, 3, 1);
+
+ /* emit "ldq_u $10, size-1($at)" */
+
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_const (newtok[1], (1 << lgsize)-1);
+ assemble_tokens ("ldq_u", newtok, 3, 1);
+
+ /* emit "insXl src, $at, $t11" */
+
+ newtok[0] = tok[0];
+ set_tok_reg (newtok[1], AXP_REG_AT);
+ set_tok_reg (newtok[2], AXP_REG_T11);
+ assemble_tokens (insXl_op[lgsize], newtok, 3, 1);
+
+ /* emit "insXh src, $at, $t12" */
+
+ set_tok_reg (newtok[2], AXP_REG_T12);
+ assemble_tokens (insXh_op[lgsize], newtok, 3, 1);
+
+ /* emit "mskXl $t9, $at, $t9" */
+
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ newtok[2] = newtok[0];
+ assemble_tokens (mskXl_op[lgsize], newtok, 3, 1);
+
+ /* emit "mskXh $t10, $at, $t10" */
+
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ newtok[2] = newtok[0];
+ assemble_tokens (mskXh_op[lgsize], newtok, 3, 1);
+
+ /* emit "or $t9, $t11, $t9" */
+
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_reg (newtok[1], AXP_REG_T11);
+ newtok[2] = newtok[0];
+ assemble_tokens ("or", newtok, 3, 1);
+
+ /* emit "or $t10, $t12, $t10" */
+
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_reg (newtok[1], AXP_REG_T12);
+ newtok[2] = newtok[0];
+ assemble_tokens ("or", newtok, 3, 1);
+
+ /* emit "stq_u $t9, 0($at)" */
+
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_const (newtok[1], 0);
+ set_tok_preg (newtok[2], AXP_REG_AT);
+ assemble_tokens ("stq_u", newtok, 3, 1);
+
+ /* emit "stq_u $t10, size-1($at)" */
+
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_const (newtok[1], (1 << lgsize)-1);
+ assemble_tokens ("stq_u", newtok, 3, 1);
+}
+
+/* Sign extend a half-word or byte. The 32-bit sign extend is
+ implemented as "addl $31, $r, $t" in the opcode table. */
+
+static void
+emit_sextX (tok, ntok, vlgsize)
+ const expressionS *tok;
+ int ntok;
+ const PTR vlgsize;
+{
+ long lgsize = (long)vlgsize;
+
+ if (alpha_target & AXP_OPCODE_BWX)
+ assemble_tokens (sextX_op[lgsize], tok, ntok, 0);
+ else
+ {
+ int bitshift = 64 - 8 * (1 << lgsize);
+ expressionS newtok[3];
+
+ /* emit "sll src,bits,dst" */
+
+ newtok[0] = tok[0];
+ set_tok_const (newtok[1], bitshift);
+ newtok[2] = tok[ntok - 1];
+ assemble_tokens ("sll", newtok, 3, 1);
+
+ /* emit "sra dst,bits,dst" */
+
+ newtok[0] = newtok[2];
+ assemble_tokens ("sra", newtok, 3, 1);
+ }
+}
+
+/* Implement the division and modulus macros. */
+
+#ifdef OBJ_EVAX
+
+/* Make register usage like in normal procedure call.
+ Don't clobber PV and RA. */
+
+static void
+emit_division (tok, ntok, symname)
+ const expressionS *tok;
+ int ntok;
+ const PTR symname;
+{
+ /* DIVISION and MODULUS. Yech.
+ *
+ * Convert
+ * OP x,y,result
+ * to
+ * mov x,R16 # if x != R16
+ * mov y,R17 # if y != R17
+ * lda AT,__OP
+ * jsr AT,(AT),0
+ * mov R0,result
+ *
+ * with appropriate optimizations if R0,R16,R17 are the registers
+ * specified by the compiler.
+ */
+
+ int xr, yr, rr;
+ symbolS *sym;
+ expressionS newtok[3];
+
+ xr = regno (tok[0].X_add_number);
+ yr = regno (tok[1].X_add_number);
+
+ if (ntok < 3)
+ rr = xr;
+ else
+ rr = regno (tok[2].X_add_number);
+
+ /* Move the operands into the right place */
+ if (yr == AXP_REG_R16 && xr == AXP_REG_R17)
+ {
+ /* They are in exactly the wrong order -- swap through AT */
+
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+
+ set_tok_reg (newtok[0], AXP_REG_R16);
+ set_tok_reg (newtok[1], AXP_REG_AT);
+ assemble_tokens ("mov", newtok, 2, 1);
+
+ set_tok_reg (newtok[0], AXP_REG_R17);
+ set_tok_reg (newtok[1], AXP_REG_R16);
+ assemble_tokens ("mov", newtok, 2, 1);
+
+ set_tok_reg (newtok[0], AXP_REG_AT);
+ set_tok_reg (newtok[1], AXP_REG_R17);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+ else
+ {
+ if (yr == AXP_REG_R16)
+ {
+ set_tok_reg (newtok[0], AXP_REG_R16);
+ set_tok_reg (newtok[1], AXP_REG_R17);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+
+ if (xr != AXP_REG_R16)
+ {
+ set_tok_reg (newtok[0], xr);
+ set_tok_reg (newtok[1], AXP_REG_R16);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+
+ if (yr != AXP_REG_R16 && yr != AXP_REG_R17)
+ {
+ set_tok_reg (newtok[0], yr);
+ set_tok_reg (newtok[1], AXP_REG_R17);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+ }
+
+ sym = symbol_find_or_make ((const char *)symname);
+
+ set_tok_reg (newtok[0], AXP_REG_AT);
+ set_tok_sym (newtok[1], sym, 0);
+ assemble_tokens ("lda", newtok, 2, 1);
+
+ /* Call the division routine */
+ set_tok_reg (newtok[0], AXP_REG_AT);
+ set_tok_cpreg (newtok[1], AXP_REG_AT);
+ set_tok_const (newtok[2], 0);
+ assemble_tokens ("jsr", newtok, 3, 1);
+
+ /* Move the result to the right place */
+ if (rr != AXP_REG_R0)
+ {
+ set_tok_reg (newtok[0], AXP_REG_R0);
+ set_tok_reg (newtok[1], rr);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+}
+
+#else /* !OBJ_EVAX */
+
+static void
+emit_division (tok, ntok, symname)
+ const expressionS *tok;
+ int ntok;
+ const PTR symname;
+{
+ /* DIVISION and MODULUS. Yech.
+ * Convert
+ * OP x,y,result
+ * to
+ * lda pv,__OP
+ * mov x,t10
+ * mov y,t11
+ * jsr t9,(pv),__OP
+ * mov t12,result
+ *
+ * with appropriate optimizations if t10,t11,t12 are the registers
+ * specified by the compiler.
+ */
+
+ int xr, yr, rr;
+ symbolS *sym;
+ expressionS newtok[3];
+
+ xr = regno (tok[0].X_add_number);
+ yr = regno (tok[1].X_add_number);
+
+ if (ntok < 3)
+ rr = xr;
+ else
+ rr = regno (tok[2].X_add_number);
+
+ sym = symbol_find_or_make ((const char *)symname);
+
+ /* Move the operands into the right place */
+ if (yr == AXP_REG_T10 && xr == AXP_REG_T11)
+ {
+ /* They are in exactly the wrong order -- swap through AT */
+
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_reg (newtok[1], AXP_REG_AT);
+ assemble_tokens ("mov", newtok, 2, 1);
+
+ set_tok_reg (newtok[0], AXP_REG_T11);
+ set_tok_reg (newtok[1], AXP_REG_T10);
+ assemble_tokens ("mov", newtok, 2, 1);
+
+ set_tok_reg (newtok[0], AXP_REG_AT);
+ set_tok_reg (newtok[1], AXP_REG_T11);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+ else
+ {
+ if (yr == AXP_REG_T10)
+ {
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_reg (newtok[1], AXP_REG_T11);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+
+ if (xr != AXP_REG_T10)
+ {
+ set_tok_reg (newtok[0], xr);
+ set_tok_reg (newtok[1], AXP_REG_T10);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+
+ if (yr != AXP_REG_T10 && yr != AXP_REG_T11)
+ {
+ set_tok_reg (newtok[0], yr);
+ set_tok_reg (newtok[1], AXP_REG_T11);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+ }
+
+ /* Call the division routine */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_sym (newtok[1], sym, 0);
+ assemble_tokens ("jsr", newtok, 2, 1);
+
+ /* Reload the GP register */
+#ifdef OBJ_AOUT
+FIXME
+#endif
+#if defined(OBJ_ECOFF) || defined(OBJ_ELF)
+ set_tok_reg (newtok[0], alpha_gp_register);
+ set_tok_const (newtok[1], 0);
+ set_tok_preg (newtok[2], AXP_REG_T9);
+ assemble_tokens ("ldgp", newtok, 3, 1);
+#endif
+
+ /* Move the result to the right place */
+ if (rr != AXP_REG_T12)
+ {
+ set_tok_reg (newtok[0], AXP_REG_T12);
+ set_tok_reg (newtok[1], rr);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+}
+
+#endif /* !OBJ_EVAX */
+
+/* The jsr and jmp macros differ from their instruction counterparts
+ in that they can load the target address and default most
+ everything. */
+
+static void
+emit_jsrjmp (tok, ntok, vopname)
+ const expressionS *tok;
+ int ntok;
+ const PTR vopname;
+{
+ const char *opname = (const char *) vopname;
+ struct alpha_insn insn;
+ expressionS newtok[3];
+ int r, tokidx = 0, lituse = 0;
+
+ if (tokidx < ntok && tok[tokidx].X_op == O_register)
+ r = regno (tok[tokidx++].X_add_number);
+ else
+ r = strcmp (opname, "jmp") == 0 ? AXP_REG_ZERO : AXP_REG_RA;
+
+ set_tok_reg (newtok[0], r);
+
+ if (tokidx < ntok &&
+ (tok[tokidx].X_op == O_pregister || tok[tokidx].X_op == O_cpregister))
+ r = regno (tok[tokidx++].X_add_number);
+#ifdef OBJ_EVAX
+ /* keep register if jsr $n.<sym> */
+#else
+ else
+ {
+ int basereg = alpha_gp_register;
+ lituse = load_expression (r = AXP_REG_PV, &tok[tokidx], &basereg, NULL);
+ }
+#endif
+
+ set_tok_cpreg (newtok[1], r);
+
+#ifdef OBJ_EVAX
+ /* FIXME: Add hint relocs to BFD for evax. */
+#else
+ if (tokidx < ntok)
+ newtok[2] = tok[tokidx];
+ else
+#endif
+ set_tok_const (newtok[2], 0);
+
+ assemble_tokens_to_insn (opname, newtok, 3, &insn);
+
+ /* add the LITUSE fixup */
+ if (lituse)
+ {
+ assert (insn.nfixups < MAX_INSN_FIXUPS);
+ if (insn.nfixups > 0)
+ {
+ memmove (&insn.fixups[1], &insn.fixups[0],
+ sizeof(struct alpha_fixup) * insn.nfixups);
+ }
+ insn.nfixups++;
+ insn.fixups[0].reloc = BFD_RELOC_ALPHA_LITUSE;
+ insn.fixups[0].exp.X_op = O_constant;
+ insn.fixups[0].exp.X_add_number = 3;
+ }
+
+ emit_insn (&insn);
+}
+
+/* The ret and jcr instructions differ from their instruction
+ counterparts in that everything can be defaulted. */
+
+static void
+emit_retjcr (tok, ntok, vopname)
+ const expressionS *tok;
+ int ntok;
+ const PTR vopname;
+{
+ const char *opname = (const char *)vopname;
+ expressionS newtok[3];
+ int r, tokidx = 0;
+
+ if (tokidx < ntok && tok[tokidx].X_op == O_register)
+ r = regno (tok[tokidx++].X_add_number);
+ else
+ r = AXP_REG_ZERO;
+
+ set_tok_reg (newtok[0], r);
+
+ if (tokidx < ntok &&
+ (tok[tokidx].X_op == O_pregister || tok[tokidx].X_op == O_cpregister))
+ r = regno (tok[tokidx++].X_add_number);
+ else
+ r = AXP_REG_RA;
+
+ set_tok_cpreg (newtok[1], r);
+
+ if (tokidx < ntok)
+ newtok[2] = tok[tokidx];
+ else
+ set_tok_const (newtok[2], strcmp(opname, "ret") == 0);
+
+ assemble_tokens (opname, newtok, 3, 0);
+}
+
+/* Assembler directives */
+
+/* Handle the .text pseudo-op. This is like the usual one, but it
+ clears alpha_insn_label and restores auto alignment. */
+
+static void
+s_alpha_text (i)
+ int i;
+
+{
+ s_text (i);
+ alpha_insn_label = NULL;
+ alpha_auto_align_on = 1;
+ alpha_current_align = 0;
+}
+
+/* Handle the .data pseudo-op. This is like the usual one, but it
+ clears alpha_insn_label and restores auto alignment. */
+
+static void
+s_alpha_data (i)
+ int i;
+{
+ s_data (i);
+ alpha_insn_label = NULL;
+ alpha_auto_align_on = 1;
+ alpha_current_align = 0;
+}
+
+#if defined (OBJ_ECOFF) || defined (OBJ_EVAX)
+
+/* Handle the OSF/1 and openVMS .comm pseudo quirks.
+ openVMS constructs a section for every common symbol. */
+
+static void
+s_alpha_comm (ignore)
+ int ignore;
+{
+ register char *name;
+ register char c;
+ register char *p;
+ offsetT temp;
+ register symbolS *symbolP;
+
+#ifdef OBJ_EVAX
+ segT current_section = now_seg;
+ int current_subsec = now_subseg;
+ segT new_seg;
+#endif
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ /* just after name is now '\0' */
+ p = input_line_pointer;
+ *p = c;
+
+ SKIP_WHITESPACE ();
+
+ /* Alpha OSF/1 compiler doesn't provide the comma, gcc does. */
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ }
+ if ((temp = get_absolute_expression ()) < 0)
+ {
+ as_warn (_(".COMMon length (%ld.) <0! Ignored."), (long) temp);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+
+#ifdef OBJ_EVAX
+ /* Make a section for the common symbol. */
+ new_seg = subseg_new (xstrdup (name), 0);
+#endif
+
+ *p = c;
+
+#ifdef OBJ_EVAX
+ /* alignment might follow */
+ if (*input_line_pointer == ',')
+ {
+ offsetT align;
+
+ input_line_pointer++;
+ align = get_absolute_expression ();
+ bfd_set_section_alignment (stdoutput, new_seg, align);
+ }
+#endif
+
+ if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
+ {
+ as_bad (_("Ignoring attempt to re-define symbol"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+#ifdef OBJ_EVAX
+ if (bfd_section_size (stdoutput, new_seg) > 0)
+ {
+ if (bfd_section_size (stdoutput, new_seg) != temp)
+ as_bad (_("Length of .comm \"%s\" is already %ld. Not changed to %ld."),
+ S_GET_NAME (symbolP),
+ (long) bfd_section_size (stdoutput, new_seg),
+ (long) temp);
+ }
+#else
+ if (S_GET_VALUE (symbolP))
+ {
+ if (S_GET_VALUE (symbolP) != (valueT) temp)
+ as_bad (_("Length of .comm \"%s\" is already %ld. Not changed to %ld."),
+ S_GET_NAME (symbolP),
+ (long) S_GET_VALUE (symbolP),
+ (long) temp);
+ }
+#endif
+ else
+ {
+#ifdef OBJ_EVAX
+ subseg_set (new_seg, 0);
+ p = frag_more (temp);
+ new_seg->flags |= SEC_IS_COMMON;
+ if (! S_IS_DEFINED (symbolP))
+ symbolP->bsym->section = new_seg;
+#else
+ S_SET_VALUE (symbolP, (valueT) temp);
+#endif
+ S_SET_EXTERNAL (symbolP);
+ }
+
+#ifdef OBJ_EVAX
+ subseg_set (current_section, current_subsec);
+#endif
+
+ know (symbolP->sy_frag == &zero_address_frag);
+
+ demand_empty_rest_of_line ();
+}
+
+#endif /* ! OBJ_ELF */
+
+#ifdef OBJ_ECOFF
+
+/* Handle the .rdata pseudo-op. This is like the usual one, but it
+ clears alpha_insn_label and restores auto alignment. */
+
+static void
+s_alpha_rdata (ignore)
+ int ignore;
+{
+ int temp;
+
+ temp = get_absolute_expression ();
+ subseg_new (".rdata", 0);
+ demand_empty_rest_of_line ();
+ alpha_insn_label = NULL;
+ alpha_auto_align_on = 1;
+ alpha_current_align = 0;
+}
+
+#endif
+
+#ifdef OBJ_ECOFF
+
+/* Handle the .sdata pseudo-op. This is like the usual one, but it
+ clears alpha_insn_label and restores auto alignment. */
+
+static void
+s_alpha_sdata (ignore)
+ int ignore;
+{
+ int temp;
+
+ temp = get_absolute_expression ();
+ subseg_new (".sdata", 0);
+ demand_empty_rest_of_line ();
+ alpha_insn_label = NULL;
+ alpha_auto_align_on = 1;
+ alpha_current_align = 0;
+}
+#endif
+
+#ifdef OBJ_ELF
+
+/* Handle the .section pseudo-op. This is like the usual one, but it
+ clears alpha_insn_label and restores auto alignment. */
+
+static void
+s_alpha_section (ignore)
+ int ignore;
+{
+ obj_elf_section (ignore);
+
+ alpha_insn_label = NULL;
+ alpha_auto_align_on = 1;
+ alpha_current_align = 0;
+}
+
+static void
+s_alpha_ent (dummy)
+ int dummy;
+{
+ if (ECOFF_DEBUGGING)
+ ecoff_directive_ent (0);
+ else
+ {
+ char *name, name_end;
+ name = input_line_pointer;
+ name_end = get_symbol_end ();
+
+ if (! is_name_beginner (*name))
+ {
+ as_warn (_(".ent directive has no name"));
+ *input_line_pointer = name_end;
+ }
+ else
+ {
+ symbolS *sym;
+
+ if (alpha_cur_ent_sym)
+ as_warn (_("nested .ent directives"));
+
+ sym = symbol_find_or_make (name);
+ sym->bsym->flags |= BSF_FUNCTION;
+ alpha_cur_ent_sym = sym;
+
+ /* The .ent directive is sometimes followed by a number. Not sure
+ what it really means, but ignore it. */
+ *input_line_pointer = name_end;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ }
+ if (isdigit (*input_line_pointer) || *input_line_pointer == '-')
+ (void) get_absolute_expression ();
+ }
+ demand_empty_rest_of_line ();
+ }
+}
+
+static void
+s_alpha_end (dummy)
+ int dummy;
+{
+ if (ECOFF_DEBUGGING)
+ ecoff_directive_end (0);
+ else
+ {
+ char *name, name_end;
+ name = input_line_pointer;
+ name_end = get_symbol_end ();
+
+ if (! is_name_beginner (*name))
+ {
+ as_warn (_(".end directive has no name"));
+ *input_line_pointer = name_end;
+ }
+ else
+ {
+ symbolS *sym;
+
+ sym = symbol_find (name);
+ if (sym != alpha_cur_ent_sym)
+ as_warn (_(".end directive names different symbol than .ent"));
+
+ /* Create an expression to calculate the size of the function. */
+ if (sym)
+ {
+ sym->sy_obj.size = (expressionS *) xmalloc (sizeof (expressionS));
+ sym->sy_obj.size->X_op = O_subtract;
+ sym->sy_obj.size->X_add_symbol
+ = symbol_new ("L0\001", now_seg, frag_now_fix (), frag_now);
+ sym->sy_obj.size->X_op_symbol = sym;
+ sym->sy_obj.size->X_add_number = 0;
+ }
+
+ alpha_cur_ent_sym = NULL;
+
+ *input_line_pointer = name_end;
+ }
+ demand_empty_rest_of_line ();
+ }
+}
+
+static void
+s_alpha_mask (fp)
+ int fp;
+{
+ if (ECOFF_DEBUGGING)
+ {
+ if (fp)
+ ecoff_directive_fmask (0);
+ else
+ ecoff_directive_mask (0);
+ }
+ else
+ discard_rest_of_line ();
+}
+
+static void
+s_alpha_frame (dummy)
+ int dummy;
+{
+ if (ECOFF_DEBUGGING)
+ ecoff_directive_frame (0);
+ else
+ discard_rest_of_line ();
+}
+
+static void
+s_alpha_prologue (ignore)
+ int ignore;
+{
+ symbolS *sym;
+ int arg;
+
+ arg = get_absolute_expression ();
+ demand_empty_rest_of_line ();
+
+ if (ECOFF_DEBUGGING)
+ sym = ecoff_get_cur_proc_sym ();
+ else
+ sym = alpha_cur_ent_sym;
+ know (sym != NULL);
+
+ switch (arg)
+ {
+ case 0: /* No PV required. */
+ S_SET_OTHER (sym, STO_ALPHA_NOPV);
+ break;
+ case 1: /* Std GP load. */
+ S_SET_OTHER (sym, STO_ALPHA_STD_GPLOAD);
+ break;
+ case 2: /* Non-std use of PV. */
+ break;
+
+ default:
+ as_bad (_("Invalid argument %d to .prologue."), arg);
+ break;
+ }
+}
+
+static void
+s_alpha_coff_wrapper (which)
+ int which;
+{
+ static void (* const fns[]) PARAMS ((int)) = {
+ ecoff_directive_begin,
+ ecoff_directive_bend,
+ ecoff_directive_def,
+ ecoff_directive_dim,
+ ecoff_directive_endef,
+ ecoff_directive_file,
+ ecoff_directive_scl,
+ ecoff_directive_tag,
+ ecoff_directive_val,
+ ecoff_directive_loc,
+ };
+
+ assert (which >= 0 && which < sizeof(fns)/sizeof(*fns));
+
+ if (ECOFF_DEBUGGING)
+ (*fns[which])(0);
+ else
+ {
+ as_bad (_("ECOFF debugging is disabled."));
+ ignore_rest_of_line ();
+ }
+}
+#endif /* OBJ_ELF */
+
+#ifdef OBJ_EVAX
+
+/* Handle the section specific pseudo-op. */
+
+static void
+s_alpha_section (secid)
+ int secid;
+{
+ int temp;
+#define EVAX_SECTION_COUNT 5
+ static char *section_name[EVAX_SECTION_COUNT+1] =
+ { "NULL", ".rdata", ".comm", ".link", ".ctors", ".dtors" };
+
+ if ((secid <= 0) || (secid > EVAX_SECTION_COUNT))
+ {
+ as_fatal (_("Unknown section directive"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ temp = get_absolute_expression ();
+ subseg_new (section_name[secid], 0);
+ demand_empty_rest_of_line ();
+ alpha_insn_label = NULL;
+ alpha_auto_align_on = 1;
+ alpha_current_align = 0;
+}
+
+
+/* Parse .ent directives. */
+
+static void
+s_alpha_ent (ignore)
+ int ignore;
+{
+ symbolS *symbol;
+ expressionS symexpr;
+
+ alpha_evax_proc.pdsckind = 0;
+ alpha_evax_proc.framereg = -1;
+ alpha_evax_proc.framesize = 0;
+ alpha_evax_proc.rsa_offset = 0;
+ alpha_evax_proc.ra_save = AXP_REG_RA;
+ alpha_evax_proc.fp_save = -1;
+ alpha_evax_proc.imask = 0;
+ alpha_evax_proc.fmask = 0;
+ alpha_evax_proc.prologue = 0;
+ alpha_evax_proc.type = 0;
+
+ expression (&symexpr);
+
+ if (symexpr.X_op != O_symbol)
+ {
+ as_fatal (_(".ent directive has no symbol"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ symbol = make_expr_symbol (&symexpr);
+ symbol->bsym->flags |= BSF_FUNCTION;
+ alpha_evax_proc.symbol = symbol;
+
+ demand_empty_rest_of_line ();
+ return;
+}
+
+
+/* Parse .frame <framreg>,<framesize>,RA,<rsa_offset> directives. */
+
+static void
+s_alpha_frame (ignore)
+ int ignore;
+{
+ long val;
+
+ alpha_evax_proc.framereg = tc_get_register (1);
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer++ != ','
+ || get_absolute_expression_and_terminator (&val) != ',')
+ {
+ as_warn (_("Bad .frame directive 1./2. param"));
+ --input_line_pointer;
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ alpha_evax_proc.framesize = val;
+
+ (void) tc_get_register (1);
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer++ != ',')
+ {
+ as_warn (_("Bad .frame directive 3./4. param"));
+ --input_line_pointer;
+ demand_empty_rest_of_line ();
+ return;
+ }
+ alpha_evax_proc.rsa_offset = get_absolute_expression ();
+
+ return;
+}
+
+static void
+s_alpha_pdesc (ignore)
+ int ignore;
+{
+ char *name;
+ char name_end;
+ long val;
+ register char *p;
+ expressionS exp;
+ symbolS *entry_sym;
+ fixS *fixp;
+ segment_info_type *seginfo = seg_info (alpha_link_section);
+
+ if (now_seg != alpha_link_section)
+ {
+ as_bad (_(".pdesc directive not in link (.link) section"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ if ((alpha_evax_proc.symbol == 0)
+ || (!S_IS_DEFINED (alpha_evax_proc.symbol)))
+ {
+ as_fatal (_(".pdesc has no matching .ent"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ alpha_evax_proc.symbol->sy_obj = (valueT)seginfo->literal_pool_size;
+
+ expression (&exp);
+ if (exp.X_op != O_symbol)
+ {
+ as_warn (_(".pdesc directive has no entry symbol"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ entry_sym = make_expr_symbol (&exp);
+ /* Save bfd symbol of proc desc in function symbol. */
+ alpha_evax_proc.symbol->bsym->udata.p = (PTR)entry_sym->bsym;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer++ != ',')
+ {
+ as_warn (_("No comma after .pdesc <entryname>"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ SKIP_WHITESPACE ();
+ name = input_line_pointer;
+ name_end = get_symbol_end ();
+
+ if (strncmp(name, "stack", 5) == 0)
+ {
+ alpha_evax_proc.pdsckind = PDSC_S_K_KIND_FP_STACK;
+ }
+ else if (strncmp(name, "reg", 3) == 0)
+ {
+ alpha_evax_proc.pdsckind = PDSC_S_K_KIND_FP_REGISTER;
+ }
+ else if (strncmp(name, "null", 4) == 0)
+ {
+ alpha_evax_proc.pdsckind = PDSC_S_K_KIND_NULL;
+ }
+ else
+ {
+ as_fatal (_("unknown procedure kind"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ *input_line_pointer = name_end;
+ demand_empty_rest_of_line ();
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ frag_align (3, 0, 0);
+ p = frag_more (16);
+ fixp = fix_new (frag_now, p - frag_now->fr_literal, 8, 0, 0, 0, 0);
+ fixp->fx_done = 1;
+ seginfo->literal_pool_size += 16;
+
+ *p = alpha_evax_proc.pdsckind
+ | ((alpha_evax_proc.framereg == 29) ? PDSC_S_M_BASE_REG_IS_FP : 0);
+ *(p+1) = PDSC_S_M_NATIVE
+ | PDSC_S_M_NO_JACKET;
+
+ switch (alpha_evax_proc.pdsckind)
+ {
+ case PDSC_S_K_KIND_NULL:
+ *(p+2) = 0;
+ *(p+3) = 0;
+ break;
+ case PDSC_S_K_KIND_FP_REGISTER:
+ *(p+2) = alpha_evax_proc.fp_save;
+ *(p+3) = alpha_evax_proc.ra_save;
+ break;
+ case PDSC_S_K_KIND_FP_STACK:
+ md_number_to_chars (p+2, (valueT)alpha_evax_proc.rsa_offset, 2);
+ break;
+ default: /* impossible */
+ break;
+ }
+
+ *(p+4) = 0;
+ *(p+5) = alpha_evax_proc.type & 0x0f;
+
+ /* Signature offset. */
+ md_number_to_chars (p+6, (valueT)0, 2);
+
+ fix_new_exp (frag_now, p-frag_now->fr_literal+8, 8, &exp, 0, BFD_RELOC_64);
+
+ if (alpha_evax_proc.pdsckind == PDSC_S_K_KIND_NULL)
+ return;
+
+ /* Add dummy fix to make add_to_link_pool work. */
+ p = frag_more (8);
+ fixp = fix_new (frag_now, p - frag_now->fr_literal, 8, 0, 0, 0, 0);
+ fixp->fx_done = 1;
+ seginfo->literal_pool_size += 8;
+
+ /* pdesc+16: Size. */
+ md_number_to_chars (p, (valueT)alpha_evax_proc.framesize, 4);
+
+ md_number_to_chars (p+4, (valueT)0, 2);
+
+ /* Entry length. */
+ md_number_to_chars (p+6, alpha_evax_proc.prologue, 2);
+
+ if (alpha_evax_proc.pdsckind == PDSC_S_K_KIND_FP_REGISTER)
+ return;
+
+ /* Add dummy fix to make add_to_link_pool work. */
+ p = frag_more (8);
+ fixp = fix_new (frag_now, p - frag_now->fr_literal, 8, 0, 0, 0, 0);
+ fixp->fx_done = 1;
+ seginfo->literal_pool_size += 8;
+
+ /* pdesc+24: register masks. */
+
+ md_number_to_chars (p, alpha_evax_proc.imask, 4);
+ md_number_to_chars (p+4, alpha_evax_proc.fmask, 4);
+
+ return;
+}
+
+
+/* Support for crash debug on vms. */
+
+static void
+s_alpha_name (ignore)
+ int ignore;
+{
+ register char *p;
+ expressionS exp;
+ segment_info_type *seginfo = seg_info (alpha_link_section);
+
+ if (now_seg != alpha_link_section)
+ {
+ as_bad (_(".name directive not in link (.link) section"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ expression (&exp);
+ if (exp.X_op != O_symbol)
+ {
+ as_warn (_(".name directive has no symbol"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ demand_empty_rest_of_line ();
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ frag_align (3, 0, 0);
+ p = frag_more (8);
+ seginfo->literal_pool_size += 8;
+
+ fix_new_exp (frag_now, p-frag_now->fr_literal, 8, &exp, 0, BFD_RELOC_64);
+
+ return;
+}
+
+
+static void
+s_alpha_linkage (ignore)
+ int ignore;
+{
+ expressionS exp;
+ char *p;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ expression (&exp);
+ if (exp.X_op != O_symbol)
+ {
+ as_fatal (_("No symbol after .linkage"));
+ }
+ else
+ {
+ p = frag_more (LKP_S_K_SIZE);
+ memset (p, 0, LKP_S_K_SIZE);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, LKP_S_K_SIZE, &exp, 0,\
+ BFD_RELOC_ALPHA_LINKAGE);
+ }
+ demand_empty_rest_of_line ();
+
+ return;
+}
+
+
+static void
+s_alpha_code_address (ignore)
+ int ignore;
+{
+ expressionS exp;
+ char *p;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ expression (&exp);
+ if (exp.X_op != O_symbol)
+ {
+ as_fatal (_("No symbol after .code_address"));
+ }
+ else
+ {
+ p = frag_more (8);
+ memset (p, 0, 8);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 8, &exp, 0,\
+ BFD_RELOC_ALPHA_CODEADDR);
+ }
+ demand_empty_rest_of_line ();
+
+ return;
+}
+
+
+static void
+s_alpha_fp_save (ignore)
+ int ignore;
+{
+
+ alpha_evax_proc.fp_save = tc_get_register (1);
+
+ demand_empty_rest_of_line ();
+ return;
+}
+
+
+static void
+s_alpha_mask (ignore)
+ int ignore;
+{
+ long val;
+
+ if (get_absolute_expression_and_terminator (&val) != ',')
+ {
+ as_warn (_("Bad .mask directive"));
+ --input_line_pointer;
+ }
+ else
+ {
+ alpha_evax_proc.imask = val;
+ (void)get_absolute_expression ();
+ }
+ demand_empty_rest_of_line ();
+
+ return;
+}
+
+
+static void
+s_alpha_fmask (ignore)
+ int ignore;
+{
+ long val;
+
+ if (get_absolute_expression_and_terminator (&val) != ',')
+ {
+ as_warn (_("Bad .fmask directive"));
+ --input_line_pointer;
+ }
+ else
+ {
+ alpha_evax_proc.fmask = val;
+ (void) get_absolute_expression ();
+ }
+ demand_empty_rest_of_line ();
+
+ return;
+}
+
+static void
+s_alpha_end (ignore)
+ int ignore;
+{
+ char c;
+
+ c = get_symbol_end ();
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+ alpha_evax_proc.symbol = 0;
+
+ return;
+}
+
+
+static void
+s_alpha_file (ignore)
+ int ignore;
+{
+ symbolS *s;
+ int length;
+ static char case_hack[32];
+
+ extern char *demand_copy_string PARAMS ((int *lenP));
+
+ sprintf (case_hack, "<CASE:%01d%01d>",
+ alpha_flag_hash_long_names, alpha_flag_show_after_trunc);
+
+ s = symbol_find_or_make (case_hack);
+ s->bsym->flags |= BSF_FILE;
+
+ get_absolute_expression ();
+ s = symbol_find_or_make (demand_copy_string (&length));
+ s->bsym->flags |= BSF_FILE;
+ demand_empty_rest_of_line ();
+
+ return;
+}
+#endif /* OBJ_EVAX */
+
+/* Handle the .gprel32 pseudo op. */
+
+static void
+s_alpha_gprel32 (ignore)
+ int ignore;
+{
+ expressionS e;
+ char *p;
+
+ SKIP_WHITESPACE ();
+ expression (&e);
+
+#ifdef OBJ_ELF
+ switch (e.X_op)
+ {
+ case O_constant:
+ e.X_add_symbol = section_symbol(absolute_section);
+ e.X_op = O_symbol;
+ /* FALLTHRU */
+ case O_symbol:
+ break;
+ default:
+ abort();
+ }
+#else
+#ifdef OBJ_ECOFF
+ switch (e.X_op)
+ {
+ case O_constant:
+ e.X_add_symbol = section_symbol (absolute_section);
+ /* fall through */
+ case O_symbol:
+ e.X_op = O_subtract;
+ e.X_op_symbol = alpha_gp_symbol;
+ break;
+ default:
+ abort ();
+ }
+#endif
+#endif
+
+ if (alpha_auto_align_on && alpha_current_align < 2)
+ alpha_align (2, (char *) NULL, alpha_insn_label, 0);
+ if (alpha_current_align > 2)
+ alpha_current_align = 2;
+ alpha_insn_label = NULL;
+
+ p = frag_more (4);
+ memset (p, 0, 4);
+ fix_new_exp (frag_now, p-frag_now->fr_literal, 4,
+ &e, 0, BFD_RELOC_GPREL32);
+}
+
+/* Handle floating point allocation pseudo-ops. This is like the
+ generic vresion, but it makes sure the current label, if any, is
+ correctly aligned. */
+
+static void
+s_alpha_float_cons (type)
+ int type;
+{
+ int log_size;
+
+ switch (type)
+ {
+ default:
+ case 'f':
+ case 'F':
+ log_size = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'G':
+ log_size = 3;
+ break;
+
+ case 'x':
+ case 'X':
+ case 'p':
+ case 'P':
+ log_size = 4;
+ break;
+ }
+
+ if (alpha_auto_align_on && alpha_current_align < log_size)
+ alpha_align (log_size, (char *) NULL, alpha_insn_label, 0);
+ if (alpha_current_align > log_size)
+ alpha_current_align = log_size;
+ alpha_insn_label = NULL;
+
+ float_cons (type);
+}
+
+/* Handle the .proc pseudo op. We don't really do much with it except
+ parse it. */
+
+static void
+s_alpha_proc (is_static)
+ int is_static;
+{
+ char *name;
+ char c;
+ char *p;
+ symbolS *symbolP;
+ int temp;
+
+ /* Takes ".proc name,nargs" */
+ SKIP_WHITESPACE ();
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = input_line_pointer;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ *p = 0;
+ as_warn (_("Expected comma after name \"%s\""), name);
+ *p = c;
+ temp = 0;
+ ignore_rest_of_line ();
+ }
+ else
+ {
+ input_line_pointer++;
+ temp = get_absolute_expression ();
+ }
+ /* symbolP->sy_other = (signed char) temp; */
+ as_warn (_("unhandled: .proc %s,%d"), name, temp);
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .set pseudo op. This is used to turn on and off most of
+ the assembler features. */
+
+static void
+s_alpha_set (x)
+ int x;
+{
+ char *name, ch, *s;
+ int yesno = 1;
+
+ SKIP_WHITESPACE ();
+ name = input_line_pointer;
+ ch = get_symbol_end ();
+
+ s = name;
+ if (s[0] == 'n' && s[1] == 'o')
+ {
+ yesno = 0;
+ s += 2;
+ }
+ if (!strcmp ("reorder", s))
+ /* ignore */ ;
+ else if (!strcmp ("at", s))
+ alpha_noat_on = !yesno;
+ else if (!strcmp ("macro", s))
+ alpha_macros_on = yesno;
+ else if (!strcmp ("move", s))
+ /* ignore */ ;
+ else if (!strcmp ("volatile", s))
+ /* ignore */ ;
+ else
+ as_warn (_("Tried to .set unrecognized mode `%s'"), name);
+
+ *input_line_pointer = ch;
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .base pseudo op. This changes the assembler's notion of
+ the $gp register. */
+
+static void
+s_alpha_base (ignore)
+ int ignore;
+{
+#if 0
+ if (first_32bit_quadrant)
+ {
+ /* not fatal, but it might not work in the end */
+ as_warn (_("File overrides no-base-register option."));
+ first_32bit_quadrant = 0;
+ }
+#endif
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '$')
+ { /* $rNN form */
+ input_line_pointer++;
+ if (*input_line_pointer == 'r')
+ input_line_pointer++;
+ }
+
+ alpha_gp_register = get_absolute_expression ();
+ if (alpha_gp_register < 0 || alpha_gp_register > 31)
+ {
+ alpha_gp_register = AXP_REG_GP;
+ as_warn (_("Bad base register, using $%d."), alpha_gp_register);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .align pseudo-op. This aligns to a power of two. It
+ also adjusts any current instruction label. We treat this the same
+ way the MIPS port does: .align 0 turns off auto alignment. */
+
+static void
+s_alpha_align (ignore)
+ int ignore;
+{
+ int align;
+ char fill, *pfill;
+ long max_alignment = 15;
+
+ align = get_absolute_expression ();
+ if (align > max_alignment)
+ {
+ align = max_alignment;
+ as_bad (_("Alignment too large: %d. assumed"), align);
+ }
+ else if (align < 0)
+ {
+ as_warn (_("Alignment negative: 0 assumed"));
+ align = 0;
+ }
+
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ fill = get_absolute_expression ();
+ pfill = &fill;
+ }
+ else
+ pfill = NULL;
+
+ if (align != 0)
+ {
+ alpha_auto_align_on = 1;
+ alpha_align (align, pfill, alpha_insn_label, 1);
+ }
+ else
+ {
+ alpha_auto_align_on = 0;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Hook the normal string processor to reset known alignment. */
+
+static void
+s_alpha_stringer (terminate)
+ int terminate;
+{
+ alpha_current_align = 0;
+ alpha_insn_label = NULL;
+ stringer (terminate);
+}
+
+/* Hook the normal space processing to reset known alignment. */
+
+static void
+s_alpha_space (ignore)
+ int ignore;
+{
+ alpha_current_align = 0;
+ alpha_insn_label = NULL;
+ s_space (ignore);
+}
+
+/* Hook into cons for auto-alignment. */
+
+void
+alpha_cons_align (size)
+ int size;
+{
+ int log_size;
+
+ log_size = 0;
+ while ((size >>= 1) != 0)
+ ++log_size;
+
+ if (alpha_auto_align_on && alpha_current_align < log_size)
+ alpha_align (log_size, (char *) NULL, alpha_insn_label, 0);
+ if (alpha_current_align > log_size)
+ alpha_current_align = log_size;
+ alpha_insn_label = NULL;
+}
+
+/* Here come the .uword, .ulong, and .uquad explicitly unaligned
+ pseudos. We just turn off auto-alignment and call down to cons. */
+
+static void
+s_alpha_ucons (bytes)
+ int bytes;
+{
+ int hold = alpha_auto_align_on;
+ alpha_auto_align_on = 0;
+ cons (bytes);
+ alpha_auto_align_on = hold;
+}
+
+/* Switch the working cpu type. */
+
+static void
+s_alpha_arch (ignored)
+ int ignored;
+{
+ char *name, ch;
+ const struct cpu_type *p;
+
+ SKIP_WHITESPACE ();
+ name = input_line_pointer;
+ ch = get_symbol_end ();
+
+ for (p = cpu_types; p->name; ++p)
+ if (strcmp(name, p->name) == 0)
+ {
+ alpha_target_name = p->name, alpha_target = p->flags;
+ goto found;
+ }
+ as_warn("Unknown CPU identifier `%s'", name);
+
+found:
+ *input_line_pointer = ch;
+ demand_empty_rest_of_line ();
+}
+
+
+
+#ifdef DEBUG1
+/* print token expression with alpha specific extension. */
+
+static void
+alpha_print_token(f, exp)
+ FILE *f;
+ const expressionS *exp;
+{
+ switch (exp->X_op)
+ {
+ case O_cpregister:
+ putc (',', f);
+ /* FALLTHRU */
+ case O_pregister:
+ putc ('(', f);
+ {
+ expressionS nexp = *exp;
+ nexp.X_op = O_register;
+ print_expr (f, &nexp);
+ }
+ putc (')', f);
+ break;
+ default:
+ print_expr (f, exp);
+ break;
+ }
+ return;
+}
+#endif
+
+/* The target specific pseudo-ops which we support. */
+
+const pseudo_typeS md_pseudo_table[] =
+{
+#ifdef OBJ_ECOFF
+ {"comm", s_alpha_comm, 0}, /* osf1 compiler does this */
+ {"rdata", s_alpha_rdata, 0},
+#endif
+ {"text", s_alpha_text, 0},
+ {"data", s_alpha_data, 0},
+#ifdef OBJ_ECOFF
+ {"sdata", s_alpha_sdata, 0},
+#endif
+#ifdef OBJ_ELF
+ {"section", s_alpha_section, 0},
+ {"section.s", s_alpha_section, 0},
+ {"sect", s_alpha_section, 0},
+ {"sect.s", s_alpha_section, 0},
+#endif
+#ifdef OBJ_EVAX
+ { "pdesc", s_alpha_pdesc, 0},
+ { "name", s_alpha_name, 0},
+ { "linkage", s_alpha_linkage, 0},
+ { "code_address", s_alpha_code_address, 0},
+ { "ent", s_alpha_ent, 0},
+ { "frame", s_alpha_frame, 0},
+ { "fp_save", s_alpha_fp_save, 0},
+ { "mask", s_alpha_mask, 0},
+ { "fmask", s_alpha_fmask, 0},
+ { "end", s_alpha_end, 0},
+ { "file", s_alpha_file, 0},
+ { "rdata", s_alpha_section, 1},
+ { "comm", s_alpha_comm, 0},
+ { "link", s_alpha_section, 3},
+ { "ctors", s_alpha_section, 4},
+ { "dtors", s_alpha_section, 5},
+#endif
+#ifdef OBJ_ELF
+ /* Frame related pseudos. */
+ {"ent", s_alpha_ent, 0},
+ {"end", s_alpha_end, 0},
+ {"mask", s_alpha_mask, 0},
+ {"fmask", s_alpha_mask, 1},
+ {"frame", s_alpha_frame, 0},
+ {"prologue", s_alpha_prologue, 0},
+ /* COFF debugging related pseudos. */
+ {"begin", s_alpha_coff_wrapper, 0},
+ {"bend", s_alpha_coff_wrapper, 1},
+ {"def", s_alpha_coff_wrapper, 2},
+ {"dim", s_alpha_coff_wrapper, 3},
+ {"endef", s_alpha_coff_wrapper, 4},
+ {"file", s_alpha_coff_wrapper, 5},
+ {"scl", s_alpha_coff_wrapper, 6},
+ {"tag", s_alpha_coff_wrapper, 7},
+ {"val", s_alpha_coff_wrapper, 8},
+ {"loc", s_alpha_coff_wrapper, 9},
+#else
+ {"prologue", s_ignore, 0},
+#endif
+ {"gprel32", s_alpha_gprel32, 0},
+ {"t_floating", s_alpha_float_cons, 'd'},
+ {"s_floating", s_alpha_float_cons, 'f'},
+ {"f_floating", s_alpha_float_cons, 'F'},
+ {"g_floating", s_alpha_float_cons, 'G'},
+ {"d_floating", s_alpha_float_cons, 'D'},
+
+ {"proc", s_alpha_proc, 0},
+ {"aproc", s_alpha_proc, 1},
+ {"set", s_alpha_set, 0},
+ {"reguse", s_ignore, 0},
+ {"livereg", s_ignore, 0},
+ {"base", s_alpha_base, 0}, /*??*/
+ {"option", s_ignore, 0},
+ {"aent", s_ignore, 0},
+ {"ugen", s_ignore, 0},
+ {"eflag", s_ignore, 0},
+
+ {"align", s_alpha_align, 0},
+ {"double", s_alpha_float_cons, 'd'},
+ {"float", s_alpha_float_cons, 'f'},
+ {"single", s_alpha_float_cons, 'f'},
+ {"ascii", s_alpha_stringer, 0},
+ {"asciz", s_alpha_stringer, 1},
+ {"string", s_alpha_stringer, 1},
+ {"space", s_alpha_space, 0},
+ {"skip", s_alpha_space, 0},
+ {"zero", s_alpha_space, 0},
+
+/* Unaligned data pseudos. */
+ {"uword", s_alpha_ucons, 2},
+ {"ulong", s_alpha_ucons, 4},
+ {"uquad", s_alpha_ucons, 8},
+
+#ifdef OBJ_ELF
+/* Dwarf wants these versions of unaligned. */
+ {"2byte", s_alpha_ucons, 2},
+ {"4byte", s_alpha_ucons, 4},
+ {"8byte", s_alpha_ucons, 8},
+#endif
+
+/* We don't do any optimizing, so we can safely ignore these. */
+ {"noalias", s_ignore, 0},
+ {"alias", s_ignore, 0},
+
+ {"arch", s_alpha_arch, 0},
+
+ {NULL, 0, 0},
+};
+
+
+/* Build a BFD section with its flags set appropriately for the .lita,
+ .lit8, or .lit4 sections. */
+
+static void
+create_literal_section (name, secp, symp)
+ const char *name;
+ segT *secp;
+ symbolS **symp;
+{
+ segT current_section = now_seg;
+ int current_subsec = now_subseg;
+ segT new_sec;
+
+ *secp = new_sec = subseg_new (name, 0);
+ subseg_set (current_section, current_subsec);
+ bfd_set_section_alignment (stdoutput, new_sec, 4);
+ bfd_set_section_flags (stdoutput, new_sec,
+ SEC_RELOC | SEC_ALLOC | SEC_LOAD | SEC_READONLY
+ | SEC_DATA);
+
+ S_CLEAR_EXTERNAL (*symp = section_symbol (new_sec));
+}
+
+#ifdef OBJ_ECOFF
+
+/* @@@ GP selection voodoo. All of this seems overly complicated and
+ unnecessary; which is the primary reason it's for ECOFF only. */
+
+static inline void
+maybe_set_gp (sec)
+ asection *sec;
+{
+ bfd_vma vma;
+ if (!sec)
+ return;
+ vma = bfd_get_section_vma (foo, sec);
+ if (vma && vma < alpha_gp_value)
+ alpha_gp_value = vma;
+}
+
+static void
+select_gp_value ()
+{
+ assert (alpha_gp_value == 0);
+
+ /* Get minus-one in whatever width... */
+ alpha_gp_value = 0; alpha_gp_value--;
+
+ /* Select the smallest VMA of these existing sections. */
+ maybe_set_gp (alpha_lita_section);
+#if 0
+ /* These were disabled before -- should we use them? */
+ maybe_set_gp (sdata);
+ maybe_set_gp (lit8_sec);
+ maybe_set_gp (lit4_sec);
+#endif
+
+/* @@ Will a simple 0x8000 work here? If not, why not? */
+#define GP_ADJUSTMENT (0x8000 - 0x10)
+
+ alpha_gp_value += GP_ADJUSTMENT;
+
+ S_SET_VALUE (alpha_gp_symbol, alpha_gp_value);
+
+#ifdef DEBUG1
+ printf (_("Chose GP value of %lx\n"), alpha_gp_value);
+#endif
+}
+#endif /* OBJ_ECOFF */
+
+/* Called internally to handle all alignment needs. This takes care
+ of eliding calls to frag_align if'n the cached current alignment
+ says we've already got it, as well as taking care of the auto-align
+ feature wrt labels. */
+
+static void
+alpha_align (n, pfill, label, force)
+ int n;
+ char *pfill;
+ symbolS *label;
+ int force;
+{
+ if (alpha_current_align >= n)
+ return;
+
+ if (pfill == NULL)
+ {
+ if (n > 2
+ && (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
+ {
+ static char const unop[4] = { 0x00, 0x00, 0xe0, 0x2f };
+ static char const nopunop[8] = {
+ 0x1f, 0x04, 0xff, 0x47,
+ 0x00, 0x00, 0xe0, 0x2f
+ };
+
+ /* First, make sure we're on a four-byte boundary, in case
+ someone has been putting .byte values into the text
+ section. The DEC assembler silently fills with unaligned
+ no-op instructions. This will zero-fill, then nop-fill
+ with proper alignment. */
+ if (alpha_current_align < 2)
+ frag_align (2, 0, 0);
+ if (alpha_current_align < 3)
+ frag_align_pattern (3, unop, sizeof unop, 0);
+ if (n > 3)
+ frag_align_pattern (n, nopunop, sizeof nopunop, 0);
+ }
+ else
+ frag_align (n, 0, 0);
+ }
+ else
+ frag_align (n, *pfill, 0);
+
+ alpha_current_align = n;
+
+ if (label != NULL)
+ {
+ assert (S_GET_SEGMENT (label) == now_seg);
+ label->sy_frag = frag_now;
+ S_SET_VALUE (label, (valueT) frag_now_fix ());
+ }
+
+ record_alignment(now_seg, n);
+
+ /* ??? if alpha_flag_relax && force && elf, record the requested alignment
+ in a reloc for the linker to see. */
+}
+
+/* The Alpha has support for some VAX floating point types, as well as for
+ IEEE floating point. We consider IEEE to be the primary floating point
+ format, and sneak in the VAX floating point support here. */
+#define md_atof vax_md_atof
+#include "config/atof-vax.c"
diff --git a/gas/config/tc-alpha.h b/gas/config/tc-alpha.h
new file mode 100644
index 0000000..ca6903a
--- /dev/null
+++ b/gas/config/tc-alpha.h
@@ -0,0 +1,104 @@
+/* This file is tc-alpha.h
+ Copyright (C) 1994, 95, 96, 97, 98, 1999 Free Software Foundation, Inc.
+ Written by Ken Raeburn <raeburn@cygnus.com>.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#define TC_ALPHA
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define WORKING_DOT_WORD
+
+#define TARGET_ARCH bfd_arch_alpha
+
+#define TARGET_FORMAT (OUTPUT_FLAVOR == bfd_target_ecoff_flavour \
+ ? "ecoff-littlealpha" \
+ : OUTPUT_FLAVOR == bfd_target_elf_flavour \
+ ? "elf64-alpha" \
+ : OUTPUT_FLAVOR == bfd_target_evax_flavour \
+ ? "vms-alpha" \
+ : "unknown-format")
+
+#define NEED_LITERAL_POOL
+#define TC_HANDLES_FX_DONE
+#define REPEAT_CONS_EXPRESSIONS
+
+extern int alpha_force_relocation PARAMS ((struct fix *));
+extern int alpha_fix_adjustable PARAMS ((struct fix *));
+
+extern unsigned long alpha_gprmask, alpha_fprmask;
+extern valueT alpha_gp_value;
+
+#define TC_FORCE_RELOCATION(FIXP) alpha_force_relocation (FIXP)
+#define tc_fix_adjustable(FIXP) alpha_fix_adjustable (FIXP)
+#define RELOC_REQUIRES_SYMBOL
+
+/* This expression evaluates to false if the relocation is for a local
+ object for which we still want to do the relocation at runtime.
+ True if we are willing to perform this relocation while building
+ the .o file. This is only used for pcrel relocations. */
+
+#define TC_RELOC_RTSYM_LOC_FIXUP(FIX) \
+ ((FIX)->fx_addsy == NULL \
+ || (! S_IS_EXTERNAL ((FIX)->fx_addsy) \
+ && ! S_IS_WEAK ((FIX)->fx_addsy) \
+ && S_IS_DEFINED ((FIX)->fx_addsy) \
+ && ! S_IS_COMMON ((FIX)->fx_addsy)))
+
+#define md_convert_frag(b,s,f) as_fatal ("alpha convert_frag\n")
+#define md_estimate_size_before_relax(f,s) \
+ (as_fatal("estimate_size_before_relax called"),1)
+#define md_operand(x)
+
+#ifdef OBJ_EVAX
+
+/* This field keeps the symbols position in the link section. */
+#define OBJ_SYMFIELD_TYPE valueT
+
+#define TC_CONS_FIX_NEW(FRAG,OFF,LEN,EXP) \
+ fix_new_exp (FRAG, OFF, (int)LEN, EXP, 0, \
+ LEN == 2 ? BFD_RELOC_16 \
+ : LEN == 4 ? BFD_RELOC_32 \
+ : LEN == 8 ? BFD_RELOC_64 \
+ : BFD_RELOC_ALPHA_LINKAGE);
+#endif
+
+#define md_number_to_chars number_to_chars_littleendian
+
+extern int tc_get_register PARAMS ((int frame));
+extern void alpha_frob_ecoff_data PARAMS ((void));
+
+#define tc_frob_label(sym) alpha_define_label (sym)
+extern void alpha_define_label PARAMS ((struct symbol *));
+
+#define md_cons_align(nbytes) alpha_cons_align (nbytes)
+extern void alpha_cons_align PARAMS ((int));
+
+#ifdef OBJ_ECOFF
+#define tc_frob_file_before_adjust() alpha_frob_file_before_adjust ()
+extern void alpha_frob_file_before_adjust PARAMS ((void));
+#endif
+
+#define DIFF_EXPR_OK /* foo-. gets turned into PC relative relocs */
+
+#ifdef OBJ_ELF
+#define ELF_TC_SPECIAL_SECTIONS \
+ { ".sdata", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_ALPHA_GPREL }, \
+ { ".sbss", SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_ALPHA_GPREL },
+#endif
diff --git a/gas/config/tc-arc.c b/gas/config/tc-arc.c
new file mode 100644
index 0000000..c07c2d7
--- /dev/null
+++ b/gas/config/tc-arc.c
@@ -0,0 +1,1481 @@
+/* tc-arc.c -- Assembler for the ARC
+ Copyright (C) 1994, 1995, 1997, 1998 Free Software Foundation, Inc.
+ Contributed by Doug Evans (dje@cygnus.com).
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "as.h"
+#include "subsegs.h"
+#include "opcode/arc.h"
+#include "elf/arc.h"
+
+extern int arc_get_mach PARAMS ((char *));
+
+static arc_insn arc_insert_operand PARAMS ((arc_insn,
+ const struct arc_operand *, int,
+ const struct arc_operand_value *,
+ offsetT, char *, unsigned int));
+static void arc_common PARAMS ((int));
+static void arc_cpu PARAMS ((int));
+/*static void arc_rename PARAMS ((int));*/
+static int get_arc_exp_reloc_type PARAMS ((int, int, expressionS *,
+ expressionS *));
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "align", s_align_bytes, 0 }, /* Defaulting is invalid (0) */
+ { "common", arc_common, 0 },
+/*{ "hword", cons, 2 }, - already exists */
+ { "word", cons, 4 },
+/*{ "xword", cons, 8 },*/
+ { "cpu", arc_cpu, 0 },
+/*{ "rename", arc_rename, 0 },*/
+ { NULL, 0, 0 },
+};
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful */
+const char comment_chars[] = "#;";
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that comments started like this one will always
+ work if '/' isn't otherwise defined. */
+const char line_comment_chars[] = "#";
+
+const char line_separator_chars[] = "";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdD";
+
+/* Byte order. */
+extern int target_big_endian;
+const char *arc_target_format = DEFAULT_TARGET_FORMAT;
+static int byte_order = DEFAULT_BYTE_ORDER;
+
+/* One of bfd_mach_arc_xxx. */
+static int arc_mach_type = bfd_mach_arc_base;
+
+/* Non-zero if the cpu type has been explicitly specified. */
+static int mach_type_specified_p = 0;
+
+/* Non-zero if opcode tables have been initialized.
+ A .cpu command must appear before any instructions. */
+static int cpu_tables_init_p = 0;
+
+static struct hash_control *arc_suffix_hash = NULL;
+
+const char *md_shortopts = "";
+struct option md_longopts[] =
+{
+#define OPTION_EB (OPTION_MD_BASE + 0)
+ {"EB", no_argument, NULL, OPTION_EB},
+#define OPTION_EL (OPTION_MD_BASE + 1)
+ {"EL", no_argument, NULL, OPTION_EL},
+ { NULL, no_argument, NULL, 0 }
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+/*
+ * md_parse_option
+ *
+ * Invocation line includes a switch not recognized by the base assembler.
+ * See if it's a processor-specific option.
+ */
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ switch (c)
+ {
+ case OPTION_EB:
+ byte_order = BIG_ENDIAN;
+ arc_target_format = "elf32-bigarc";
+ break;
+ case OPTION_EL:
+ byte_order = LITTLE_ENDIAN;
+ arc_target_format = "elf32-littlearc";
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ fprintf (stream, _("\
+ARC options:\n\
+-EB generate big endian output\n\
+-EL generate little endian output\n"));
+}
+
+/* This function is called once, at assembler startup time. It should
+ set up all the tables, etc. that the MD part of the assembler will need.
+ Opcode selection is defered until later because we might see a .cpu
+ command. */
+
+void
+md_begin ()
+{
+ /* The endianness can be chosen "at the factory". */
+ target_big_endian = byte_order == BIG_ENDIAN;
+
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, arc_mach_type))
+ as_warn (_("could not set architecture and machine"));
+
+ /* Assume the base cpu. This call is necessary because we need to
+ initialize `arc_operand_map' which may be needed before we see the
+ first insn. */
+ arc_opcode_init_tables (arc_get_opcode_mach (bfd_mach_arc_base,
+ target_big_endian));
+}
+
+/* Initialize the various opcode and operand tables.
+ MACH is one of bfd_mach_arc_xxx. */
+
+static void
+init_opcode_tables (mach)
+ int mach;
+{
+ register unsigned int i;
+ char *last;
+
+ if ((arc_suffix_hash = hash_new ()) == NULL)
+ as_fatal (_("virtual memory exhausted"));
+
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach))
+ as_warn (_("could not set architecture and machine"));
+
+ /* This initializes a few things in arc-opc.c that we need.
+ This must be called before the various arc_xxx_supported fns. */
+ arc_opcode_init_tables (arc_get_opcode_mach (mach, target_big_endian));
+
+ /* Only put the first entry of each equivalently named suffix in the
+ table. */
+ last = "";
+ for (i = 0; i < arc_suffixes_count; i++)
+ {
+ if (! arc_opval_supported (&arc_suffixes[i]))
+ continue;
+ if (strcmp (arc_suffixes[i].name, last) != 0)
+ hash_insert (arc_suffix_hash, arc_suffixes[i].name, (PTR) (arc_suffixes + i));
+ last = arc_suffixes[i].name;
+ }
+
+ /* Since registers don't have a prefix, we put them in the symbol table so
+ they can't be used as symbols. This also simplifies argument parsing as
+ we can let gas parse registers for us. The recorded register number is
+ the index in `arc_reg_names'. */
+ for (i = 0; i < arc_reg_names_count; i++)
+ {
+ if (! arc_opval_supported (&arc_reg_names[i]))
+ continue;
+ /* Use symbol_create here instead of symbol_new so we don't try to
+ output registers into the object file's symbol table. */
+ symbol_table_insert (symbol_create (arc_reg_names[i].name, reg_section,
+ i, &zero_address_frag));
+ }
+
+ /* Tell `s_cpu' it's too late. */
+ cpu_tables_init_p = 1;
+}
+
+/* Insert an operand value into an instruction.
+ If REG is non-NULL, it is a register number and ignore VAL. */
+
+static arc_insn
+arc_insert_operand (insn, operand, mods, reg, val, file, line)
+ arc_insn insn;
+ const struct arc_operand *operand;
+ int mods;
+ const struct arc_operand_value *reg;
+ offsetT val;
+ char *file;
+ unsigned int line;
+{
+ if (operand->bits != 32)
+ {
+ long min, max;
+ offsetT test;
+
+ if ((operand->flags & ARC_OPERAND_SIGNED) != 0)
+ {
+ if ((operand->flags & ARC_OPERAND_SIGNOPT) != 0)
+ max = (1 << operand->bits) - 1;
+ else
+ max = (1 << (operand->bits - 1)) - 1;
+ min = - (1 << (operand->bits - 1));
+ }
+ else
+ {
+ max = (1 << operand->bits) - 1;
+ min = 0;
+ }
+
+ if ((operand->flags & ARC_OPERAND_NEGATIVE) != 0)
+ test = - val;
+ else
+ test = val;
+
+ if (test < (offsetT) min || test > (offsetT) max)
+ {
+ const char *err =
+ _("operand out of range (%s not between %ld and %ld)");
+ char buf[100];
+
+ sprint_value (buf, test);
+ if (file == (char *) NULL)
+ as_warn (err, buf, min, max);
+ else
+ as_warn_where (file, line, err, buf, min, max);
+ }
+ }
+
+ if (operand->insert)
+ {
+ const char *errmsg;
+
+ errmsg = NULL;
+ insn = (*operand->insert) (insn, operand, mods, reg, (long) val, &errmsg);
+ if (errmsg != (const char *) NULL)
+ as_warn (errmsg);
+ }
+ else
+ insn |= (((long) val & ((1 << operand->bits) - 1))
+ << operand->shift);
+
+ return insn;
+}
+
+/* We need to keep a list of fixups. We can't simply generate them as
+ we go, because that would require us to first create the frag, and
+ that would screw up references to ``.''. */
+
+struct arc_fixup
+{
+ /* index into `arc_operands' */
+ int opindex;
+ expressionS exp;
+};
+
+#define MAX_FIXUPS 5
+
+#define MAX_SUFFIXES 5
+
+/* This routine is called for each instruction to be assembled. */
+
+void
+md_assemble (str)
+ char *str;
+{
+ const struct arc_opcode *opcode;
+ char *start;
+ arc_insn insn;
+ static int init_tables_p = 0;
+
+ /* Opcode table initialization is deferred until here because we have to
+ wait for a possible .cpu command. */
+ if (!init_tables_p)
+ {
+ init_opcode_tables (arc_mach_type);
+ init_tables_p = 1;
+ }
+
+ /* Skip leading white space. */
+ while (isspace (*str))
+ str++;
+
+ /* The instructions are stored in lists hashed by the first letter (though
+ we needn't care how they're hashed). Get the first in the list. */
+
+ opcode = arc_opcode_lookup_asm (str);
+
+ /* Keep looking until we find a match. */
+
+ start = str;
+ for ( ; opcode != NULL; opcode = ARC_OPCODE_NEXT_ASM (opcode))
+ {
+ int past_opcode_p, fc, num_suffixes;
+ char *syn;
+ struct arc_fixup fixups[MAX_FIXUPS];
+ /* Used as a sanity check. If we need a limm reloc, make sure we ask
+ for an extra 4 bytes from frag_more. */
+ int limm_reloc_p;
+ const struct arc_operand_value *insn_suffixes[MAX_SUFFIXES];
+
+ /* Is this opcode supported by the selected cpu? */
+ if (! arc_opcode_supported (opcode))
+ continue;
+
+ /* Scan the syntax string. If it doesn't match, try the next one. */
+
+ arc_opcode_init_insert ();
+ insn = opcode->value;
+ fc = 0;
+ past_opcode_p = 0;
+ num_suffixes = 0;
+ limm_reloc_p = 0;
+
+ /* We don't check for (*str != '\0') here because we want to parse
+ any trailing fake arguments in the syntax string. */
+ for (str = start, syn = opcode->syntax; *syn != '\0'; )
+ {
+ int mods;
+ const struct arc_operand *operand;
+
+ /* Non operand chars must match exactly. */
+ if (*syn != '%' || *++syn == '%')
+ {
+ /* Handle '+' specially as we want to allow "ld r0,[sp-4]". */
+ /* ??? The syntax has changed to [sp,-4]. */
+ if (0 && *syn == '+' && *str == '-')
+ {
+ /* Skip over syn's +, but leave str's - alone.
+ That makes the case identical to "ld r0,[sp+-4]". */
+ ++syn;
+ }
+ else if (*str == *syn)
+ {
+ if (*syn == ' ')
+ past_opcode_p = 1;
+ ++syn;
+ ++str;
+ }
+ else
+ break;
+ continue;
+ }
+
+ /* We have an operand. Pick out any modifiers. */
+ mods = 0;
+ while (ARC_MOD_P (arc_operands[arc_operand_map[*syn]].flags))
+ {
+ mods |= arc_operands[arc_operand_map[*syn]].flags & ARC_MOD_BITS;
+ ++syn;
+ }
+ operand = arc_operands + arc_operand_map[*syn];
+ if (operand->fmt == 0)
+ as_fatal (_("unknown syntax format character `%c'"), *syn);
+
+ if (operand->flags & ARC_OPERAND_FAKE)
+ {
+ const char *errmsg = NULL;
+ if (operand->insert)
+ {
+ insn = (*operand->insert) (insn, operand, mods, NULL, 0, &errmsg);
+ /* If we get an error, go on to try the next insn. */
+ if (errmsg)
+ break;
+ }
+ ++syn;
+ }
+ /* Are we finished with suffixes? */
+ else if (!past_opcode_p)
+ {
+ int found;
+ char c;
+ char *s,*t;
+ const struct arc_operand_value *suf,*suffix,*suffix_end;
+
+ if (!(operand->flags & ARC_OPERAND_SUFFIX))
+ abort ();
+
+ /* If we're at a space in the input string, we want to skip the
+ remaining suffixes. There may be some fake ones though, so
+ just go on to try the next one. */
+ if (*str == ' ')
+ {
+ ++syn;
+ continue;
+ }
+
+ s = str;
+ if (mods & ARC_MOD_DOT)
+ {
+ if (*s != '.')
+ break;
+ ++s;
+ }
+ else
+ {
+ /* This can happen in "b.nd foo" and we're currently looking
+ for "%q" (ie: a condition code suffix). */
+ if (*s == '.')
+ {
+ ++syn;
+ continue;
+ }
+ }
+
+ /* Pick the suffix out and look it up via the hash table. */
+ for (t = s; *t && isalpha (*t); ++t)
+ continue;
+ c = *t;
+ *t = '\0';
+ suf = hash_find (arc_suffix_hash, s);
+ *t = c;
+ if (!suf)
+ {
+ /* This can happen in "blle foo" and we're currently using
+ the template "b%q%.n %j". The "bl" insn occurs later in
+ the table so "lle" isn't an illegal suffix. */
+ break;
+ }
+
+ /* Is it the right type? Note that the same character is used
+ several times, so we have to examine all of them. This is
+ relatively efficient as equivalent entries are kept
+ together. If it's not the right type, don't increment `str'
+ so we try the next one in the series. */
+ found = 0;
+ suffix_end = arc_suffixes + arc_suffixes_count;
+ for (suffix = suf;
+ suffix < suffix_end && strcmp (suffix->name, suf->name) == 0;
+ ++suffix)
+ {
+ if (arc_operands[suffix->type].fmt == *syn)
+ {
+ /* Insert the suffix's value into the insn. */
+ if (operand->insert)
+ insn = (*operand->insert) (insn, operand,
+ mods, NULL, suffix->value,
+ NULL);
+ else
+ insn |= suffix->value << operand->shift;
+
+ str = t;
+ found = 1;
+ break;
+ }
+ }
+ ++syn;
+ if (!found)
+ ; /* Wrong type. Just go on to try next insn entry. */
+ else
+ {
+ if (num_suffixes == MAX_SUFFIXES)
+ as_bad (_("too many suffixes"));
+ else
+ insn_suffixes[num_suffixes++] = suffix;
+ }
+ }
+ else
+ /* This is either a register or an expression of some kind. */
+ {
+ char c;
+ char *hold;
+ const struct arc_operand_value *reg = NULL;
+ long value = 0;
+ expressionS exp;
+
+ if (operand->flags & ARC_OPERAND_SUFFIX)
+ abort ();
+
+ /* Is there anything left to parse?
+ We don't check for this at the top because we want to parse
+ any trailing fake arguments in the syntax string. */
+ if (*str == '\0')
+ break;
+#if 0
+ /* Is this a syntax character? Eg: is there a '[' present when
+ there shouldn't be? */
+ if (!isalnum (*str)
+ /* '.' as in ".LLC0" */
+ && *str != '.'
+ /* '_' as in "_print" */
+ && *str != '_'
+ /* '-' as in "[fp,-4]" */
+ && *str != '-'
+ /* '%' as in "%ia(_func)" */
+ && *str != '%')
+ break;
+#endif
+
+ /* Parse the operand. */
+ hold = input_line_pointer;
+ input_line_pointer = str;
+ expression (&exp);
+ str = input_line_pointer;
+ input_line_pointer = hold;
+
+ if (exp.X_op == O_illegal)
+ as_bad (_("illegal operand"));
+ else if (exp.X_op == O_absent)
+ as_bad (_("missing operand"));
+ else if (exp.X_op == O_constant)
+ {
+ value = exp.X_add_number;
+ }
+ else if (exp.X_op == O_register)
+ {
+ reg = arc_reg_names + exp.X_add_number;
+ }
+ else
+ {
+ /* We need to generate a fixup for this expression. */
+ if (fc >= MAX_FIXUPS)
+ as_fatal (_("too many fixups"));
+ fixups[fc].exp = exp;
+
+ /* If this is a register constant (IE: one whose
+ register value gets stored as 61-63) then this
+ must be a limm. We don't support shimm relocs. */
+ /* ??? This bit could use some cleaning up.
+ Referencing the format chars like this goes
+ against style. */
+#define IS_REG_OPERAND(o) ((o) == 'a' || (o) == 'b' || (o) == 'c')
+ if (IS_REG_OPERAND (*syn))
+ {
+ const char *junk;
+
+ fixups[fc].opindex = arc_operand_map['L'];
+ limm_reloc_p = 1;
+ /* Tell insert_reg we need a limm. This is
+ needed because the value at this point is
+ zero, a shimm. */
+ /* ??? We need a cleaner interface than this. */
+ (*arc_operands[arc_operand_map['Q']].insert)
+ (insn, operand, mods, reg, 0L, &junk);
+ }
+ else
+ fixups[fc].opindex = arc_operand_map[*syn];
+ ++fc;
+ value = 0;
+ }
+
+ /* Insert the register or expression into the instruction. */
+ if (operand->insert)
+ {
+ const char *errmsg = NULL;
+ insn = (*operand->insert) (insn, operand, mods,
+ reg, (long) value, &errmsg);
+#if 0
+ if (errmsg != (const char *) NULL)
+ as_warn (errmsg);
+#endif
+ /* FIXME: We want to try shimm insns for limm ones. But if
+ the constant won't fit, we must go on to try the next
+ possibility. Where do we issue warnings for constants
+ that are too big then? At present, we'll flag the insn
+ as unrecognizable! Maybe have the "bad instruction"
+ error message include our `errmsg'? */
+ if (errmsg != (const char *) NULL)
+ break;
+ }
+ else
+ insn |= (value & ((1 << operand->bits) - 1)) << operand->shift;
+
+ ++syn;
+ }
+ }
+
+ /* If we're at the end of the syntax string, we're done. */
+ /* FIXME: try to move this to a separate function. */
+ if (*syn == '\0')
+ {
+ int i;
+ char *f;
+ long limm, limm_p;
+
+ /* For the moment we assume a valid `str' can only contain blanks
+ now. IE: We needn't try again with a longer version of the
+ insn and it is assumed that longer versions of insns appear
+ before shorter ones (eg: lsr r2,r3,1 vs lsr r2,r3). */
+
+ while (isspace (*str))
+ ++str;
+
+ if (*str != '\0')
+ as_bad (_("junk at end of line: `%s'"), str);
+
+ /* Is there a limm value? */
+ limm_p = arc_opcode_limm_p (&limm);
+
+ /* Perform various error and warning tests. */
+
+ {
+ static int in_delay_slot_p = 0;
+ static int prev_insn_needs_cc_nop_p = 0;
+ /* delay slot type seen */
+ int delay_slot_type = ARC_DELAY_NONE;
+ /* conditional execution flag seen */
+ int conditional = 0;
+ /* 1 if condition codes are being set */
+ int cc_set_p = 0;
+ /* 1 if conditional branch, including `b' "branch always" */
+ int cond_branch_p = opcode->flags & ARC_OPCODE_COND_BRANCH;
+ int need_cc_nop_p = 0;
+
+ for (i = 0; i < num_suffixes; ++i)
+ {
+ switch (arc_operands[insn_suffixes[i]->type].fmt)
+ {
+ case 'n' :
+ delay_slot_type = insn_suffixes[i]->value;
+ break;
+ case 'q' :
+ conditional = insn_suffixes[i]->value;
+ break;
+ case 'f' :
+ cc_set_p = 1;
+ break;
+ }
+ }
+
+ /* Putting an insn with a limm value in a delay slot is supposed to
+ be legal, but let's warn the user anyway. Ditto for 8 byte
+ jumps with delay slots. */
+ if (in_delay_slot_p && limm_p)
+ as_warn (_("8 byte instruction in delay slot"));
+ if (delay_slot_type != ARC_DELAY_NONE && limm_p)
+ as_warn (_("8 byte jump instruction with delay slot"));
+ in_delay_slot_p = (delay_slot_type != ARC_DELAY_NONE) && !limm_p;
+
+ /* Warn when a conditional branch immediately follows a set of
+ the condition codes. Note that this needn't be done if the
+ insn that sets the condition codes uses a limm. */
+ if (cond_branch_p && conditional != 0 /* 0 = "always" */
+ && prev_insn_needs_cc_nop_p)
+ as_warn (_("conditional branch follows set of flags"));
+ prev_insn_needs_cc_nop_p = cc_set_p && !limm_p;
+ }
+
+ /* Write out the instruction.
+ It is important to fetch enough space in one call to `frag_more'.
+ We use (f - frag_now->fr_literal) to compute where we are and we
+ don't want frag_now to change between calls. */
+ if (limm_p)
+ {
+ f = frag_more (8);
+ md_number_to_chars (f, insn, 4);
+ md_number_to_chars (f + 4, limm, 4);
+ }
+ else if (limm_reloc_p)
+ {
+ /* We need a limm reloc, but the tables think we don't. */
+ abort ();
+ }
+ else
+ {
+ f = frag_more (4);
+ md_number_to_chars (f, insn, 4);
+ }
+
+ /* Create any fixups. */
+ for (i = 0; i < fc; ++i)
+ {
+ int op_type, reloc_type;
+ expressionS exptmp;
+ const struct arc_operand *operand;
+
+ /* Create a fixup for this operand.
+ At this point we do not use a bfd_reloc_code_real_type for
+ operands residing in the insn, but instead just use the
+ operand index. This lets us easily handle fixups for any
+ operand type, although that is admittedly not a very exciting
+ feature. We pick a BFD reloc type in md_apply_fix.
+
+ Limm values (4 byte immediate "constants") must be treated
+ normally because they're not part of the actual insn word
+ and thus the insertion routines don't handle them. */
+
+ if (arc_operands[fixups[i].opindex].flags & ARC_OPERAND_LIMM)
+ {
+ op_type = fixups[i].opindex;
+ /* FIXME: can we add this data to the operand table? */
+ if (op_type == arc_operand_map['L'])
+ reloc_type = BFD_RELOC_32;
+ else if (op_type == arc_operand_map['J'])
+ reloc_type = BFD_RELOC_ARC_B26;
+ else
+ abort ();
+ reloc_type = get_arc_exp_reloc_type (1, reloc_type,
+ &fixups[i].exp,
+ &exptmp);
+ }
+ else
+ {
+ op_type = get_arc_exp_reloc_type (0, fixups[i].opindex,
+ &fixups[i].exp, &exptmp);
+ reloc_type = op_type + (int) BFD_RELOC_UNUSED;
+ }
+ operand = &arc_operands[op_type];
+ fix_new_exp (frag_now,
+ ((f - frag_now->fr_literal)
+ + (operand->flags & ARC_OPERAND_LIMM ? 4 : 0)), 4,
+ &exptmp,
+ (operand->flags & ARC_OPERAND_RELATIVE_BRANCH) != 0,
+ (bfd_reloc_code_real_type) reloc_type);
+ }
+
+ /* All done. */
+ return;
+ }
+
+ /* Try the next entry. */
+ }
+
+ as_bad (_("bad instruction `%s'"), start);
+}
+
+/* ??? This was copied from tc-sparc.c, I think. Is it necessary? */
+
+static void
+arc_common (ignore)
+ int ignore;
+{
+ char *name;
+ char c;
+ char *p;
+ int temp, size;
+ symbolS *symbolP;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ /* just after name is now '\0' */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after symbol-name"));
+ ignore_rest_of_line ();
+ return;
+ }
+ input_line_pointer++; /* skip ',' */
+ if ((temp = get_absolute_expression ()) < 0)
+ {
+ as_bad (_(".COMMon length (%d.) <0! Ignored."), temp);
+ ignore_rest_of_line ();
+ return;
+ }
+ size = temp;
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+ if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
+ {
+ as_bad (_("ignoring attempt to re-define symbol"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (S_GET_VALUE (symbolP) != 0)
+ {
+ if (S_GET_VALUE (symbolP) != size)
+ {
+ as_warn (_("Length of .comm \"%s\" is already %ld. Not changed to %d."),
+ S_GET_NAME (symbolP), (long) S_GET_VALUE (symbolP), size);
+ }
+ }
+ assert (symbolP->sy_frag == &zero_address_frag);
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after common length"));
+ ignore_rest_of_line ();
+ return;
+ }
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != '"')
+ {
+ temp = get_absolute_expression ();
+ if (temp < 0)
+ {
+ temp = 0;
+ as_warn (_("Common alignment negative; 0 assumed"));
+ }
+ if (symbolP->local)
+ {
+ segT old_sec;
+ int old_subsec;
+ char *p;
+ int align;
+
+ allocate_bss:
+ old_sec = now_seg;
+ old_subsec = now_subseg;
+ align = temp;
+ record_alignment (bss_section, align);
+ subseg_set (bss_section, 0);
+ if (align)
+ frag_align (align, 0, 0);
+ if (S_GET_SEGMENT (symbolP) == bss_section)
+ symbolP->sy_frag->fr_symbol = 0;
+ symbolP->sy_frag = frag_now;
+ p = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
+ (offsetT) size, (char *) 0);
+ *p = 0;
+ S_SET_SEGMENT (symbolP, bss_section);
+ S_CLEAR_EXTERNAL (symbolP);
+ subseg_set (old_sec, old_subsec);
+ }
+ else
+ {
+ allocate_common:
+ S_SET_VALUE (symbolP, (valueT) size);
+ S_SET_ALIGN (symbolP, temp);
+ S_SET_EXTERNAL (symbolP);
+ S_SET_SEGMENT (symbolP, bfd_com_section_ptr);
+ }
+ }
+ else
+ {
+ input_line_pointer++;
+ /* ??? Some say data, some say bss. */
+ if (strncmp (input_line_pointer, ".bss\"", 5)
+ && strncmp (input_line_pointer, ".data\"", 6))
+ {
+ input_line_pointer--;
+ goto bad_common_segment;
+ }
+ while (*input_line_pointer++ != '"')
+ ;
+ goto allocate_common;
+ }
+ demand_empty_rest_of_line ();
+ return;
+
+ {
+ bad_common_segment:
+ p = input_line_pointer;
+ while (*p && *p != '\n')
+ p++;
+ c = *p;
+ *p = '\0';
+ as_bad (_("bad .common segment %s"), input_line_pointer + 1);
+ *p = c;
+ input_line_pointer = p;
+ ignore_rest_of_line ();
+ return;
+ }
+}
+
+/* Select the cpu we're assembling for. */
+
+static void
+arc_cpu (ignore)
+ int ignore;
+{
+ int mach;
+ char c;
+ char *cpu;
+
+ /* If an instruction has already been seen, it's too late. */
+ if (cpu_tables_init_p)
+ {
+ as_bad (_(".cpu command must appear before any instructions"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ cpu = input_line_pointer;
+ c = get_symbol_end ();
+ mach = arc_get_mach (cpu);
+ *input_line_pointer = c;
+ if (mach == -1)
+ goto bad_cpu;
+
+ demand_empty_rest_of_line ();
+
+ /* The cpu may have been selected on the command line.
+ The choices must match. */
+ /* ??? This was a command line option early on. It's gone now, but
+ leave this in. */
+ if (mach_type_specified_p && mach != arc_mach_type)
+ as_bad (_(".cpu conflicts with previous value"));
+ else
+ {
+ arc_mach_type = mach;
+ mach_type_specified_p = 1;
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach))
+ as_warn (_("could not set architecture and machine"));
+ }
+ return;
+
+ bad_cpu:
+ as_bad (_("bad .cpu op"));
+ ignore_rest_of_line ();
+}
+
+#if 0
+/* The .rename pseudo-op. This is used by gcc to implement
+ -mmangle-cpu-libgcc. */
+
+static void
+arc_rename (ignore)
+ int ignore;
+{
+ char *name,*new;
+ char c;
+ symbolS *sym;
+ int len;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ sym = symbol_find_or_make (name);
+ *input_line_pointer = c;
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing rename string"));
+ ignore_rest_of_line ();
+ return;
+ }
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ if (*name == '\0')
+ {
+ *input_line_pointer = c;
+ as_bad (_("invalid symbol to rename to"));
+ ignore_rest_of_line ();
+ return;
+ }
+ new = (char *) xmalloc (strlen (name) + 1);
+ strcpy (new, name);
+ *input_line_pointer = c;
+ sym->sy_tc.real_name = new;
+
+ demand_empty_rest_of_line ();
+}
+#endif
+
+/* Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP.
+ An error message is returned, or NULL on OK. */
+
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+char *
+md_atof (type, litP, sizeP)
+ char type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+ char *atof_ieee ();
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ prec = 4;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("bad call to md_atof");
+ }
+
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+ *sizeP = prec * sizeof (LITTLENUM_TYPE);
+ for (wordP = words; prec--;)
+ {
+ md_number_to_chars (litP, (valueT) (*wordP++), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+
+ return NULL;
+}
+
+/* Write a value out to the object file, using the appropriate
+ endianness. */
+
+void
+md_number_to_chars (buf, val, n)
+ char *buf;
+ valueT val;
+ int n;
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+/* Round up a section size to the appropriate boundary. */
+
+valueT
+md_section_align (segment, size)
+ segT segment;
+ valueT size;
+{
+ int align = bfd_get_section_alignment (stdoutput, segment);
+
+ return ((size + (1 << align) - 1) & (-1 << align));
+}
+
+/* We don't have any form of relaxing. */
+
+int
+md_estimate_size_before_relax (fragp, seg)
+ fragS *fragp;
+ asection *seg;
+{
+ abort ();
+}
+
+/* Convert a machine dependent frag. We never generate these. */
+
+void
+md_convert_frag (abfd, sec, fragp)
+ bfd *abfd;
+ asection *sec;
+ fragS *fragp;
+{
+ abort ();
+}
+
+/* Parse an operand that is machine-specific.
+
+ The ARC has a special %-op to adjust addresses so they're usable in
+ branches. The "st" is short for the STatus register.
+ ??? Later expand this to take a flags value too.
+
+ ??? We can't create new expression types so we map the %-op's onto the
+ existing syntax. This means that the user could use the chosen syntax
+ to achieve the same effect. Perhaps put a special cookie in X_add_number
+ to mark the expression as special. */
+
+void
+md_operand (expressionP)
+ expressionS *expressionP;
+{
+ char *p = input_line_pointer;
+
+ if (*p == '%' && strncmp (p, "%st(", 4) == 0)
+ {
+ input_line_pointer += 4;
+ expression (expressionP);
+ if (*input_line_pointer != ')')
+ {
+ as_bad (_("missing ')' in %-op"));
+ return;
+ }
+ ++input_line_pointer;
+ if (expressionP->X_op == O_symbol
+ && expressionP->X_add_number == 0
+ /* I think this test is unnecessary but just as a sanity check... */
+ && expressionP->X_op_symbol == NULL)
+ {
+ expressionS two;
+
+ expressionP->X_op = O_right_shift;
+ two.X_op = O_constant;
+ two.X_add_symbol = two.X_op_symbol = NULL;
+ two.X_add_number = 2;
+ expressionP->X_op_symbol = make_expr_symbol (&two);
+ }
+ /* allow %st(sym1-sym2) */
+ else if (expressionP->X_op == O_subtract
+ && expressionP->X_add_symbol != NULL
+ && expressionP->X_op_symbol != NULL
+ && expressionP->X_add_number == 0)
+ {
+ expressionS two;
+
+ expressionP->X_add_symbol = make_expr_symbol (expressionP);
+ expressionP->X_op = O_right_shift;
+ two.X_op = O_constant;
+ two.X_add_symbol = two.X_op_symbol = NULL;
+ two.X_add_number = 2;
+ expressionP->X_op_symbol = make_expr_symbol (&two);
+ }
+ else
+ {
+ as_bad (_("expression too complex for %%st"));
+ return;
+ }
+ }
+}
+
+/* We have no need to default values of symbols.
+ We could catch register names here, but that is handled by inserting
+ them all in the symbol table to begin with. */
+
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ return 0;
+}
+
+/* Functions concerning expressions. */
+
+/* Parse a .byte, .word, etc. expression.
+
+ Values for the status register are specified with %st(label).
+ `label' will be right shifted by 2. */
+
+void
+arc_parse_cons_expression (exp, nbytes)
+ expressionS *exp;
+ int nbytes;
+{
+ expr (0, exp);
+}
+
+/* Record a fixup for a cons expression. */
+
+void
+arc_cons_fix_new (frag, where, nbytes, exp)
+ fragS *frag;
+ int where;
+ int nbytes;
+ expressionS *exp;
+{
+ if (nbytes == 4)
+ {
+ int reloc_type;
+ expressionS exptmp;
+
+ /* This may be a special ARC reloc (eg: %st()). */
+ reloc_type = get_arc_exp_reloc_type (1, BFD_RELOC_32, exp, &exptmp);
+ fix_new_exp (frag, where, nbytes, &exptmp, 0, reloc_type);
+ }
+ else
+ {
+ fix_new_exp (frag, where, nbytes, exp, 0,
+ nbytes == 2 ? BFD_RELOC_16
+ : nbytes == 8 ? BFD_RELOC_64
+ : BFD_RELOC_32);
+ }
+}
+
+/* Functions concerning relocs. */
+
+/* The location from which a PC relative jump should be calculated,
+ given a PC relative reloc. */
+
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ if (fixP->fx_addsy != (symbolS *) NULL
+ && ! S_IS_DEFINED (fixP->fx_addsy))
+ {
+ /* The symbol is undefined. Let the linker figure it out. */
+ return 0;
+ }
+
+ /* Return the address of the delay slot. */
+ return fixP->fx_frag->fr_address + fixP->fx_where + fixP->fx_size;
+}
+
+/* Compute the reloc type of an expression.
+ The possibly modified expression is stored in EXPNEW.
+
+ This is used to convert the expressions generated by the %-op's into
+ the appropriate operand type. It is called for both data in instructions
+ (operands) and data outside instructions (variables, debugging info, etc.).
+
+ Currently supported %-ops:
+
+ %st(symbol): represented as "symbol >> 2"
+ "st" is short for STatus as in the status register (pc)
+
+ DEFAULT_TYPE is the type to use if no special processing is required.
+
+ DATA_P is non-zero for data or limm values, zero for insn operands.
+ Remember that the opcode "insertion fns" cannot be used on data, they're
+ only for inserting operands into insns. They also can't be used for limm
+ values as the insertion routines don't handle limm values. When called for
+ insns we return fudged reloc types (real_value - BFD_RELOC_UNUSED). When
+ called for data or limm values we use real reloc types. */
+
+static int
+get_arc_exp_reloc_type (data_p, default_type, exp, expnew)
+ int data_p;
+ int default_type;
+ expressionS *exp;
+ expressionS *expnew;
+{
+ /* If the expression is "symbol >> 2" we must change it to just "symbol",
+ as fix_new_exp can't handle it. Similarily for (symbol - symbol) >> 2.
+ That's ok though. What's really going on here is that we're using
+ ">> 2" as a special syntax for specifying BFD_RELOC_ARC_B26. */
+
+ if (exp->X_op == O_right_shift
+ && exp->X_op_symbol != NULL
+ && exp->X_op_symbol->sy_value.X_op == O_constant
+ && exp->X_op_symbol->sy_value.X_add_number == 2
+ && exp->X_add_number == 0)
+ {
+ if (exp->X_add_symbol != NULL
+ && (exp->X_add_symbol->sy_value.X_op == O_constant
+ || exp->X_add_symbol->sy_value.X_op == O_symbol))
+ {
+ *expnew = *exp;
+ expnew->X_op = O_symbol;
+ expnew->X_op_symbol = NULL;
+ return data_p ? BFD_RELOC_ARC_B26 : arc_operand_map['J'];
+ }
+ else if (exp->X_add_symbol != NULL
+ && exp->X_add_symbol->sy_value.X_op == O_subtract)
+ {
+ *expnew = exp->X_add_symbol->sy_value;
+ return data_p ? BFD_RELOC_ARC_B26 : arc_operand_map['J'];
+ }
+ }
+
+ *expnew = *exp;
+ return default_type;
+}
+
+/* Apply a fixup to the object code. This is called for all the
+ fixups we generated by the call to fix_new_exp, above. In the call
+ above we used a reloc code which was the largest legal reloc code
+ plus the operand index. Here we undo that to recover the operand
+ index. At this point all symbol values should be fully resolved,
+ and we attempt to completely resolve the reloc. If we can not do
+ that, we determine the correct reloc code and put it back in the fixup. */
+
+int
+md_apply_fix3 (fixP, valueP, seg)
+ fixS *fixP;
+ valueT *valueP;
+ segT seg;
+{
+ /*char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;*/
+ valueT value;
+
+ /* FIXME FIXME FIXME: The value we are passed in *valueP includes
+ the symbol values. Since we are using BFD_ASSEMBLER, if we are
+ doing this relocation the code in write.c is going to call
+ bfd_perform_relocation, which is also going to use the symbol
+ value. That means that if the reloc is fully resolved we want to
+ use *valueP since bfd_perform_relocation is not being used.
+ However, if the reloc is not fully resolved we do not want to use
+ *valueP, and must use fx_offset instead. However, if the reloc
+ is PC relative, we do want to use *valueP since it includes the
+ result of md_pcrel_from. This is confusing. */
+
+ if (fixP->fx_addsy == (symbolS *) NULL)
+ {
+ value = *valueP;
+ fixP->fx_done = 1;
+ }
+ else if (fixP->fx_pcrel)
+ {
+ value = *valueP;
+ /* ELF relocations are against symbols.
+ If this symbol is in a different section then we need to leave it for
+ the linker to deal with. Unfortunately, md_pcrel_from can't tell,
+ so we have to undo it's effects here. */
+ if (S_IS_DEFINED (fixP->fx_addsy)
+ && S_GET_SEGMENT (fixP->fx_addsy) != seg)
+ value += md_pcrel_from (fixP);
+ }
+ else
+ {
+ value = fixP->fx_offset;
+ if (fixP->fx_subsy != (symbolS *) NULL)
+ {
+ if (S_GET_SEGMENT (fixP->fx_subsy) == absolute_section)
+ value -= S_GET_VALUE (fixP->fx_subsy);
+ else
+ {
+ /* We can't actually support subtracting a symbol. */
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("expression too complex"));
+ }
+ }
+ }
+
+ if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
+ {
+ int opindex;
+ const struct arc_operand *operand;
+ char *where;
+ arc_insn insn;
+
+ opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
+
+ operand = &arc_operands[opindex];
+
+ /* Fetch the instruction, insert the fully resolved operand
+ value, and stuff the instruction back again. */
+ where = fixP->fx_frag->fr_literal + fixP->fx_where;
+ if (target_big_endian)
+ insn = bfd_getb32 ((unsigned char *) where);
+ else
+ insn = bfd_getl32 ((unsigned char *) where);
+ insn = arc_insert_operand (insn, operand, -1, NULL, (offsetT) value,
+ fixP->fx_file, fixP->fx_line);
+ if (target_big_endian)
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ else
+ bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
+
+ if (fixP->fx_done)
+ {
+ /* Nothing else to do here. */
+ return 1;
+ }
+
+ /* Determine a BFD reloc value based on the operand information.
+ We are only prepared to turn a few of the operands into relocs.
+ !!! Note that we can't handle limm values here. Since we're using
+ implicit addends the addend must be inserted into the instruction,
+ however, the opcode insertion routines currently do nothing with
+ limm values. */
+ if (operand->fmt == 'B')
+ {
+ assert ((operand->flags & ARC_OPERAND_RELATIVE_BRANCH) != 0
+ && operand->bits == 20
+ && operand->shift == 7);
+ fixP->fx_r_type = BFD_RELOC_ARC_B22_PCREL;
+ }
+ else if (0 && operand->fmt == 'J')
+ {
+ assert ((operand->flags & ARC_OPERAND_ABSOLUTE_BRANCH) != 0
+ && operand->bits == 24
+ && operand->shift == 32);
+ fixP->fx_r_type = BFD_RELOC_ARC_B26;
+ }
+ else if (0 && operand->fmt == 'L')
+ {
+ assert ((operand->flags & ARC_OPERAND_LIMM) != 0
+ && operand->bits == 32
+ && operand->shift == 32);
+ fixP->fx_r_type = BFD_RELOC_32;
+ }
+ else
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unresolved expression that must be resolved"));
+ fixP->fx_done = 1;
+ return 1;
+ }
+ }
+ else
+ {
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ value, 1);
+ break;
+ case BFD_RELOC_16:
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ value, 2);
+ break;
+ case BFD_RELOC_32:
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ value, 4);
+ break;
+#if 0
+ case BFD_RELOC_64:
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ value, 8);
+ break;
+#endif
+ case BFD_RELOC_ARC_B26:
+ /* If !fixP->fx_done then `value' is an implicit addend.
+ We must shift it right by 2 in this case as well because the
+ linker performs the relocation and then adds this in (as opposed
+ to adding this in and then shifting right by 2). */
+ value >>= 2;
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ value, 4);
+ break;
+ default:
+ abort ();
+ }
+ }
+
+ fixP->fx_addnumber = value;
+
+ return 1;
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format. */
+
+arelent *
+tc_gen_reloc (section, fixP)
+ asection *section;
+ fixS *fixP;
+{
+ arelent *reloc;
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+
+ reloc->sym_ptr_ptr = &fixP->fx_addsy->bsym;
+ reloc->address = fixP->fx_frag->fr_address + fixP->fx_where;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("internal error: can't export reloc type %d (`%s')"),
+ fixP->fx_r_type, bfd_get_reloc_code_name (fixP->fx_r_type));
+ return NULL;
+ }
+
+ assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
+
+ reloc->addend = fixP->fx_addnumber;
+
+ return reloc;
+}
+
+/* Frobbers. */
+
+#if 0
+/* Set the real name if the .rename pseudo-op was used.
+ Return 1 if the symbol should not be included in the symbol table. */
+
+int
+arc_frob_symbol (sym)
+ symbolS *sym;
+{
+ if (sym->sy_tc.real_name != (char *) NULL)
+ S_SET_NAME (sym, sym->sy_tc.real_name);
+
+ return 0;
+}
+#endif
diff --git a/gas/config/tc-arc.h b/gas/config/tc-arc.h
new file mode 100644
index 0000000..6a95ff4
--- /dev/null
+++ b/gas/config/tc-arc.h
@@ -0,0 +1,71 @@
+/* tc-arc.h - Macros and type defines for the ARC.
+ Copyright (C) 1994, 1995, 1997 Free Software Foundation, Inc.
+ Contributed by Doug Evans (dje@cygnus.com).
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#define TC_ARC 1
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define LOCAL_LABELS_FB 1
+
+#define TARGET_ARCH bfd_arch_arc
+
+#define LITTLE_ENDIAN 1234
+#define BIG_ENDIAN 4321
+
+/* The endianness of the target format may change based on command
+ line arguments. */
+extern const char *arc_target_format;
+#define DEFAULT_TARGET_FORMAT "elf32-littlearc"
+#define TARGET_FORMAT arc_target_format
+#define DEFAULT_BYTE_ORDER LITTLE_ENDIAN
+
+#define WORKING_DOT_WORD
+
+#define LISTING_HEADER "ARC GAS "
+
+#define TC_HANDLES_FX_DONE
+
+#define MD_APPLY_FIX3
+
+/* The ARC needs to parse reloc specifiers in .word. */
+
+extern void arc_parse_cons_expression ();
+#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) \
+arc_parse_cons_expression (EXP, NBYTES)
+
+extern void arc_cons_fix_new ();
+#define TC_CONS_FIX_NEW(FRAG, WHERE, NBYTES, EXP) \
+arc_cons_fix_new (FRAG, WHERE, NBYTES, EXP)
+
+#if 0
+/* Extra stuff that we need to keep track of for each symbol. */
+struct arc_tc_sy
+{
+ /* The real name, if the symbol was renamed. */
+ char *real_name;
+};
+
+#define TC_SYMFIELD_TYPE struct arc_tc_sy
+
+/* Finish up the symbol. */
+extern int arc_frob_symbol PARAMS ((struct symbol *));
+#define tc_frob_symbol(sym, punt) punt = arc_frob_symbol (sym)
+#endif
diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c
new file mode 100644
index 0000000..d898bf0
--- /dev/null
+++ b/gas/config/tc-arm.c
@@ -0,0 +1,6857 @@
+/* tc-arm.c -- Assemble for the ARM
+ Copyright (C) 1994, 95, 96, 97, 98, 1999 Free Software Foundation, Inc.
+ Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
+ Modified by David Taylor (dtaylor@armltd.co.uk)
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#include <ctype.h>
+#include <string.h>
+#define NO_RELOC 0
+#include "as.h"
+
+/* need TARGET_CPU */
+#include "config.h"
+#include "subsegs.h"
+#include "obstack.h"
+#include "symbols.h"
+#include "listing.h"
+
+#ifdef OBJ_ELF
+#include "elf/arm.h"
+#endif
+
+/* Types of processor to assemble for. */
+#define ARM_1 0x00000001
+#define ARM_2 0x00000002
+#define ARM_3 0x00000004
+#define ARM_250 ARM_3
+#define ARM_6 0x00000008
+#define ARM_7 ARM_6 /* same core instruction set */
+#define ARM_8 ARM_6 /* same core instruction set */
+#define ARM_9 ARM_6 /* same core instruction set */
+#define ARM_CPU_MASK 0x0000000f
+
+/* The following bitmasks control CPU extensions (ARM7 onwards): */
+#define ARM_LONGMUL 0x00000010 /* allow long multiplies */
+#define ARM_HALFWORD 0x00000020 /* allow half word loads */
+#define ARM_THUMB 0x00000040 /* allow BX instruction */
+
+#define ARM_ARCHv4 (ARM_7 | ARM_LONGMUL | ARM_HALFWORD)
+
+/* Some useful combinations: */
+#define ARM_ANY 0x00ffffff
+#define ARM_2UP 0x00fffffe
+#define ARM_ALL ARM_2UP /* Not arm1 only */
+#define ARM_3UP 0x00fffffc
+#define ARM_6UP 0x00fffff8 /* Includes ARM7 */
+
+#define FPU_CORE 0x80000000
+#define FPU_FPA10 0x40000000
+#define FPU_FPA11 0x40000000
+#define FPU_NONE 0
+
+/* Some useful combinations */
+#define FPU_ALL 0xff000000 /* Note this is ~ARM_ANY */
+#define FPU_MEMMULTI 0x7f000000 /* Not fpu_core */
+
+
+#ifndef CPU_DEFAULT
+#if defined __thumb__
+#define CPU_DEFAULT (ARM_ARCHv4 | ARM_THUMB)
+#else
+#define CPU_DEFAULT ARM_ALL
+#endif
+#endif
+
+#ifndef FPU_DEFAULT
+#define FPU_DEFAULT FPU_ALL
+#endif
+
+#define streq(a,b) (strcmp (a, b) == 0)
+
+static unsigned long cpu_variant = CPU_DEFAULT | FPU_DEFAULT;
+static int target_oabi = 0;
+
+#if defined OBJ_COFF || defined OBJ_ELF
+/* Flags stored in private area of BFD structure */
+static boolean uses_apcs_26 = false;
+static boolean support_interwork = false;
+static boolean uses_apcs_float = false;
+static boolean pic_code = false;
+#endif
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful */
+CONST char comment_chars[] = "@";
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that comments like this one will always work. */
+CONST char line_comment_chars[] = "#";
+
+#ifdef TE_LINUX
+CONST char line_separator_chars[] = ";";
+#else
+CONST char line_separator_chars[] = "";
+#endif
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+CONST char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+
+CONST char FLT_CHARS[] = "rRsSfFdDxXeEpP";
+
+/* Prefix characters that indicate the start of an immediate
+ value. */
+#define is_immediate_prefix(C) ((C) == '#' || (C) == '$')
+
+#ifdef OBJ_ELF
+symbolS * GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
+#endif
+
+CONST int md_reloc_size = 8; /* Size of relocation record */
+
+static int thumb_mode = 0; /* non-zero if assembling thumb instructions */
+
+typedef struct arm_fix
+{
+ int thumb_mode;
+} arm_fix_data;
+
+struct arm_it
+{
+ CONST char * error;
+ unsigned long instruction;
+ int suffix;
+ int size;
+ struct
+ {
+ bfd_reloc_code_real_type type;
+ expressionS exp;
+ int pc_rel;
+ } reloc;
+};
+
+struct arm_it inst;
+
+struct asm_shift
+{
+ CONST char * template;
+ unsigned long value;
+};
+
+static CONST struct asm_shift shift[] =
+{
+ {"asl", 0},
+ {"lsl", 0},
+ {"lsr", 0x00000020},
+ {"asr", 0x00000040},
+ {"ror", 0x00000060},
+ {"rrx", 0x00000060},
+ {"ASL", 0},
+ {"LSL", 0},
+ {"LSR", 0x00000020},
+ {"ASR", 0x00000040},
+ {"ROR", 0x00000060},
+ {"RRX", 0x00000060}
+};
+
+#define NO_SHIFT_RESTRICT 1
+#define SHIFT_RESTRICT 0
+
+#define NUM_FLOAT_VALS 8
+
+CONST char * fp_const[] =
+{
+ "0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "0.5", "10.0", 0
+};
+
+/* Number of littlenums required to hold an extended precision number */
+#define MAX_LITTLENUMS 6
+
+LITTLENUM_TYPE fp_values[NUM_FLOAT_VALS][MAX_LITTLENUMS];
+
+#define FAIL (-1)
+#define SUCCESS (0)
+
+#define SUFF_S 1
+#define SUFF_D 2
+#define SUFF_E 3
+#define SUFF_P 4
+
+#define CP_T_X 0x00008000
+#define CP_T_Y 0x00400000
+#define CP_T_Pre 0x01000000
+#define CP_T_UD 0x00800000
+#define CP_T_WB 0x00200000
+
+#define CONDS_BIT (0x00100000)
+#define LOAD_BIT (0x00100000)
+#define TRANS_BIT (0x00200000)
+
+struct asm_cond
+{
+ CONST char * template;
+ unsigned long value;
+};
+
+/* This is to save a hash look-up in the common case */
+#define COND_ALWAYS 0xe0000000
+
+static CONST struct asm_cond conds[] =
+{
+ {"eq", 0x00000000},
+ {"ne", 0x10000000},
+ {"cs", 0x20000000}, {"hs", 0x20000000},
+ {"cc", 0x30000000}, {"ul", 0x30000000}, {"lo", 0x30000000},
+ {"mi", 0x40000000},
+ {"pl", 0x50000000},
+ {"vs", 0x60000000},
+ {"vc", 0x70000000},
+ {"hi", 0x80000000},
+ {"ls", 0x90000000},
+ {"ge", 0xa0000000},
+ {"lt", 0xb0000000},
+ {"gt", 0xc0000000},
+ {"le", 0xd0000000},
+ {"al", 0xe0000000},
+ {"nv", 0xf0000000}
+};
+
+/* Warning: If the top bit of the set_bits is set, then the standard
+ instruction bitmask is ignored, and the new bitmask is taken from
+ the set_bits: */
+struct asm_flg
+{
+ CONST char * template; /* Basic flag string */
+ unsigned long set_bits; /* Bits to set */
+};
+
+static CONST struct asm_flg s_flag[] =
+{
+ {"s", CONDS_BIT},
+ {NULL, 0}
+};
+
+static CONST struct asm_flg ldr_flags[] =
+{
+ {"b", 0x00400000},
+ {"t", TRANS_BIT},
+ {"bt", 0x00400000 | TRANS_BIT},
+ {"h", 0x801000b0},
+ {"sh", 0x801000f0},
+ {"sb", 0x801000d0},
+ {NULL, 0}
+};
+
+static CONST struct asm_flg str_flags[] =
+{
+ {"b", 0x00400000},
+ {"t", TRANS_BIT},
+ {"bt", 0x00400000 | TRANS_BIT},
+ {"h", 0x800000b0},
+ {NULL, 0}
+};
+
+static CONST struct asm_flg byte_flag[] =
+{
+ {"b", 0x00400000},
+ {NULL, 0}
+};
+
+static CONST struct asm_flg cmp_flags[] =
+{
+ {"s", CONDS_BIT},
+ {"p", 0x0010f000},
+ {NULL, 0}
+};
+
+static CONST struct asm_flg ldm_flags[] =
+{
+ {"ed", 0x01800000},
+ {"fd", 0x00800000},
+ {"ea", 0x01000000},
+ {"fa", 0x08000000},
+ {"ib", 0x01800000},
+ {"ia", 0x00800000},
+ {"db", 0x01000000},
+ {"da", 0x08000000},
+ {NULL, 0}
+};
+
+static CONST struct asm_flg stm_flags[] =
+{
+ {"ed", 0x08000000},
+ {"fd", 0x01000000},
+ {"ea", 0x00800000},
+ {"fa", 0x01800000},
+ {"ib", 0x01800000},
+ {"ia", 0x00800000},
+ {"db", 0x01000000},
+ {"da", 0x08000000},
+ {NULL, 0}
+};
+
+static CONST struct asm_flg lfm_flags[] =
+{
+ {"fd", 0x00800000},
+ {"ea", 0x01000000},
+ {NULL, 0}
+};
+
+static CONST struct asm_flg sfm_flags[] =
+{
+ {"fd", 0x01000000},
+ {"ea", 0x00800000},
+ {NULL, 0}
+};
+
+static CONST struct asm_flg round_flags[] =
+{
+ {"p", 0x00000020},
+ {"m", 0x00000040},
+ {"z", 0x00000060},
+ {NULL, 0}
+};
+
+/* The implementation of the FIX instruction is broken on some assemblers,
+ in that it accepts a precision specifier as well as a rounding specifier,
+ despite the fact that this is meaningless. To be more compatible, we
+ accept it as well, though of course it does not set any bits. */
+static CONST struct asm_flg fix_flags[] =
+{
+ {"p", 0x00000020},
+ {"m", 0x00000040},
+ {"z", 0x00000060},
+ {"sp", 0x00000020},
+ {"sm", 0x00000040},
+ {"sz", 0x00000060},
+ {"dp", 0x00000020},
+ {"dm", 0x00000040},
+ {"dz", 0x00000060},
+ {"ep", 0x00000020},
+ {"em", 0x00000040},
+ {"ez", 0x00000060},
+ {NULL, 0}
+};
+
+static CONST struct asm_flg except_flag[] =
+{
+ {"e", 0x00400000},
+ {NULL, 0}
+};
+
+static CONST struct asm_flg cplong_flag[] =
+{
+ {"l", 0x00400000},
+ {NULL, 0}
+};
+
+struct asm_psr
+{
+ CONST char * template;
+ unsigned long number;
+};
+
+#define PSR_FIELD_MASK 0x000f0000
+
+#define PSR_FLAGS 0x00080000
+#define PSR_CONTROL 0x00010000 /* Undocumented instruction, its use is discouraged by ARM */
+#define PSR_ALL 0x00090000
+
+#define CPSR_ALL 0
+#define SPSR_ALL 1
+#define CPSR_FLG 2
+#define SPSR_FLG 3
+#define CPSR_CTL 4
+#define SPSR_CTL 5
+
+static CONST struct asm_psr psrs[] =
+{
+ /* Valid <psr>'s */
+ {"cpsr", CPSR_ALL},
+ {"cpsr_all", CPSR_ALL},
+ {"spsr", SPSR_ALL},
+ {"spsr_all", SPSR_ALL},
+
+ /* Valid <psrf>'s */
+ {"cpsr_flg", CPSR_FLG},
+ {"spsr_flg", SPSR_FLG},
+
+ /* Valid <psrc>'s */
+ {"cpsr_c", CPSR_CTL},
+ {"cpsr_ctl", CPSR_CTL},
+ {"spsr_c", SPSR_CTL},
+ {"spsr_ctl", SPSR_CTL}
+};
+
+/* Functions called by parser */
+/* ARM instructions */
+static void do_arit PARAMS ((char *operands, unsigned long flags));
+static void do_cmp PARAMS ((char *operands, unsigned long flags));
+static void do_mov PARAMS ((char *operands, unsigned long flags));
+static void do_ldst PARAMS ((char *operands, unsigned long flags));
+static void do_ldmstm PARAMS ((char *operands, unsigned long flags));
+static void do_branch PARAMS ((char *operands, unsigned long flags));
+static void do_swi PARAMS ((char *operands, unsigned long flags));
+/* Pseudo Op codes */
+static void do_adr PARAMS ((char *operands, unsigned long flags));
+static void do_nop PARAMS ((char *operands, unsigned long flags));
+/* ARM 2 */
+static void do_mul PARAMS ((char *operands, unsigned long flags));
+static void do_mla PARAMS ((char *operands, unsigned long flags));
+/* ARM 3 */
+static void do_swap PARAMS ((char *operands, unsigned long flags));
+/* ARM 6 */
+static void do_msr PARAMS ((char *operands, unsigned long flags));
+static void do_mrs PARAMS ((char *operands, unsigned long flags));
+/* ARM 7M */
+static void do_mull PARAMS ((char *operands, unsigned long flags));
+/* ARM THUMB */
+static void do_bx PARAMS ((char *operands, unsigned long flags));
+
+/* Coprocessor Instructions */
+static void do_cdp PARAMS ((char *operands, unsigned long flags));
+static void do_lstc PARAMS ((char *operands, unsigned long flags));
+static void do_co_reg PARAMS ((char *operands, unsigned long flags));
+static void do_fp_ctrl PARAMS ((char *operands, unsigned long flags));
+static void do_fp_ldst PARAMS ((char *operands, unsigned long flags));
+static void do_fp_ldmstm PARAMS ((char *operands, unsigned long flags));
+static void do_fp_dyadic PARAMS ((char *operands, unsigned long flags));
+static void do_fp_monadic PARAMS ((char *operands, unsigned long flags));
+static void do_fp_cmp PARAMS ((char *operands, unsigned long flags));
+static void do_fp_from_reg PARAMS ((char *operands, unsigned long flags));
+static void do_fp_to_reg PARAMS ((char *operands, unsigned long flags));
+
+static void fix_new_arm PARAMS ((fragS *frag, int where,
+ short int size, expressionS *exp,
+ int pc_rel, int reloc));
+static int arm_reg_parse PARAMS ((char **ccp));
+static int arm_psr_parse PARAMS ((char **ccp));
+static void symbol_locate PARAMS ((symbolS *, CONST char *, segT,
+ valueT, fragS *));
+static int add_to_lit_pool PARAMS ((void));
+static unsigned validate_immediate PARAMS ((unsigned));
+static int validate_offset_imm PARAMS ((int, int));
+static void opcode_select PARAMS ((int));
+static void end_of_line PARAMS ((char *));
+static int reg_required_here PARAMS ((char **, int));
+static int psr_required_here PARAMS ((char **, int, int));
+static int co_proc_number PARAMS ((char **));
+static int cp_opc_expr PARAMS ((char **, int, int));
+static int cp_reg_required_here PARAMS ((char **, int));
+static int fp_reg_required_here PARAMS ((char **, int));
+static int cp_address_offset PARAMS ((char **));
+static int cp_address_required_here PARAMS ((char **));
+static int my_get_float_expression PARAMS ((char **));
+static int skip_past_comma PARAMS ((char **));
+static int walk_no_bignums PARAMS ((symbolS *));
+static int negate_data_op PARAMS ((unsigned long *,
+ unsigned long));
+static int data_op2 PARAMS ((char **));
+static int fp_op2 PARAMS ((char **));
+static long reg_list PARAMS ((char **));
+static void thumb_load_store PARAMS ((char *, int, int));
+static int decode_shift PARAMS ((char **, int));
+static int ldst_extend PARAMS ((char **, int));
+static void thumb_add_sub PARAMS ((char *, int));
+static void insert_reg PARAMS ((int));
+static void thumb_shift PARAMS ((char *, int));
+static void thumb_mov_compare PARAMS ((char *, int));
+static void set_constant_flonums PARAMS ((void));
+static valueT md_chars_to_number PARAMS ((char *, int));
+static void insert_reg_alias PARAMS ((char *, int));
+static void output_inst PARAMS ((char *));
+#ifdef OBJ_ELF
+static bfd_reloc_code_real_type arm_parse_reloc(void);
+#endif
+
+/* ARM instructions take 4bytes in the object file, Thumb instructions
+ take 2: */
+#define INSN_SIZE 4
+
+/* LONGEST_INST is the longest basic instruction name without conditions or
+ * flags.
+ * ARM7M has 4 of length 5
+ */
+
+#define LONGEST_INST 5
+
+struct asm_opcode
+{
+ CONST char * template; /* Basic string to match */
+ unsigned long value; /* Basic instruction code */
+ CONST char * comp_suffix; /* Compulsory suffix that must follow conds */
+ CONST struct asm_flg * flags; /* Bits to toggle if flag 'n' set */
+ unsigned long variants; /* Which CPU variants this exists for */
+ /* Function to call to parse args */
+ void (* parms) PARAMS ((char *, unsigned long));
+};
+
+static CONST struct asm_opcode insns[] =
+{
+/* ARM Instructions */
+ {"and", 0x00000000, NULL, s_flag, ARM_ANY, do_arit},
+ {"eor", 0x00200000, NULL, s_flag, ARM_ANY, do_arit},
+ {"sub", 0x00400000, NULL, s_flag, ARM_ANY, do_arit},
+ {"rsb", 0x00600000, NULL, s_flag, ARM_ANY, do_arit},
+ {"add", 0x00800000, NULL, s_flag, ARM_ANY, do_arit},
+ {"adc", 0x00a00000, NULL, s_flag, ARM_ANY, do_arit},
+ {"sbc", 0x00c00000, NULL, s_flag, ARM_ANY, do_arit},
+ {"rsc", 0x00e00000, NULL, s_flag, ARM_ANY, do_arit},
+ {"orr", 0x01800000, NULL, s_flag, ARM_ANY, do_arit},
+ {"bic", 0x01c00000, NULL, s_flag, ARM_ANY, do_arit},
+ {"tst", 0x01000000, NULL, cmp_flags, ARM_ANY, do_cmp},
+ {"teq", 0x01200000, NULL, cmp_flags, ARM_ANY, do_cmp},
+ {"cmp", 0x01400000, NULL, cmp_flags, ARM_ANY, do_cmp},
+ {"cmn", 0x01600000, NULL, cmp_flags, ARM_ANY, do_cmp},
+ {"mov", 0x01a00000, NULL, s_flag, ARM_ANY, do_mov},
+ {"mvn", 0x01e00000, NULL, s_flag, ARM_ANY, do_mov},
+ {"str", 0x04000000, NULL, str_flags, ARM_ANY, do_ldst},
+ {"ldr", 0x04100000, NULL, ldr_flags, ARM_ANY, do_ldst},
+ {"stm", 0x08000000, NULL, stm_flags, ARM_ANY, do_ldmstm},
+ {"ldm", 0x08100000, NULL, ldm_flags, ARM_ANY, do_ldmstm},
+ {"swi", 0x0f000000, NULL, NULL, ARM_ANY, do_swi},
+ {"bl", 0x0bfffffe, NULL, NULL, ARM_ANY, do_branch},
+ {"b", 0x0afffffe, NULL, NULL, ARM_ANY, do_branch},
+
+/* Pseudo ops */
+ {"adr", 0x028f0000, NULL, NULL, ARM_ANY, do_adr},
+ {"nop", 0x01a00000, NULL, NULL, ARM_ANY, do_nop},
+
+/* ARM 2 multiplies */
+ {"mul", 0x00000090, NULL, s_flag, ARM_2UP, do_mul},
+ {"mla", 0x00200090, NULL, s_flag, ARM_2UP, do_mla},
+
+/* ARM 3 - swp instructions */
+ {"swp", 0x01000090, NULL, byte_flag, ARM_3UP, do_swap},
+
+/* ARM 6 Coprocessor instructions */
+ {"mrs", 0x010f0000, NULL, NULL, ARM_6UP, do_mrs},
+ {"msr", 0x0120f000, NULL, NULL, ARM_6UP, do_msr},
+/* ScottB: our code uses 0x0128f000 for msr.
+ NickC: but this is wrong because the bits 16 and 19 are handled
+ by the PSR_xxx defines above. */
+
+/* ARM 7M long multiplies - need signed/unsigned flags! */
+ {"smull", 0x00c00090, NULL, s_flag, ARM_LONGMUL, do_mull},
+ {"umull", 0x00800090, NULL, s_flag, ARM_LONGMUL, do_mull},
+ {"smlal", 0x00e00090, NULL, s_flag, ARM_LONGMUL, do_mull},
+ {"umlal", 0x00a00090, NULL, s_flag, ARM_LONGMUL, do_mull},
+
+/* ARM THUMB interworking */
+ {"bx", 0x012fff10, NULL, NULL, ARM_THUMB, do_bx},
+
+/* Floating point instructions */
+ {"wfs", 0x0e200110, NULL, NULL, FPU_ALL, do_fp_ctrl},
+ {"rfs", 0x0e300110, NULL, NULL, FPU_ALL, do_fp_ctrl},
+ {"wfc", 0x0e400110, NULL, NULL, FPU_ALL, do_fp_ctrl},
+ {"rfc", 0x0e500110, NULL, NULL, FPU_ALL, do_fp_ctrl},
+ {"ldf", 0x0c100100, "sdep", NULL, FPU_ALL, do_fp_ldst},
+ {"stf", 0x0c000100, "sdep", NULL, FPU_ALL, do_fp_ldst},
+ {"lfm", 0x0c100200, NULL, lfm_flags, FPU_MEMMULTI, do_fp_ldmstm},
+ {"sfm", 0x0c000200, NULL, sfm_flags, FPU_MEMMULTI, do_fp_ldmstm},
+ {"mvf", 0x0e008100, "sde", round_flags, FPU_ALL, do_fp_monadic},
+ {"mnf", 0x0e108100, "sde", round_flags, FPU_ALL, do_fp_monadic},
+ {"abs", 0x0e208100, "sde", round_flags, FPU_ALL, do_fp_monadic},
+ {"rnd", 0x0e308100, "sde", round_flags, FPU_ALL, do_fp_monadic},
+ {"sqt", 0x0e408100, "sde", round_flags, FPU_ALL, do_fp_monadic},
+ {"log", 0x0e508100, "sde", round_flags, FPU_ALL, do_fp_monadic},
+ {"lgn", 0x0e608100, "sde", round_flags, FPU_ALL, do_fp_monadic},
+ {"exp", 0x0e708100, "sde", round_flags, FPU_ALL, do_fp_monadic},
+ {"sin", 0x0e808100, "sde", round_flags, FPU_ALL, do_fp_monadic},
+ {"cos", 0x0e908100, "sde", round_flags, FPU_ALL, do_fp_monadic},
+ {"tan", 0x0ea08100, "sde", round_flags, FPU_ALL, do_fp_monadic},
+ {"asn", 0x0eb08100, "sde", round_flags, FPU_ALL, do_fp_monadic},
+ {"acs", 0x0ec08100, "sde", round_flags, FPU_ALL, do_fp_monadic},
+ {"atn", 0x0ed08100, "sde", round_flags, FPU_ALL, do_fp_monadic},
+ {"urd", 0x0ee08100, "sde", round_flags, FPU_ALL, do_fp_monadic},
+ {"nrm", 0x0ef08100, "sde", round_flags, FPU_ALL, do_fp_monadic},
+ {"adf", 0x0e000100, "sde", round_flags, FPU_ALL, do_fp_dyadic},
+ {"suf", 0x0e200100, "sde", round_flags, FPU_ALL, do_fp_dyadic},
+ {"rsf", 0x0e300100, "sde", round_flags, FPU_ALL, do_fp_dyadic},
+ {"muf", 0x0e100100, "sde", round_flags, FPU_ALL, do_fp_dyadic},
+ {"dvf", 0x0e400100, "sde", round_flags, FPU_ALL, do_fp_dyadic},
+ {"rdf", 0x0e500100, "sde", round_flags, FPU_ALL, do_fp_dyadic},
+ {"pow", 0x0e600100, "sde", round_flags, FPU_ALL, do_fp_dyadic},
+ {"rpw", 0x0e700100, "sde", round_flags, FPU_ALL, do_fp_dyadic},
+ {"rmf", 0x0e800100, "sde", round_flags, FPU_ALL, do_fp_dyadic},
+ {"fml", 0x0e900100, "sde", round_flags, FPU_ALL, do_fp_dyadic},
+ {"fdv", 0x0ea00100, "sde", round_flags, FPU_ALL, do_fp_dyadic},
+ {"frd", 0x0eb00100, "sde", round_flags, FPU_ALL, do_fp_dyadic},
+ {"pol", 0x0ec00100, "sde", round_flags, FPU_ALL, do_fp_dyadic},
+ {"cmf", 0x0e90f110, NULL, except_flag, FPU_ALL, do_fp_cmp},
+ {"cnf", 0x0eb0f110, NULL, except_flag, FPU_ALL, do_fp_cmp},
+/* The FPA10 data sheet suggests that the 'E' of cmfe/cnfe should not
+ be an optional suffix, but part of the instruction. To be compatible,
+ we accept either. */
+ {"cmfe", 0x0ed0f110, NULL, NULL, FPU_ALL, do_fp_cmp},
+ {"cnfe", 0x0ef0f110, NULL, NULL, FPU_ALL, do_fp_cmp},
+ {"flt", 0x0e000110, "sde", round_flags, FPU_ALL, do_fp_from_reg},
+ {"fix", 0x0e100110, NULL, fix_flags, FPU_ALL, do_fp_to_reg},
+
+/* Generic copressor instructions */
+ {"cdp", 0x0e000000, NULL, NULL, ARM_2UP, do_cdp},
+ {"ldc", 0x0c100000, NULL, cplong_flag, ARM_2UP, do_lstc},
+ {"stc", 0x0c000000, NULL, cplong_flag, ARM_2UP, do_lstc},
+ {"mcr", 0x0e000010, NULL, NULL, ARM_2UP, do_co_reg},
+ {"mrc", 0x0e100010, NULL, NULL, ARM_2UP, do_co_reg},
+};
+
+/* defines for various bits that we will want to toggle */
+
+#define INST_IMMEDIATE 0x02000000
+#define OFFSET_REG 0x02000000
+#define HWOFFSET_IMM 0x00400000
+#define SHIFT_BY_REG 0x00000010
+#define PRE_INDEX 0x01000000
+#define INDEX_UP 0x00800000
+#define WRITE_BACK 0x00200000
+#define MULTI_SET_PSR 0x00400000
+
+#define LITERAL_MASK 0xf000f000
+#define COND_MASK 0xf0000000
+#define OPCODE_MASK 0xfe1fffff
+#define DATA_OP_SHIFT 21
+
+/* Codes to distinguish the arithmetic instructions */
+
+#define OPCODE_AND 0
+#define OPCODE_EOR 1
+#define OPCODE_SUB 2
+#define OPCODE_RSB 3
+#define OPCODE_ADD 4
+#define OPCODE_ADC 5
+#define OPCODE_SBC 6
+#define OPCODE_RSC 7
+#define OPCODE_TST 8
+#define OPCODE_TEQ 9
+#define OPCODE_CMP 10
+#define OPCODE_CMN 11
+#define OPCODE_ORR 12
+#define OPCODE_MOV 13
+#define OPCODE_BIC 14
+#define OPCODE_MVN 15
+
+static void do_t_nop PARAMS ((char *operands));
+static void do_t_arit PARAMS ((char *operands));
+static void do_t_add PARAMS ((char *operands));
+static void do_t_asr PARAMS ((char *operands));
+static void do_t_branch9 PARAMS ((char *operands));
+static void do_t_branch12 PARAMS ((char *operands));
+static void do_t_branch23 PARAMS ((char *operands));
+static void do_t_bx PARAMS ((char *operands));
+static void do_t_compare PARAMS ((char *operands));
+static void do_t_ldmstm PARAMS ((char *operands));
+static void do_t_ldr PARAMS ((char *operands));
+static void do_t_ldrb PARAMS ((char *operands));
+static void do_t_ldrh PARAMS ((char *operands));
+static void do_t_lds PARAMS ((char *operands));
+static void do_t_lsl PARAMS ((char *operands));
+static void do_t_lsr PARAMS ((char *operands));
+static void do_t_mov PARAMS ((char *operands));
+static void do_t_push_pop PARAMS ((char *operands));
+static void do_t_str PARAMS ((char *operands));
+static void do_t_strb PARAMS ((char *operands));
+static void do_t_strh PARAMS ((char *operands));
+static void do_t_sub PARAMS ((char *operands));
+static void do_t_swi PARAMS ((char *operands));
+static void do_t_adr PARAMS ((char *operands));
+
+#define T_OPCODE_MUL 0x4340
+#define T_OPCODE_TST 0x4200
+#define T_OPCODE_CMN 0x42c0
+#define T_OPCODE_NEG 0x4240
+#define T_OPCODE_MVN 0x43c0
+
+#define T_OPCODE_ADD_R3 0x1800
+#define T_OPCODE_SUB_R3 0x1a00
+#define T_OPCODE_ADD_HI 0x4400
+#define T_OPCODE_ADD_ST 0xb000
+#define T_OPCODE_SUB_ST 0xb080
+#define T_OPCODE_ADD_SP 0xa800
+#define T_OPCODE_ADD_PC 0xa000
+#define T_OPCODE_ADD_I8 0x3000
+#define T_OPCODE_SUB_I8 0x3800
+#define T_OPCODE_ADD_I3 0x1c00
+#define T_OPCODE_SUB_I3 0x1e00
+
+#define T_OPCODE_ASR_R 0x4100
+#define T_OPCODE_LSL_R 0x4080
+#define T_OPCODE_LSR_R 0x40c0
+#define T_OPCODE_ASR_I 0x1000
+#define T_OPCODE_LSL_I 0x0000
+#define T_OPCODE_LSR_I 0x0800
+
+#define T_OPCODE_MOV_I8 0x2000
+#define T_OPCODE_CMP_I8 0x2800
+#define T_OPCODE_CMP_LR 0x4280
+#define T_OPCODE_MOV_HR 0x4600
+#define T_OPCODE_CMP_HR 0x4500
+
+#define T_OPCODE_LDR_PC 0x4800
+#define T_OPCODE_LDR_SP 0x9800
+#define T_OPCODE_STR_SP 0x9000
+#define T_OPCODE_LDR_IW 0x6800
+#define T_OPCODE_STR_IW 0x6000
+#define T_OPCODE_LDR_IH 0x8800
+#define T_OPCODE_STR_IH 0x8000
+#define T_OPCODE_LDR_IB 0x7800
+#define T_OPCODE_STR_IB 0x7000
+#define T_OPCODE_LDR_RW 0x5800
+#define T_OPCODE_STR_RW 0x5000
+#define T_OPCODE_LDR_RH 0x5a00
+#define T_OPCODE_STR_RH 0x5200
+#define T_OPCODE_LDR_RB 0x5c00
+#define T_OPCODE_STR_RB 0x5400
+
+#define T_OPCODE_PUSH 0xb400
+#define T_OPCODE_POP 0xbc00
+
+#define T_OPCODE_BRANCH 0xe7fe
+
+static int thumb_reg PARAMS ((char ** str, int hi_lo));
+
+#define THUMB_SIZE 2 /* Size of thumb instruction */
+#define THUMB_REG_LO 0x1
+#define THUMB_REG_HI 0x2
+#define THUMB_REG_ANY 0x3
+
+#define THUMB_H1 0x0080
+#define THUMB_H2 0x0040
+
+#define THUMB_ASR 0
+#define THUMB_LSL 1
+#define THUMB_LSR 2
+
+#define THUMB_MOVE 0
+#define THUMB_COMPARE 1
+
+#define THUMB_LOAD 0
+#define THUMB_STORE 1
+
+#define THUMB_PP_PC_LR 0x0100
+
+/* These three are used for immediate shifts, do not alter */
+#define THUMB_WORD 2
+#define THUMB_HALFWORD 1
+#define THUMB_BYTE 0
+
+struct thumb_opcode
+{
+ CONST char * template; /* Basic string to match */
+ unsigned long value; /* Basic instruction code */
+ int size;
+ void (* parms) PARAMS ((char *)); /* Function to call to parse args */
+};
+
+static CONST struct thumb_opcode tinsns[] =
+{
+ {"adc", 0x4140, 2, do_t_arit},
+ {"add", 0x0000, 2, do_t_add},
+ {"and", 0x4000, 2, do_t_arit},
+ {"asr", 0x0000, 2, do_t_asr},
+ {"b", T_OPCODE_BRANCH, 2, do_t_branch12},
+ {"beq", 0xd0fe, 2, do_t_branch9},
+ {"bne", 0xd1fe, 2, do_t_branch9},
+ {"bcs", 0xd2fe, 2, do_t_branch9},
+ {"bhs", 0xd2fe, 2, do_t_branch9},
+ {"bcc", 0xd3fe, 2, do_t_branch9},
+ {"bul", 0xd3fe, 2, do_t_branch9},
+ {"blo", 0xd3fe, 2, do_t_branch9},
+ {"bmi", 0xd4fe, 2, do_t_branch9},
+ {"bpl", 0xd5fe, 2, do_t_branch9},
+ {"bvs", 0xd6fe, 2, do_t_branch9},
+ {"bvc", 0xd7fe, 2, do_t_branch9},
+ {"bhi", 0xd8fe, 2, do_t_branch9},
+ {"bls", 0xd9fe, 2, do_t_branch9},
+ {"bge", 0xdafe, 2, do_t_branch9},
+ {"blt", 0xdbfe, 2, do_t_branch9},
+ {"bgt", 0xdcfe, 2, do_t_branch9},
+ {"ble", 0xddfe, 2, do_t_branch9},
+ {"bic", 0x4380, 2, do_t_arit},
+ {"bl", 0xf7fffffe, 4, do_t_branch23},
+ {"bx", 0x4700, 2, do_t_bx},
+ {"cmn", T_OPCODE_CMN, 2, do_t_arit},
+ {"cmp", 0x0000, 2, do_t_compare},
+ {"eor", 0x4040, 2, do_t_arit},
+ {"ldmia", 0xc800, 2, do_t_ldmstm},
+ {"ldr", 0x0000, 2, do_t_ldr},
+ {"ldrb", 0x0000, 2, do_t_ldrb},
+ {"ldrh", 0x0000, 2, do_t_ldrh},
+ {"ldrsb", 0x5600, 2, do_t_lds},
+ {"ldrsh", 0x5e00, 2, do_t_lds},
+ {"ldsb", 0x5600, 2, do_t_lds},
+ {"ldsh", 0x5e00, 2, do_t_lds},
+ {"lsl", 0x0000, 2, do_t_lsl},
+ {"lsr", 0x0000, 2, do_t_lsr},
+ {"mov", 0x0000, 2, do_t_mov},
+ {"mul", T_OPCODE_MUL, 2, do_t_arit},
+ {"mvn", T_OPCODE_MVN, 2, do_t_arit},
+ {"neg", T_OPCODE_NEG, 2, do_t_arit},
+ {"orr", 0x4300, 2, do_t_arit},
+ {"pop", 0xbc00, 2, do_t_push_pop},
+ {"push", 0xb400, 2, do_t_push_pop},
+ {"ror", 0x41c0, 2, do_t_arit},
+ {"sbc", 0x4180, 2, do_t_arit},
+ {"stmia", 0xc000, 2, do_t_ldmstm},
+ {"str", 0x0000, 2, do_t_str},
+ {"strb", 0x0000, 2, do_t_strb},
+ {"strh", 0x0000, 2, do_t_strh},
+ {"swi", 0xdf00, 2, do_t_swi},
+ {"sub", 0x0000, 2, do_t_sub},
+ {"tst", T_OPCODE_TST, 2, do_t_arit},
+ /* Pseudo ops: */
+ {"adr", 0x0000, 2, do_t_adr},
+ {"nop", 0x46C0, 2, do_t_nop}, /* mov r8,r8 */
+};
+
+struct reg_entry
+{
+ CONST char * name;
+ int number;
+};
+
+#define int_register(reg) ((reg) >= 0 && (reg) <= 15)
+#define cp_register(reg) ((reg) >= 32 && (reg) <= 47)
+#define fp_register(reg) ((reg) >= 16 && (reg) <= 23)
+
+#define REG_PC 15
+#define REG_LR 14
+#define REG_SP 13
+
+/* These are the standard names; Users can add aliases with .req */
+static CONST struct reg_entry reg_table[] =
+{
+ /* Processor Register Numbers */
+ {"r0", 0}, {"r1", 1}, {"r2", 2}, {"r3", 3},
+ {"r4", 4}, {"r5", 5}, {"r6", 6}, {"r7", 7},
+ {"r8", 8}, {"r9", 9}, {"r10", 10}, {"r11", 11},
+ {"r12", 12}, {"r13", REG_SP},{"r14", REG_LR},{"r15", REG_PC},
+ /* APCS conventions */
+ {"a1", 0}, {"a2", 1}, {"a3", 2}, {"a4", 3},
+ {"v1", 4}, {"v2", 5}, {"v3", 6}, {"v4", 7}, {"v5", 8},
+ {"v6", 9}, {"sb", 9}, {"v7", 10}, {"sl", 10},
+ {"fp", 11}, {"ip", 12}, {"sp", REG_SP},{"lr", REG_LR},{"pc", REG_PC},
+ /* FP Registers */
+ {"f0", 16}, {"f1", 17}, {"f2", 18}, {"f3", 19},
+ {"f4", 20}, {"f5", 21}, {"f6", 22}, {"f7", 23},
+ {"c0", 32}, {"c1", 33}, {"c2", 34}, {"c3", 35},
+ {"c4", 36}, {"c5", 37}, {"c6", 38}, {"c7", 39},
+ {"c8", 40}, {"c9", 41}, {"c10", 42}, {"c11", 43},
+ {"c12", 44}, {"c13", 45}, {"c14", 46}, {"c15", 47},
+ {"cr0", 32}, {"cr1", 33}, {"cr2", 34}, {"cr3", 35},
+ {"cr4", 36}, {"cr5", 37}, {"cr6", 38}, {"cr7", 39},
+ {"cr8", 40}, {"cr9", 41}, {"cr10", 42}, {"cr11", 43},
+ {"cr12", 44}, {"cr13", 45}, {"cr14", 46}, {"cr15", 47},
+ {NULL, 0}
+};
+
+#define bad_args _("Bad arguments to instruction");
+#define bad_pc _("r15 not allowed here");
+
+static struct hash_control * arm_ops_hsh = NULL;
+static struct hash_control * arm_tops_hsh = NULL;
+static struct hash_control * arm_cond_hsh = NULL;
+static struct hash_control * arm_shift_hsh = NULL;
+static struct hash_control * arm_reg_hsh = NULL;
+static struct hash_control * arm_psr_hsh = NULL;
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ Integer arg to pass to the function
+ */
+
+static void s_req PARAMS ((int));
+static void s_align PARAMS ((int));
+static void s_bss PARAMS ((int));
+static void s_even PARAMS ((int));
+static void s_ltorg PARAMS ((int));
+static void s_arm PARAMS ((int));
+static void s_thumb PARAMS ((int));
+static void s_code PARAMS ((int));
+static void s_force_thumb PARAMS ((int));
+static void s_thumb_func PARAMS ((int));
+#ifdef OBJ_ELF
+static void s_arm_elf_cons PARAMS ((int));
+#endif
+
+static int my_get_expression PARAMS ((expressionS *, char **));
+
+CONST pseudo_typeS md_pseudo_table[] =
+{
+ {"req", s_req, 0}, /* Never called becasue '.req' does not start line */
+ {"bss", s_bss, 0},
+ {"align", s_align, 0},
+ {"arm", s_arm, 0},
+ {"thumb", s_thumb, 0},
+ {"code", s_code, 0},
+ {"force_thumb", s_force_thumb, 0},
+ {"thumb_func", s_thumb_func, 0},
+ {"even", s_even, 0},
+ {"ltorg", s_ltorg, 0},
+ {"pool", s_ltorg, 0},
+#ifdef OBJ_ELF
+ {"word", s_arm_elf_cons, 4},
+ {"long", s_arm_elf_cons, 4},
+#else
+ {"word", cons, 4},
+#endif
+ {"extend", float_cons, 'x'},
+ {"ldouble", float_cons, 'x'},
+ {"packed", float_cons, 'p'},
+ {0, 0, 0}
+};
+
+/* Stuff needed to resolve the label ambiguity
+ As:
+ ...
+ label: <insn>
+ may differ from:
+ ...
+ label:
+ <insn>
+*/
+
+symbolS * last_label_seen;
+static int label_is_thumb_function_name = false;
+
+/* Literal stuff */
+
+#define MAX_LITERAL_POOL_SIZE 1024
+
+typedef struct literalS
+{
+ struct expressionS exp;
+ struct arm_it * inst;
+} literalT;
+
+literalT literals[MAX_LITERAL_POOL_SIZE];
+int next_literal_pool_place = 0; /* Next free entry in the pool */
+int lit_pool_num = 1; /* Next literal pool number */
+symbolS * current_poolP = NULL;
+symbolS * symbol_make_empty PARAMS ((void));
+
+static int
+add_to_lit_pool ()
+{
+ int lit_count = 0;
+
+ if (current_poolP == NULL)
+ current_poolP = symbol_make_empty ();
+
+ /* Check if this literal value is already in the pool: */
+ while (lit_count < next_literal_pool_place)
+ {
+ if (literals[lit_count].exp.X_op == inst.reloc.exp.X_op
+ && inst.reloc.exp.X_op == O_constant
+ && literals[lit_count].exp.X_add_number == inst.reloc.exp.X_add_number
+ && literals[lit_count].exp.X_unsigned == inst.reloc.exp.X_unsigned)
+ break;
+ lit_count++;
+ }
+
+ if (lit_count == next_literal_pool_place) /* new entry */
+ {
+ if (next_literal_pool_place > MAX_LITERAL_POOL_SIZE)
+ {
+ inst.error = _("Literal Pool Overflow");
+ return FAIL;
+ }
+
+ literals[next_literal_pool_place].exp = inst.reloc.exp;
+ lit_count = next_literal_pool_place++;
+ }
+
+ inst.reloc.exp.X_op = O_symbol;
+ inst.reloc.exp.X_add_number = (lit_count) * 4 - 8;
+ inst.reloc.exp.X_add_symbol = current_poolP;
+
+ return SUCCESS;
+}
+
+/* Can't use symbol_new here, so have to create a symbol and then at
+ a later date assign it a value. Thats what these functions do. */
+static void
+symbol_locate (symbolP, name, segment, valu, frag)
+ symbolS * symbolP;
+ CONST char * name; /* It is copied, the caller can modify */
+ segT segment; /* Segment identifier (SEG_<something>) */
+ valueT valu; /* Symbol value */
+ fragS * frag; /* Associated fragment */
+{
+ unsigned int name_length;
+ char * preserved_copy_of_name;
+
+ name_length = strlen (name) + 1; /* +1 for \0 */
+ obstack_grow (&notes, name, name_length);
+ preserved_copy_of_name = obstack_finish (&notes);
+#ifdef STRIP_UNDERSCORE
+ if (preserved_copy_of_name[0] == '_')
+ preserved_copy_of_name++;
+#endif
+
+#ifdef tc_canonicalize_symbol_name
+ preserved_copy_of_name =
+ tc_canonicalize_symbol_name (preserved_copy_of_name);
+#endif
+
+ S_SET_NAME (symbolP, preserved_copy_of_name);
+
+ S_SET_SEGMENT (symbolP, segment);
+ S_SET_VALUE (symbolP, valu);
+ symbol_clear_list_pointers(symbolP);
+
+ symbolP->sy_frag = frag;
+
+ /* Link to end of symbol chain. */
+ {
+ extern int symbol_table_frozen;
+ if (symbol_table_frozen)
+ abort ();
+ }
+
+ symbol_append (symbolP, symbol_lastP, & symbol_rootP, & symbol_lastP);
+
+ obj_symbol_new_hook (symbolP);
+
+#ifdef tc_symbol_new_hook
+ tc_symbol_new_hook (symbolP);
+#endif
+
+#ifdef DEBUG_SYMS
+ verify_symbol_chain (symbol_rootP, symbol_lastP);
+#endif /* DEBUG_SYMS */
+}
+
+symbolS *
+symbol_make_empty ()
+{
+ symbolS * symbolP;
+
+ symbolP = (symbolS *) obstack_alloc (&notes, sizeof (symbolS));
+
+ /* symbol must be born in some fixed state. This seems as good as any. */
+ memset (symbolP, 0, sizeof (symbolS));
+
+ symbolP->bsym = bfd_make_empty_symbol (stdoutput);
+ assert (symbolP->bsym != 0);
+ symbolP->bsym->udata.p = (PTR) symbolP;
+
+ return symbolP;
+}
+
+/* Check that an immediate is valid, and if so, convert it to the right format. */
+
+static unsigned int
+validate_immediate (val)
+ unsigned int val;
+{
+ unsigned int a;
+ unsigned int i;
+
+#define rotate_left(v, n) (v << n | v >> (32 - n))
+
+ for (i = 0; i < 32; i += 2)
+ if ((a = rotate_left (val, i)) <= 0xff)
+ return a | (i << 7); /* 12-bit pack: [shift-cnt,const] */
+
+ return FAIL;
+}
+
+static int
+validate_offset_imm (val, hwse)
+ int val;
+ int hwse;
+{
+ if ((hwse && (val < -255 || val > 255))
+ || (val < -4095 || val > 4095))
+ return FAIL;
+ return val;
+}
+
+
+static void
+s_req (a)
+ int a;
+{
+ as_bad (_("Invalid syntax for .req directive."));
+}
+
+static void
+s_bss (ignore)
+ int ignore;
+{
+ /* We don't support putting frags in the BSS segment, we fake it by
+ marking in_bss, then looking at s_skip for clues?.. */
+ subseg_set (bss_section, 0);
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_even (ignore)
+ int ignore;
+{
+ if (!need_pass_2) /* Never make frag if expect extra pass. */
+ frag_align (1, 0, 0);
+
+ record_alignment (now_seg, 1);
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_ltorg (internal)
+ int internal;
+{
+ int lit_count = 0;
+ char sym_name[20];
+
+ if (current_poolP == NULL)
+ {
+ /* Nothing to do */
+ if (!internal)
+ as_tsktsk (_("Nothing to put in the pool\n"));
+ return;
+ }
+
+ /* Align pool as you have word accesses */
+ /* Only make a frag if we have to ... */
+ if (!need_pass_2)
+ frag_align (2, 0, 0);
+
+ record_alignment (now_seg, 2);
+
+ if (internal)
+ as_tsktsk (_("Inserting implicit pool at change of section"));
+
+ sprintf (sym_name, "$$lit_\002%x", lit_pool_num++);
+
+ symbol_locate (current_poolP, sym_name, now_seg,
+ (valueT) frag_now_fix (), frag_now);
+ symbol_table_insert (current_poolP);
+
+ ARM_SET_THUMB (current_poolP, thumb_mode);
+
+#if defined OBJ_COFF || defined OBJ_ELF
+ ARM_SET_INTERWORK (current_poolP, support_interwork);
+#endif
+
+ while (lit_count < next_literal_pool_place)
+ /* First output the expression in the instruction to the pool */
+ emit_expr (&(literals[lit_count++].exp), 4); /* .word */
+
+ next_literal_pool_place = 0;
+ current_poolP = NULL;
+}
+
+static void
+s_align (unused) /* Same as s_align_ptwo but align 0 => align 2 */
+ int unused;
+{
+ register int temp;
+ register long temp_fill;
+ long max_alignment = 15;
+
+ temp = get_absolute_expression ();
+ if (temp > max_alignment)
+ as_bad (_("Alignment too large: %d. assumed."), temp = max_alignment);
+ else if (temp < 0)
+ {
+ as_bad (_("Alignment negative. 0 assumed."));
+ temp = 0;
+ }
+
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ temp_fill = get_absolute_expression ();
+ }
+ else
+ temp_fill = 0;
+
+ if (!temp)
+ temp = 2;
+
+ /* Only make a frag if we HAVE to. . . */
+ if (temp && !need_pass_2)
+ frag_align (temp, (int) temp_fill, 0);
+ demand_empty_rest_of_line ();
+
+ record_alignment (now_seg, temp);
+}
+
+static void
+s_force_thumb (ignore)
+ int ignore;
+{
+ /* If we are not already in thumb mode go into it, EVEN if
+ the target processor does not support thumb instructions.
+ This is used by gcc/config/arm/lib1funcs.asm for example
+ to compile interworking support functions even if the
+ target processor should not support interworking. */
+
+ if (! thumb_mode)
+ {
+ thumb_mode = 1;
+
+ record_alignment (now_seg, 1);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_thumb_func (ignore)
+ int ignore;
+{
+ /* The following label is the name/address of the start of a Thumb function.
+ We need to know this for the interworking support. */
+
+ label_is_thumb_function_name = true;
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+opcode_select (width)
+ int width;
+{
+ switch (width)
+ {
+ case 16:
+ if (! thumb_mode)
+ {
+ if (! (cpu_variant & ARM_THUMB))
+ as_bad (_("selected processor does not support THUMB opcodes"));
+ thumb_mode = 1;
+ /* No need to force the alignment, since we will have been
+ coming from ARM mode, which is word-aligned. */
+ record_alignment (now_seg, 1);
+ }
+ break;
+
+ case 32:
+ if (thumb_mode)
+ {
+ if ((cpu_variant & ARM_ANY) == ARM_THUMB)
+ as_bad (_("selected processor does not support ARM opcodes"));
+ thumb_mode = 0;
+ if (!need_pass_2)
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 1);
+ }
+ break;
+
+ default:
+ as_bad (_("invalid instruction size selected (%d)"), width);
+ }
+}
+
+static void
+s_arm (ignore)
+ int ignore;
+{
+ opcode_select (32);
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_thumb (ignore)
+ int ignore;
+{
+ opcode_select (16);
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_code (unused)
+ int unused;
+{
+ register int temp;
+
+ temp = get_absolute_expression ();
+ switch (temp)
+ {
+ case 16:
+ case 32:
+ opcode_select (temp);
+ break;
+
+ default:
+ as_bad (_("invalid operand to .code directive (%d) (expecting 16 or 32)"), temp);
+ }
+}
+
+static void
+end_of_line (str)
+ char * str;
+{
+ while (*str == ' ')
+ str++;
+
+ if (*str != '\0')
+ inst.error = _("Garbage following instruction");
+}
+
+static int
+skip_past_comma (str)
+ char ** str;
+{
+ char *p = *str, c;
+ int comma = 0;
+
+ while ((c = *p) == ' ' || c == ',')
+ {
+ p++;
+ if (c == ',' && comma++)
+ return FAIL;
+ }
+
+ if (c == '\0')
+ return FAIL;
+
+ *str = p;
+ return comma ? SUCCESS : FAIL;
+}
+
+/* A standard register must be given at this point. Shift is the place to
+ put it in the instruction. */
+
+static int
+reg_required_here (str, shift)
+ char ** str;
+ int shift;
+{
+ static char buff [128]; /* XXX */
+ int reg;
+ char * start = *str;
+
+ if ((reg = arm_reg_parse (str)) != FAIL && int_register (reg))
+ {
+ if (shift >= 0)
+ inst.instruction |= reg << shift;
+ return reg;
+ }
+
+ /* Restore the start point, we may have got a reg of the wrong class. */
+ *str = start;
+
+ /* In the few cases where we might be able to accept something else
+ this error can be overridden */
+ sprintf (buff, _("Register expected, not '%.100s'"), start);
+ inst.error = buff;
+
+ return FAIL;
+}
+
+static int
+psr_required_here (str, cpsr, spsr)
+ char ** str;
+ int cpsr;
+ int spsr;
+{
+ int psr;
+ char * start = *str;
+ psr = arm_psr_parse (str);
+
+ if (psr == cpsr || psr == spsr)
+ {
+ if (psr == spsr)
+ inst.instruction |= 1 << 22;
+
+ return SUCCESS;
+ }
+
+ /* In the few cases where we might be able to accept something else
+ this error can be overridden */
+ inst.error = _("<psr(f)> expected");
+
+ /* Restore the start point. */
+ *str = start;
+ return FAIL;
+}
+
+static int
+co_proc_number (str)
+ char **str;
+{
+ int processor, pchar;
+
+ while (**str == ' ')
+ (*str)++;
+
+ /* The data sheet seems to imply that just a number on its own is valid
+ here, but the RISC iX assembler seems to accept a prefix 'p'. We will
+ accept either. */
+ if (**str == 'p' || **str == 'P')
+ (*str)++;
+
+ pchar = *(*str)++;
+ if (pchar >= '0' && pchar <= '9')
+ {
+ processor = pchar - '0';
+ if (**str >= '0' && **str <= '9')
+ {
+ processor = processor * 10 + *(*str)++ - '0';
+ if (processor > 15)
+ {
+ inst.error = _("Illegal co-processor number");
+ return FAIL;
+ }
+ }
+ }
+ else
+ {
+ inst.error = _("Bad or missing co-processor number");
+ return FAIL;
+ }
+
+ inst.instruction |= processor << 8;
+ return SUCCESS;
+}
+
+static int
+cp_opc_expr (str, where, length)
+ char ** str;
+ int where;
+ int length;
+{
+ expressionS expr;
+
+ while (**str == ' ')
+ (*str)++;
+
+ memset (&expr, '\0', sizeof (expr));
+
+ if (my_get_expression (&expr, str))
+ return FAIL;
+ if (expr.X_op != O_constant)
+ {
+ inst.error = _("bad or missing expression");
+ return FAIL;
+ }
+
+ if ((expr.X_add_number & ((1 << length) - 1)) != expr.X_add_number)
+ {
+ inst.error = _("immediate co-processor expression too large");
+ return FAIL;
+ }
+
+ inst.instruction |= expr.X_add_number << where;
+ return SUCCESS;
+}
+
+static int
+cp_reg_required_here (str, where)
+ char ** str;
+ int where;
+{
+ int reg;
+ char * start = *str;
+
+ if ((reg = arm_reg_parse (str)) != FAIL && cp_register (reg))
+ {
+ reg &= 15;
+ inst.instruction |= reg << where;
+ return reg;
+ }
+
+ /* In the few cases where we might be able to accept something else
+ this error can be overridden */
+ inst.error = _("Co-processor register expected");
+
+ /* Restore the start point */
+ *str = start;
+ return FAIL;
+}
+
+static int
+fp_reg_required_here (str, where)
+ char ** str;
+ int where;
+{
+ int reg;
+ char * start = *str;
+
+ if ((reg = arm_reg_parse (str)) != FAIL && fp_register (reg))
+ {
+ reg &= 7;
+ inst.instruction |= reg << where;
+ return reg;
+ }
+
+ /* In the few cases where we might be able to accept something else
+ this error can be overridden */
+ inst.error = _("Floating point register expected");
+
+ /* Restore the start point */
+ *str = start;
+ return FAIL;
+}
+
+static int
+cp_address_offset (str)
+ char ** str;
+{
+ int offset;
+
+ while (**str == ' ')
+ (*str)++;
+
+ if (! is_immediate_prefix (**str))
+ {
+ inst.error = _("immediate expression expected");
+ return FAIL;
+ }
+
+ (*str)++;
+
+ if (my_get_expression (& inst.reloc.exp, str))
+ return FAIL;
+
+ if (inst.reloc.exp.X_op == O_constant)
+ {
+ offset = inst.reloc.exp.X_add_number;
+
+ if (offset & 3)
+ {
+ inst.error = _("co-processor address must be word aligned");
+ return FAIL;
+ }
+
+ if (offset > 1023 || offset < -1023)
+ {
+ inst.error = _("offset too large");
+ return FAIL;
+ }
+
+ if (offset >= 0)
+ inst.instruction |= INDEX_UP;
+ else
+ offset = -offset;
+
+ inst.instruction |= offset >> 2;
+ }
+ else
+ inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
+
+ return SUCCESS;
+}
+
+static int
+cp_address_required_here (str)
+ char ** str;
+{
+ char * p = * str;
+ int pre_inc = 0;
+ int write_back = 0;
+
+ if (*p == '[')
+ {
+ int reg;
+
+ p++;
+ while (*p == ' ')
+ p++;
+
+ if ((reg = reg_required_here (& p, 16)) == FAIL)
+ return FAIL;
+
+ while (*p == ' ')
+ p++;
+
+ if (*p == ']')
+ {
+ p++;
+
+ if (skip_past_comma (& p) == SUCCESS)
+ {
+ /* [Rn], #expr */
+ write_back = WRITE_BACK;
+
+ if (reg == REG_PC)
+ {
+ inst.error = _("pc may not be used in post-increment");
+ return FAIL;
+ }
+
+ if (cp_address_offset (& p) == FAIL)
+ return FAIL;
+ }
+ else
+ pre_inc = PRE_INDEX | INDEX_UP;
+ }
+ else
+ {
+ /* '['Rn, #expr']'[!] */
+
+ if (skip_past_comma (& p) == FAIL)
+ {
+ inst.error = _("pre-indexed expression expected");
+ return FAIL;
+ }
+
+ pre_inc = PRE_INDEX;
+
+ if (cp_address_offset (& p) == FAIL)
+ return FAIL;
+
+ while (*p == ' ')
+ p++;
+
+ if (*p++ != ']')
+ {
+ inst.error = _("missing ]");
+ return FAIL;
+ }
+
+ while (*p == ' ')
+ p++;
+
+ if (*p == '!')
+ {
+ if (reg == REG_PC)
+ {
+ inst.error = _("pc may not be used with write-back");
+ return FAIL;
+ }
+
+ p++;
+ write_back = WRITE_BACK;
+ }
+ }
+ }
+ else
+ {
+ if (my_get_expression (&inst.reloc.exp, &p))
+ return FAIL;
+
+ inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
+ inst.reloc.exp.X_add_number -= 8; /* PC rel adjust */
+ inst.reloc.pc_rel = 1;
+ inst.instruction |= (REG_PC << 16);
+ pre_inc = PRE_INDEX;
+ }
+
+ inst.instruction |= write_back | pre_inc;
+ *str = p;
+ return SUCCESS;
+}
+
+static void
+do_nop (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ /* Do nothing really */
+ inst.instruction |= flags; /* This is pointless */
+ end_of_line (str);
+ return;
+}
+
+static void
+do_mrs (str, flags)
+ char *str;
+ unsigned long flags;
+{
+ /* Only one syntax */
+ while (*str == ' ')
+ str++;
+
+ if (reg_required_here (&str, 12) == FAIL)
+ {
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || psr_required_here (& str, CPSR_ALL, SPSR_ALL) == FAIL)
+ {
+ inst.error = _("<psr> expected");
+ return;
+ }
+
+ inst.instruction |= flags;
+ end_of_line (str);
+ return;
+}
+
+/* Three possible forms: "<psr>, Rm", "<psrf>, Rm", "<psrf>, #expression" */
+static void
+do_msr (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ int reg;
+
+ while (*str == ' ')
+ str ++;
+
+ if (psr_required_here (&str, CPSR_ALL, SPSR_ALL) == SUCCESS)
+ {
+ inst.instruction |= PSR_ALL;
+
+ /* Sytax should be "<psr>, Rm" */
+ if (skip_past_comma (&str) == FAIL
+ || (reg = reg_required_here (&str, 0)) == FAIL)
+ {
+ inst.error = bad_args;
+ return;
+ }
+ }
+ else
+ {
+ if (psr_required_here (& str, CPSR_FLG, SPSR_FLG) == SUCCESS)
+ inst.instruction |= PSR_FLAGS;
+ else if (psr_required_here (& str, CPSR_CTL, SPSR_CTL) == SUCCESS)
+ inst.instruction |= PSR_CONTROL;
+ else
+ {
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL)
+ {
+ inst.error = bad_args;
+ return;
+ }
+
+ /* Syntax could be "<psrf>, rm", "<psrf>, #expression" */
+
+ if ((reg = reg_required_here (& str, 0)) != FAIL)
+ ;
+ /* Immediate expression */
+ else if (is_immediate_prefix (* str))
+ {
+ str ++;
+ inst.error = NULL;
+
+ if (my_get_expression (& inst.reloc.exp, & str))
+ {
+ inst.error = _("Register or shift expression expected");
+ return;
+ }
+
+ if (inst.reloc.exp.X_add_symbol)
+ {
+ inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
+ inst.reloc.pc_rel = 0;
+ }
+ else
+ {
+ unsigned value = validate_immediate (inst.reloc.exp.X_add_number);
+ if (value == FAIL)
+ {
+ inst.error = _("Invalid constant");
+ return;
+ }
+
+ inst.instruction |= value;
+ }
+
+ flags |= INST_IMMEDIATE;
+ }
+ else
+ {
+ inst.error = _("Error: unrecognised syntax for second argument to msr instruction");
+ return;
+ }
+ }
+
+ inst.error = NULL;
+ inst.instruction |= flags;
+ end_of_line (str);
+ return;
+}
+
+/* Long Multiply Parser
+ UMULL RdLo, RdHi, Rm, Rs
+ SMULL RdLo, RdHi, Rm, Rs
+ UMLAL RdLo, RdHi, Rm, Rs
+ SMLAL RdLo, RdHi, Rm, Rs
+*/
+static void
+do_mull (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ int rdlo, rdhi, rm, rs;
+
+ /* only one format "rdlo, rdhi, rm, rs" */
+ while (*str == ' ')
+ str++;
+
+ if ((rdlo = reg_required_here (&str, 12)) == FAIL)
+ {
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || (rdhi = reg_required_here (&str, 16)) == FAIL)
+ {
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || (rm = reg_required_here (&str, 0)) == FAIL)
+ {
+ inst.error = bad_args;
+ return;
+ }
+
+ /* rdhi, rdlo and rm must all be different */
+ if (rdlo == rdhi || rdlo == rm || rdhi == rm)
+ as_tsktsk (_("rdhi, rdlo and rm must all be different"));
+
+ if (skip_past_comma (&str) == FAIL
+ || (rs = reg_required_here (&str, 8)) == FAIL)
+ {
+ inst.error = bad_args;
+ return;
+ }
+
+ if (rdhi == REG_PC || rdhi == REG_PC || rdhi == REG_PC || rdhi == REG_PC)
+ {
+ inst.error = bad_pc;
+ return;
+ }
+
+ inst.instruction |= flags;
+ end_of_line (str);
+ return;
+}
+
+static void
+do_mul (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ int rd, rm;
+
+ /* only one format "rd, rm, rs" */
+ while (*str == ' ')
+ str++;
+
+ if ((rd = reg_required_here (&str, 16)) == FAIL)
+ {
+ inst.error = bad_args;
+ return;
+ }
+
+ if (rd == REG_PC)
+ {
+ inst.error = bad_pc;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || (rm = reg_required_here (&str, 0)) == FAIL)
+ {
+ inst.error = bad_args;
+ return;
+ }
+
+ if (rm == REG_PC)
+ {
+ inst.error = bad_pc;
+ return;
+ }
+
+ if (rm == rd)
+ as_tsktsk (_("rd and rm should be different in mul"));
+
+ if (skip_past_comma (&str) == FAIL
+ || (rm = reg_required_here (&str, 8)) == FAIL)
+ {
+ inst.error = bad_args;
+ return;
+ }
+
+ if (rm == REG_PC)
+ {
+ inst.error = bad_pc;
+ return;
+ }
+
+ inst.instruction |= flags;
+ end_of_line (str);
+ return;
+}
+
+static void
+do_mla (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ int rd, rm;
+
+ /* only one format "rd, rm, rs, rn" */
+ while (*str == ' ')
+ str++;
+
+ if ((rd = reg_required_here (&str, 16)) == FAIL)
+ {
+ inst.error = bad_args;
+ return;
+ }
+
+ if (rd == REG_PC)
+ {
+ inst.error = bad_pc;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || (rm = reg_required_here (&str, 0)) == FAIL)
+ {
+ inst.error = bad_args;
+ return;
+ }
+
+ if (rm == REG_PC)
+ {
+ inst.error = bad_pc;
+ return;
+ }
+
+ if (rm == rd)
+ as_tsktsk (_("rd and rm should be different in mla"));
+
+ if (skip_past_comma (&str) == FAIL
+ || (rd = reg_required_here (&str, 8)) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || (rm = reg_required_here (&str, 12)) == FAIL)
+ {
+ inst.error = bad_args;
+ return;
+ }
+
+ if (rd == REG_PC || rm == REG_PC)
+ {
+ inst.error = bad_pc;
+ return;
+ }
+
+ inst.instruction |= flags;
+ end_of_line (str);
+ return;
+}
+
+/* Returns the index into fp_values of a floating point number, or -1 if
+ not in the table. */
+static int
+my_get_float_expression (str)
+ char ** str;
+{
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ char * save_in;
+ expressionS exp;
+ int i;
+ int j;
+
+ memset (words, 0, MAX_LITTLENUMS * sizeof (LITTLENUM_TYPE));
+ /* Look for a raw floating point number */
+ if ((save_in = atof_ieee (*str, 'x', words)) != NULL
+ && (is_end_of_line [(int)(*save_in)] || *save_in == '\0'))
+ {
+ for (i = 0; i < NUM_FLOAT_VALS; i++)
+ {
+ for (j = 0; j < MAX_LITTLENUMS; j++)
+ {
+ if (words[j] != fp_values[i][j])
+ break;
+ }
+
+ if (j == MAX_LITTLENUMS)
+ {
+ *str = save_in;
+ return i;
+ }
+ }
+ }
+
+ /* Try and parse a more complex expression, this will probably fail
+ unless the code uses a floating point prefix (eg "0f") */
+ save_in = input_line_pointer;
+ input_line_pointer = *str;
+ if (expression (&exp) == absolute_section
+ && exp.X_op == O_big
+ && exp.X_add_number < 0)
+ {
+ /* FIXME: 5 = X_PRECISION, should be #define'd where we can use it.
+ Ditto for 15. */
+ if (gen_to_words (words, 5, (long)15) == 0)
+ {
+ for (i = 0; i < NUM_FLOAT_VALS; i++)
+ {
+ for (j = 0; j < MAX_LITTLENUMS; j++)
+ {
+ if (words[j] != fp_values[i][j])
+ break;
+ }
+
+ if (j == MAX_LITTLENUMS)
+ {
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ return i;
+ }
+ }
+ }
+ }
+
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ return -1;
+}
+
+/* Return true if anything in the expression is a bignum */
+static int
+walk_no_bignums (sp)
+ symbolS * sp;
+{
+ if (sp->sy_value.X_op == O_big)
+ return 1;
+
+ if (sp->sy_value.X_add_symbol)
+ {
+ return (walk_no_bignums (sp->sy_value.X_add_symbol)
+ || (sp->sy_value.X_op_symbol
+ && walk_no_bignums (sp->sy_value.X_op_symbol)));
+ }
+
+ return 0;
+}
+
+static int
+my_get_expression (ep, str)
+ expressionS * ep;
+ char ** str;
+{
+ char * save_in;
+ segT seg;
+
+ save_in = input_line_pointer;
+ input_line_pointer = *str;
+ seg = expression (ep);
+
+#ifdef OBJ_AOUT
+ if (seg != absolute_section
+ && seg != text_section
+ && seg != data_section
+ && seg != bss_section
+ && seg != undefined_section)
+ {
+ inst.error = _("bad_segment");
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ return 1;
+ }
+#endif
+
+ /* Get rid of any bignums now, so that we don't generate an error for which
+ we can't establish a line number later on. Big numbers are never valid
+ in instructions, which is where this routine is always called. */
+ if (ep->X_op == O_big
+ || (ep->X_add_symbol
+ && (walk_no_bignums (ep->X_add_symbol)
+ || (ep->X_op_symbol
+ && walk_no_bignums (ep->X_op_symbol)))))
+ {
+ inst.error = _("Invalid constant");
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ return 1;
+ }
+
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ return 0;
+}
+
+/* unrestrict should be one if <shift> <register> is permitted for this
+ instruction */
+
+static int
+decode_shift (str, unrestrict)
+ char ** str;
+ int unrestrict;
+{
+ struct asm_shift * shft;
+ char * p;
+ char c;
+
+ while (**str == ' ')
+ (*str)++;
+
+ for (p = *str; isalpha (*p); p++)
+ ;
+
+ if (p == *str)
+ {
+ inst.error = _("Shift expression expected");
+ return FAIL;
+ }
+
+ c = *p;
+ *p = '\0';
+ shft = (struct asm_shift *) hash_find (arm_shift_hsh, *str);
+ *p = c;
+ if (shft)
+ {
+ if (!strncmp (*str, "rrx", 3)
+ || !strncmp (*str, "RRX", 3))
+ {
+ *str = p;
+ inst.instruction |= shft->value;
+ return SUCCESS;
+ }
+
+ while (*p == ' ')
+ p++;
+
+ if (unrestrict && reg_required_here (&p, 8) != FAIL)
+ {
+ inst.instruction |= shft->value | SHIFT_BY_REG;
+ *str = p;
+ return SUCCESS;
+ }
+ else if (is_immediate_prefix (* p))
+ {
+ inst.error = NULL;
+ p++;
+ if (my_get_expression (&inst.reloc.exp, &p))
+ return FAIL;
+
+ /* Validate some simple #expressions */
+ if (inst.reloc.exp.X_op == O_constant)
+ {
+ unsigned num = inst.reloc.exp.X_add_number;
+
+ /* Reject operations greater than 32, or lsl #32 */
+ if (num > 32 || (num == 32 && shft->value == 0))
+ {
+ inst.error = _("Invalid immediate shift");
+ return FAIL;
+ }
+
+ /* Shifts of zero should be converted to lsl (which is zero)*/
+ if (num == 0)
+ {
+ *str = p;
+ return SUCCESS;
+ }
+
+ /* Shifts of 32 are encoded as 0, for those shifts that
+ support it. */
+ if (num == 32)
+ num = 0;
+
+ inst.instruction |= (num << 7) | shft->value;
+ *str = p;
+ return SUCCESS;
+ }
+
+ inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
+ inst.reloc.pc_rel = 0;
+ inst.instruction |= shft->value;
+ *str = p;
+ return SUCCESS;
+ }
+ else
+ {
+ inst.error = unrestrict ? _("shift requires register or #expression")
+ : _("shift requires #expression");
+ *str = p;
+ return FAIL;
+ }
+ }
+
+ inst.error = _("Shift expression expected");
+ return FAIL;
+}
+
+/* Do those data_ops which can take a negative immediate constant */
+/* by altering the instuction. A bit of a hack really */
+/* MOV <-> MVN
+ AND <-> BIC
+ ADC <-> SBC
+ by inverting the second operand, and
+ ADD <-> SUB
+ CMP <-> CMN
+ by negating the second operand.
+*/
+static int
+negate_data_op (instruction, value)
+ unsigned long * instruction;
+ unsigned long value;
+{
+ int op, new_inst;
+ unsigned long negated, inverted;
+
+ negated = validate_immediate (-value);
+ inverted = validate_immediate (~value);
+
+ op = (*instruction >> DATA_OP_SHIFT) & 0xf;
+ switch (op)
+ {
+ /* First negates */
+ case OPCODE_SUB: /* ADD <-> SUB */
+ new_inst = OPCODE_ADD;
+ value = negated;
+ break;
+
+ case OPCODE_ADD:
+ new_inst = OPCODE_SUB;
+ value = negated;
+ break;
+
+ case OPCODE_CMP: /* CMP <-> CMN */
+ new_inst = OPCODE_CMN;
+ value = negated;
+ break;
+
+ case OPCODE_CMN:
+ new_inst = OPCODE_CMP;
+ value = negated;
+ break;
+
+ /* Now Inverted ops */
+ case OPCODE_MOV: /* MOV <-> MVN */
+ new_inst = OPCODE_MVN;
+ value = inverted;
+ break;
+
+ case OPCODE_MVN:
+ new_inst = OPCODE_MOV;
+ value = inverted;
+ break;
+
+ case OPCODE_AND: /* AND <-> BIC */
+ new_inst = OPCODE_BIC;
+ value = inverted;
+ break;
+
+ case OPCODE_BIC:
+ new_inst = OPCODE_AND;
+ value = inverted;
+ break;
+
+ case OPCODE_ADC: /* ADC <-> SBC */
+ new_inst = OPCODE_SBC;
+ value = inverted;
+ break;
+
+ case OPCODE_SBC:
+ new_inst = OPCODE_ADC;
+ value = inverted;
+ break;
+
+ /* We cannot do anything */
+ default:
+ return FAIL;
+ }
+
+ if (value == FAIL)
+ return FAIL;
+
+ *instruction &= OPCODE_MASK;
+ *instruction |= new_inst << DATA_OP_SHIFT;
+ return value;
+}
+
+static int
+data_op2 (str)
+ char ** str;
+{
+ int value;
+ expressionS expr;
+
+ while (**str == ' ')
+ (*str)++;
+
+ if (reg_required_here (str, 0) != FAIL)
+ {
+ if (skip_past_comma (str) == SUCCESS)
+ {
+ /* Shift operation on register */
+ return decode_shift (str, NO_SHIFT_RESTRICT);
+ }
+ return SUCCESS;
+ }
+ else
+ {
+ /* Immediate expression */
+ if (is_immediate_prefix (**str))
+ {
+ (*str)++;
+ inst.error = NULL;
+ if (my_get_expression (&inst.reloc.exp, str))
+ return FAIL;
+
+ if (inst.reloc.exp.X_add_symbol)
+ {
+ inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
+ inst.reloc.pc_rel = 0;
+ }
+ else
+ {
+ if (skip_past_comma (str) == SUCCESS)
+ {
+ /* #x, y -- ie explicit rotation by Y */
+ if (my_get_expression (&expr, str))
+ return FAIL;
+
+ if (expr.X_op != O_constant)
+ {
+ inst.error = _("Constant expression expected");
+ return FAIL;
+ }
+
+ /* Rotate must be a multiple of 2 */
+ if (((unsigned) expr.X_add_number) > 30
+ || (expr.X_add_number & 1) != 0
+ || ((unsigned) inst.reloc.exp.X_add_number) > 255)
+ {
+ inst.error = _("Invalid constant");
+ return FAIL;
+ }
+ inst.instruction |= INST_IMMEDIATE;
+ inst.instruction |= inst.reloc.exp.X_add_number;
+ inst.instruction |= expr.X_add_number << 7;
+ return SUCCESS;
+ }
+
+ /* Implicit rotation, select a suitable one */
+ value = validate_immediate (inst.reloc.exp.X_add_number);
+
+ if (value == FAIL)
+ {
+ /* Can't be done, perhaps the code reads something like
+ "add Rd, Rn, #-n", where "sub Rd, Rn, #n" would be ok */
+ if ((value = negate_data_op (&inst.instruction,
+ inst.reloc.exp.X_add_number))
+ == FAIL)
+ {
+ inst.error = _("Invalid constant");
+ return FAIL;
+ }
+ }
+
+ inst.instruction |= value;
+ }
+
+ inst.instruction |= INST_IMMEDIATE;
+ return SUCCESS;
+ }
+
+ (*str)++;
+ inst.error = _("Register or shift expression expected");
+ return FAIL;
+ }
+}
+
+static int
+fp_op2 (str)
+ char ** str;
+{
+ while (**str == ' ')
+ (*str)++;
+
+ if (fp_reg_required_here (str, 0) != FAIL)
+ return SUCCESS;
+ else
+ {
+ /* Immediate expression */
+ if (*((*str)++) == '#')
+ {
+ int i;
+
+ inst.error = NULL;
+ while (**str == ' ')
+ (*str)++;
+
+ /* First try and match exact strings, this is to guarantee that
+ some formats will work even for cross assembly */
+
+ for (i = 0; fp_const[i]; i++)
+ {
+ if (strncmp (*str, fp_const[i], strlen (fp_const[i])) == 0)
+ {
+ char *start = *str;
+
+ *str += strlen (fp_const[i]);
+ if (is_end_of_line[(int)**str] || **str == '\0')
+ {
+ inst.instruction |= i + 8;
+ return SUCCESS;
+ }
+ *str = start;
+ }
+ }
+
+ /* Just because we didn't get a match doesn't mean that the
+ constant isn't valid, just that it is in a format that we
+ don't automatically recognize. Try parsing it with
+ the standard expression routines. */
+ if ((i = my_get_float_expression (str)) >= 0)
+ {
+ inst.instruction |= i + 8;
+ return SUCCESS;
+ }
+
+ inst.error = _("Invalid floating point immediate expression");
+ return FAIL;
+ }
+ inst.error = _("Floating point register or immediate expression expected");
+ return FAIL;
+ }
+}
+
+static void
+do_arit (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ while (*str == ' ')
+ str++;
+
+ if (reg_required_here (&str, 12) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || reg_required_here (&str, 16) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || data_op2 (&str) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ inst.instruction |= flags;
+ end_of_line (str);
+ return;
+}
+
+static void
+do_adr (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ /* This is a pseudo-op of the form "adr rd, label" to be converted
+ into a relative address of the form "add rd, pc, #label-.-8" */
+
+ while (*str == ' ')
+ str++;
+
+ if (reg_required_here (&str, 12) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || my_get_expression (&inst.reloc.exp, &str))
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+ /* Frag hacking will turn this into a sub instruction if the offset turns
+ out to be negative. */
+ inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
+ inst.reloc.exp.X_add_number -= 8; /* PC relative adjust */
+ inst.reloc.pc_rel = 1;
+ inst.instruction |= flags;
+ end_of_line (str);
+ return;
+}
+
+static void
+do_cmp (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ while (*str == ' ')
+ str++;
+
+ if (reg_required_here (&str, 16) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || data_op2 (&str) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ inst.instruction |= flags;
+ if ((flags & 0x0000f000) == 0)
+ inst.instruction |= CONDS_BIT;
+
+ end_of_line (str);
+ return;
+}
+
+static void
+do_mov (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ while (*str == ' ')
+ str++;
+
+ if (reg_required_here (&str, 12) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || data_op2 (&str) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ inst.instruction |= flags;
+ end_of_line (str);
+ return;
+}
+
+static int
+ldst_extend (str, hwse)
+ char ** str;
+ int hwse;
+{
+ int add = INDEX_UP;
+
+ switch (**str)
+ {
+ case '#':
+ case '$':
+ (*str)++;
+ if (my_get_expression (& inst.reloc.exp, str))
+ return FAIL;
+
+ if (inst.reloc.exp.X_op == O_constant)
+ {
+ int value = inst.reloc.exp.X_add_number;
+
+ if ((hwse && (value < -255 || value > 255))
+ || (value < -4095 || value > 4095))
+ {
+ inst.error = _("address offset too large");
+ return FAIL;
+ }
+
+ if (value < 0)
+ {
+ value = -value;
+ add = 0;
+ }
+
+ /* Halfword and signextension instructions have the
+ immediate value split across bits 11..8 and bits 3..0 */
+ if (hwse)
+ inst.instruction |= add | HWOFFSET_IMM | (value >> 4) << 8 | value & 0xF;
+ else
+ inst.instruction |= add | value;
+ }
+ else
+ {
+ if (hwse)
+ {
+ inst.instruction |= HWOFFSET_IMM;
+ inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
+ }
+ else
+ inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
+ inst.reloc.pc_rel = 0;
+ }
+ return SUCCESS;
+
+ case '-':
+ add = 0; /* and fall through */
+ case '+':
+ (*str)++; /* and fall through */
+ default:
+ if (reg_required_here (str, 0) == FAIL)
+ return FAIL;
+
+ if (hwse)
+ inst.instruction |= add;
+ else
+ {
+ inst.instruction |= add | OFFSET_REG;
+ if (skip_past_comma (str) == SUCCESS)
+ return decode_shift (str, SHIFT_RESTRICT);
+ }
+
+ return SUCCESS;
+ }
+}
+
+static void
+do_ldst (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ int halfword = 0;
+ int pre_inc = 0;
+ int conflict_reg;
+ int value;
+
+ /* This is not ideal, but it is the simplest way of dealing with the
+ ARM7T halfword instructions (since they use a different
+ encoding, but the same mnemonic): */
+ if (halfword = ((flags & 0x80000000) != 0))
+ {
+ /* This is actually a load/store of a halfword, or a
+ signed-extension load */
+ if ((cpu_variant & ARM_HALFWORD) == 0)
+ {
+ inst.error
+ = _("Processor does not support halfwords or signed bytes");
+ return;
+ }
+
+ inst.instruction = (inst.instruction & COND_MASK)
+ | (flags & ~COND_MASK);
+
+ flags = 0;
+ }
+
+ while (*str == ' ')
+ str++;
+
+ if ((conflict_reg = reg_required_here (& str, 12)) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (& str) == FAIL)
+ {
+ inst.error = _("Address expected");
+ return;
+ }
+
+ if (*str == '[')
+ {
+ int reg;
+
+ str++;
+ while (*str == ' ')
+ str++;
+
+ if ((reg = reg_required_here (&str, 16)) == FAIL)
+ return;
+
+ conflict_reg = (((conflict_reg == reg)
+ && (inst.instruction & LOAD_BIT))
+ ? 1 : 0);
+
+ while (*str == ' ')
+ str++;
+
+ if (*str == ']')
+ {
+ str++;
+ if (skip_past_comma (&str) == SUCCESS)
+ {
+ /* [Rn],... (post inc) */
+ if (ldst_extend (&str, halfword) == FAIL)
+ return;
+ if (conflict_reg)
+ as_warn (_("destination register same as write-back base\n"));
+ }
+ else
+ {
+ /* [Rn] */
+ if (halfword)
+ inst.instruction |= HWOFFSET_IMM;
+
+ while (*str == ' ')
+ str++;
+
+ if (*str == '!')
+ {
+ if (conflict_reg)
+ as_warn (_("destination register same as write-back base\n"));
+ str++;
+ inst.instruction |= WRITE_BACK;
+ }
+
+ flags |= INDEX_UP;
+ if (! (flags & TRANS_BIT))
+ pre_inc = 1;
+ }
+ }
+ else
+ {
+ /* [Rn,...] */
+ if (skip_past_comma (&str) == FAIL)
+ {
+ inst.error = _("pre-indexed expression expected");
+ return;
+ }
+
+ pre_inc = 1;
+ if (ldst_extend (&str, halfword) == FAIL)
+ return;
+
+ while (*str == ' ')
+ str++;
+
+ if (*str++ != ']')
+ {
+ inst.error = _("missing ]");
+ return;
+ }
+
+ while (*str == ' ')
+ str++;
+
+ if (*str == '!')
+ {
+ if (conflict_reg)
+ as_tsktsk (_("destination register same as write-back base\n"));
+ str++;
+ inst.instruction |= WRITE_BACK;
+ }
+ }
+ }
+ else if (*str == '=')
+ {
+ /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op */
+ str++;
+
+ while (*str == ' ')
+ str++;
+
+ if (my_get_expression (&inst.reloc.exp, &str))
+ return;
+
+ if (inst.reloc.exp.X_op != O_constant
+ && inst.reloc.exp.X_op != O_symbol)
+ {
+ inst.error = _("Constant expression expected");
+ return;
+ }
+
+ if (inst.reloc.exp.X_op == O_constant
+ && (value = validate_immediate(inst.reloc.exp.X_add_number)) != FAIL)
+ {
+ /* This can be done with a mov instruction */
+ inst.instruction &= LITERAL_MASK;
+ inst.instruction |= INST_IMMEDIATE | (OPCODE_MOV << DATA_OP_SHIFT);
+ inst.instruction |= (flags & COND_MASK) | (value & 0xfff);
+ end_of_line(str);
+ return;
+ }
+ else
+ {
+ /* Insert into literal pool */
+ if (add_to_lit_pool () == FAIL)
+ {
+ if (!inst.error)
+ inst.error = _("literal pool insertion failed");
+ return;
+ }
+
+ /* Change the instruction exp to point to the pool */
+ if (halfword)
+ {
+ inst.instruction |= HWOFFSET_IMM;
+ inst.reloc.type = BFD_RELOC_ARM_HWLITERAL;
+ }
+ else
+ inst.reloc.type = BFD_RELOC_ARM_LITERAL;
+ inst.reloc.pc_rel = 1;
+ inst.instruction |= (REG_PC << 16);
+ pre_inc = 1;
+ }
+ }
+ else
+ {
+ if (my_get_expression (&inst.reloc.exp, &str))
+ return;
+
+ if (halfword)
+ {
+ inst.instruction |= HWOFFSET_IMM;
+ inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
+ }
+ else
+ inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
+ inst.reloc.exp.X_add_number -= 8; /* PC rel adjust */
+ inst.reloc.pc_rel = 1;
+ inst.instruction |= (REG_PC << 16);
+ pre_inc = 1;
+ }
+
+ if (pre_inc && (flags & TRANS_BIT))
+ inst.error = _("Pre-increment instruction with translate");
+
+ inst.instruction |= flags | (pre_inc ? PRE_INDEX : 0);
+ end_of_line (str);
+ return;
+}
+
+static long
+reg_list (strp)
+ char ** strp;
+{
+ char * str = *strp;
+ long range = 0;
+ int another_range;
+
+ /* We come back here if we get ranges concatenated by '+' or '|' */
+ do
+ {
+ another_range = 0;
+
+ if (*str == '{')
+ {
+ int in_range = 0;
+ int cur_reg = -1;
+
+ str++;
+ do
+ {
+ int reg;
+
+ while (*str == ' ')
+ str++;
+
+ if ((reg = reg_required_here (& str, -1)) == FAIL)
+ return FAIL;
+
+ if (in_range)
+ {
+ int i;
+
+ if (reg <= cur_reg)
+ {
+ inst.error = _("Bad range in register list");
+ return FAIL;
+ }
+
+ for (i = cur_reg + 1; i < reg; i++)
+ {
+ if (range & (1 << i))
+ as_tsktsk
+ (_("Warning: Duplicated register (r%d) in register list"),
+ i);
+ else
+ range |= 1 << i;
+ }
+ in_range = 0;
+ }
+
+ if (range & (1 << reg))
+ as_tsktsk (_("Warning: Duplicated register (r%d) in register list"),
+ reg);
+ else if (reg <= cur_reg)
+ as_tsktsk (_("Warning: Register range not in ascending order"));
+
+ range |= 1 << reg;
+ cur_reg = reg;
+ } while (skip_past_comma (&str) != FAIL
+ || (in_range = 1, *str++ == '-'));
+ str--;
+ while (*str == ' ')
+ str++;
+
+ if (*str++ != '}')
+ {
+ inst.error = _("Missing `}'");
+ return FAIL;
+ }
+ }
+ else
+ {
+ expressionS expr;
+
+ if (my_get_expression (&expr, &str))
+ return FAIL;
+
+ if (expr.X_op == O_constant)
+ {
+ if (expr.X_add_number
+ != (expr.X_add_number & 0x0000ffff))
+ {
+ inst.error = _("invalid register mask");
+ return FAIL;
+ }
+
+ if ((range & expr.X_add_number) != 0)
+ {
+ int regno = range & expr.X_add_number;
+
+ regno &= -regno;
+ regno = (1 << regno) - 1;
+ as_tsktsk
+ (_("Warning: Duplicated register (r%d) in register list"),
+ regno);
+ }
+
+ range |= expr.X_add_number;
+ }
+ else
+ {
+ if (inst.reloc.type != 0)
+ {
+ inst.error = _("expression too complex");
+ return FAIL;
+ }
+
+ memcpy (&inst.reloc.exp, &expr, sizeof (expressionS));
+ inst.reloc.type = BFD_RELOC_ARM_MULTI;
+ inst.reloc.pc_rel = 0;
+ }
+ }
+
+ while (*str == ' ')
+ str++;
+
+ if (*str == '|' || *str == '+')
+ {
+ str++;
+ another_range = 1;
+ }
+ } while (another_range);
+
+ *strp = str;
+ return range;
+}
+
+static void
+do_ldmstm (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ int base_reg;
+ long range;
+
+ while (*str == ' ')
+ str++;
+
+ if ((base_reg = reg_required_here (&str, 16)) == FAIL)
+ return;
+
+ if (base_reg == REG_PC)
+ {
+ inst.error = _("r15 not allowed as base register");
+ return;
+ }
+
+ while (*str == ' ')
+ str++;
+ if (*str == '!')
+ {
+ flags |= WRITE_BACK;
+ str++;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || (range = reg_list (&str)) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (*str == '^')
+ {
+ str++;
+ flags |= MULTI_SET_PSR;
+ }
+
+ inst.instruction |= flags | range;
+ end_of_line (str);
+ return;
+}
+
+static void
+do_swi (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ while (*str == ' ')
+ str++;
+
+ /* Allow optional leading '#'. */
+ if (is_immediate_prefix (*str))
+ str++;
+
+ if (my_get_expression (& inst.reloc.exp, & str))
+ return;
+
+ inst.reloc.type = BFD_RELOC_ARM_SWI;
+ inst.reloc.pc_rel = 0;
+ inst.instruction |= flags;
+
+ end_of_line (str);
+
+ return;
+}
+
+static void
+do_swap (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ int reg;
+
+ while (*str == ' ')
+ str++;
+
+ if ((reg = reg_required_here (&str, 12)) == FAIL)
+ return;
+
+ if (reg == REG_PC)
+ {
+ inst.error = _("r15 not allowed in swap");
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || (reg = reg_required_here (&str, 0)) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (reg == REG_PC)
+ {
+ inst.error = _("r15 not allowed in swap");
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || *str++ != '[')
+ {
+ inst.error = bad_args;
+ return;
+ }
+
+ while (*str == ' ')
+ str++;
+
+ if ((reg = reg_required_here (&str, 16)) == FAIL)
+ return;
+
+ if (reg == REG_PC)
+ {
+ inst.error = bad_pc;
+ return;
+ }
+
+ while (*str == ' ')
+ str++;
+
+ if (*str++ != ']')
+ {
+ inst.error = _("missing ]");
+ return;
+ }
+
+ inst.instruction |= flags;
+ end_of_line (str);
+ return;
+}
+
+static void
+do_branch (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ if (my_get_expression (&inst.reloc.exp, &str))
+ return;
+
+#ifdef OBJ_ELF
+ {
+ char * save_in;
+
+ /* ScottB: February 5, 1998 */
+ /* Check to see of PLT32 reloc required for the instruction. */
+
+ /* arm_parse_reloc() works on input_line_pointer.
+ We actually want to parse the operands to the branch instruction
+ passed in 'str'. Save the input pointer and restore it later. */
+ save_in = input_line_pointer;
+ input_line_pointer = str;
+ if (inst.reloc.exp.X_op == O_symbol
+ && *str == '('
+ && arm_parse_reloc () == BFD_RELOC_ARM_PLT32)
+ {
+ inst.reloc.type = BFD_RELOC_ARM_PLT32;
+ inst.reloc.pc_rel = 0;
+ /* Modify str to point to after parsed operands, otherwise
+ end_of_line() will complain about the (PLT) left in str. */
+ str = input_line_pointer;
+ }
+ else
+ {
+ inst.reloc.type = BFD_RELOC_ARM_PCREL_BRANCH;
+ inst.reloc.pc_rel = 1;
+ }
+ input_line_pointer = save_in;
+ }
+#else
+ inst.reloc.type = BFD_RELOC_ARM_PCREL_BRANCH;
+ inst.reloc.pc_rel = 1;
+#endif /* OBJ_ELF */
+
+ end_of_line (str);
+ return;
+}
+
+static void
+do_bx (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ int reg;
+
+ while (*str == ' ')
+ str++;
+
+ if ((reg = reg_required_here (&str, 0)) == FAIL)
+ return;
+
+ if (reg == REG_PC)
+ as_tsktsk (_("Use of r15 in bx has undefined behaviour"));
+
+ end_of_line (str);
+ return;
+}
+
+static void
+do_cdp (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ /* Co-processor data operation.
+ Format: CDP{cond} CP#,<expr>,CRd,CRn,CRm{,<expr>} */
+ while (*str == ' ')
+ str++;
+
+ if (co_proc_number (&str) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || cp_opc_expr (&str, 20,4) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || cp_reg_required_here (&str, 12) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || cp_reg_required_here (&str, 16) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || cp_reg_required_here (&str, 0) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == SUCCESS)
+ {
+ if (cp_opc_expr (&str, 5, 3) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+ }
+
+ end_of_line (str);
+ return;
+}
+
+static void
+do_lstc (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ /* Co-processor register load/store.
+ Format: <LDC|STC{cond}[L] CP#,CRd,<address> */
+
+ while (*str == ' ')
+ str++;
+
+ if (co_proc_number (&str) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || cp_reg_required_here (&str, 12) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || cp_address_required_here (&str) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ inst.instruction |= flags;
+ end_of_line (str);
+ return;
+}
+
+static void
+do_co_reg (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ /* Co-processor register transfer.
+ Format: <MCR|MRC>{cond} CP#,<expr1>,Rd,CRn,CRm{,<expr2>} */
+
+ while (*str == ' ')
+ str++;
+
+ if (co_proc_number (&str) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || cp_opc_expr (&str, 21, 3) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || reg_required_here (&str, 12) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || cp_reg_required_here (&str, 16) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || cp_reg_required_here (&str, 0) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == SUCCESS)
+ {
+ if (cp_opc_expr (&str, 5, 3) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+ }
+
+ end_of_line (str);
+ return;
+}
+
+static void
+do_fp_ctrl (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ /* FP control registers.
+ Format: <WFS|RFS|WFC|RFC>{cond} Rn */
+
+ while (*str == ' ')
+ str++;
+
+ if (reg_required_here (&str, 12) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ end_of_line (str);
+ return;
+}
+
+static void
+do_fp_ldst (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ while (*str == ' ')
+ str++;
+
+ switch (inst.suffix)
+ {
+ case SUFF_S:
+ break;
+ case SUFF_D:
+ inst.instruction |= CP_T_X;
+ break;
+ case SUFF_E:
+ inst.instruction |= CP_T_Y;
+ break;
+ case SUFF_P:
+ inst.instruction |= CP_T_X | CP_T_Y;
+ break;
+ default:
+ abort ();
+ }
+
+ if (fp_reg_required_here (&str, 12) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || cp_address_required_here (&str) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ end_of_line (str);
+}
+
+static void
+do_fp_ldmstm (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ int num_regs;
+
+ while (*str == ' ')
+ str++;
+
+ if (fp_reg_required_here (&str, 12) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ /* Get Number of registers to transfer */
+ if (skip_past_comma (&str) == FAIL
+ || my_get_expression (&inst.reloc.exp, &str))
+ {
+ if (! inst.error)
+ inst.error = _("constant expression expected");
+ return;
+ }
+
+ if (inst.reloc.exp.X_op != O_constant)
+ {
+ inst.error = _("Constant value required for number of registers");
+ return;
+ }
+
+ num_regs = inst.reloc.exp.X_add_number;
+
+ if (num_regs < 1 || num_regs > 4)
+ {
+ inst.error = _("number of registers must be in the range [1:4]");
+ return;
+ }
+
+ switch (num_regs)
+ {
+ case 1:
+ inst.instruction |= CP_T_X;
+ break;
+ case 2:
+ inst.instruction |= CP_T_Y;
+ break;
+ case 3:
+ inst.instruction |= CP_T_Y | CP_T_X;
+ break;
+ case 4:
+ break;
+ default:
+ abort ();
+ }
+
+ if (flags)
+ {
+ int reg;
+ int write_back;
+ int offset;
+
+ /* The instruction specified "ea" or "fd", so we can only accept
+ [Rn]{!}. The instruction does not really support stacking or
+ unstacking, so we have to emulate these by setting appropriate
+ bits and offsets. */
+ if (skip_past_comma (&str) == FAIL
+ || *str != '[')
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ str++;
+ while (*str == ' ')
+ str++;
+
+ if ((reg = reg_required_here (&str, 16)) == FAIL)
+ return;
+
+ while (*str == ' ')
+ str++;
+
+ if (*str != ']')
+ {
+ inst.error = bad_args;
+ return;
+ }
+
+ str++;
+ if (*str == '!')
+ {
+ write_back = 1;
+ str++;
+ if (reg == REG_PC)
+ {
+ inst.error = _("R15 not allowed as base register with write-back");
+ return;
+ }
+ }
+ else
+ write_back = 0;
+
+ if (flags & CP_T_Pre)
+ {
+ /* Pre-decrement */
+ offset = 3 * num_regs;
+ if (write_back)
+ flags |= CP_T_WB;
+ }
+ else
+ {
+ /* Post-increment */
+ if (write_back)
+ {
+ flags |= CP_T_WB;
+ offset = 3 * num_regs;
+ }
+ else
+ {
+ /* No write-back, so convert this into a standard pre-increment
+ instruction -- aesthetically more pleasing. */
+ flags = CP_T_Pre | CP_T_UD;
+ offset = 0;
+ }
+ }
+
+ inst.instruction |= flags | offset;
+ }
+ else if (skip_past_comma (&str) == FAIL
+ || cp_address_required_here (&str) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ end_of_line (str);
+}
+
+static void
+do_fp_dyadic (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ while (*str == ' ')
+ str++;
+
+ switch (inst.suffix)
+ {
+ case SUFF_S:
+ break;
+ case SUFF_D:
+ inst.instruction |= 0x00000080;
+ break;
+ case SUFF_E:
+ inst.instruction |= 0x00080000;
+ break;
+ default:
+ abort ();
+ }
+
+ if (fp_reg_required_here (&str, 12) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || fp_reg_required_here (&str, 16) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || fp_op2 (&str) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ inst.instruction |= flags;
+ end_of_line (str);
+ return;
+}
+
+static void
+do_fp_monadic (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ while (*str == ' ')
+ str++;
+
+ switch (inst.suffix)
+ {
+ case SUFF_S:
+ break;
+ case SUFF_D:
+ inst.instruction |= 0x00000080;
+ break;
+ case SUFF_E:
+ inst.instruction |= 0x00080000;
+ break;
+ default:
+ abort ();
+ }
+
+ if (fp_reg_required_here (&str, 12) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || fp_op2 (&str) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ inst.instruction |= flags;
+ end_of_line (str);
+ return;
+}
+
+static void
+do_fp_cmp (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ while (*str == ' ')
+ str++;
+
+ if (fp_reg_required_here (&str, 16) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || fp_op2 (&str) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ inst.instruction |= flags;
+ end_of_line (str);
+ return;
+}
+
+static void
+do_fp_from_reg (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ while (*str == ' ')
+ str++;
+
+ switch (inst.suffix)
+ {
+ case SUFF_S:
+ break;
+ case SUFF_D:
+ inst.instruction |= 0x00000080;
+ break;
+ case SUFF_E:
+ inst.instruction |= 0x00080000;
+ break;
+ default:
+ abort ();
+ }
+
+ if (fp_reg_required_here (&str, 16) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || reg_required_here (&str, 12) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ inst.instruction |= flags;
+ end_of_line (str);
+ return;
+}
+
+static void
+do_fp_to_reg (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ while (*str == ' ')
+ str++;
+
+ if (reg_required_here (&str, 12) == FAIL)
+ return;
+
+ if (skip_past_comma (&str) == FAIL
+ || fp_reg_required_here (&str, 0) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ inst.instruction |= flags;
+ end_of_line (str);
+ return;
+}
+
+/* Thumb specific routines */
+
+/* Parse and validate that a register is of the right form, this saves
+ repeated checking of this information in many similar cases.
+ Unlike the 32-bit case we do not insert the register into the opcode
+ here, since the position is often unknown until the full instruction
+ has been parsed. */
+static int
+thumb_reg (strp, hi_lo)
+ char ** strp;
+ int hi_lo;
+{
+ int reg;
+
+ if ((reg = reg_required_here (strp, -1)) == FAIL)
+ return FAIL;
+
+ switch (hi_lo)
+ {
+ case THUMB_REG_LO:
+ if (reg > 7)
+ {
+ inst.error = _("lo register required");
+ return FAIL;
+ }
+ break;
+
+ case THUMB_REG_HI:
+ if (reg < 8)
+ {
+ inst.error = _("hi register required");
+ return FAIL;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return reg;
+}
+
+/* Parse an add or subtract instruction, SUBTRACT is non-zero if the opcode
+ was SUB. */
+static void
+thumb_add_sub (str, subtract)
+ char * str;
+ int subtract;
+{
+ int Rd, Rs, Rn = FAIL;
+
+ while (*str == ' ')
+ str++;
+
+ if ((Rd = thumb_reg (&str, THUMB_REG_ANY)) == FAIL
+ || skip_past_comma (&str) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (is_immediate_prefix (*str))
+ {
+ Rs = Rd;
+ str++;
+ if (my_get_expression (&inst.reloc.exp, &str))
+ return;
+ }
+ else
+ {
+ if ((Rs = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
+ return;
+
+ if (skip_past_comma (&str) == FAIL)
+ {
+ /* Two operand format, shuffle the registers and pretend there
+ are 3 */
+ Rn = Rs;
+ Rs = Rd;
+ }
+ else if (is_immediate_prefix (*str))
+ {
+ str++;
+ if (my_get_expression (&inst.reloc.exp, &str))
+ return;
+ }
+ else if ((Rn = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
+ return;
+ }
+
+ /* We now have Rd and Rs set to registers, and Rn set to a register or FAIL;
+ for the latter case, EXPR contains the immediate that was found. */
+ if (Rn != FAIL)
+ {
+ /* All register format. */
+ if (Rd > 7 || Rs > 7 || Rn > 7)
+ {
+ if (Rs != Rd)
+ {
+ inst.error = _("dest and source1 must be the same register");
+ return;
+ }
+
+ /* Can't do this for SUB */
+ if (subtract)
+ {
+ inst.error = _("subtract valid only on lo regs");
+ return;
+ }
+
+ inst.instruction = (T_OPCODE_ADD_HI
+ | (Rd > 7 ? THUMB_H1 : 0)
+ | (Rn > 7 ? THUMB_H2 : 0));
+ inst.instruction |= (Rd & 7) | ((Rn & 7) << 3);
+ }
+ else
+ {
+ inst.instruction = subtract ? T_OPCODE_SUB_R3 : T_OPCODE_ADD_R3;
+ inst.instruction |= Rd | (Rs << 3) | (Rn << 6);
+ }
+ }
+ else
+ {
+ /* Immediate expression, now things start to get nasty. */
+
+ /* First deal with HI regs, only very restricted cases allowed:
+ Adjusting SP, and using PC or SP to get an address. */
+ if ((Rd > 7 && (Rd != REG_SP || Rs != REG_SP))
+ || (Rs > 7 && Rs != REG_SP && Rs != REG_PC))
+ {
+ inst.error = _("invalid Hi register with immediate");
+ return;
+ }
+
+ if (inst.reloc.exp.X_op != O_constant)
+ {
+ /* Value isn't known yet, all we can do is store all the fragments
+ we know about in the instruction and let the reloc hacking
+ work it all out. */
+ inst.instruction = (subtract ? 0x8000 : 0) | (Rd << 4) | Rs;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+ }
+ else
+ {
+ int offset = inst.reloc.exp.X_add_number;
+
+ if (subtract)
+ offset = -offset;
+
+ if (offset < 0)
+ {
+ offset = -offset;
+ subtract = 1;
+
+ /* Quick check, in case offset is MIN_INT */
+ if (offset < 0)
+ {
+ inst.error = _("immediate value out of range");
+ return;
+ }
+ }
+ else
+ subtract = 0;
+
+ if (Rd == REG_SP)
+ {
+ if (offset & ~0x1fc)
+ {
+ inst.error = _("invalid immediate value for stack adjust");
+ return;
+ }
+ inst.instruction = subtract ? T_OPCODE_SUB_ST : T_OPCODE_ADD_ST;
+ inst.instruction |= offset >> 2;
+ }
+ else if (Rs == REG_PC || Rs == REG_SP)
+ {
+ if (subtract
+ || (offset & ~0x3fc))
+ {
+ inst.error = _("invalid immediate for address calculation");
+ return;
+ }
+ inst.instruction = (Rs == REG_PC ? T_OPCODE_ADD_PC
+ : T_OPCODE_ADD_SP);
+ inst.instruction |= (Rd << 8) | (offset >> 2);
+ }
+ else if (Rs == Rd)
+ {
+ if (offset & ~0xff)
+ {
+ inst.error = _("immediate value out of range");
+ return;
+ }
+ inst.instruction = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8;
+ inst.instruction |= (Rd << 8) | offset;
+ }
+ else
+ {
+ if (offset & ~0x7)
+ {
+ inst.error = _("immediate value out of range");
+ return;
+ }
+ inst.instruction = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3;
+ inst.instruction |= Rd | (Rs << 3) | (offset << 6);
+ }
+ }
+ }
+ end_of_line (str);
+}
+
+static void
+thumb_shift (str, shift)
+ char * str;
+ int shift;
+{
+ int Rd, Rs, Rn = FAIL;
+
+ while (*str == ' ')
+ str++;
+
+ if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
+ || skip_past_comma (&str) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (is_immediate_prefix (*str))
+ {
+ /* Two operand immediate format, set Rs to Rd. */
+ Rs = Rd;
+ str++;
+ if (my_get_expression (&inst.reloc.exp, &str))
+ return;
+ }
+ else
+ {
+ if ((Rs = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
+ return;
+
+ if (skip_past_comma (&str) == FAIL)
+ {
+ /* Two operand format, shuffle the registers and pretend there
+ are 3 */
+ Rn = Rs;
+ Rs = Rd;
+ }
+ else if (is_immediate_prefix (*str))
+ {
+ str++;
+ if (my_get_expression (&inst.reloc.exp, &str))
+ return;
+ }
+ else if ((Rn = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
+ return;
+ }
+
+ /* We now have Rd and Rs set to registers, and Rn set to a register or FAIL;
+ for the latter case, EXPR contains the immediate that was found. */
+
+ if (Rn != FAIL)
+ {
+ if (Rs != Rd)
+ {
+ inst.error = _("source1 and dest must be same register");
+ return;
+ }
+
+ switch (shift)
+ {
+ case THUMB_ASR: inst.instruction = T_OPCODE_ASR_R; break;
+ case THUMB_LSL: inst.instruction = T_OPCODE_LSL_R; break;
+ case THUMB_LSR: inst.instruction = T_OPCODE_LSR_R; break;
+ }
+
+ inst.instruction |= Rd | (Rn << 3);
+ }
+ else
+ {
+ switch (shift)
+ {
+ case THUMB_ASR: inst.instruction = T_OPCODE_ASR_I; break;
+ case THUMB_LSL: inst.instruction = T_OPCODE_LSL_I; break;
+ case THUMB_LSR: inst.instruction = T_OPCODE_LSR_I; break;
+ }
+
+ if (inst.reloc.exp.X_op != O_constant)
+ {
+ /* Value isn't known yet, create a dummy reloc and let reloc
+ hacking fix it up */
+
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
+ }
+ else
+ {
+ unsigned shift_value = inst.reloc.exp.X_add_number;
+
+ if (shift_value > 32 || (shift_value == 32 && shift == THUMB_LSL))
+ {
+ inst.error = _("Invalid immediate for shift");
+ return;
+ }
+
+ /* Shifts of zero are handled by converting to LSL */
+ if (shift_value == 0)
+ inst.instruction = T_OPCODE_LSL_I;
+
+ /* Shifts of 32 are encoded as a shift of zero */
+ if (shift_value == 32)
+ shift_value = 0;
+
+ inst.instruction |= shift_value << 6;
+ }
+
+ inst.instruction |= Rd | (Rs << 3);
+ }
+ end_of_line (str);
+}
+
+static void
+thumb_mov_compare (str, move)
+ char * str;
+ int move;
+{
+ int Rd, Rs = FAIL;
+
+ while (*str == ' ')
+ str++;
+
+ if ((Rd = thumb_reg (&str, THUMB_REG_ANY)) == FAIL
+ || skip_past_comma (&str) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (is_immediate_prefix (*str))
+ {
+ str++;
+ if (my_get_expression (&inst.reloc.exp, &str))
+ return;
+ }
+ else if ((Rs = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
+ return;
+
+ if (Rs != FAIL)
+ {
+ if (Rs < 8 && Rd < 8)
+ {
+ if (move == THUMB_MOVE)
+ /* A move of two lowregs is encoded as ADD Rd, Rs, #0
+ since a MOV instruction produces unpredictable results */
+ inst.instruction = T_OPCODE_ADD_I3;
+ else
+ inst.instruction = T_OPCODE_CMP_LR;
+ inst.instruction |= Rd | (Rs << 3);
+ }
+ else
+ {
+ if (move == THUMB_MOVE)
+ inst.instruction = T_OPCODE_MOV_HR;
+ else
+ inst.instruction = T_OPCODE_CMP_HR;
+
+ if (Rd > 7)
+ inst.instruction |= THUMB_H1;
+
+ if (Rs > 7)
+ inst.instruction |= THUMB_H2;
+
+ inst.instruction |= (Rd & 7) | ((Rs & 7) << 3);
+ }
+ }
+ else
+ {
+ if (Rd > 7)
+ {
+ inst.error = _("only lo regs allowed with immediate");
+ return;
+ }
+
+ if (move == THUMB_MOVE)
+ inst.instruction = T_OPCODE_MOV_I8;
+ else
+ inst.instruction = T_OPCODE_CMP_I8;
+
+ inst.instruction |= Rd << 8;
+
+ if (inst.reloc.exp.X_op != O_constant)
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_IMM;
+ else
+ {
+ unsigned value = inst.reloc.exp.X_add_number;
+
+ if (value > 255)
+ {
+ inst.error = _("invalid immediate");
+ return;
+ }
+
+ inst.instruction |= value;
+ }
+ }
+
+ end_of_line (str);
+}
+
+static void
+thumb_load_store (str, load_store, size)
+ char * str;
+ int load_store;
+ int size;
+{
+ int Rd, Rb, Ro = FAIL;
+
+ while (*str == ' ')
+ str++;
+
+ if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
+ || skip_past_comma (&str) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (*str == '[')
+ {
+ str++;
+ if ((Rb = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
+ return;
+
+ if (skip_past_comma (&str) != FAIL)
+ {
+ if (is_immediate_prefix (*str))
+ {
+ str++;
+ if (my_get_expression (&inst.reloc.exp, &str))
+ return;
+ }
+ else if ((Ro = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
+ return;
+ }
+ else
+ {
+ inst.reloc.exp.X_op = O_constant;
+ inst.reloc.exp.X_add_number = 0;
+ }
+
+ if (*str != ']')
+ {
+ inst.error = _("expected ']'");
+ return;
+ }
+ str++;
+ }
+ else if (*str == '=')
+ {
+ /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op */
+ str++;
+
+ while (*str == ' ')
+ str++;
+
+ if (my_get_expression (& inst.reloc.exp, & str))
+ return;
+
+ end_of_line (str);
+
+ if ( inst.reloc.exp.X_op != O_constant
+ && inst.reloc.exp.X_op != O_symbol)
+ {
+ inst.error = "Constant expression expected";
+ return;
+ }
+
+ if (inst.reloc.exp.X_op == O_constant
+ && ((inst.reloc.exp.X_add_number & ~0xFF) == 0))
+ {
+ /* This can be done with a mov instruction */
+
+ inst.instruction = T_OPCODE_MOV_I8 | (Rd << 8);
+ inst.instruction |= inst.reloc.exp.X_add_number;
+ return;
+ }
+
+ /* Insert into literal pool */
+ if (add_to_lit_pool () == FAIL)
+ {
+ if (!inst.error)
+ inst.error = "literal pool insertion failed";
+ return;
+ }
+
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+ inst.reloc.pc_rel = 1;
+ inst.instruction = T_OPCODE_LDR_PC | (Rd << 8);
+ inst.reloc.exp.X_add_number += 4; /* Adjust ARM pipeline offset to Thumb */
+
+ return;
+ }
+ else
+ {
+ if (my_get_expression (&inst.reloc.exp, &str))
+ return;
+
+ inst.instruction = T_OPCODE_LDR_PC | (Rd << 8);
+ inst.reloc.pc_rel = 1;
+ inst.reloc.exp.X_add_number -= 4; /* Pipeline offset */
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+ end_of_line (str);
+ return;
+ }
+
+ if (Rb == REG_PC || Rb == REG_SP)
+ {
+ if (size != THUMB_WORD)
+ {
+ inst.error = _("byte or halfword not valid for base register");
+ return;
+ }
+ else if (Rb == REG_PC && load_store != THUMB_LOAD)
+ {
+ inst.error = _("R15 based store not allowed");
+ return;
+ }
+ else if (Ro != FAIL)
+ {
+ inst.error = _("Invalid base register for register offset");
+ return;
+ }
+
+ if (Rb == REG_PC)
+ inst.instruction = T_OPCODE_LDR_PC;
+ else if (load_store == THUMB_LOAD)
+ inst.instruction = T_OPCODE_LDR_SP;
+ else
+ inst.instruction = T_OPCODE_STR_SP;
+
+ inst.instruction |= Rd << 8;
+ if (inst.reloc.exp.X_op == O_constant)
+ {
+ unsigned offset = inst.reloc.exp.X_add_number;
+
+ if (offset & ~0x3fc)
+ {
+ inst.error = _("invalid offset");
+ return;
+ }
+
+ inst.instruction |= offset >> 2;
+ }
+ else
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+ }
+ else if (Rb > 7)
+ {
+ inst.error = _("invalid base register in load/store");
+ return;
+ }
+ else if (Ro == FAIL)
+ {
+ /* Immediate offset */
+ if (size == THUMB_WORD)
+ inst.instruction = (load_store == THUMB_LOAD
+ ? T_OPCODE_LDR_IW : T_OPCODE_STR_IW);
+ else if (size == THUMB_HALFWORD)
+ inst.instruction = (load_store == THUMB_LOAD
+ ? T_OPCODE_LDR_IH : T_OPCODE_STR_IH);
+ else
+ inst.instruction = (load_store == THUMB_LOAD
+ ? T_OPCODE_LDR_IB : T_OPCODE_STR_IB);
+
+ inst.instruction |= Rd | (Rb << 3);
+
+ if (inst.reloc.exp.X_op == O_constant)
+ {
+ unsigned offset = inst.reloc.exp.X_add_number;
+
+ if (offset & ~(0x1f << size))
+ {
+ inst.error = _("Invalid offset");
+ return;
+ }
+ inst.instruction |= (offset >> size) << 6;
+ }
+ else
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+ }
+ else
+ {
+ /* Register offset */
+ if (size == THUMB_WORD)
+ inst.instruction = (load_store == THUMB_LOAD
+ ? T_OPCODE_LDR_RW : T_OPCODE_STR_RW);
+ else if (size == THUMB_HALFWORD)
+ inst.instruction = (load_store == THUMB_LOAD
+ ? T_OPCODE_LDR_RH : T_OPCODE_STR_RH);
+ else
+ inst.instruction = (load_store == THUMB_LOAD
+ ? T_OPCODE_LDR_RB : T_OPCODE_STR_RB);
+
+ inst.instruction |= Rd | (Rb << 3) | (Ro << 6);
+ }
+
+ end_of_line (str);
+}
+
+static void
+do_t_nop (str)
+ char * str;
+{
+ /* Do nothing */
+ end_of_line (str);
+ return;
+}
+
+/* Handle the Format 4 instructions that do not have equivalents in other
+ formats. That is, ADC, AND, EOR, SBC, ROR, TST, NEG, CMN, ORR, MUL,
+ BIC and MVN. */
+static void
+do_t_arit (str)
+ char * str;
+{
+ int Rd, Rs, Rn;
+
+ while (*str == ' ')
+ str++;
+
+ if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
+ return;
+
+ if (skip_past_comma (&str) == FAIL
+ || (Rs = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (skip_past_comma (&str) != FAIL)
+ {
+ /* Three operand format not allowed for TST, CMN, NEG and MVN.
+ (It isn't allowed for CMP either, but that isn't handled by this
+ function.) */
+ if (inst.instruction == T_OPCODE_TST
+ || inst.instruction == T_OPCODE_CMN
+ || inst.instruction == T_OPCODE_NEG
+ || inst.instruction == T_OPCODE_MVN)
+ {
+ inst.error = bad_args;
+ return;
+ }
+
+ if ((Rn = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
+ return;
+
+ if (Rs != Rd)
+ {
+ inst.error = _("dest and source1 one must be the same register");
+ return;
+ }
+ Rs = Rn;
+ }
+
+ if (inst.instruction == T_OPCODE_MUL
+ && Rs == Rd)
+ as_tsktsk (_("Rs and Rd must be different in MUL"));
+
+ inst.instruction |= Rd | (Rs << 3);
+ end_of_line (str);
+}
+
+static void
+do_t_add (str)
+ char * str;
+{
+ thumb_add_sub (str, 0);
+}
+
+static void
+do_t_asr (str)
+ char * str;
+{
+ thumb_shift (str, THUMB_ASR);
+}
+
+static void
+do_t_branch9 (str)
+ char * str;
+{
+ if (my_get_expression (&inst.reloc.exp, &str))
+ return;
+ inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH9;
+ inst.reloc.pc_rel = 1;
+ end_of_line (str);
+}
+
+static void
+do_t_branch12 (str)
+ char * str;
+{
+ if (my_get_expression (&inst.reloc.exp, &str))
+ return;
+ inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH12;
+ inst.reloc.pc_rel = 1;
+ end_of_line (str);
+}
+
+/* Find the real, Thumb encoded start of a Thumb function. */
+
+static symbolS *
+find_real_start (symbolP)
+ symbolS * symbolP;
+{
+ char * real_start;
+ const char * name = S_GET_NAME (symbolP);
+ symbolS * new_target;
+
+ /* This definitonmust agree with the one in gcc/config/arm/thumb.c */
+#define STUB_NAME ".real_start_of"
+
+ if (name == NULL)
+ abort();
+
+ /* Names that start with '.' are local labels, not function entry points.
+ The compiler may generate BL instructions to these labels because it
+ needs to perform a branch to a far away location. */
+ if (name[0] == '.')
+ return symbolP;
+
+ real_start = malloc (strlen (name) + strlen (STUB_NAME) + 1);
+ sprintf (real_start, "%s%s", STUB_NAME, name);
+
+ new_target = symbol_find (real_start);
+
+ if (new_target == NULL)
+ {
+ as_warn ("Failed to find real start of function: %s\n", name);
+ new_target = symbolP;
+ }
+
+ free (real_start);
+
+ return new_target;
+}
+
+
+static void
+do_t_branch23 (str)
+ char * str;
+{
+ if (my_get_expression (&inst.reloc.exp, &str))
+ return;
+ inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH23;
+ inst.reloc.pc_rel = 1;
+ end_of_line (str);
+
+ /* If the destination of the branch is a defined symbol which does not have
+ the THUMB_FUNC attribute, then we must be calling a function which has
+ the (interfacearm) attribute. We look for the Thumb entry point to that
+ function and change the branch to refer to that function instead. */
+ if ( inst.reloc.exp.X_op == O_symbol
+ && inst.reloc.exp.X_add_symbol != NULL
+ && S_IS_DEFINED (inst.reloc.exp.X_add_symbol)
+ && ! THUMB_IS_FUNC (inst.reloc.exp.X_add_symbol))
+ inst.reloc.exp.X_add_symbol = find_real_start (inst.reloc.exp.X_add_symbol);
+}
+
+static void
+do_t_bx (str)
+ char * str;
+{
+ int reg;
+
+ while (*str == ' ')
+ str++;
+
+ if ((reg = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
+ return;
+
+ /* This sets THUMB_H2 from the top bit of reg. */
+ inst.instruction |= reg << 3;
+
+ /* ??? FIXME: Should add a hacky reloc here if reg is REG_PC. The reloc
+ should cause the alignment to be checked once it is known. This is
+ because BX PC only works if the instruction is word aligned. */
+
+ end_of_line (str);
+}
+
+static void
+do_t_compare (str)
+ char * str;
+{
+ thumb_mov_compare (str, THUMB_COMPARE);
+}
+
+static void
+do_t_ldmstm (str)
+ char * str;
+{
+ int Rb;
+ long range;
+
+ while (*str == ' ')
+ str++;
+
+ if ((Rb = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
+ return;
+
+ if (*str != '!')
+ as_warn (_("Inserted missing '!': load/store multiple always writes back base register"));
+ else
+ str++;
+
+ if (skip_past_comma (&str) == FAIL
+ || (range = reg_list (&str)) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (inst.reloc.type != BFD_RELOC_NONE)
+ {
+ /* This really doesn't seem worth it. */
+ inst.reloc.type = BFD_RELOC_NONE;
+ inst.error = _("Expression too complex");
+ return;
+ }
+
+ if (range & ~0xff)
+ {
+ inst.error = _("only lo-regs valid in load/store multiple");
+ return;
+ }
+
+ inst.instruction |= (Rb << 8) | range;
+ end_of_line (str);
+}
+
+static void
+do_t_ldr (str)
+ char * str;
+{
+ thumb_load_store (str, THUMB_LOAD, THUMB_WORD);
+}
+
+static void
+do_t_ldrb (str)
+ char * str;
+{
+ thumb_load_store (str, THUMB_LOAD, THUMB_BYTE);
+}
+
+static void
+do_t_ldrh (str)
+ char * str;
+{
+ thumb_load_store (str, THUMB_LOAD, THUMB_HALFWORD);
+}
+
+static void
+do_t_lds (str)
+ char * str;
+{
+ int Rd, Rb, Ro;
+
+ while (*str == ' ')
+ str++;
+
+ if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || *str++ != '['
+ || (Rb = thumb_reg (&str, THUMB_REG_LO)) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || (Ro = thumb_reg (&str, THUMB_REG_LO)) == FAIL
+ || *str++ != ']')
+ {
+ if (! inst.error)
+ inst.error = _("Syntax: ldrs[b] Rd, [Rb, Ro]");
+ return;
+ }
+
+ inst.instruction |= Rd | (Rb << 3) | (Ro << 6);
+ end_of_line (str);
+}
+
+static void
+do_t_lsl (str)
+ char * str;
+{
+ thumb_shift (str, THUMB_LSL);
+}
+
+static void
+do_t_lsr (str)
+ char * str;
+{
+ thumb_shift (str, THUMB_LSR);
+}
+
+static void
+do_t_mov (str)
+ char * str;
+{
+ thumb_mov_compare (str, THUMB_MOVE);
+}
+
+static void
+do_t_push_pop (str)
+ char * str;
+{
+ long range;
+
+ while (*str == ' ')
+ str++;
+
+ if ((range = reg_list (&str)) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ if (inst.reloc.type != BFD_RELOC_NONE)
+ {
+ /* This really doesn't seem worth it. */
+ inst.reloc.type = BFD_RELOC_NONE;
+ inst.error = _("Expression too complex");
+ return;
+ }
+
+ if (range & ~0xff)
+ {
+ if ((inst.instruction == T_OPCODE_PUSH
+ && (range & ~0xff) == 1 << REG_LR)
+ || (inst.instruction == T_OPCODE_POP
+ && (range & ~0xff) == 1 << REG_PC))
+ {
+ inst.instruction |= THUMB_PP_PC_LR;
+ range &= 0xff;
+ }
+ else
+ {
+ inst.error = _("invalid register list to push/pop instruction");
+ return;
+ }
+ }
+
+ inst.instruction |= range;
+ end_of_line (str);
+}
+
+static void
+do_t_str (str)
+ char * str;
+{
+ thumb_load_store (str, THUMB_STORE, THUMB_WORD);
+}
+
+static void
+do_t_strb (str)
+ char * str;
+{
+ thumb_load_store (str, THUMB_STORE, THUMB_BYTE);
+}
+
+static void
+do_t_strh (str)
+ char * str;
+{
+ thumb_load_store (str, THUMB_STORE, THUMB_HALFWORD);
+}
+
+static void
+do_t_sub (str)
+ char * str;
+{
+ thumb_add_sub (str, 1);
+}
+
+static void
+do_t_swi (str)
+ char * str;
+{
+ while (*str == ' ')
+ str++;
+
+ if (my_get_expression (&inst.reloc.exp, &str))
+ return;
+
+ inst.reloc.type = BFD_RELOC_ARM_SWI;
+ end_of_line (str);
+ return;
+}
+
+static void
+do_t_adr (str)
+ char * str;
+{
+ /* This is a pseudo-op of the form "adr rd, label" to be converted
+ into a relative address of the form "add rd, pc, #label-.-4" */
+ while (*str == ' ')
+ str++;
+
+ if (reg_required_here (&str, 4) == FAIL /* Store Rd in temporary location inside instruction. */
+ || skip_past_comma (&str) == FAIL
+ || my_get_expression (&inst.reloc.exp, &str))
+ {
+ if (!inst.error)
+ inst.error = bad_args;
+ return;
+ }
+
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+ inst.reloc.exp.X_add_number -= 4; /* PC relative adjust */
+ inst.reloc.pc_rel = 1;
+ inst.instruction |= REG_PC; /* Rd is already placed into the instruction */
+ end_of_line (str);
+}
+
+static void
+insert_reg (entry)
+ int entry;
+{
+ int len = strlen (reg_table[entry].name) + 2;
+ char * buf = (char *) xmalloc (len);
+ char * buf2 = (char *) xmalloc (len);
+ int i = 0;
+
+#ifdef REGISTER_PREFIX
+ buf[i++] = REGISTER_PREFIX;
+#endif
+
+ strcpy (buf + i, reg_table[entry].name);
+
+ for (i = 0; buf[i]; i++)
+ buf2[i] = islower (buf[i]) ? toupper (buf[i]) : buf[i];
+
+ buf2[i] = '\0';
+
+ hash_insert (arm_reg_hsh, buf, (PTR) &reg_table[entry]);
+ hash_insert (arm_reg_hsh, buf2, (PTR) &reg_table[entry]);
+}
+
+static void
+insert_reg_alias (str, regnum)
+ char *str;
+ int regnum;
+{
+ struct reg_entry *new =
+ (struct reg_entry *)xmalloc (sizeof (struct reg_entry));
+ char *name = xmalloc (strlen (str) + 1);
+ strcpy (name, str);
+
+ new->name = name;
+ new->number = regnum;
+
+ hash_insert (arm_reg_hsh, name, (PTR) new);
+}
+
+static void
+set_constant_flonums ()
+{
+ int i;
+
+ for (i = 0; i < NUM_FLOAT_VALS; i++)
+ if (atof_ieee ((char *)fp_const[i], 'x', fp_values[i]) == NULL)
+ abort ();
+}
+
+void
+md_begin ()
+{
+ int i;
+
+ if ( (arm_ops_hsh = hash_new ()) == NULL
+ || (arm_tops_hsh = hash_new ()) == NULL
+ || (arm_cond_hsh = hash_new ()) == NULL
+ || (arm_shift_hsh = hash_new ()) == NULL
+ || (arm_reg_hsh = hash_new ()) == NULL
+ || (arm_psr_hsh = hash_new ()) == NULL)
+ as_fatal (_("Virtual memory exhausted"));
+
+ for (i = 0; i < sizeof (insns) / sizeof (struct asm_opcode); i++)
+ hash_insert (arm_ops_hsh, insns[i].template, (PTR) (insns + i));
+ for (i = 0; i < sizeof (tinsns) / sizeof (struct thumb_opcode); i++)
+ hash_insert (arm_tops_hsh, tinsns[i].template, (PTR) (tinsns + i));
+ for (i = 0; i < sizeof (conds) / sizeof (struct asm_cond); i++)
+ hash_insert (arm_cond_hsh, conds[i].template, (PTR) (conds + i));
+ for (i = 0; i < sizeof (shift) / sizeof (struct asm_shift); i++)
+ hash_insert (arm_shift_hsh, shift[i].template, (PTR) (shift + i));
+ for (i = 0; i < sizeof (psrs) / sizeof (struct asm_psr); i++)
+ hash_insert (arm_psr_hsh, psrs[i].template, (PTR) (psrs + i));
+
+ for (i = 0; reg_table[i].name; i++)
+ insert_reg (i);
+
+ set_constant_flonums ();
+
+#if defined OBJ_COFF || defined OBJ_ELF
+ {
+ unsigned int flags = 0;
+
+ /* Set the flags in the private structure */
+ if (uses_apcs_26) flags |= F_APCS26;
+ if (support_interwork) flags |= F_INTERWORK;
+ if (uses_apcs_float) flags |= F_APCS_FLOAT;
+ if (pic_code) flags |= F_PIC;
+
+ bfd_set_private_flags (stdoutput, flags);
+ }
+#endif
+
+ {
+ unsigned mach;
+
+ /* Record the CPU type as well */
+ switch (cpu_variant & ARM_CPU_MASK)
+ {
+ case ARM_2:
+ mach = bfd_mach_arm_2;
+ break;
+
+ case ARM_3: /* also ARM_250 */
+ mach = bfd_mach_arm_2a;
+ break;
+
+ default:
+ case ARM_6 | ARM_3 | ARM_2: /* Actually no CPU type defined */
+ mach = bfd_mach_arm_4;
+ break;
+
+ case ARM_7: /* also ARM_6 */
+ mach = bfd_mach_arm_3;
+ break;
+ }
+
+ /* Catch special cases */
+ if (cpu_variant != (FPU_DEFAULT | CPU_DEFAULT))
+ {
+ if (cpu_variant & ARM_THUMB)
+ mach = bfd_mach_arm_4T;
+ else if ((cpu_variant & ARM_ARCHv4) == ARM_ARCHv4)
+ mach = bfd_mach_arm_4;
+ else if (cpu_variant & ARM_LONGMUL)
+ mach = bfd_mach_arm_3M;
+ }
+
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach);
+ }
+}
+
+/* Turn an integer of n bytes (in val) into a stream of bytes appropriate
+ for use in the a.out file, and stores them in the array pointed to by buf.
+ This knows about the endian-ness of the target machine and does
+ THE RIGHT THING, whatever it is. Possible values for n are 1 (byte)
+ 2 (short) and 4 (long) Floating numbers are put out as a series of
+ LITTLENUMS (shorts, here at least)
+ */
+void
+md_number_to_chars (buf, val, n)
+ char * buf;
+ valueT val;
+ int n;
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+static valueT
+md_chars_to_number (buf, n)
+ char * buf;
+ int n;
+{
+ valueT result = 0;
+ unsigned char * where = (unsigned char *) buf;
+
+ if (target_big_endian)
+ {
+ while (n--)
+ {
+ result <<= 8;
+ result |= (*where++ & 255);
+ }
+ }
+ else
+ {
+ while (n--)
+ {
+ result <<= 8;
+ result |= (where[n] & 255);
+ }
+ }
+
+ return result;
+}
+
+/* Turn a string in input_line_pointer into a floating point constant
+ of type TYPE, and store the appropriate bytes in *litP. The number
+ of LITTLENUMS emitted is stored in *sizeP . An error message is
+ returned, or NULL on OK.
+
+ Note that fp constants aren't represent in the normal way on the ARM.
+ In big endian mode, things are as expected. However, in little endian
+ mode fp constants are big-endian word-wise, and little-endian byte-wise
+ within the words. For example, (double) 1.1 in big endian mode is
+ the byte sequence 3f f1 99 99 99 99 99 9a, and in little endian mode is
+ the byte sequence 99 99 f1 3f 9a 99 99 99.
+
+ ??? The format of 12 byte floats is uncertain according to gcc's arm.h. */
+
+char *
+md_atof (type, litP, sizeP)
+ char type;
+ char * litP;
+ int * sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ char *t;
+ int i;
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("Bad call to MD_ATOF()");
+ }
+
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+ *sizeP = prec * 2;
+
+ if (target_big_endian)
+ {
+ for (i = 0; i < prec; i++)
+ {
+ md_number_to_chars (litP, (valueT) words[i], 2);
+ litP += 2;
+ }
+ }
+ else
+ {
+ /* For a 4 byte float the order of elements in `words' is 1 0. For an
+ 8 byte float the order is 1 0 3 2. */
+ for (i = 0; i < prec; i += 2)
+ {
+ md_number_to_chars (litP, (valueT) words[i + 1], 2);
+ md_number_to_chars (litP + 2, (valueT) words[i], 2);
+ litP += 4;
+ }
+ }
+
+ return 0;
+}
+
+/* The knowledge of the PC's pipeline offset is built into the relocs
+ for the ELF port and into the insns themselves for the COFF port. */
+long
+md_pcrel_from (fixP)
+ fixS * fixP;
+{
+ if ( fixP->fx_addsy
+ && S_GET_SEGMENT (fixP->fx_addsy) == undefined_section
+ && fixP->fx_subsy == NULL)
+ return 0;
+
+ if (fixP->fx_pcrel && (fixP->fx_r_type == BFD_RELOC_ARM_THUMB_ADD))
+ {
+ /* PC relative addressing on the Thumb is slightly odd
+ as the bottom two bits of the PC are forced to zero
+ for the calculation */
+ return (fixP->fx_where + fixP->fx_frag->fr_address) & ~3;
+ }
+
+ return fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+/* Round up a section size to the appropriate boundary. */
+valueT
+md_section_align (segment, size)
+ segT segment;
+ valueT size;
+{
+#ifdef OBJ_ELF
+ /* Don't align the dwarf2 debug sections */
+ if (!strncmp (segment->name, ".debug", 5))
+ return size;
+#endif
+ /* Round all sects to multiple of 4 */
+ return (size + 3) & ~3;
+}
+
+/* Under ELF we need to default _GLOBAL_OFFSET_TABLE. Otherwise
+ we have no need to default values of symbols. */
+
+/* ARGSUSED */
+symbolS *
+md_undefined_symbol (name)
+ char * name;
+{
+#ifdef OBJ_ELF
+ if (name[0] == '_' && name[1] == 'G'
+ && streq (name, GLOBAL_OFFSET_TABLE_NAME))
+ {
+ if (!GOT_symbol)
+ {
+ if (symbol_find (name))
+ as_bad ("GOT already in the symbol table");
+
+ GOT_symbol = symbol_new (name, undefined_section,
+ (valueT)0, & zero_address_frag);
+ }
+
+ return GOT_symbol;
+ }
+#endif
+
+ return 0;
+}
+
+/* arm_reg_parse () := if it looks like a register, return its token and
+ advance the pointer. */
+
+static int
+arm_reg_parse (ccp)
+ register char ** ccp;
+{
+ char * start = * ccp;
+ char c;
+ char * p;
+ struct reg_entry * reg;
+
+#ifdef REGISTER_PREFIX
+ if (*start != REGISTER_PREFIX)
+ return FAIL;
+ p = start + 1;
+#else
+ p = start;
+#ifdef OPTIONAL_REGISTER_PREFIX
+ if (*p == OPTIONAL_REGISTER_PREFIX)
+ p++, start++;
+#endif
+#endif
+ if (!isalpha (*p) || !is_name_beginner (*p))
+ return FAIL;
+
+ c = *p++;
+ while (isalpha (c) || isdigit (c) || c == '_')
+ c = *p++;
+
+ *--p = 0;
+ reg = (struct reg_entry *) hash_find (arm_reg_hsh, start);
+ *p = c;
+
+ if (reg)
+ {
+ *ccp = p;
+ return reg->number;
+ }
+
+ return FAIL;
+}
+
+static int
+arm_psr_parse (ccp)
+ register char ** ccp;
+{
+ char * start = * ccp;
+ char c;
+ char * p;
+ CONST struct asm_psr * psr;
+
+ p = start;
+ c = *p++;
+ while (isalpha (c) || c == '_')
+ c = *p++;
+
+ *--p = 0;
+ psr = (CONST struct asm_psr *) hash_find (arm_psr_hsh, start);
+ *p = c;
+
+ if (psr)
+ {
+ *ccp = p;
+ return psr->number;
+ }
+
+ return FAIL;
+}
+
+int
+md_apply_fix3 (fixP, val, seg)
+ fixS * fixP;
+ valueT * val;
+ segT seg;
+{
+ offsetT value = * val;
+ offsetT newval;
+ unsigned int newimm;
+ unsigned long temp;
+ int sign;
+ char * buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ arm_fix_data * arm_data = (arm_fix_data *) fixP->tc_fix_data;
+
+ assert (fixP->fx_r_type < BFD_RELOC_UNUSED);
+
+ /* Note whether this will delete the relocation. */
+#if 0 /* patch from REarnshaw to JDavis (disabled for the moment, since it doesn't work fully) */
+ if ((fixP->fx_addsy == 0 || fixP->fx_addsy->sy_value.X_op == O_constant)
+ && !fixP->fx_pcrel)
+#else
+ if (fixP->fx_addsy == 0 && !fixP->fx_pcrel)
+#endif
+ fixP->fx_done = 1;
+
+ /* If this symbol is in a different section then we need to leave it for
+ the linker to deal with. Unfortunately, md_pcrel_from can't tell,
+ so we have to undo it's effects here. */
+ if (fixP->fx_pcrel)
+ {
+ if (fixP->fx_addsy != NULL
+ && S_IS_DEFINED (fixP->fx_addsy)
+ && S_GET_SEGMENT (fixP->fx_addsy) != seg)
+ {
+ if (fixP->fx_r_type == BFD_RELOC_ARM_PCREL_BRANCH)
+ value = 0;
+ else
+ value += md_pcrel_from (fixP);
+ }
+ }
+
+ fixP->fx_addnumber = value; /* Remember value for emit_reloc */
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_ARM_IMMEDIATE:
+ newimm = validate_immediate (value);
+ temp = md_chars_to_number (buf, INSN_SIZE);
+
+ /* If the instruction will fail, see if we can fix things up by
+ changing the opcode. */
+ if (newimm == (unsigned int) FAIL
+ && (newimm = negate_data_op (&temp, value)) == (unsigned int) FAIL)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid constant (%x) after fixup\n"), value);
+ break;
+ }
+
+ newimm |= (temp & 0xfffff000);
+ md_number_to_chars (buf, (valueT) newimm, INSN_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_OFFSET_IMM:
+ sign = value >= 0;
+ if ((value = validate_offset_imm (value, 0)) == FAIL)
+ {
+ as_bad (_("bad immediate value for offset (%d)"), val);
+ break;
+ }
+ if (value < 0)
+ value = -value;
+
+ newval = md_chars_to_number (buf, INSN_SIZE);
+ newval &= 0xff7ff000;
+ newval |= value | (sign ? INDEX_UP : 0);
+ md_number_to_chars (buf, newval, INSN_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_OFFSET_IMM8:
+ case BFD_RELOC_ARM_HWLITERAL:
+ sign = value >= 0;
+ if ((value = validate_offset_imm (value, 1)) == FAIL)
+ {
+ if (fixP->fx_r_type == BFD_RELOC_ARM_HWLITERAL)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid literal constant: pool needs to be closer\n"));
+ else
+ as_bad (_("bad immediate value for offset (%d)"), value);
+ break;
+ }
+
+ if (value < 0)
+ value = -value;
+
+ newval = md_chars_to_number (buf, INSN_SIZE);
+ newval &= 0xff7ff0f0;
+ newval |= ((value >> 4) << 8) | value & 0xf | (sign ? INDEX_UP : 0);
+ md_number_to_chars (buf, newval, INSN_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_LITERAL:
+ sign = value >= 0;
+ if (value < 0)
+ value = -value;
+
+ if ((value = validate_offset_imm (value, 0)) == FAIL)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid literal constant: pool needs to be closer\n"));
+ break;
+ }
+
+ newval = md_chars_to_number (buf, INSN_SIZE);
+ newval &= 0xff7ff000;
+ newval |= value | (sign ? INDEX_UP : 0);
+ md_number_to_chars (buf, newval, INSN_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_SHIFT_IMM:
+ newval = md_chars_to_number (buf, INSN_SIZE);
+ if (((unsigned long) value) > 32
+ || (value == 32
+ && (((newval & 0x60) == 0) || (newval & 0x60) == 0x60)))
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("shift expression is too large"));
+ break;
+ }
+
+ if (value == 0)
+ newval &= ~0x60; /* Shifts of zero must be done as lsl */
+ else if (value == 32)
+ value = 0;
+ newval &= 0xfffff07f;
+ newval |= (value & 0x1f) << 7;
+ md_number_to_chars (buf, newval , INSN_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_SWI:
+ if (arm_data->thumb_mode)
+ {
+ if (((unsigned long) value) > 0xff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Invalid swi expression"));
+ newval = md_chars_to_number (buf, THUMB_SIZE) & 0xff00;
+ newval |= value;
+ md_number_to_chars (buf, newval, THUMB_SIZE);
+ }
+ else
+ {
+ if (((unsigned long) value) > 0x00ffffff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Invalid swi expression"));
+ newval = md_chars_to_number (buf, INSN_SIZE) & 0xff000000;
+ newval |= value;
+ md_number_to_chars (buf, newval , INSN_SIZE);
+ }
+ break;
+
+ case BFD_RELOC_ARM_MULTI:
+ if (((unsigned long) value) > 0xffff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Invalid expression in load/store multiple"));
+ newval = value | md_chars_to_number (buf, INSN_SIZE);
+ md_number_to_chars (buf, newval, INSN_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_PCREL_BRANCH:
+ newval = md_chars_to_number (buf, INSN_SIZE);
+#ifdef OBJ_ELF
+ newval &= 0xff000000;
+ if (! target_oabi)
+ value = fixP->fx_offset;
+ else
+#else
+ value = (value >> 2) & 0x00ffffff;
+#endif
+ value = (value + (newval & 0x00ffffff)) & 0x00ffffff;
+ newval = value | (newval & 0xff000000);
+ md_number_to_chars (buf, newval, INSN_SIZE);
+ break;
+
+ case BFD_RELOC_THUMB_PCREL_BRANCH9: /* conditional branch */
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ {
+ addressT diff = (newval & 0xff) << 1;
+ if (diff & 0x100)
+ diff |= ~0xff;
+
+ value += diff;
+ if ((value & ~0xff) && ((value & ~0xff) != ~0xff))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Branch out of range"));
+ newval = (newval & 0xff00) | ((value & 0x1ff) >> 1);
+ }
+ md_number_to_chars (buf, newval, THUMB_SIZE);
+ break;
+
+ case BFD_RELOC_THUMB_PCREL_BRANCH12: /* unconditional branch */
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ {
+ addressT diff = (newval & 0x7ff) << 1;
+ if (diff & 0x800)
+ diff |= ~0x7ff;
+
+ value += diff;
+ if ((value & ~0x7ff) && ((value & ~0x7ff) != ~0x7ff))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Branch out of range"));
+ newval = (newval & 0xf800) | ((value & 0xfff) >> 1);
+ }
+ md_number_to_chars (buf, newval, THUMB_SIZE);
+ break;
+
+ case BFD_RELOC_THUMB_PCREL_BRANCH23:
+ {
+ offsetT newval2;
+ addressT diff;
+
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ newval2 = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE);
+ diff = ((newval & 0x7ff) << 12) | ((newval2 & 0x7ff) << 1);
+ if (diff & 0x400000)
+ diff |= ~0x3fffff;
+ value += diff;
+ if ((value & ~0x3fffff) && ((value & ~0x3fffff) != ~0x3fffff))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Branch with link out of range"));
+
+ newval = (newval & 0xf800) | ((value & 0x7fffff) >> 12);
+ newval2 = (newval2 & 0xf800) | ((value & 0xfff) >> 1);
+ md_number_to_chars (buf, newval, THUMB_SIZE);
+ md_number_to_chars (buf + THUMB_SIZE, newval2, THUMB_SIZE);
+ }
+ break;
+
+ case BFD_RELOC_8:
+ if (fixP->fx_done || fixP->fx_pcrel)
+ md_number_to_chars (buf, value, 1);
+#ifdef OBJ_ELF
+ else if (!target_oabi)
+ {
+ value = fixP->fx_offset;
+ md_number_to_chars (buf, value, 1);
+ }
+#endif
+ break;
+
+ case BFD_RELOC_16:
+ if (fixP->fx_done || fixP->fx_pcrel)
+ md_number_to_chars (buf, value, 2);
+#ifdef OBJ_ELF
+ else if (!target_oabi)
+ {
+ value = fixP->fx_offset;
+ md_number_to_chars (buf, value, 2);
+ }
+#endif
+ break;
+
+#ifdef OBJ_ELF
+ case BFD_RELOC_ARM_GOT32:
+ case BFD_RELOC_ARM_GOTOFF:
+ md_number_to_chars (buf, 0, 4);
+ break;
+#endif
+
+ case BFD_RELOC_RVA:
+ case BFD_RELOC_32:
+ if (fixP->fx_done || fixP->fx_pcrel)
+ md_number_to_chars (buf, value, 4);
+#ifdef OBJ_ELF
+ else if (!target_oabi)
+ {
+ value = fixP->fx_offset;
+ md_number_to_chars (buf, value, 4);
+ }
+#endif
+ break;
+
+#ifdef OBJ_ELF
+ case BFD_RELOC_ARM_PLT32:
+ /* It appears the instruction is fully prepared at this point. */
+ break;
+#endif
+
+ case BFD_RELOC_ARM_GOTPC:
+ md_number_to_chars (buf, value, 4);
+ break;
+
+ case BFD_RELOC_ARM_CP_OFF_IMM:
+ sign = value >= 0;
+ if (value < -1023 || value > 1023 || (value & 3))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Illegal value for co-processor offset"));
+ if (value < 0)
+ value = -value;
+ newval = md_chars_to_number (buf, INSN_SIZE) & 0xff7fff00;
+ newval |= (value >> 2) | (sign ? INDEX_UP : 0);
+ md_number_to_chars (buf, newval , INSN_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_THUMB_OFFSET:
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ /* Exactly what ranges, and where the offset is inserted depends on
+ the type of instruction, we can establish this from the top 4 bits */
+ switch (newval >> 12)
+ {
+ case 4: /* PC load */
+ /* Thumb PC loads are somewhat odd, bit 1 of the PC is
+ forced to zero for these loads, so we will need to round
+ up the offset if the instruction address is not word
+ aligned (since the final address produced must be, and
+ we can only describe word-aligned immediate offsets). */
+
+ if ((fixP->fx_frag->fr_address + fixP->fx_where + value) & 3)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Invalid offset, target not word aligned (0x%08X)"),
+ (unsigned int)(fixP->fx_frag->fr_address + fixP->fx_where + value));
+
+ if ((value + 2) & ~0x3fe)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Invalid offset"));
+
+ /* Round up, since pc will be rounded down. */
+ newval |= (value + 2) >> 2;
+ break;
+
+ case 9: /* SP load/store */
+ if (value & ~0x3fc)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Invalid offset"));
+ newval |= value >> 2;
+ break;
+
+ case 6: /* Word load/store */
+ if (value & ~0x7c)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Invalid offset"));
+ newval |= value << 4; /* 6 - 2 */
+ break;
+
+ case 7: /* Byte load/store */
+ if (value & ~0x1f)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Invalid offset"));
+ newval |= value << 6;
+ break;
+
+ case 8: /* Halfword load/store */
+ if (value & ~0x3e)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Invalid offset"));
+ newval |= value << 5; /* 6 - 1 */
+ break;
+
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ "Unable to process relocation for thumb opcode: %x", newval);
+ break;
+ }
+ md_number_to_chars (buf, newval, THUMB_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_THUMB_ADD:
+ /* This is a complicated relocation, since we use it for all of
+ the following immediate relocations:
+ 3bit ADD/SUB
+ 8bit ADD/SUB
+ 9bit ADD/SUB SP word-aligned
+ 10bit ADD PC/SP word-aligned
+
+ The type of instruction being processed is encoded in the
+ instruction field:
+ 0x8000 SUB
+ 0x00F0 Rd
+ 0x000F Rs
+ */
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ {
+ int rd = (newval >> 4) & 0xf;
+ int rs = newval & 0xf;
+ int subtract = newval & 0x8000;
+
+ if (rd == REG_SP)
+ {
+ if (value & ~0x1fc)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Invalid immediate for stack address calculation"));
+ newval = subtract ? T_OPCODE_SUB_ST : T_OPCODE_ADD_ST;
+ newval |= value >> 2;
+ }
+ else if (rs == REG_PC || rs == REG_SP)
+ {
+ if (subtract ||
+ value & ~0x3fc)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Invalid immediate for address calculation (value = 0x%08X)"), value);
+ newval = (rs == REG_PC ? T_OPCODE_ADD_PC : T_OPCODE_ADD_SP);
+ newval |= rd << 8;
+ newval |= value >> 2;
+ }
+ else if (rs == rd)
+ {
+ if (value & ~0xff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Invalid 8bit immediate"));
+ newval = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8;
+ newval |= (rd << 8) | value;
+ }
+ else
+ {
+ if (value & ~0x7)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Invalid 3bit immediate"));
+ newval = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3;
+ newval |= rd | (rs << 3) | (value << 6);
+ }
+ }
+ md_number_to_chars (buf, newval , THUMB_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_THUMB_IMM:
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ switch (newval >> 11)
+ {
+ case 0x04: /* 8bit immediate MOV */
+ case 0x05: /* 8bit immediate CMP */
+ if (value < 0 || value > 255)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Invalid immediate: %d is too large"), value);
+ newval |= value;
+ break;
+
+ default:
+ abort ();
+ }
+ md_number_to_chars (buf, newval , THUMB_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_THUMB_SHIFT:
+ /* 5bit shift value (0..31) */
+ if (value < 0 || value > 31)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Illegal Thumb shift value: %d"), value);
+ newval = md_chars_to_number (buf, THUMB_SIZE) & 0xf03f;
+ newval |= value << 6;
+ md_number_to_chars (buf, newval , THUMB_SIZE);
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ return 1;
+
+ case BFD_RELOC_NONE:
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Bad relocation fixup type (%d)\n"), fixP->fx_r_type);
+ }
+
+ return 1;
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format. */
+arelent *
+tc_gen_reloc (section, fixp)
+ asection * section;
+ fixS * fixp;
+{
+ arelent * reloc;
+ bfd_reloc_code_real_type code;
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+
+ reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ /* @@ Why fx_addnumber sometimes and fx_offset other times? */
+#ifndef OBJ_ELF
+ if (fixp->fx_pcrel == 0)
+ reloc->addend = fixp->fx_offset;
+ else
+ reloc->addend = fixp->fx_offset = reloc->address;
+#else /* OBJ_ELF */
+ reloc->addend = fixp->fx_offset;
+#endif
+
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ if (fixp->fx_pcrel)
+ {
+ code = BFD_RELOC_8_PCREL;
+ break;
+ }
+
+ case BFD_RELOC_16:
+ if (fixp->fx_pcrel)
+ {
+ code = BFD_RELOC_16_PCREL;
+ break;
+ }
+
+ case BFD_RELOC_32:
+ if (fixp->fx_pcrel)
+ {
+ code = BFD_RELOC_32_PCREL;
+ break;
+ }
+
+ case BFD_RELOC_ARM_PCREL_BRANCH:
+ case BFD_RELOC_RVA:
+ case BFD_RELOC_THUMB_PCREL_BRANCH9:
+ case BFD_RELOC_THUMB_PCREL_BRANCH12:
+ case BFD_RELOC_THUMB_PCREL_BRANCH23:
+ case BFD_RELOC_VTABLE_ENTRY:
+ case BFD_RELOC_VTABLE_INHERIT:
+ code = fixp->fx_r_type;
+ break;
+
+ case BFD_RELOC_ARM_LITERAL:
+ case BFD_RELOC_ARM_HWLITERAL:
+ /* If this is called then the a literal has been referenced across
+ a section boundry - possibly due to an implicit dump */
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Literal referenced across section boundry (Implicit dump?)"));
+ return NULL;
+
+ case BFD_RELOC_ARM_GOTPC:
+ assert (fixp->fx_pcrel != 0);
+ code = fixp->fx_r_type;
+ code = BFD_RELOC_32_PCREL;
+ break;
+
+#ifdef OBJ_ELF
+ case BFD_RELOC_ARM_GOT32:
+ case BFD_RELOC_ARM_GOTOFF:
+ case BFD_RELOC_ARM_PLT32:
+ code = fixp->fx_r_type;
+ break;
+#endif
+
+ case BFD_RELOC_ARM_IMMEDIATE:
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Internal_relocation (type %d) not fixed up (IMMEDIATE)"),
+ fixp->fx_r_type);
+ return NULL;
+
+ case BFD_RELOC_ARM_OFFSET_IMM:
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Internal_relocation (type %d) not fixed up (OFFSET_IMM)"),
+ fixp->fx_r_type);
+ return NULL;
+
+ default:
+ {
+ char * type;
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_ARM_IMMEDIATE: type = "IMMEDIATE"; break;
+ case BFD_RELOC_ARM_OFFSET_IMM: type = "OFFSET_IMM"; break;
+ case BFD_RELOC_ARM_OFFSET_IMM8: type = "OFFSET_IMM8"; break;
+ case BFD_RELOC_ARM_SHIFT_IMM: type = "SHIFT_IMM"; break;
+ case BFD_RELOC_ARM_SWI: type = "SWI"; break;
+ case BFD_RELOC_ARM_MULTI: type = "MULTI"; break;
+ case BFD_RELOC_ARM_CP_OFF_IMM: type = "CP_OFF_IMM"; break;
+ case BFD_RELOC_ARM_THUMB_ADD: type = "THUMB_ADD"; break;
+ case BFD_RELOC_ARM_THUMB_SHIFT: type = "THUMB_SHIFT"; break;
+ case BFD_RELOC_ARM_THUMB_IMM: type = "THUMB_IMM"; break;
+ case BFD_RELOC_ARM_THUMB_OFFSET: type = "THUMB_OFFSET"; break;
+ default: type = "<unknown>"; break;
+ }
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Can not represent %s relocation in this object file format (%d)"),
+ type, fixp->fx_pcrel);
+ return NULL;
+ }
+ }
+
+#ifdef OBJ_ELF
+ if (code == BFD_RELOC_32_PCREL
+ && GOT_symbol
+ && fixp->fx_addsy == GOT_symbol)
+ code = BFD_RELOC_ARM_GOTPC;
+#endif
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Can not represent %s relocation in this object file format"),
+ bfd_get_reloc_code_name (code));
+ return NULL;
+ }
+
+ return reloc;
+}
+
+int
+md_estimate_size_before_relax (fragP, segtype)
+ fragS * fragP;
+ segT segtype;
+{
+ as_fatal (_("md_estimate_size_before_relax\n"));
+ return 1;
+}
+
+static void
+output_inst (str)
+ char * str;
+{
+ char * to = NULL;
+
+ if (inst.error)
+ {
+ as_bad (inst.error);
+ return;
+ }
+
+ to = frag_more (inst.size);
+ if (thumb_mode && (inst.size > THUMB_SIZE))
+ {
+ assert (inst.size == (2 * THUMB_SIZE));
+ md_number_to_chars (to, inst.instruction >> 16, THUMB_SIZE);
+ md_number_to_chars (to + 2, inst.instruction, THUMB_SIZE);
+ }
+ else
+ md_number_to_chars (to, inst.instruction, inst.size);
+
+ if (inst.reloc.type != BFD_RELOC_NONE)
+ fix_new_arm (frag_now, to - frag_now->fr_literal,
+ inst.size, & inst.reloc.exp, inst.reloc.pc_rel,
+ inst.reloc.type);
+
+ return;
+}
+
+void
+md_assemble (str)
+ char * str;
+{
+ char c;
+ char * p;
+ char * q;
+ char * start;
+
+ /* Align the instruction.
+ This may not be the right thing to do but ... */
+ /* arm_align (2, 0); */
+ listing_prev_line (); /* Defined in listing.h */
+
+ /* Align the previous label if needed. */
+ if (last_label_seen != NULL)
+ {
+ last_label_seen->sy_frag = frag_now;
+ S_SET_VALUE (last_label_seen, (valueT) frag_now_fix ());
+ S_SET_SEGMENT (last_label_seen, now_seg);
+ }
+
+ memset (&inst, '\0', sizeof (inst));
+ inst.reloc.type = BFD_RELOC_NONE;
+
+ if (*str == ' ')
+ str++; /* Skip leading white space */
+
+ /* Scan up to the end of the op-code, which must end in white space or
+ end of string. */
+ for (start = p = str; *p != '\0'; p++)
+ if (*p == ' ')
+ break;
+
+ if (p == str)
+ {
+ as_bad (_("No operator -- statement `%s'\n"), str);
+ return;
+ }
+
+ if (thumb_mode)
+ {
+ CONST struct thumb_opcode *opcode;
+
+ c = *p;
+ *p = '\0';
+ opcode = (CONST struct thumb_opcode *) hash_find (arm_tops_hsh, str);
+ *p = c;
+ if (opcode)
+ {
+ inst.instruction = opcode->value;
+ inst.size = opcode->size;
+ (*opcode->parms)(p);
+ output_inst (start);
+ return;
+ }
+ }
+ else
+ {
+ CONST struct asm_opcode *opcode;
+
+ inst.size = INSN_SIZE;
+ /* p now points to the end of the opcode, probably white space, but we
+ have to break the opcode up in case it contains condionals and flags;
+ keep trying with progressively smaller basic instructions until one
+ matches, or we run out of opcode. */
+ q = (p - str > LONGEST_INST) ? str + LONGEST_INST : p;
+ for (; q != str; q--)
+ {
+ c = *q;
+ *q = '\0';
+ opcode = (CONST struct asm_opcode *) hash_find (arm_ops_hsh, str);
+ *q = c;
+ if (opcode && opcode->template)
+ {
+ unsigned long flag_bits = 0;
+ char *r;
+
+ /* Check that this instruction is supported for this CPU */
+ if ((opcode->variants & cpu_variant) == 0)
+ goto try_shorter;
+
+ inst.instruction = opcode->value;
+ if (q == p) /* Just a simple opcode */
+ {
+ if (opcode->comp_suffix != 0)
+ as_bad (_("Opcode `%s' must have suffix from <%s>\n"), str,
+ opcode->comp_suffix);
+ else
+ {
+ inst.instruction |= COND_ALWAYS;
+ (*opcode->parms)(q, 0);
+ }
+ output_inst (start);
+ return;
+ }
+
+ /* Now check for a conditional */
+ r = q;
+ if (p - r >= 2)
+ {
+ CONST struct asm_cond *cond;
+ char d = *(r + 2);
+
+ *(r + 2) = '\0';
+ cond = (CONST struct asm_cond *) hash_find (arm_cond_hsh, r);
+ *(r + 2) = d;
+ if (cond)
+ {
+ if (cond->value == 0xf0000000)
+ as_tsktsk (
+_("Warning: Use of the 'nv' conditional is deprecated\n"));
+
+ inst.instruction |= cond->value;
+ r += 2;
+ }
+ else
+ inst.instruction |= COND_ALWAYS;
+ }
+ else
+ inst.instruction |= COND_ALWAYS;
+
+ /* if there is a compulsory suffix, it should come here, before
+ any optional flags. */
+ if (opcode->comp_suffix)
+ {
+ CONST char *s = opcode->comp_suffix;
+
+ while (*s)
+ {
+ inst.suffix++;
+ if (*r == *s)
+ break;
+ s++;
+ }
+
+ if (*s == '\0')
+ {
+ as_bad (_("Opcode `%s' must have suffix from <%s>\n"), str,
+ opcode->comp_suffix);
+ return;
+ }
+
+ r++;
+ }
+
+ /* The remainder, if any should now be flags for the instruction;
+ Scan these checking each one found with the opcode. */
+ if (r != p)
+ {
+ char d;
+ CONST struct asm_flg *flag = opcode->flags;
+
+ if (flag)
+ {
+ int flagno;
+
+ d = *p;
+ *p = '\0';
+
+ for (flagno = 0; flag[flagno].template; flagno++)
+ {
+ if (streq (r, flag[flagno].template))
+ {
+ flag_bits |= flag[flagno].set_bits;
+ break;
+ }
+ }
+
+ *p = d;
+ if (! flag[flagno].template)
+ goto try_shorter;
+ }
+ else
+ goto try_shorter;
+ }
+
+ (*opcode->parms) (p, flag_bits);
+ output_inst (start);
+ return;
+ }
+
+ try_shorter:
+ ;
+ }
+ }
+
+ /* It wasn't an instruction, but it might be a register alias of the form
+ alias .req reg
+ */
+ q = p;
+ while (*q == ' ')
+ q++;
+
+ c = *p;
+ *p = '\0';
+
+ if (*q && !strncmp (q, ".req ", 4))
+ {
+ int reg;
+ char * copy_of_str = str;
+ char * r;
+
+ q += 4;
+ while (*q == ' ')
+ q++;
+
+ for (r = q; *r != '\0'; r++)
+ if (*r == ' ')
+ break;
+
+ if (r != q)
+ {
+ int regnum;
+ char d = *r;
+
+ *r = '\0';
+ regnum = arm_reg_parse (& q);
+ *r = d;
+
+ reg = arm_reg_parse (& str);
+
+ if (reg == FAIL)
+ {
+ if (regnum != FAIL)
+ {
+ insert_reg_alias (str, regnum);
+ }
+ else
+ {
+ as_warn (_("register '%s' does not exist\n"), q);
+ }
+ }
+ else if (regnum != FAIL)
+ {
+ if (reg != regnum)
+ as_warn (_("ignoring redefinition of register alias '%s'"), copy_of_str );
+
+ /* Do not warn abpout redefinitions to the same alias. */
+ }
+ else
+ as_warn (_("ignoring redefinition of register alias '%s' to non-existant register '%s'"),
+ copy_of_str, q);
+ }
+ else
+ as_warn (_("ignoring incomplete .req pseuso op"));
+
+ *p = c;
+ return;
+ }
+
+ *p = c;
+ as_bad (_("bad instruction `%s'"), start);
+}
+
+/*
+ * md_parse_option
+ * Invocation line includes a switch not recognized by the base assembler.
+ * See if it's a processor-specific option. These are:
+ * Cpu variants, the arm part is optional:
+ * -m[arm]1 Currently not supported.
+ * -m[arm]2, -m[arm]250 Arm 2 and Arm 250 processor
+ * -m[arm]3 Arm 3 processor
+ * -m[arm]6[xx], Arm 6 processors
+ * -m[arm]7[xx][t][[d]m] Arm 7 processors
+ * -mstrongarm[110] Arm 8 processors
+ * -mall All (except the ARM1)
+ * FP variants:
+ * -mfpa10, -mfpa11 FPA10 and 11 co-processor instructions
+ * -mfpe-old (No float load/store multiples)
+ * -mno-fpu Disable all floating point instructions
+ * Run-time endian selection:
+ * -EB big endian cpu
+ * -EL little endian cpu
+ * ARM Procedure Calling Standard:
+ * -mapcs-32 32 bit APCS
+ * -mapcs-26 26 bit APCS
+ * -mapcs-float Pass floats in float regs
+ * -mapcs-reentrant Position independent code
+ * -mthumb-interwork Code supports Arm/Thumb interworking
+ * -moabi Old ELF ABI
+ */
+
+CONST char * md_shortopts = "m:k";
+struct option md_longopts[] =
+{
+#ifdef ARM_BI_ENDIAN
+#define OPTION_EB (OPTION_MD_BASE + 0)
+ {"EB", no_argument, NULL, OPTION_EB},
+#define OPTION_EL (OPTION_MD_BASE + 1)
+ {"EL", no_argument, NULL, OPTION_EL},
+#ifdef OBJ_ELF
+#define OPTION_OABI (OPTION_MD_BASE +2)
+ {"oabi", no_argument, NULL, OPTION_OABI},
+#endif
+#endif
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (c, arg)
+ int c;
+ char * arg;
+{
+ char * str = arg;
+
+ switch (c)
+ {
+#ifdef ARM_BI_ENDIAN
+ case OPTION_EB:
+ target_big_endian = 1;
+ break;
+ case OPTION_EL:
+ target_big_endian = 0;
+ break;
+#endif
+
+ case 'm':
+ switch (*str)
+ {
+ case 'f':
+ if (streq (str, "fpa10"))
+ cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_FPA10;
+ else if (streq (str, "fpa11"))
+ cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_FPA11;
+ else if (streq (str, "fpe-old"))
+ cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_CORE;
+ else
+ goto bad;
+ break;
+
+ case 'n':
+ if (streq (str, "no-fpu"))
+ cpu_variant &= ~FPU_ALL;
+ break;
+
+#ifdef OBJ_ELF
+ case 'o':
+ if (streq (str, "oabi"))
+ target_oabi = true;
+ break;
+#endif
+
+ case 't':
+ /* Limit assembler to generating only Thumb instructions: */
+ if (streq (str, "thumb"))
+ {
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_THUMB;
+ cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_NONE;
+ thumb_mode = 1;
+ }
+ else if (streq (str, "thumb-interwork"))
+ {
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_THUMB | ARM_ARCHv4;
+#if defined OBJ_COFF || defined OBJ_ELF
+ support_interwork = true;
+#endif
+ }
+ else
+ goto bad;
+ break;
+
+ default:
+ if (streq (str, "all"))
+ {
+ cpu_variant = ARM_ALL | FPU_ALL;
+ return 1;
+ }
+#if defined OBJ_COFF || defined OBJ_ELF
+ if (! strncmp (str, "apcs-", 5))
+ {
+ /* GCC passes on all command line options starting "-mapcs-..."
+ to us, so we must parse them here. */
+
+ str += 5;
+
+ if (streq (str, "32"))
+ {
+ uses_apcs_26 = false;
+ return 1;
+ }
+ else if (streq (str, "26"))
+ {
+ uses_apcs_26 = true;
+ return 1;
+ }
+ else if (streq (str, "frame"))
+ {
+ /* Stack frames are being generated - does not affect
+ linkage of code. */
+ return 1;
+ }
+ else if (streq (str, "stack-check"))
+ {
+ /* Stack checking is being performed - does not affect
+ linkage, but does require that the functions
+ __rt_stkovf_split_small and __rt_stkovf_split_big be
+ present in the final link. */
+
+ return 1;
+ }
+ else if (streq (str, "float"))
+ {
+ /* Floating point arguments are being passed in the floating
+ point registers. This does affect linking, since this
+ version of the APCS is incompatible with the version that
+ passes floating points in the integer registers. */
+
+ uses_apcs_float = true;
+ return 1;
+ }
+ else if (streq (str, "reentrant"))
+ {
+ /* Reentrant code has been generated. This does affect
+ linking, since there is no point in linking reentrant/
+ position independent code with absolute position code. */
+ pic_code = true;
+ return 1;
+ }
+
+ as_bad (_("Unrecognised APCS switch -m%s"), arg);
+ return 0;
+ }
+#endif
+ /* Strip off optional "arm" */
+ if (! strncmp (str, "arm", 3))
+ str += 3;
+
+ switch (*str)
+ {
+ case '1':
+ if (streq (str, "1"))
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_1;
+ else
+ goto bad;
+ break;
+
+ case '2':
+ if (streq (str, "2"))
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_2;
+ else if (streq (str, "250"))
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_250;
+ else
+ goto bad;
+ break;
+
+ case '3':
+ if (streq (str, "3"))
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_3;
+ else
+ goto bad;
+ break;
+
+ case '6':
+ switch (strtol (str, NULL, 10))
+ {
+ case 6:
+ case 60:
+ case 600:
+ case 610:
+ case 620:
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_6;
+ break;
+ default:
+ goto bad;
+ }
+ break;
+
+ case '7':
+ switch (strtol (str, & str, 10)) /* Eat the processor name */
+ {
+ case 7:
+ case 70:
+ case 700:
+ case 710:
+ case 7100:
+ case 7500:
+ break;
+ default:
+ goto bad;
+ }
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_7;
+ for (; *str; str++)
+ {
+ switch (* str)
+ {
+ case 't':
+ cpu_variant |= (ARM_THUMB | ARM_ARCHv4);
+ break;
+
+ case 'm':
+ cpu_variant |= ARM_LONGMUL;
+ break;
+
+ case 'f': /* fe => fp enabled cpu. */
+ if (str[1] == 'e')
+ ++ str;
+ else
+ goto bad;
+
+ case 'c': /* Left over from 710c processor name. */
+ case 'd': /* Debug */
+ case 'i': /* Embedded ICE */
+ /* Included for completeness in ARM processor naming. */
+ break;
+
+ default:
+ goto bad;
+ }
+ }
+ break;
+
+ case '8':
+ if (streq (str, "8") || streq (str, "810"))
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_8 | ARM_ARCHv4 | ARM_LONGMUL;
+ else
+ goto bad;
+ break;
+
+ case '9':
+ if (streq (str, "9"))
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_9 | ARM_ARCHv4 | ARM_LONGMUL;
+ else if (streq (str, "9tdmi"))
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_9 | ARM_ARCHv4 | ARM_LONGMUL | ARM_THUMB;
+ else
+ goto bad;
+ break;
+
+ case 's':
+ if (streq (str, "strongarm")
+ || streq (str, "strongarm110")
+ || streq (str, "strongarm1100"))
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_8 | ARM_ARCHv4 | ARM_LONGMUL;
+ else
+ goto bad;
+ break;
+
+ case 'v':
+ /* Select variant based on architecture rather than processor */
+ switch (*++str)
+ {
+ case '2':
+ switch (*++str)
+ {
+ case 'a': cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_3; break;
+ case 0: cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_2; break;
+ default: as_bad (_("Invalid architecture variant -m%s"), arg); break;
+ }
+ break;
+
+ case '3':
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_7;
+
+ switch (*++str)
+ {
+ case 'm': cpu_variant |= ARM_LONGMUL; break;
+ case 0: break;
+ default: as_bad (_("Invalid architecture variant -m%s"), arg); break;
+ }
+ break;
+
+ case '4':
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_ARCHv4;
+
+ switch (*++str)
+ {
+ case 't': cpu_variant |= ARM_THUMB; break;
+ case 0: break;
+ default: as_bad (_("Invalid architecture variant -m%s"), arg); break;
+ }
+ break;
+
+ default:
+ as_bad (_("Invalid architecture variant -m%s"), arg);
+ break;
+ }
+ break;
+
+ default:
+ bad:
+ as_bad (_("Invalid processor variant -m%s"), arg);
+ return 0;
+ }
+ }
+ break;
+
+ case 'k':
+ pic_code = 1;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (fp)
+ FILE * fp;
+{
+ fprintf (fp,
+_("\
+ ARM Specific Assembler Options:\n\
+ -m[arm][<processor name>] select processor variant\n\
+ -m[arm]v[2|2a|3|3m|4|4t] select architecture variant\n\
+ -mthumb only allow Thumb instructions\n\
+ -mthumb-interwork mark the assembled code as supporting interworking\n\
+ -mall allow any instruction\n\
+ -mfpa10, -mfpa11 select floating point architecture\n\
+ -mfpe-old don't allow floating-point multiple instructions\n\
+ -mno-fpu don't allow any floating-point instructions.\n"));
+ fprintf (fp,
+_("\
+ -k generate PIC code.\n"));
+#if defined OBJ_COFF || defined OBJ_ELF
+ fprintf (fp,
+_("\
+ -mapcs-32, -mapcs-26 specify which ARM Procedure Calling Standard to use\n"));
+ fprintf (fp,
+_("\
+ -mapcs-float floating point args are passed in FP regs\n"));
+ fprintf (fp,
+_("\
+ -mapcs-reentrant the code is position independent/reentrant\n"));
+ #endif
+#ifdef OBJ_ELF
+ fprintf (fp,
+_("\
+ -moabi support the old ELF ABI\n"));
+#endif
+#ifdef ARM_BI_ENDIAN
+ fprintf (fp,
+_("\
+ -EB assemble code for a big endian cpu\n\
+ -EL assemble code for a little endian cpu\n"));
+#endif
+}
+
+/* We need to be able to fix up arbitrary expressions in some statements.
+ This is so that we can handle symbols that are an arbitrary distance from
+ the pc. The most common cases are of the form ((+/-sym -/+ . - 8) & mask),
+ which returns part of an address in a form which will be valid for
+ a data instruction. We do this by pushing the expression into a symbol
+ in the expr_section, and creating a fix for that. */
+
+static void
+fix_new_arm (frag, where, size, exp, pc_rel, reloc)
+ fragS * frag;
+ int where;
+ short int size;
+ expressionS * exp;
+ int pc_rel;
+ int reloc;
+{
+ fixS * new_fix;
+ arm_fix_data * arm_data;
+
+ switch (exp->X_op)
+ {
+ case O_constant:
+ case O_symbol:
+ case O_add:
+ case O_subtract:
+ new_fix = fix_new_exp (frag, where, size, exp, pc_rel, reloc);
+ break;
+
+ default:
+ new_fix = fix_new (frag, where, size, make_expr_symbol (exp), 0,
+ pc_rel, reloc);
+ break;
+ }
+
+ /* Mark whether the fix is to a THUMB instruction, or an ARM instruction */
+ arm_data = (arm_fix_data *) obstack_alloc (& notes, sizeof (arm_fix_data));
+ new_fix->tc_fix_data = (PTR) arm_data;
+ arm_data->thumb_mode = thumb_mode;
+
+ return;
+}
+
+
+/*
+ * This fix_new is called by cons via TC_CONS_FIX_NEW
+ *
+ * We check the expression to see if it is of the form
+ * __GLOBAL_OFFSET_TABLE + ???
+ * If it is then this is a PC relative reference to the GOT.
+ * i.e.
+ * ldr sl, L1
+ * add sl, pc, sl
+ * L2:
+ * ...
+ * L1:
+ * .word __GLOBAL_OFFSET_TABLE + (. - (L2 + 4))
+ *
+ * In this case use a reloc type BFD_RELOC_ARM_GOTPC instead of the
+ * normal BFD_RELOC_{16,32,64}
+ */
+
+void
+cons_fix_new_arm (frag, where, size, exp)
+ fragS * frag;
+ int where;
+ int size;
+ expressionS * exp;
+{
+ bfd_reloc_code_real_type type;
+ int pcrel = 0;
+
+ /* Pick a reloc ...
+ *
+ * @@ Should look at CPU word size.
+ */
+ switch (size)
+ {
+ case 2:
+ type = BFD_RELOC_16;
+ break;
+ case 4:
+ default:
+ type = BFD_RELOC_32;
+ break;
+ case 8:
+ type = BFD_RELOC_64;
+ break;
+ }
+
+ /* Look for possible GOTPC reloc */
+
+ /*
+ * Look for pic assembler and 'undef symbol + expr symbol' expression
+ * and a 32 bit size.
+ */
+
+ fix_new_exp (frag, where, (int) size, exp, pcrel, type);
+}
+
+/* A good place to do this, although this was probably not intended
+ * for this kind of use. We need to dump the literal pool before
+ * references are made to a null symbol pointer. */
+void
+arm_cleanup ()
+{
+ if (current_poolP != NULL)
+ {
+ subseg_set (text_section, 0); /* Put it at the end of text section */
+ s_ltorg (0);
+ listing_prev_line ();
+ }
+}
+
+void
+arm_start_line_hook ()
+{
+ last_label_seen = NULL;
+}
+
+void
+arm_frob_label (sym)
+ symbolS * sym;
+{
+ last_label_seen = sym;
+
+ ARM_SET_THUMB (sym, thumb_mode);
+
+#if defined OBJ_COFF || defined OBJ_ELF
+ ARM_SET_INTERWORK (sym, support_interwork);
+#endif
+
+ if (label_is_thumb_function_name)
+ {
+ /* When the address of a Thumb function is taken the bottom
+ bit of that address should be set. This will allow
+ interworking between Arm and Thumb functions to work
+ correctly. */
+
+ THUMB_SET_FUNC (sym, 1);
+
+ label_is_thumb_function_name = false;
+ }
+}
+
+/* Adjust the symbol table. This marks Thumb symbols as distinct from
+ ARM ones. */
+
+void
+arm_adjust_symtab ()
+{
+#ifdef OBJ_COFF
+ symbolS * sym;
+
+ for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
+ {
+ if (ARM_IS_THUMB (sym))
+ {
+ if (THUMB_IS_FUNC (sym))
+ {
+ /* Mark the symbol as a Thumb function. */
+ if ( S_GET_STORAGE_CLASS (sym) == C_STAT
+ || S_GET_STORAGE_CLASS (sym) == C_LABEL) /* This can happen! */
+ S_SET_STORAGE_CLASS (sym, C_THUMBSTATFUNC);
+
+ else if (S_GET_STORAGE_CLASS (sym) == C_EXT)
+ S_SET_STORAGE_CLASS (sym, C_THUMBEXTFUNC);
+ else
+ as_bad (_("%s: unexpected function type: %d"),
+ S_GET_NAME (sym), S_GET_STORAGE_CLASS (sym));
+ }
+ else switch (S_GET_STORAGE_CLASS (sym))
+ {
+ case C_EXT:
+ S_SET_STORAGE_CLASS (sym, C_THUMBEXT);
+ break;
+ case C_STAT:
+ S_SET_STORAGE_CLASS (sym, C_THUMBSTAT);
+ break;
+ case C_LABEL:
+ S_SET_STORAGE_CLASS (sym, C_THUMBLABEL);
+ break;
+ default: /* do nothing */
+ break;
+ }
+ }
+
+ if (ARM_IS_INTERWORK (sym))
+ coffsymbol(sym->bsym)->native->u.syment.n_flags = 0xFF;
+ }
+#endif
+#ifdef OBJ_ELF
+ symbolS * sym;
+ elf_symbol_type * elf_sym;
+ char bind;
+
+ for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
+ {
+ if (ARM_IS_THUMB (sym))
+ {
+ if (THUMB_IS_FUNC (sym))
+ {
+ elf_sym = elf_symbol (sym->bsym);
+ bind = ELF_ST_BIND (elf_sym);
+ elf_sym->internal_elf_sym.st_info = ELF_ST_INFO (bind, STT_ARM_TFUNC);
+ }
+ }
+ }
+#endif
+}
+
+int
+arm_data_in_code ()
+{
+ if (thumb_mode && ! strncmp (input_line_pointer + 1, "data:", 5))
+ {
+ *input_line_pointer = '/';
+ input_line_pointer += 5;
+ *input_line_pointer = 0;
+ return 1;
+ }
+
+ return 0;
+}
+
+char *
+arm_canonicalize_symbol_name (name)
+ char * name;
+{
+ int len;
+
+ if (thumb_mode && (len = strlen (name)) > 5
+ && streq (name + len - 5, "/data"))
+ {
+ *(name + len - 5) = 0;
+ }
+
+ return name;
+}
+
+boolean
+arm_validate_fix (fixP)
+ fixS * fixP;
+{
+ /* If the destination of the branch is a defined symbol which does not have
+ the THUMB_FUNC attribute, then we must be calling a function which has
+ the (interfacearm) attribute. We look for the Thumb entry point to that
+ function and change the branch to refer to that function instead. */
+ if ( fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BRANCH23
+ && fixP->fx_addsy != NULL
+ && S_IS_DEFINED (fixP->fx_addsy)
+ && ! THUMB_IS_FUNC (fixP->fx_addsy))
+ {
+ fixP->fx_addsy = find_real_start (fixP->fx_addsy);
+ return true;
+ }
+
+ return false;
+}
+
+#ifdef OBJ_ELF
+/* Relocations against Thumb function names must be left unadjusted,
+ so that the linker can use this information to correctly set the
+ bottom bit of their addresses. The MIPS version of this function
+ also prevents relocations that are mips-16 specific, but I do not
+ know why it does this.
+
+ FIXME:
+ There is one other problem that ought to be addressed here, but
+ which currently is not: Taking the address of a label (rather
+ than a function) and then later jumping to that address. Such
+ addresses also ought to have their bottom bit set (assuming that
+ they reside in Thumb code), but at the moment they will not. */
+
+boolean
+arm_fix_adjustable (fixP)
+ fixS * fixP;
+{
+
+ if (fixP->fx_addsy == NULL)
+ return 1;
+
+ /* Prevent all adjustments to global symbols. */
+ if (S_IS_EXTERN (fixP->fx_addsy))
+ return 0;
+
+ if (S_IS_WEAK (fixP->fx_addsy))
+ return 0;
+
+ if (THUMB_IS_FUNC (fixP->fx_addsy)
+ && fixP->fx_subsy == NULL)
+ return 0;
+
+ /* We need the symbol name for the VTABLE entries */
+ if ( fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ return 1;
+}
+
+const char *
+elf32_arm_target_format ()
+{
+ if (target_big_endian)
+ if (target_oabi)
+ return "elf32-bigarm-oabi";
+ else
+ return "elf32-bigarm";
+ else
+ if (target_oabi)
+ return "elf32-littlearm-oabi";
+ else
+ return "elf32-littlearm";
+}
+
+void
+armelf_frob_symbol (symp, puntp)
+ symbolS * symp;
+ int * puntp;
+{
+ elf_frob_symbol (symp, puntp);
+}
+
+int
+arm_force_relocation (fixp)
+ struct fix * fixp;
+{
+ if ( fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY
+ || fixp->fx_r_type == BFD_RELOC_ARM_PCREL_BRANCH)
+ return 1;
+
+ return 0;
+}
+
+static bfd_reloc_code_real_type
+arm_parse_reloc ()
+{
+ char id[16];
+ char * ip;
+ int i;
+ static struct
+ {
+ char * str;
+ int len;
+ bfd_reloc_code_real_type reloc;
+ }
+ reloc_map[] =
+ {
+#define MAP(str,reloc) { str, sizeof (str)-1, reloc }
+ MAP ("(got)", BFD_RELOC_ARM_GOT32),
+ MAP ("(gotoff)", BFD_RELOC_ARM_GOTOFF),
+ /* ScottB: Jan 30, 1998 */
+ /* Added support for parsing "var(PLT)" branch instructions */
+ /* generated by GCC for PLT relocs */
+ MAP ("(plt)", BFD_RELOC_ARM_PLT32),
+ NULL, 0, BFD_RELOC_UNUSED
+#undef MAP
+ };
+
+ for (i = 0, ip = input_line_pointer;
+ i < sizeof (id) && (isalnum (*ip) || ispunct (*ip));
+ i++, ip++)
+ id[i] = tolower (*ip);
+
+ for (i = 0; reloc_map[i].str; i++)
+ if (strncmp (id, reloc_map[i].str, reloc_map[i].len) == 0)
+ break;
+
+ input_line_pointer += reloc_map[i].len;
+
+ return reloc_map[i].reloc;
+}
+
+static void
+s_arm_elf_cons (nbytes)
+ int nbytes;
+{
+ expressionS exp;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+#ifdef md_cons_align
+ md_cons_align (nbytes);
+#endif
+
+ do
+ {
+ bfd_reloc_code_real_type reloc;
+
+ expression (& exp);
+
+ if (exp.X_op == O_symbol
+ && * input_line_pointer == '('
+ && (reloc = arm_parse_reloc()) != BFD_RELOC_UNUSED)
+ {
+ reloc_howto_type * howto = bfd_reloc_type_lookup (stdoutput, reloc);
+ int size = bfd_get_reloc_size (howto);
+
+ if (size > nbytes)
+ as_bad ("%s relocations do not fit in %d bytes", howto->name, nbytes);
+ else
+ {
+ register char * p = frag_more ((int) nbytes);
+ int offset = nbytes - size;
+
+ fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size,
+ & exp, 0, reloc);
+ }
+ }
+ else
+ emit_expr (& exp, (unsigned int) nbytes);
+ }
+ while (*input_line_pointer++ == ',');
+
+ input_line_pointer--; /* Put terminator back into stream. */
+ demand_empty_rest_of_line ();
+}
+
+#endif /* OBJ_ELF */
+
diff --git a/gas/config/tc-arm.h b/gas/config/tc-arm.h
new file mode 100644
index 0000000..bf99e24
--- /dev/null
+++ b/gas/config/tc-arm.h
@@ -0,0 +1,204 @@
+/* This file is tc-arm.h
+ Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+ Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
+ Modified by David Taylor (dtaylor@armltd.co.uk)
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#define TC_ARM 1
+
+#ifndef TARGET_BYTES_BIG_ENDIAN
+#define TARGET_BYTES_BIG_ENDIAN 0
+#endif
+
+#define WORKING_DOT_WORD
+
+#define COFF_MAGIC ARMMAGIC
+#define TARGET_ARCH bfd_arch_arm
+
+#define AOUT_MACHTYPE 0
+
+#define DIFF_EXPR_OK
+
+#ifdef LITTLE_ENDIAN
+#undef LITTLE_ENDIAN
+#endif
+
+#ifdef BIG_ENDIAN
+#undef BIG_ENDIAN
+#endif
+
+#define LITTLE_ENDIAN 1234
+#define BIG_ENDIAN 4321
+
+#if defined OBJ_AOUT
+#if defined TE_RISCIX
+# define TARGET_FORMAT "a.out-riscix"
+#elif defined TE_LINUX
+# define ARM_BI_ENDIAN
+# define TARGET_FORMAT "a.out-arm-linux"
+#elif defined TE_NetBSD
+# define TARGET_FORMAT "a.out-arm-netbsd"
+#else
+# define ARM_BI_ENDIAN
+# define TARGET_FORMAT \
+ (target_big_endian ? "a.out-arm-big" : "a.out-arm-little")
+#endif
+#endif /* OBJ_AOUT */
+
+#ifdef OBJ_AIF
+#define TARGET_FORMAT "aif"
+#endif
+
+#if defined OBJ_COFF || defined OBJ_ELF
+# define ARM_BI_ENDIAN
+
+# define TC_VALIDATE_FIX(fixP, segType, Label) \
+ if (arm_validate_fix (fixP)) add_symbolP = fixP->fx_addsy
+ extern boolean arm_validate_fix PARAMS ((struct fix *));
+#endif
+
+#ifdef OBJ_COFF
+# if defined TE_PE
+# define TC_FORCE_RELOCATION(x) ((x)->fx_r_type == BFD_RELOC_RVA)
+# ifdef TE_EPOC
+# define TARGET_FORMAT (target_big_endian ? "epoc-pe-arm-big" : "epoc-pe-arm-little")
+# else
+# define TARGET_FORMAT (target_big_endian ? "pe-arm-big" : "pe-arm-little")
+# endif
+# else
+# define TARGET_FORMAT (target_big_endian ? "coff-arm-big" : "coff-arm-little")
+# endif
+#endif
+
+#ifdef OBJ_ELF
+# define TARGET_FORMAT elf32_arm_target_format()
+ extern const char * elf32_arm_target_format PARAMS ((void));
+
+# define TC_FORCE_RELOCATION(fixp) arm_force_relocation (fixp)
+ extern int arm_force_relocation PARAMS ((struct fix *));
+#endif
+
+#define md_convert_frag(b, s, f) {as_fatal (_("arm convert_frag\n"));}
+
+#define md_cleanup() arm_cleanup ()
+ extern void arm_cleanup PARAMS ((void));
+
+#define md_start_line_hook() arm_start_line_hook ()
+ extern void arm_start_line_hook PARAMS ((void));
+
+#define tc_frob_label(S) arm_frob_label (S)
+ extern void arm_frob_label PARAMS ((struct symbol *));
+
+/* We also need to mark assembler created symbols: */
+#define tc_frob_fake_label(S) arm_frob_label (S)
+
+/* NOTE: The fake label creation in stabs.c:s_stab_generic() has
+ deliberately not been updated to mark assembler created stabs
+ symbols as Thumb. */
+
+#ifdef OBJ_ELF
+#define obj_fix_adjustable(fixP) arm_fix_adjustable (fixP)
+#else
+#define obj_fix_adjustable(fixP) 0
+#endif
+
+/* We need to keep some local information on symbols. */
+
+#define TC_SYMFIELD_TYPE unsigned int
+#define ARM_GET_FLAG(s) ((s)->sy_tc)
+#define ARM_SET_FLAG(s,v) ((s)->sy_tc |= (v))
+#define ARM_RESET_FLAG(s,v) ((s)->sy_tc &= ~(v))
+
+#define ARM_FLAG_THUMB (1 << 0) /* The symbol is a Thumb symbol rather than an Arm symbol. */
+#define ARM_FLAG_INTERWORK (1 << 1) /* The symbol is attached to code that suppports interworking. */
+#define THUMB_FLAG_FUNC (1 << 2) /* The symbol is attached to the start of a Thumb function. */
+
+#define ARM_IS_THUMB(s) (ARM_GET_FLAG (s) & ARM_FLAG_THUMB)
+#define ARM_IS_INTERWORK(s) (ARM_GET_FLAG (s) & ARM_FLAG_INTERWORK)
+#define THUMB_IS_FUNC(s) (ARM_GET_FLAG (s) & THUMB_FLAG_FUNC)
+
+#define ARM_SET_THUMB(s,t) ((t) ? ARM_SET_FLAG (s, ARM_FLAG_THUMB) : ARM_RESET_FLAG (s, ARM_FLAG_THUMB))
+#define ARM_SET_INTERWORK(s,t) ((t) ? ARM_SET_FLAG (s, ARM_FLAG_INTERWORK) : ARM_RESET_FLAG (s, ARM_FLAG_INTERWORK))
+#define THUMB_SET_FUNC(s,t) ((t) ? ARM_SET_FLAG (s, THUMB_FLAG_FUNC) : ARM_RESET_FLAG (s, THUMB_FLAG_FUNC))
+
+
+#define TC_FIX_TYPE PTR
+#define TC_INIT_FIX_DATA(FIXP) ((FIXP)->tc_fix_data = NULL)
+
+#define TC_START_LABEL(C,STR) \
+ (c == ':' || (c == '/' && arm_data_in_code ()))
+int arm_data_in_code PARAMS ((void));
+
+#define tc_canonicalize_symbol_name(str) \
+ arm_canonicalize_symbol_name (str);
+char * arm_canonicalize_symbol_name PARAMS ((char *));
+
+#define obj_adjust_symtab() arm_adjust_symtab ()
+ extern void arm_adjust_symtab PARAMS ((void));
+
+#ifdef OBJ_ELF
+#define obj_frob_symbol(sym, punt) armelf_frob_symbol (sym, punt)
+#endif
+
+#define tc_aout_pre_write_hook(x) {;} /* not used */
+
+#define LISTING_HEADER "ARM GAS "
+
+#define OPTIONAL_REGISTER_PREFIX '%'
+
+#define md_operand(x)
+
+#define TC_HANDLES_FX_DONE
+
+#define MD_APPLY_FIX3
+
+#define LOCAL_LABEL(name) (name[0] == '.' && (name[1] == 'L'))
+#define LOCAL_LABELS_FB 1
+
+/* This expression evaluates to false if the relocation is for a local object
+ for which we still want to do the relocation at runtime. True if we
+ are willing to perform this relocation while building the .o file.
+ This is only used for pcrel relocations, so GOTOFF does not need to be
+ checked here. I am not sure if some of the others are ever used with
+ pcrel, but it is easier to be safe than sorry. */
+
+#define TC_RELOC_RTSYM_LOC_FIXUP(FIX) \
+ ( (FIX)->fx_r_type != BFD_RELOC_ARM_GOT12 \
+ && (FIX)->fx_r_type != BFD_RELOC_ARM_GOT32 \
+ && (FIX)->fx_r_type != BFD_RELOC_32)
+
+#define TC_CONS_FIX_NEW cons_fix_new_arm
+ extern void cons_fix_new_arm PARAMS ((fragS *, int, int, expressionS *));
+
+/* Don't allow symbols to be discarded on GOT related relocs,
+ nor on globals. */
+#define tc_fix_adjustable(x) (\
+ ((x)->fx_r_type == BFD_RELOC_ARM_PLT32 \
+ || (x)->fx_r_type == BFD_RELOC_ARM_GOT32 \
+ || (x)->fx_r_type == BFD_RELOC_ARM_GOTOFF \
+ || S_IS_EXTERN ((x)->fx_addsy) \
+ || S_IS_WEAK ((x)->fx_addsy)) ? 0 : 1)
+
+#ifdef OBJ_ELF
+#define GLOBAL_OFFSET_TABLE_NAME "_GLOBAL_OFFSET_TABLE_"
+#else
+#define GLOBAL_OFFSET_TABLE_NAME "__GLOBAL_OFFSET_TABLE_"
+#endif
+
+/* end of tc-arm.h */
diff --git a/gas/config/tc-d10v.c b/gas/config/tc-d10v.c
new file mode 100644
index 0000000..cf38f3e
--- /dev/null
+++ b/gas/config/tc-d10v.c
@@ -0,0 +1,1636 @@
+/* tc-d10v.c -- Assembler code for the Mitsubishi D10V
+
+ Copyright (C) 1996, 1997, 1998 Free Software Foundation.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "as.h"
+#include "subsegs.h"
+#include "opcode/d10v.h"
+#include "elf/ppc.h"
+
+const char comment_chars[] = ";";
+const char line_comment_chars[] = "#";
+const char line_separator_chars[] = "";
+const char *md_shortopts = "O";
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "dD";
+
+int Optimizing = 0;
+
+#define AT_WORD_P(X) ((X)->X_op == O_right_shift \
+ && (X)->X_op_symbol != NULL \
+ && (X)->X_op_symbol->sy_value.X_op == O_constant \
+ && (X)->X_op_symbol->sy_value.X_add_number == AT_WORD_RIGHT_SHIFT)
+#define AT_WORD_RIGHT_SHIFT 2
+
+
+/* fixups */
+#define MAX_INSN_FIXUPS (5)
+struct d10v_fixup
+{
+ expressionS exp;
+ int operand;
+ int pcrel;
+ int size;
+ bfd_reloc_code_real_type reloc;
+};
+
+typedef struct _fixups
+{
+ int fc;
+ struct d10v_fixup fix[MAX_INSN_FIXUPS];
+ struct _fixups *next;
+} Fixups;
+
+static Fixups FixUps[2];
+static Fixups *fixups;
+
+/* True if instruction swapping warnings should be inhibited. */
+static unsigned char flag_warn_suppress_instructionswap; /* --nowarnswap */
+
+/* local functions */
+static int reg_name_search PARAMS ((char *name));
+static int register_name PARAMS ((expressionS *expressionP));
+static int check_range PARAMS ((unsigned long num, int bits, int flags));
+static int postfix PARAMS ((char *p));
+static bfd_reloc_code_real_type get_reloc PARAMS ((struct d10v_operand *op));
+static int get_operands PARAMS ((expressionS exp[]));
+static struct d10v_opcode *find_opcode PARAMS ((struct d10v_opcode *opcode, expressionS ops[]));
+static unsigned long build_insn PARAMS ((struct d10v_opcode *opcode, expressionS *opers, unsigned long insn));
+static void write_long PARAMS ((struct d10v_opcode *opcode, unsigned long insn, Fixups *fx));
+static void write_1_short PARAMS ((struct d10v_opcode *opcode, unsigned long insn, Fixups *fx));
+static int write_2_short PARAMS ((struct d10v_opcode *opcode1, unsigned long insn1,
+ struct d10v_opcode *opcode2, unsigned long insn2, int exec_type, Fixups *fx));
+static unsigned long do_assemble PARAMS ((char *str, struct d10v_opcode **opcode));
+static unsigned long d10v_insert_operand PARAMS (( unsigned long insn, int op_type,
+ offsetT value, int left, fixS *fix));
+static int parallel_ok PARAMS ((struct d10v_opcode *opcode1, unsigned long insn1,
+ struct d10v_opcode *opcode2, unsigned long insn2,
+ int exec_type));
+static symbolS * find_symbol_matching_register PARAMS ((expressionS *));
+
+struct option md_longopts[] =
+{
+#define OPTION_NOWARNSWAP (OPTION_MD_BASE)
+ {"nowarnswap", no_argument, NULL, OPTION_NOWARNSWAP},
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
+
+static void d10v_dot_word PARAMS ((int));
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "word", d10v_dot_word, 2 },
+ { NULL, NULL, 0 }
+};
+
+/* Opcode hash table. */
+static struct hash_control *d10v_hash;
+
+/* reg_name_search does a binary search of the d10v_predefined_registers
+ array to see if "name" is a valid regiter name. Returns the register
+ number from the array on success, or -1 on failure. */
+
+static int
+reg_name_search (name)
+ char *name;
+{
+ int middle, low, high;
+ int cmp;
+
+ low = 0;
+ high = d10v_reg_name_cnt() - 1;
+
+ do
+ {
+ middle = (low + high) / 2;
+ cmp = strcasecmp (name, d10v_predefined_registers[middle].name);
+ if (cmp < 0)
+ high = middle - 1;
+ else if (cmp > 0)
+ low = middle + 1;
+ else
+ return d10v_predefined_registers[middle].value;
+ }
+ while (low <= high);
+ return -1;
+}
+
+/* register_name() checks the string at input_line_pointer
+ to see if it is a valid register name */
+
+static int
+register_name (expressionP)
+ expressionS *expressionP;
+{
+ int reg_number;
+ char c, *p = input_line_pointer;
+
+ while (*p && *p!='\n' && *p!='\r' && *p !=',' && *p!=' ' && *p!=')')
+ p++;
+
+ c = *p;
+ if (c)
+ *p++ = 0;
+
+ /* look to see if it's in the register table */
+ reg_number = reg_name_search (input_line_pointer);
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ /* temporarily store a pointer to the string here */
+ expressionP->X_op_symbol = (struct symbol *)input_line_pointer;
+ expressionP->X_add_number = reg_number;
+ input_line_pointer = p;
+ return 1;
+ }
+ if (c)
+ *(p-1) = c;
+ return 0;
+}
+
+
+static int
+check_range (num, bits, flags)
+ unsigned long num;
+ int bits;
+ int flags;
+{
+ long min, max, bit1;
+ int retval=0;
+
+ /* don't bother checking 16-bit values */
+ if (bits == 16)
+ return 0;
+
+ if (flags & OPERAND_SHIFT)
+ {
+ /* all special shift operands are unsigned */
+ /* and <= 16. We allow 0 for now. */
+ if (num>16)
+ return 1;
+ else
+ return 0;
+ }
+
+ if (flags & OPERAND_SIGNED)
+ {
+ max = (1 << (bits - 1))-1;
+ min = - (1 << (bits - 1));
+ if (((long)num > max) || ((long)num < min))
+ retval = 1;
+ }
+ else
+ {
+ max = (1 << bits) - 1;
+ min = 0;
+ if ((num > max) || (num < min))
+ retval = 1;
+ }
+ return retval;
+}
+
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ fprintf(stream, _("D10V options:\n\
+-O optimize. Will do some operations in parallel.\n"));
+}
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ switch (c)
+ {
+ case 'O':
+ /* Optimize. Will attempt to parallelize operations */
+ Optimizing = 1;
+ break;
+ case OPTION_NOWARNSWAP:
+ flag_warn_suppress_instructionswap = 1;
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ return 0;
+}
+
+/* Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP . An error message is returned, or NULL on OK.
+ */
+char *
+md_atof (type, litP, sizeP)
+ int type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[4];
+ char *t;
+ int i;
+
+ switch (type)
+ {
+ case 'f':
+ prec = 2;
+ break;
+ case 'd':
+ prec = 4;
+ break;
+ default:
+ *sizeP = 0;
+ return _("bad call to md_atof");
+ }
+
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+
+ *sizeP = prec * 2;
+
+ for (i = 0; i < prec; i++)
+ {
+ md_number_to_chars (litP, (valueT) words[i], 2);
+ litP += 2;
+ }
+ return NULL;
+}
+
+void
+md_convert_frag (abfd, sec, fragP)
+ bfd *abfd;
+ asection *sec;
+ fragS *fragP;
+{
+ abort ();
+}
+
+valueT
+md_section_align (seg, addr)
+ asection *seg;
+ valueT addr;
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+
+void
+md_begin ()
+{
+ char *prev_name = "";
+ struct d10v_opcode *opcode;
+ d10v_hash = hash_new();
+
+ /* Insert unique names into hash table. The D10v instruction set
+ has many identical opcode names that have different opcodes based
+ on the operands. This hash table then provides a quick index to
+ the first opcode with a particular name in the opcode table. */
+
+ for (opcode = (struct d10v_opcode *)d10v_opcodes; opcode->name; opcode++)
+ {
+ if (strcmp (prev_name, opcode->name))
+ {
+ prev_name = (char *)opcode->name;
+ hash_insert (d10v_hash, opcode->name, (char *) opcode);
+ }
+ }
+
+ fixups = &FixUps[0];
+ FixUps[0].next = &FixUps[1];
+ FixUps[1].next = &FixUps[0];
+}
+
+
+/* this function removes the postincrement or postdecrement
+ operator ( '+' or '-' ) from an expression */
+
+static int postfix (p)
+ char *p;
+{
+ while (*p != '-' && *p != '+')
+ {
+ if (*p==0 || *p=='\n' || *p=='\r')
+ break;
+ p++;
+ }
+
+ if (*p == '-')
+ {
+ *p = ' ';
+ return (-1);
+ }
+ if (*p == '+')
+ {
+ *p = ' ';
+ return (1);
+ }
+
+ return (0);
+}
+
+
+static bfd_reloc_code_real_type
+get_reloc (op)
+ struct d10v_operand *op;
+{
+ int bits = op->bits;
+
+ if (bits <= 4)
+ return (0);
+
+ if (op->flags & OPERAND_ADDR)
+ {
+ if (bits == 8)
+ return (BFD_RELOC_D10V_10_PCREL_R);
+ else
+ return (BFD_RELOC_D10V_18_PCREL);
+ }
+
+ return (BFD_RELOC_16);
+}
+
+
+/* get_operands parses a string of operands and returns
+ an array of expressions */
+
+static int
+get_operands (exp)
+ expressionS exp[];
+{
+ char *p = input_line_pointer;
+ int numops = 0;
+ int post = 0;
+
+ while (*p)
+ {
+ while (*p == ' ' || *p == '\t' || *p == ',')
+ p++;
+ if (*p==0 || *p=='\n' || *p=='\r')
+ break;
+
+ if (*p == '@')
+ {
+ p++;
+ exp[numops].X_op = O_absent;
+ if (*p == '(')
+ {
+ p++;
+ exp[numops].X_add_number = OPERAND_ATPAR;
+ }
+ else if (*p == '-')
+ {
+ p++;
+ exp[numops].X_add_number = OPERAND_ATMINUS;
+ }
+ else
+ {
+ exp[numops].X_add_number = OPERAND_ATSIGN;
+ post = postfix (p);
+ }
+ numops++;
+ continue;
+ }
+
+ if (*p == ')')
+ {
+ /* just skip the trailing paren */
+ p++;
+ continue;
+ }
+
+ input_line_pointer = p;
+
+ /* check to see if it might be a register name */
+ if (!register_name (&exp[numops]))
+ {
+ /* parse as an expression */
+ expression (&exp[numops]);
+ }
+
+ if (strncasecmp (input_line_pointer, "@word", 5) == 0)
+ {
+ input_line_pointer += 5;
+ if (exp[numops].X_op == O_register)
+ {
+ /* if it looked like a register name but was followed by
+ "@word" then it was really a symbol, so change it to
+ one */
+ exp[numops].X_op = O_symbol;
+ exp[numops].X_add_symbol = symbol_find_or_make ((char *)exp[numops].X_op_symbol);
+ }
+
+ /* check for identifier@word+constant */
+ if (*input_line_pointer == '-' || *input_line_pointer == '+')
+ {
+ char *orig_line = input_line_pointer;
+ expressionS new_exp;
+ expression (&new_exp);
+ exp[numops].X_add_number = new_exp.X_add_number;
+ }
+
+ /* convert expr into a right shift by AT_WORD_RIGHT_SHIFT */
+ {
+ expressionS new_exp;
+ memset (&new_exp, 0, sizeof new_exp);
+ new_exp.X_add_number = AT_WORD_RIGHT_SHIFT;
+ new_exp.X_op = O_constant;
+ new_exp.X_unsigned = 1;
+ exp[numops].X_op_symbol = make_expr_symbol (&new_exp);
+ exp[numops].X_op = O_right_shift;
+ }
+
+ know (AT_WORD_P (&exp[numops]));
+ }
+
+ if (exp[numops].X_op == O_illegal)
+ as_bad (_("illegal operand"));
+ else if (exp[numops].X_op == O_absent)
+ as_bad (_("missing operand"));
+
+ numops++;
+ p = input_line_pointer;
+ }
+
+ switch (post)
+ {
+ case -1: /* postdecrement mode */
+ exp[numops].X_op = O_absent;
+ exp[numops++].X_add_number = OPERAND_MINUS;
+ break;
+ case 1: /* postincrement mode */
+ exp[numops].X_op = O_absent;
+ exp[numops++].X_add_number = OPERAND_PLUS;
+ break;
+ }
+
+ exp[numops].X_op = 0;
+ return (numops);
+}
+
+static unsigned long
+d10v_insert_operand (insn, op_type, value, left, fix)
+ unsigned long insn;
+ int op_type;
+ offsetT value;
+ int left;
+ fixS *fix;
+{
+ int shift, bits;
+
+ shift = d10v_operands[op_type].shift;
+ if (left)
+ shift += 15;
+
+ bits = d10v_operands[op_type].bits;
+
+ /* truncate to the proper number of bits */
+ if (check_range (value, bits, d10v_operands[op_type].flags))
+ as_bad_where (fix->fx_file, fix->fx_line, _("operand out of range: %d"), value);
+
+ value &= 0x7FFFFFFF >> (31 - bits);
+ insn |= (value << shift);
+
+ return insn;
+}
+
+
+/* build_insn takes a pointer to the opcode entry in the opcode table
+ and the array of operand expressions and returns the instruction */
+
+static unsigned long
+build_insn (opcode, opers, insn)
+ struct d10v_opcode *opcode;
+ expressionS *opers;
+ unsigned long insn;
+{
+ int i, bits, shift, flags, format;
+ unsigned long number;
+
+ /* the insn argument is only used for the DIVS kludge */
+ if (insn)
+ format = LONG_R;
+ else
+ {
+ insn = opcode->opcode;
+ format = opcode->format;
+ }
+
+ for (i=0;opcode->operands[i];i++)
+ {
+ flags = d10v_operands[opcode->operands[i]].flags;
+ bits = d10v_operands[opcode->operands[i]].bits;
+ shift = d10v_operands[opcode->operands[i]].shift;
+ number = opers[i].X_add_number;
+
+ if (flags & OPERAND_REG)
+ {
+ number &= REGISTER_MASK;
+ if (format == LONG_L)
+ shift += 15;
+ }
+
+ if (opers[i].X_op != O_register && opers[i].X_op != O_constant)
+ {
+ /* now create a fixup */
+
+ if (fixups->fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ if (AT_WORD_P (&opers[i]))
+ {
+ /* Reconize XXX>>1+N aka XXX@word+N as special (AT_WORD) */
+ fixups->fix[fixups->fc].reloc = BFD_RELOC_D10V_18;
+ opers[i].X_op = O_symbol;
+ opers[i].X_op_symbol = NULL; /* Should free it */
+ /* number is left shifted by AT_WORD_RIGHT_SHIFT so
+ that, it is aligned with the symbol's value. Later,
+ BFD_RELOC_D10V_18 will right shift (symbol_value +
+ X_add_number). */
+ number <<= AT_WORD_RIGHT_SHIFT;
+ opers[i].X_add_number = number;
+ }
+ else
+ fixups->fix[fixups->fc].reloc =
+ get_reloc((struct d10v_operand *)&d10v_operands[opcode->operands[i]]);
+
+ if (fixups->fix[fixups->fc].reloc == BFD_RELOC_16 ||
+ fixups->fix[fixups->fc].reloc == BFD_RELOC_D10V_18)
+ fixups->fix[fixups->fc].size = 2;
+ else
+ fixups->fix[fixups->fc].size = 4;
+
+ fixups->fix[fixups->fc].exp = opers[i];
+ fixups->fix[fixups->fc].operand = opcode->operands[i];
+ fixups->fix[fixups->fc].pcrel = (flags & OPERAND_ADDR) ? true : false;
+ (fixups->fc)++;
+ }
+
+ /* truncate to the proper number of bits */
+ if ((opers[i].X_op == O_constant) && check_range (number, bits, flags))
+ as_bad (_("operand out of range: %d"),number);
+ number &= 0x7FFFFFFF >> (31 - bits);
+ insn = insn | (number << shift);
+ }
+
+ /* kludge: for DIVS, we need to put the operands in twice */
+ /* on the second pass, format is changed to LONG_R to force */
+ /* the second set of operands to not be shifted over 15 */
+ if ((opcode->opcode == OPCODE_DIVS) && (format==LONG_L))
+ insn = build_insn (opcode, opers, insn);
+
+ return insn;
+}
+
+/* write out a long form instruction */
+static void
+write_long (opcode, insn, fx)
+ struct d10v_opcode *opcode;
+ unsigned long insn;
+ Fixups *fx;
+{
+ int i, where;
+ char *f = frag_more(4);
+
+ insn |= FM11;
+ number_to_chars_bigendian (f, insn, 4);
+
+ for (i=0; i < fx->fc; i++)
+ {
+ if (fx->fix[i].reloc)
+ {
+ where = f - frag_now->fr_literal;
+ if (fx->fix[i].size == 2)
+ where += 2;
+
+ if (fx->fix[i].reloc == BFD_RELOC_D10V_18)
+ fx->fix[i].operand |= 4096;
+
+ fix_new_exp (frag_now,
+ where,
+ fx->fix[i].size,
+ &(fx->fix[i].exp),
+ fx->fix[i].pcrel,
+ fx->fix[i].operand|2048);
+ }
+ }
+ fx->fc = 0;
+}
+
+
+/* write out a short form instruction by itself */
+static void
+write_1_short (opcode, insn, fx)
+ struct d10v_opcode *opcode;
+ unsigned long insn;
+ Fixups *fx;
+{
+ char *f = frag_more(4);
+ int i, where;
+
+ if (opcode->exec_type & PARONLY)
+ as_fatal (_("Instruction must be executed in parallel with another instruction."));
+
+ /* the other container needs to be NOP */
+ /* according to 4.3.1: for FM=00, sub-instructions performed only
+ by IU cannot be encoded in L-container. */
+ if (opcode->unit == IU)
+ insn |= FM00 | (NOP << 15); /* right container */
+ else
+ insn = FM00 | (insn << 15) | NOP; /* left container */
+
+ number_to_chars_bigendian (f, insn, 4);
+ for (i=0; i < fx->fc; i++)
+ {
+ if (fx->fix[i].reloc)
+ {
+ where = f - frag_now->fr_literal;
+ if (fx->fix[i].size == 2)
+ where += 2;
+
+ if (fx->fix[i].reloc == BFD_RELOC_D10V_18)
+ fx->fix[i].operand |= 4096;
+
+ /* if it's an R reloc, we may have to switch it to L */
+ if ( (fx->fix[i].reloc == BFD_RELOC_D10V_10_PCREL_R) && (opcode->unit != IU) )
+ fx->fix[i].operand |= 1024;
+
+ fix_new_exp (frag_now,
+ where,
+ fx->fix[i].size,
+ &(fx->fix[i].exp),
+ fx->fix[i].pcrel,
+ fx->fix[i].operand|2048);
+ }
+ }
+ fx->fc = 0;
+}
+
+/* write out a short form instruction if possible */
+/* return number of instructions not written out */
+static int
+write_2_short (opcode1, insn1, opcode2, insn2, exec_type, fx)
+ struct d10v_opcode *opcode1, *opcode2;
+ unsigned long insn1, insn2;
+ int exec_type;
+ Fixups *fx;
+{
+ unsigned long insn;
+ char *f;
+ int i,j, where;
+
+ if ( (exec_type != 1) && ((opcode1->exec_type & PARONLY)
+ || (opcode2->exec_type & PARONLY)))
+ as_fatal (_("Instruction must be executed in parallel"));
+
+ if ( (opcode1->format & LONG_OPCODE) || (opcode2->format & LONG_OPCODE))
+ as_fatal (_("Long instructions may not be combined."));
+
+ if(opcode1->exec_type & BRANCH_LINK && exec_type == 0)
+ {
+ /* Instructions paired with a subroutine call are executed before the
+ subroutine, so don't do these pairings unless explicitly requested. */
+ write_1_short (opcode1, insn1, fx->next);
+ return (1);
+ }
+
+ switch (exec_type)
+ {
+ case 0: /* order not specified */
+ if ( Optimizing && parallel_ok (opcode1, insn1, opcode2, insn2, exec_type))
+ {
+ /* parallel */
+ if (opcode1->unit == IU)
+ insn = FM00 | (insn2 << 15) | insn1;
+ else if (opcode2->unit == MU)
+ insn = FM00 | (insn2 << 15) | insn1;
+ else
+ {
+ insn = FM00 | (insn1 << 15) | insn2;
+ fx = fx->next;
+ }
+ }
+ else if (opcode1->unit == IU)
+ {
+ /* reverse sequential */
+ insn = FM10 | (insn2 << 15) | insn1;
+ }
+ else
+ {
+ /* sequential */
+ insn = FM01 | (insn1 << 15) | insn2;
+ fx = fx->next;
+ }
+ break;
+ case 1: /* parallel */
+ if (opcode1->exec_type & SEQ || opcode2->exec_type & SEQ)
+ as_fatal (_("One of these instructions may not be executed in parallel."));
+
+ if (opcode1->unit == IU)
+ {
+ if (opcode2->unit == IU)
+ as_fatal (_("Two IU instructions may not be executed in parallel"));
+ if (!flag_warn_suppress_instructionswap)
+ as_warn (_("Swapping instruction order"));
+ insn = FM00 | (insn2 << 15) | insn1;
+ }
+ else if (opcode2->unit == MU)
+ {
+ if (opcode1->unit == MU)
+ as_fatal (_("Two MU instructions may not be executed in parallel"));
+ if (!flag_warn_suppress_instructionswap)
+ as_warn (_("Swapping instruction order"));
+ insn = FM00 | (insn2 << 15) | insn1;
+ }
+ else
+ {
+ insn = FM00 | (insn1 << 15) | insn2;
+ fx = fx->next;
+ }
+ break;
+ case 2: /* sequential */
+ if (opcode1->unit != IU)
+ insn = FM01 | (insn1 << 15) | insn2;
+ else if (opcode2->unit == MU || opcode2->unit == EITHER)
+ {
+ if (!flag_warn_suppress_instructionswap)
+ as_warn (_("Swapping instruction order"));
+ insn = FM10 | (insn2 << 15) | insn1;
+ }
+ else
+ as_fatal (_("IU instruction may not be in the left container"));
+ fx = fx->next;
+ break;
+ case 3: /* reverse sequential */
+ if (opcode2->unit != MU)
+ insn = FM10 | (insn1 << 15) | insn2;
+ else if (opcode1->unit == IU || opcode1->unit == EITHER)
+ {
+ if (!flag_warn_suppress_instructionswap)
+ as_warn (_("Swapping instruction order"));
+ insn = FM01 | (insn2 << 15) | insn1;
+ }
+ else
+ as_fatal (_("MU instruction may not be in the right container"));
+ fx = fx->next;
+ break;
+ default:
+ as_fatal (_("unknown execution type passed to write_2_short()"));
+ }
+
+ f = frag_more(4);
+ number_to_chars_bigendian (f, insn, 4);
+
+ for (j=0; j<2; j++)
+ {
+ for (i=0; i < fx->fc; i++)
+ {
+ if (fx->fix[i].reloc)
+ {
+ where = f - frag_now->fr_literal;
+ if (fx->fix[i].size == 2)
+ where += 2;
+
+ if ( (fx->fix[i].reloc == BFD_RELOC_D10V_10_PCREL_R) && (j == 0) )
+ fx->fix[i].operand |= 1024;
+
+ if (fx->fix[i].reloc == BFD_RELOC_D10V_18)
+ fx->fix[i].operand |= 4096;
+
+ fix_new_exp (frag_now,
+ where,
+ fx->fix[i].size,
+ &(fx->fix[i].exp),
+ fx->fix[i].pcrel,
+ fx->fix[i].operand|2048);
+ }
+ }
+ fx->fc = 0;
+ fx = fx->next;
+ }
+ return (0);
+}
+
+
+/* Check 2 instructions and determine if they can be safely */
+/* executed in parallel. Returns 1 if they can be. */
+static int
+parallel_ok (op1, insn1, op2, insn2, exec_type)
+ struct d10v_opcode *op1, *op2;
+ unsigned long insn1, insn2;
+ int exec_type;
+{
+ int i, j, flags, mask, shift, regno;
+ unsigned long ins, mod[2], used[2];
+ struct d10v_opcode *op;
+
+ if ((op1->exec_type & SEQ) != 0 || (op2->exec_type & SEQ) != 0
+ || (op1->exec_type & PAR) == 0 || (op2->exec_type & PAR) == 0
+ || (op1->unit == BOTH) || (op2->unit == BOTH)
+ || (op1->unit == IU && op2->unit == IU)
+ || (op1->unit == MU && op2->unit == MU))
+ return 0;
+
+ /* If the first instruction is a branch and this is auto parallazation,
+ don't combine with any second instruction. */
+ if (exec_type == 0 && (op1->exec_type & BRANCH) != 0)
+ return 0;
+
+ /* The idea here is to create two sets of bitmasks (mod and used)
+ which indicate which registers are modified or used by each
+ instruction. The operation can only be done in parallel if
+ instruction 1 and instruction 2 modify different registers, and
+ the first instruction does not modify registers that the second
+ is using (The second instruction can modify registers that the
+ first is using as they are only written back after the first
+ instruction has completed). Accesses to control registers, PSW,
+ and memory are treated as accesses to a single register. So if
+ both instructions write memory or if the first instruction writes
+ memory and the second reads, then they cannot be done in
+ parallel. Likewise, if the first instruction mucks with the psw
+ and the second reads the PSW (which includes C, F0, and F1), then
+ they cannot operate safely in parallel. */
+
+ /* the bitmasks (mod and used) look like this (bit 31 = MSB) */
+ /* r0-r15 0-15 */
+ /* a0-a1 16-17 */
+ /* cr (not psw) 18 */
+ /* psw 19 */
+ /* mem 20 */
+
+ for (j=0;j<2;j++)
+ {
+ if (j == 0)
+ {
+ op = op1;
+ ins = insn1;
+ }
+ else
+ {
+ op = op2;
+ ins = insn2;
+ }
+ mod[j] = used[j] = 0;
+ if (op->exec_type & BRANCH_LINK)
+ mod[j] |= 1 << 13;
+
+ for (i = 0; op->operands[i]; i++)
+ {
+ flags = d10v_operands[op->operands[i]].flags;
+ shift = d10v_operands[op->operands[i]].shift;
+ mask = 0x7FFFFFFF >> (31 - d10v_operands[op->operands[i]].bits);
+ if (flags & OPERAND_REG)
+ {
+ regno = (ins >> shift) & mask;
+ if (flags & (OPERAND_ACC0|OPERAND_ACC1))
+ regno += 16;
+ else if (flags & OPERAND_CONTROL) /* mvtc or mvfc */
+ {
+ if (regno == 0)
+ regno = 19;
+ else
+ regno = 18;
+ }
+ else if (flags & (OPERAND_FFLAG|OPERAND_CFLAG))
+ regno = 19;
+
+ if ( flags & OPERAND_DEST )
+ {
+ mod[j] |= 1 << regno;
+ if (flags & OPERAND_EVEN)
+ mod[j] |= 1 << (regno + 1);
+ }
+ else
+ {
+ used[j] |= 1 << regno ;
+ if (flags & OPERAND_EVEN)
+ used[j] |= 1 << (regno + 1);
+
+ /* Auto inc/dec also modifies the register. */
+ if (op->operands[i+1] != 0
+ && (d10v_operands[op->operands[i+1]].flags
+ & (OPERAND_PLUS | OPERAND_MINUS)) != 0)
+ mod[j] |= 1 << regno;
+ }
+ }
+ else if (flags & OPERAND_ATMINUS)
+ {
+ /* SP implicitly used/modified */
+ mod[j] |= 1 << 15;
+ used[j] |= 1 << 15;
+ }
+ }
+ if (op->exec_type & RMEM)
+ used[j] |= 1 << 20;
+ else if (op->exec_type & WMEM)
+ mod[j] |= 1 << 20;
+ else if (op->exec_type & RF0)
+ used[j] |= 1 << 19;
+ else if (op->exec_type & WF0)
+ mod[j] |= 1 << 19;
+ else if (op->exec_type & WCAR)
+ mod[j] |= 1 << 19;
+ }
+ if ((mod[0] & mod[1]) == 0 && (mod[0] & used[1]) == 0)
+ return 1;
+ return 0;
+}
+
+
+/* This is the main entry point for the machine-dependent assembler. str points to a
+ machine-dependent instruction. This function is supposed to emit the frags/bytes
+ it assembles to. For the D10V, it mostly handles the special VLIW parsing and packing
+ and leaves the difficult stuff to do_assemble().
+ */
+
+static unsigned long prev_insn;
+static struct d10v_opcode *prev_opcode = 0;
+static subsegT prev_subseg;
+static segT prev_seg = 0;;
+static int etype = 0; /* saved extype. used for multiline instructions */
+
+void
+md_assemble (str)
+ char *str;
+{
+ struct d10v_opcode * opcode;
+ unsigned long insn;
+ int extype = 0; /* execution type; parallel, etc */
+ char * str2;
+
+ if (etype == 0)
+ {
+ /* look for the special multiple instruction separators */
+ str2 = strstr (str, "||");
+ if (str2)
+ extype = 1;
+ else
+ {
+ str2 = strstr (str, "->");
+ if (str2)
+ extype = 2;
+ else
+ {
+ str2 = strstr (str, "<-");
+ if (str2)
+ extype = 3;
+ }
+ }
+ /* str2 points to the separator, if one */
+ if (str2)
+ {
+ *str2 = 0;
+
+ /* if two instructions are present and we already have one saved
+ then first write it out */
+ d10v_cleanup ();
+
+ /* assemble first instruction and save it */
+ prev_insn = do_assemble (str, &prev_opcode);
+ if (prev_insn == -1)
+ as_fatal (_("can't find opcode "));
+ fixups = fixups->next;
+ str = str2 + 2;
+ }
+ }
+
+ insn = do_assemble (str, &opcode);
+ if (insn == -1)
+ {
+ if (extype)
+ {
+ etype = extype;
+ return;
+ }
+ as_fatal (_("can't find opcode "));
+ }
+
+ if (etype)
+ {
+ extype = etype;
+ etype = 0;
+ }
+
+ /* if this is a long instruction, write it and any previous short instruction */
+ if (opcode->format & LONG_OPCODE)
+ {
+ if (extype)
+ as_fatal (_("Unable to mix instructions as specified"));
+ d10v_cleanup ();
+ write_long (opcode, insn, fixups);
+ prev_opcode = NULL;
+ return;
+ }
+
+ if (prev_opcode && prev_seg && ((prev_seg != now_seg) || (prev_subseg != now_subseg)))
+ d10v_cleanup();
+
+ if (prev_opcode && (write_2_short (prev_opcode, prev_insn, opcode, insn, extype, fixups) == 0))
+ {
+ /* no instructions saved */
+ prev_opcode = NULL;
+ }
+ else
+ {
+ if (extype)
+ as_fatal (_("Unable to mix instructions as specified"));
+ /* save off last instruction so it may be packed on next pass */
+ prev_opcode = opcode;
+ prev_insn = insn;
+ prev_seg = now_seg;
+ prev_subseg = now_subseg;
+ fixups = fixups->next;
+ }
+}
+
+
+/* do_assemble assembles a single instruction and returns an opcode */
+/* it returns -1 (an invalid opcode) on error */
+
+static unsigned long
+do_assemble (str, opcode)
+ char *str;
+ struct d10v_opcode **opcode;
+{
+ unsigned char *op_start, *save;
+ unsigned char *op_end;
+ char name[20];
+ int nlen = 0;
+ expressionS myops[6];
+ unsigned long insn;
+
+ /* Drop leading whitespace. */
+ while (*str == ' ')
+ str++;
+
+ /* Find the opcode end. */
+ for (op_start = op_end = (unsigned char *) (str);
+ *op_end
+ && nlen < 20
+ && !is_end_of_line[*op_end] && *op_end != ' ';
+ op_end++)
+ {
+ name[nlen] = tolower (op_start[nlen]);
+ nlen++;
+ }
+ name[nlen] = 0;
+
+ if (nlen == 0)
+ return -1;
+
+ /* Find the first opcode with the proper name. */
+ *opcode = (struct d10v_opcode *)hash_find (d10v_hash, name);
+ if (*opcode == NULL)
+ as_fatal (_("unknown opcode: %s"),name);
+
+ save = input_line_pointer;
+ input_line_pointer = op_end;
+ *opcode = find_opcode (*opcode, myops);
+ if (*opcode == 0)
+ return -1;
+ input_line_pointer = save;
+
+ insn = build_insn ((*opcode), myops, 0);
+ return (insn);
+}
+
+/* Find the symbol which has the same name as the register in the given expression. */
+static symbolS *
+find_symbol_matching_register (exp)
+ expressionS * exp;
+{
+ int i;
+
+ if (exp->X_op != O_register)
+ return NULL;
+
+ /* Find the name of the register. */
+ for (i = d10v_reg_name_cnt (); i--;)
+ if (d10v_predefined_registers [i].value == exp->X_add_number)
+ break;
+
+ if (i < 0)
+ abort ();
+
+ /* Now see if a symbol has been defined with the same name. */
+ return symbol_find (d10v_predefined_registers [i].name);
+}
+
+
+/* find_opcode() gets a pointer to an entry in the opcode table. */
+/* It must look at all opcodes with the same name and use the operands */
+/* to choose the correct opcode. */
+
+static struct d10v_opcode *
+find_opcode (opcode, myops)
+ struct d10v_opcode *opcode;
+ expressionS myops[];
+{
+ int i, match, done;
+ struct d10v_opcode *next_opcode;
+
+ /* get all the operands and save them as expressions */
+ get_operands (myops);
+
+ /* now see if the operand is a fake. If so, find the correct size */
+ /* instruction, if possible */
+ if (opcode->format == OPCODE_FAKE)
+ {
+ int opnum = opcode->operands[0];
+ int flags;
+
+ if (myops[opnum].X_op == O_register)
+ {
+ myops[opnum].X_op = O_symbol;
+ myops[opnum].X_add_symbol = symbol_find_or_make ((char *)myops[opnum].X_op_symbol);
+ myops[opnum].X_add_number = 0;
+ myops[opnum].X_op_symbol = NULL;
+ }
+
+ next_opcode=opcode+1;
+
+ /* If the first operand is supposed to be a register, make sure
+ we got a valid one. */
+ flags = d10v_operands[next_opcode->operands[0]].flags;
+ if (flags & OPERAND_REG)
+ {
+ int X_op = myops[0].X_op;
+ int num = myops[0].X_add_number;
+
+ if (X_op != O_register
+ || (num & ~flags
+ & (OPERAND_GPR | OPERAND_ACC0 | OPERAND_ACC1
+ | OPERAND_FFLAG | OPERAND_CFLAG | OPERAND_CONTROL)))
+ {
+ as_bad (_("bad opcode or operands"));
+ return 0;
+ }
+ }
+
+ if (myops[opnum].X_op == O_constant || (myops[opnum].X_op == O_symbol &&
+ S_IS_DEFINED(myops[opnum].X_add_symbol) &&
+ (S_GET_SEGMENT(myops[opnum].X_add_symbol) == now_seg)))
+ {
+ for (i=0; opcode->operands[i+1]; i++)
+ {
+ int bits = d10v_operands[next_opcode->operands[opnum]].bits;
+ int flags = d10v_operands[next_opcode->operands[opnum]].flags;
+ if (flags & OPERAND_ADDR)
+ bits += 2;
+ if (myops[opnum].X_op == O_constant)
+ {
+ if (!check_range (myops[opnum].X_add_number, bits, flags))
+ return next_opcode;
+ }
+ else
+ {
+ fragS *f;
+ long value;
+ /* calculate the current address by running through the previous frags */
+ /* and adding our current offset */
+ for (value = 0, f = frchain_now->frch_root; f; f = f->fr_next)
+ value += f->fr_fix + f->fr_offset;
+
+ if (flags & OPERAND_ADDR)
+ value = S_GET_VALUE(myops[opnum].X_add_symbol) - value -
+ (obstack_next_free(&frchain_now->frch_obstack) - frag_now->fr_literal);
+ else
+ value = S_GET_VALUE(myops[opnum].X_add_symbol);
+
+ if (AT_WORD_P (&myops[opnum]))
+ {
+ if (bits > 4)
+ {
+ bits += 2;
+ if (!check_range (value, bits, flags))
+ return next_opcode;
+ }
+ }
+ else if (!check_range (value, bits, flags))
+ return next_opcode;
+ }
+ next_opcode++;
+ }
+ as_fatal (_("value out of range"));
+ }
+ else
+ {
+ /* not a constant, so use a long instruction */
+ return opcode+2;
+ }
+ }
+ else
+ {
+ match = 0;
+ /* now search the opcode table table for one with operands */
+ /* that matches what we've got */
+ while (!match)
+ {
+ match = 1;
+ for (i = 0; opcode->operands[i]; i++)
+ {
+ int flags = d10v_operands[opcode->operands[i]].flags;
+ int X_op = myops[i].X_op;
+ int num = myops[i].X_add_number;
+
+ if (X_op == 0)
+ {
+ match = 0;
+ break;
+ }
+
+ if (flags & OPERAND_REG)
+ {
+ if ((X_op != O_register)
+ || (num & ~flags
+ & (OPERAND_GPR | OPERAND_ACC0 | OPERAND_ACC1
+ | OPERAND_FFLAG | OPERAND_CFLAG
+ | OPERAND_CONTROL)))
+ {
+ match = 0;
+ break;
+ }
+ }
+
+ if (((flags & OPERAND_MINUS) && ((X_op != O_absent) || (num != OPERAND_MINUS))) ||
+ ((flags & OPERAND_PLUS) && ((X_op != O_absent) || (num != OPERAND_PLUS))) ||
+ ((flags & OPERAND_ATMINUS) && ((X_op != O_absent) || (num != OPERAND_ATMINUS))) ||
+ ((flags & OPERAND_ATPAR) && ((X_op != O_absent) || (num != OPERAND_ATPAR))) ||
+ ((flags & OPERAND_ATSIGN) && ((X_op != O_absent) || (num != OPERAND_ATSIGN))))
+ {
+ match = 0;
+ break;
+ }
+
+ /* Unfortunatly, for the indirect operand in instructions such as
+ ``ldb r1, @(c,r14)'' this function can be passed X_op == O_register
+ (because 'c' is a valid register name). However we cannot just
+ ignore the case when X_op == O_register but flags & OPERAND_REG is
+ null, so we check to see if a symbol of the same name as the register
+ exists. If the symbol does exist, then the parser was unable to
+ distinguish the two cases and we fix things here. (Ref: PR14826) */
+
+ if (!(flags & OPERAND_REG) && (X_op == O_register))
+ {
+ symbolS * sym;
+
+ sym = find_symbol_matching_register (& myops[i]);
+
+ if (sym != NULL)
+ {
+ myops [i].X_op == X_op == O_symbol;
+ myops [i].X_add_symbol = sym;
+ }
+ else
+ as_bad
+ (_("illegal operand - register name found where none expected"));
+ }
+ }
+
+ /* We're only done if the operands matched so far AND there
+ are no more to check. */
+ if (match && myops[i].X_op == 0)
+ break;
+ else
+ match = 0;
+
+ next_opcode = opcode + 1;
+
+ if (next_opcode->opcode == 0)
+ break;
+
+ if (strcmp (next_opcode->name, opcode->name))
+ break;
+
+ opcode = next_opcode;
+ }
+ }
+
+ if (!match)
+ {
+ as_bad (_("bad opcode or operands"));
+ return (0);
+ }
+
+ /* Check that all registers that are required to be even are. */
+ /* Also, if any operands were marked as registers, but were really symbols */
+ /* fix that here. */
+ for (i=0; opcode->operands[i]; i++)
+ {
+ if ((d10v_operands[opcode->operands[i]].flags & OPERAND_EVEN) &&
+ (myops[i].X_add_number & 1))
+ as_fatal (_("Register number must be EVEN"));
+ if (myops[i].X_op == O_register)
+ {
+ if (!(d10v_operands[opcode->operands[i]].flags & OPERAND_REG))
+ {
+ myops[i].X_op = O_symbol;
+ myops[i].X_add_symbol = symbol_find_or_make ((char *)myops[i].X_op_symbol);
+ myops[i].X_add_number = 0;
+ myops[i].X_op_symbol = NULL;
+ }
+ }
+ }
+ return opcode;
+}
+
+/* if while processing a fixup, a reloc really needs to be created */
+/* then it is done here */
+
+arelent *
+tc_gen_reloc (seg, fixp)
+ asection *seg;
+ fixS *fixp;
+{
+ arelent *reloc;
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("reloc %d not supported by object file format"), (int)fixp->fx_r_type);
+ return NULL;
+ }
+
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ reloc->address = fixp->fx_offset;
+
+ reloc->addend = fixp->fx_addnumber;
+
+ return reloc;
+}
+
+int
+md_estimate_size_before_relax (fragp, seg)
+ fragS *fragp;
+ asection *seg;
+{
+ abort ();
+ return 0;
+}
+
+long
+md_pcrel_from_section (fixp, sec)
+ fixS *fixp;
+ segT sec;
+{
+ if (fixp->fx_addsy != (symbolS *)NULL && (!S_IS_DEFINED (fixp->fx_addsy) ||
+ (S_GET_SEGMENT (fixp->fx_addsy) != sec)))
+ return 0;
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+}
+
+int
+md_apply_fix3 (fixp, valuep, seg)
+ fixS *fixp;
+ valueT *valuep;
+ segT seg;
+{
+ char *where;
+ unsigned long insn;
+ long value;
+ int op_type;
+ int left=0;
+
+ if (fixp->fx_addsy == (symbolS *) NULL)
+ {
+ value = *valuep;
+ fixp->fx_done = 1;
+ }
+ else if (fixp->fx_pcrel)
+ value = *valuep;
+ else
+ {
+ value = fixp->fx_offset;
+ if (fixp->fx_subsy != (symbolS *) NULL)
+ {
+ if (S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
+ value -= S_GET_VALUE (fixp->fx_subsy);
+ else
+ {
+ /* We don't actually support subtracting a symbol. */
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("expression too complex"));
+ }
+ }
+ }
+
+ op_type = fixp->fx_r_type;
+ if (op_type & 2048)
+ {
+ op_type -= 2048;
+ if (op_type & 1024)
+ {
+ op_type -= 1024;
+ fixp->fx_r_type = BFD_RELOC_D10V_10_PCREL_L;
+ left = 1;
+ }
+ else if (op_type & 4096)
+ {
+ op_type -= 4096;
+ fixp->fx_r_type = BFD_RELOC_D10V_18;
+ }
+ else
+ fixp->fx_r_type = get_reloc((struct d10v_operand *)&d10v_operands[op_type]);
+ }
+
+ /* Fetch the instruction, insert the fully resolved operand
+ value, and stuff the instruction back again. */
+ where = fixp->fx_frag->fr_literal + fixp->fx_where;
+ insn = bfd_getb32 ((unsigned char *) where);
+
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_D10V_10_PCREL_L:
+ case BFD_RELOC_D10V_10_PCREL_R:
+ case BFD_RELOC_D10V_18_PCREL:
+ case BFD_RELOC_D10V_18:
+ /* instruction addresses are always right-shifted by 2 */
+ value >>= AT_WORD_RIGHT_SHIFT;
+ if (fixp->fx_size == 2)
+ bfd_putb16 ((bfd_vma) value, (unsigned char *) where);
+ else
+ {
+ struct d10v_opcode *rep, *repi;
+
+ rep = (struct d10v_opcode *) hash_find (d10v_hash, "rep");
+ repi = (struct d10v_opcode *) hash_find (d10v_hash, "repi");
+ if ((insn & FM11) == FM11
+ && (repi != NULL && (insn & repi->mask) == repi->opcode
+ || rep != NULL && (insn & rep->mask) == rep->opcode)
+ && value < 4)
+ as_fatal
+ (_("line %d: rep or repi must include at least 4 instructions"),
+ fixp->fx_line);
+ insn = d10v_insert_operand (insn, op_type, (offsetT)value, left, fixp);
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ }
+ break;
+ case BFD_RELOC_32:
+ bfd_putb32 ((bfd_vma) value, (unsigned char *) where);
+ break;
+ case BFD_RELOC_16:
+ bfd_putb16 ((bfd_vma) value, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixp->fx_done = 0;
+ return 1;
+
+ default:
+ as_fatal (_("line %d: unknown relocation type: 0x%x"),fixp->fx_line,fixp->fx_r_type);
+ }
+ return 0;
+}
+
+/* d10v_cleanup() is called after the assembler has finished parsing the input
+ file or after a label is defined. Because the D10V assembler sometimes saves short
+ instructions to see if it can package them with the next instruction, there may
+ be a short instruction that still needs written. */
+int
+d10v_cleanup ()
+{
+ segT seg;
+ subsegT subseg;
+
+ if (prev_opcode && etype == 0)
+ {
+ seg = now_seg;
+ subseg = now_subseg;
+ if (prev_seg)
+ subseg_set (prev_seg, prev_subseg);
+ write_1_short (prev_opcode, prev_insn, fixups->next);
+ subseg_set (seg, subseg);
+ prev_opcode = NULL;
+ }
+ return 1;
+}
+
+/* Like normal .word, except support @word */
+/* clobbers input_line_pointer, checks end-of-line. */
+static void
+d10v_dot_word (nbytes)
+ register int nbytes; /* 1=.byte, 2=.word, 4=.long */
+{
+ expressionS exp;
+ bfd_reloc_code_real_type reloc;
+ char *p;
+ int offset;
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ do
+ {
+ expression (&exp);
+ if (!strncasecmp (input_line_pointer, "@word", 5))
+ {
+ exp.X_add_number = 0;
+ input_line_pointer += 5;
+
+ p = frag_more (2);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 2,
+ &exp, 0, BFD_RELOC_D10V_18);
+ }
+ else
+ emit_expr (&exp, 2);
+ }
+ while (*input_line_pointer++ == ',');
+
+ input_line_pointer--; /* Put terminator back into stream. */
+ demand_empty_rest_of_line ();
+}
+
+
+/* Mitsubishi asked that we support some old syntax that apparently */
+/* had immediate operands starting with '#'. This is in some of their */
+/* sample code but is not documented (although it appears in some */
+/* examples in their assembler manual). For now, we'll solve this */
+/* compatibility problem by simply ignoring any '#' at the beginning */
+/* of an operand. */
+
+/* operands that begin with '#' should fall through to here */
+/* from expr.c */
+
+void
+md_operand (expressionP)
+ expressionS *expressionP;
+{
+ if (*input_line_pointer == '#')
+ {
+ input_line_pointer++;
+ expression (expressionP);
+ }
+}
+
+boolean
+d10v_fix_adjustable (fixP)
+ fixS *fixP;
+{
+
+ if (fixP->fx_addsy == NULL)
+ return 1;
+
+ /* Prevent all adjustments to global symbols. */
+ if (S_IS_EXTERN (fixP->fx_addsy))
+ return 0;
+ if (S_IS_WEAK (fixP->fx_addsy))
+ return 0;
+
+ /* We need the symbol name for the VTABLE entries */
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ return 1;
+}
+
+int
+d10v_force_relocation (fixp)
+ struct fix *fixp;
+{
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 1;
+
+ return 0;
+}
diff --git a/gas/config/tc-d10v.h b/gas/config/tc-d10v.h
new file mode 100644
index 0000000..c6ffd5c
--- /dev/null
+++ b/gas/config/tc-d10v.h
@@ -0,0 +1,62 @@
+/* tc-d10v.h -- Header file for tc-d10v.c.
+ Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+ Written by Martin Hunt, Cygnus Support.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#define TC_D10V
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#ifndef BFD_ASSEMBLER
+ #error D10V support requires BFD_ASSEMBLER
+#endif
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_d10v
+
+#define TARGET_FORMAT "elf32-d10v"
+
+#define MD_APPLY_FIX3
+
+/* call md_pcrel_from_section, not md_pcrel_from */
+#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section(FIXP, SEC)
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+#define DIFF_EXPR_OK /* .-foo gets turned into PC relative relocs */
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+#define md_number_to_chars number_to_chars_bigendian
+
+int d10v_cleanup PARAMS ((void));
+#define md_after_pass_hook() d10v_cleanup()
+#define md_cleanup() d10v_cleanup()
+#define md_do_align(a,b,c,d,e) d10v_cleanup()
+#define tc_frob_label(sym) do {\
+ d10v_cleanup(); \
+ S_SET_VALUE (sym, (valueT) frag_now_fix ()); \
+} while (0)
+
+#define obj_fix_adjustable(fixP) d10v_fix_adjustable(fixP)
+#define TC_FORCE_RELOCATION(fixp) d10v_force_relocation(fixp)
+extern int d10v_force_relocation PARAMS ((struct fix *));
+
diff --git a/gas/config/tc-d30v.c b/gas/config/tc-d30v.c
new file mode 100644
index 0000000..c5033ba
--- /dev/null
+++ b/gas/config/tc-d30v.c
@@ -0,0 +1,2218 @@
+/* tc-d30v.c -- Assembler code for the Mitsubishi D30V
+
+ Copyright (C) 1997, 1998 Free Software Foundation.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "as.h"
+#include "subsegs.h"
+#include "opcode/d30v.h"
+
+const char comment_chars[] = ";";
+const char line_comment_chars[] = "#";
+const char line_separator_chars[] = "";
+const char *md_shortopts = "OnNcC";
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "dD";
+
+#define NOP_MULTIPLY 1
+#define NOP_ALL 2
+static int warn_nops = 0;
+static int Optimizing = 0;
+static int warn_register_name_conflicts = 1;
+
+#define FORCE_SHORT 1
+#define FORCE_LONG 2
+
+/* EXEC types. */
+typedef enum _exec_type
+{
+ EXEC_UNKNOWN, /* no order specified */
+ EXEC_PARALLEL, /* done in parallel (FM=00) */
+ EXEC_SEQ, /* sequential (FM=01) */
+ EXEC_REVSEQ /* reverse sequential (FM=10) */
+} exec_type_enum;
+
+/* fixups */
+#define MAX_INSN_FIXUPS (5)
+struct d30v_fixup
+{
+ expressionS exp;
+ int operand;
+ int pcrel;
+ int size;
+ bfd_reloc_code_real_type reloc;
+};
+
+typedef struct _fixups
+{
+ int fc;
+ struct d30v_fixup fix[MAX_INSN_FIXUPS];
+ struct _fixups *next;
+} Fixups;
+
+static Fixups FixUps[2];
+static Fixups *fixups;
+
+/* Whether current and previous instruction are word multiply insns. */
+static int cur_mul32_p = 0;
+static int prev_mul32_p = 0;
+
+/* The flag_explicitly_parallel is true iff the instruction being assembled
+ has been explicitly written as a parallel short-instruction pair by the
+ human programmer. It is used in parallel_ok() to distinguish between
+ those dangerous parallelizations attempted by the human, which are to be
+ allowed, and those attempted by the assembler, which are not. It is set
+ from md_assemble(). */
+static int flag_explicitly_parallel = 0;
+static int flag_xp_state = 0;
+
+/* Whether current and previous left sub-instruction disables
+ execution of right sub-instruction. */
+static int cur_left_kills_right_p = 0;
+static int prev_left_kills_right_p = 0;
+
+/* The known current alignment of the current section. */
+static int d30v_current_align;
+static segT d30v_current_align_seg;
+
+/* The last seen label in the current section. This is used to auto-align
+ labels preceeding instructions. */
+static symbolS *d30v_last_label;
+
+/* Two nops */
+#define NOP_LEFT ((long long) NOP << 32)
+#define NOP_RIGHT ((long long) NOP)
+#define NOP2 (FM00 | NOP_LEFT | NOP_RIGHT)
+
+/* local functions */
+static int reg_name_search PARAMS ((char *name));
+static int register_name PARAMS ((expressionS *expressionP));
+static int check_range PARAMS ((unsigned long num, int bits, int flags));
+static int postfix PARAMS ((char *p));
+static bfd_reloc_code_real_type get_reloc PARAMS ((struct d30v_operand *op, int rel_flag));
+static int get_operands PARAMS ((expressionS exp[], int cmp_hack));
+static struct d30v_format *find_format PARAMS ((struct d30v_opcode *opcode,
+ expressionS ops[],int fsize, int cmp_hack));
+static long long build_insn PARAMS ((struct d30v_insn *opcode, expressionS *opers));
+static void write_long PARAMS ((struct d30v_insn *opcode, long long insn, Fixups *fx));
+static void write_1_short PARAMS ((struct d30v_insn *opcode, long long insn,
+ Fixups *fx, int use_sequential));
+static int write_2_short PARAMS ((struct d30v_insn *opcode1, long long insn1,
+ struct d30v_insn *opcode2, long long insn2, exec_type_enum exec_type, Fixups *fx));
+static long long do_assemble PARAMS ((char *str, struct d30v_insn *opcode,
+ int shortp, int is_parallel));
+static int parallel_ok PARAMS ((struct d30v_insn *opcode1, unsigned long insn1,
+ struct d30v_insn *opcode2, unsigned long insn2,
+ exec_type_enum exec_type));
+static void d30v_number_to_chars PARAMS ((char *buf, long long value, int nbytes));
+static void check_size PARAMS ((long value, int bits, char *file, int line));
+static void d30v_align PARAMS ((int, char *, symbolS *));
+static void s_d30v_align PARAMS ((int));
+static void s_d30v_text PARAMS ((int));
+static void s_d30v_data PARAMS ((int));
+static void s_d30v_section PARAMS ((int));
+
+struct option md_longopts[] = {
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
+
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "word", cons, 4 },
+ { "hword", cons, 2 },
+ { "align", s_d30v_align, 0 },
+ { "text", s_d30v_text, 0 },
+ { "data", s_d30v_data, 0 },
+ { "section", s_d30v_section, 0 },
+ { "section.s", s_d30v_section, 0 },
+ { "sect", s_d30v_section, 0 },
+ { "sect.s", s_d30v_section, 0 },
+ { NULL, NULL, 0 }
+};
+
+/* Opcode hash table. */
+static struct hash_control *d30v_hash;
+
+/* reg_name_search does a binary search of the pre_defined_registers
+ array to see if "name" is a valid regiter name. Returns the register
+ number from the array on success, or -1 on failure. */
+
+static int
+reg_name_search (name)
+ char *name;
+{
+ int middle, low, high;
+ int cmp;
+
+ low = 0;
+ high = reg_name_cnt () - 1;
+
+ do
+ {
+ middle = (low + high) / 2;
+ cmp = strcasecmp (name, pre_defined_registers[middle].name);
+ if (cmp < 0)
+ high = middle - 1;
+ else if (cmp > 0)
+ low = middle + 1;
+ else
+ {
+ if (symbol_find (name) != NULL)
+ {
+ if (warn_register_name_conflicts)
+ as_warn (_("Register name %s conflicts with symbol of the same name"),
+ name);
+ }
+
+ return pre_defined_registers[middle].value;
+ }
+ }
+ while (low <= high);
+
+ return -1;
+}
+
+/* register_name() checks the string at input_line_pointer
+ to see if it is a valid register name. */
+
+static int
+register_name (expressionP)
+ expressionS *expressionP;
+{
+ int reg_number;
+ char c, *p = input_line_pointer;
+
+ while (*p && *p!='\n' && *p!='\r' && *p !=',' && *p!=' ' && *p!=')')
+ p++;
+
+ c = *p;
+ if (c)
+ *p++ = 0;
+
+ /* look to see if it's in the register table */
+ reg_number = reg_name_search (input_line_pointer);
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ /* temporarily store a pointer to the string here */
+ expressionP->X_op_symbol = (struct symbol *)input_line_pointer;
+ expressionP->X_add_number = reg_number;
+ input_line_pointer = p;
+ return 1;
+ }
+ if (c)
+ *(p-1) = c;
+ return 0;
+}
+
+
+static int
+check_range (num, bits, flags)
+ unsigned long num;
+ int bits;
+ int flags;
+{
+ long min, max;
+ int retval=0;
+
+ /* don't bother checking 32-bit values */
+ if (bits == 32)
+ return 0;
+
+ if (flags & OPERAND_SHIFT)
+ {
+ /* We know that all shifts are right by three bits.... */
+
+ if (flags & OPERAND_SIGNED)
+ num = (unsigned long) (((/*signed*/ long) num) >> 3);
+ else
+ num >>= 3;
+ }
+
+ if (flags & OPERAND_SIGNED)
+ {
+ max = (1 << (bits - 1))-1;
+ min = - (1 << (bits - 1));
+ if (((long)num > max) || ((long)num < min))
+ retval = 1;
+ }
+ else
+ {
+ max = (1 << bits) - 1;
+ min = 0;
+ if ((num > max) || (num < min))
+ retval = 1;
+ }
+
+ return retval;
+}
+
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ fprintf (stream, _("\nD30V options:\n\
+-O Make adjacent short instructions parallel if possible.\n\
+-n Warn about all NOPs inserted by the assembler.\n\
+-N Warn about NOPs inserted after word multiplies.\n\
+-c Warn about symbols whoes names match register names.\n\
+-C Opposite of -C. -c is the default.\n"));
+}
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ switch (c)
+ {
+ /* Optimize. Will attempt to parallelize operations */
+ case 'O':
+ Optimizing = 1;
+ break;
+
+ /* Warn about all NOPS that the assembler inserts. */
+ case 'n':
+ warn_nops = NOP_ALL;
+ break;
+
+ /* Warn about the NOPS that the assembler inserts because of the
+ multiply hazard. */
+ case 'N':
+ warn_nops = NOP_MULTIPLY;
+ break;
+
+ case 'c':
+ warn_register_name_conflicts = 1;
+ break;
+
+ case 'C':
+ warn_register_name_conflicts = 0;
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ return 0;
+}
+
+/* Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP . An error message is returned, or NULL on OK.
+ */
+char *
+md_atof (type, litP, sizeP)
+ int type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[4];
+ char *t;
+ int i;
+
+ switch (type)
+ {
+ case 'f':
+ prec = 2;
+ break;
+ case 'd':
+ prec = 4;
+ break;
+ default:
+ *sizeP = 0;
+ return _("bad call to md_atof");
+ }
+
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+
+ *sizeP = prec * 2;
+
+ for (i = 0; i < prec; i++)
+ {
+ md_number_to_chars (litP, (valueT) words[i], 2);
+ litP += 2;
+ }
+ return NULL;
+}
+
+void
+md_convert_frag (abfd, sec, fragP)
+ bfd *abfd;
+ asection *sec;
+ fragS *fragP;
+{
+ abort ();
+}
+
+valueT
+md_section_align (seg, addr)
+ asection *seg;
+ valueT addr;
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+
+void
+md_begin ()
+{
+ struct d30v_opcode * opcode;
+ d30v_hash = hash_new ();
+
+ /* Insert opcode names into a hash table. */
+ for (opcode = (struct d30v_opcode *)d30v_opcode_table; opcode->name; opcode++)
+ hash_insert (d30v_hash, opcode->name, (char *) opcode);
+
+ fixups = &FixUps[0];
+ FixUps[0].next = &FixUps[1];
+ FixUps[1].next = &FixUps[0];
+
+ d30v_current_align_seg = now_seg;
+}
+
+
+/* this function removes the postincrement or postdecrement
+ operator ( '+' or '-' ) from an expression */
+
+static int postfix (p)
+ char *p;
+{
+ while (*p != '-' && *p != '+')
+ {
+ if (*p==0 || *p=='\n' || *p=='\r' || *p==' ' || *p==',')
+ break;
+ p++;
+ }
+
+ if (*p == '-')
+ {
+ *p = ' ';
+ return (-1);
+ }
+ if (*p == '+')
+ {
+ *p = ' ';
+ return (1);
+ }
+
+ return (0);
+}
+
+
+static bfd_reloc_code_real_type
+get_reloc (op, rel_flag)
+ struct d30v_operand *op;
+ int rel_flag;
+{
+ switch (op->bits)
+ {
+ case 6:
+ if (op->flags & OPERAND_SHIFT)
+ return BFD_RELOC_D30V_9_PCREL;
+ else
+ return BFD_RELOC_D30V_6;
+ break;
+ case 12:
+ if (!(op->flags & OPERAND_SHIFT))
+ as_warn (_("unexpected 12-bit reloc type"));
+ if (rel_flag == RELOC_PCREL)
+ return BFD_RELOC_D30V_15_PCREL;
+ else
+ return BFD_RELOC_D30V_15;
+ case 18:
+ if (!(op->flags & OPERAND_SHIFT))
+ as_warn (_("unexpected 18-bit reloc type"));
+ if (rel_flag == RELOC_PCREL)
+ return BFD_RELOC_D30V_21_PCREL;
+ else
+ return BFD_RELOC_D30V_21;
+ case 32:
+ if (rel_flag == RELOC_PCREL)
+ return BFD_RELOC_D30V_32_PCREL;
+ else
+ return BFD_RELOC_D30V_32;
+ default:
+ return 0;
+ }
+}
+
+/* get_operands parses a string of operands and returns
+ an array of expressions */
+
+static int
+get_operands (exp, cmp_hack)
+ expressionS exp[];
+ int cmp_hack;
+{
+ char *p = input_line_pointer;
+ int numops = 0;
+ int post = 0;
+
+ if (cmp_hack)
+ {
+ exp[numops].X_op = O_absent;
+ exp[numops++].X_add_number = cmp_hack - 1;
+ }
+
+ while (*p)
+ {
+ while (*p == ' ' || *p == '\t' || *p == ',')
+ p++;
+ if (*p==0 || *p=='\n' || *p=='\r')
+ break;
+
+ if (*p == '@')
+ {
+ p++;
+ exp[numops].X_op = O_absent;
+ if (*p == '(')
+ {
+ p++;
+ exp[numops].X_add_number = OPERAND_ATPAR;
+ post = postfix (p);
+ }
+ else if (*p == '-')
+ {
+ p++;
+ exp[numops].X_add_number = OPERAND_ATMINUS;
+ }
+ else
+ {
+ exp[numops].X_add_number = OPERAND_ATSIGN;
+ post = postfix (p);
+ }
+ numops++;
+ continue;
+ }
+
+ if (*p == ')')
+ {
+ /* just skip the trailing paren */
+ p++;
+ continue;
+ }
+
+ input_line_pointer = p;
+
+ /* check to see if it might be a register name */
+ if (!register_name (&exp[numops]))
+ {
+ /* parse as an expression */
+ expression (&exp[numops]);
+ }
+
+ if (exp[numops].X_op == O_illegal)
+ as_bad (_("illegal operand"));
+ else if (exp[numops].X_op == O_absent)
+ as_bad (_("missing operand"));
+
+ numops++;
+ p = input_line_pointer;
+
+ switch (post)
+ {
+ case -1: /* postdecrement mode */
+ exp[numops].X_op = O_absent;
+ exp[numops++].X_add_number = OPERAND_MINUS;
+ break;
+ case 1: /* postincrement mode */
+ exp[numops].X_op = O_absent;
+ exp[numops++].X_add_number = OPERAND_PLUS;
+ break;
+ }
+ post = 0;
+ }
+
+ exp[numops].X_op = 0;
+ return (numops);
+}
+
+/* build_insn generates the instruction. It does everything */
+/* but write the FM bits. */
+
+static long long
+build_insn (opcode, opers)
+ struct d30v_insn *opcode;
+ expressionS *opers;
+{
+ int i, length, bits, shift, flags;
+ unsigned int number, id=0;
+ long long insn;
+ struct d30v_opcode *op = opcode->op;
+ struct d30v_format *form = opcode->form;
+
+ insn = opcode->ecc << 28 | op->op1 << 25 | op->op2 << 20 | form->modifier << 18;
+
+ for (i=0; form->operands[i]; i++)
+ {
+ flags = d30v_operand_table[form->operands[i]].flags;
+
+ /* must be a register or number */
+ if (!(flags & OPERAND_REG) && !(flags & OPERAND_NUM) &&
+ !(flags & OPERAND_NAME) && !(flags & OPERAND_SPECIAL))
+ continue;
+
+ bits = d30v_operand_table[form->operands[i]].bits;
+ if (flags & OPERAND_SHIFT)
+ bits += 3;
+
+ length = d30v_operand_table[form->operands[i]].length;
+ shift = 12 - d30v_operand_table[form->operands[i]].position;
+ if (opers[i].X_op != O_symbol)
+ number = opers[i].X_add_number;
+ else
+ number = 0;
+ if (flags & OPERAND_REG)
+ {
+ /* check for mvfsys or mvtsys control registers */
+ if (flags & OPERAND_CONTROL && (number & 0x7f) > MAX_CONTROL_REG)
+ {
+ /* PSWL or PSWH */
+ id = (number & 0x7f) - MAX_CONTROL_REG;
+ number = 0;
+ }
+ else if (number & OPERAND_FLAG)
+ {
+ id = 3; /* number is a flag register */
+ }
+ number &= 0x7F;
+ }
+ else if (flags & OPERAND_SPECIAL)
+ {
+ number = id;
+ }
+
+ if (opers[i].X_op != O_register && opers[i].X_op != O_constant && !(flags & OPERAND_NAME))
+ {
+ /* now create a fixup */
+
+ if (fixups->fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ fixups->fix[fixups->fc].reloc =
+ get_reloc ((struct d30v_operand *)&d30v_operand_table[form->operands[i]], op->reloc_flag);
+ fixups->fix[fixups->fc].size = 4;
+ fixups->fix[fixups->fc].exp = opers[i];
+ fixups->fix[fixups->fc].operand = form->operands[i];
+ if (fixups->fix[fixups->fc].reloc == BFD_RELOC_D30V_9_PCREL)
+ fixups->fix[fixups->fc].pcrel = RELOC_PCREL;
+ else
+ fixups->fix[fixups->fc].pcrel = op->reloc_flag;
+ (fixups->fc)++;
+ }
+
+ /* truncate to the proper number of bits */
+ if ((opers[i].X_op == O_constant) && check_range (number, bits, flags))
+ as_bad (_("operand out of range: %d"),number);
+ if (bits < 31)
+ number &= 0x7FFFFFFF >> (31 - bits);
+ if (flags & OPERAND_SHIFT)
+ number >>= 3;
+ if (bits == 32)
+ {
+ /* it's a LONG instruction */
+ insn |= (number >> 26); /* top 6 bits */
+ insn <<= 32; /* shift the first word over */
+ insn |= ((number & 0x03FC0000) << 2); /* next 8 bits */
+ insn |= number & 0x0003FFFF; /* bottom 18 bits */
+ }
+ else
+ insn |= number << shift;
+ }
+ return insn;
+}
+
+
+/* write out a long form instruction */
+static void
+write_long (opcode, insn, fx)
+ struct d30v_insn *opcode;
+ long long insn;
+ Fixups *fx;
+{
+ int i, where;
+ char *f = frag_more (8);
+
+ insn |= FM11;
+ d30v_number_to_chars (f, insn, 8);
+
+ for (i=0; i < fx->fc; i++)
+ {
+ if (fx->fix[i].reloc)
+ {
+ where = f - frag_now->fr_literal;
+ fix_new_exp (frag_now,
+ where,
+ fx->fix[i].size,
+ &(fx->fix[i].exp),
+ fx->fix[i].pcrel,
+ fx->fix[i].reloc);
+ }
+ }
+ fx->fc = 0;
+}
+
+
+/* Write out a short form instruction by itself. */
+static void
+write_1_short (opcode, insn, fx, use_sequential)
+ struct d30v_insn *opcode;
+ long long insn;
+ Fixups *fx;
+ int use_sequential;
+{
+ char *f = frag_more (8);
+ int i, where;
+
+ if (warn_nops == NOP_ALL)
+ as_warn (_("%s NOP inserted"), use_sequential ?
+ _("sequential") : _("parallel"));
+
+ /* The other container needs to be NOP. */
+ if (use_sequential)
+ {
+ /* Use a sequential NOP rather than a parallel one,
+ as the current instruction is a FLAG_MUL32 type one
+ and the next instruction is a load. */
+
+ /* According to 4.3.1: for FM=01, sub-instructions performed
+ only by IU cannot be encoded in L-container. */
+
+ if (opcode->op->unit == IU)
+ insn |= FM10 | NOP_LEFT; /* right then left */
+ else
+ insn = FM01 | (insn << 32) | NOP_RIGHT; /* left then right */
+ }
+ else
+ {
+ /* According to 4.3.1: for FM=00, sub-instructions performed
+ only by IU cannot be encoded in L-container. */
+
+ if (opcode->op->unit == IU)
+ insn |= FM00 | NOP_LEFT; /* right container */
+ else
+ insn = FM00 | (insn << 32) | NOP_RIGHT; /* left container */
+ }
+
+ d30v_number_to_chars (f, insn, 8);
+
+ for (i=0; i < fx->fc; i++)
+ {
+ if (fx->fix[i].reloc)
+ {
+ where = f - frag_now->fr_literal;
+ fix_new_exp (frag_now,
+ where,
+ fx->fix[i].size,
+ &(fx->fix[i].exp),
+ fx->fix[i].pcrel,
+ fx->fix[i].reloc);
+ }
+ }
+ fx->fc = 0;
+}
+
+/* Write out a short form instruction if possible.
+ Return number of instructions not written out. */
+static int
+write_2_short (opcode1, insn1, opcode2, insn2, exec_type, fx)
+ struct d30v_insn *opcode1, *opcode2;
+ long long insn1, insn2;
+ exec_type_enum exec_type;
+ Fixups *fx;
+{
+ long long insn = NOP2;
+ char *f;
+ int i,j, where;
+
+ if (exec_type == EXEC_SEQ
+ && (opcode1->op->flags_used & (FLAG_JMP | FLAG_JSR))
+ && ((opcode1->op->flags_used & FLAG_DELAY) == 0)
+ && ((opcode1->ecc == ECC_AL) || ! Optimizing))
+ {
+ /* Unconditional, non-delayed branches kill instructions in
+ the right bin. Conditional branches don't always but if
+ we are not optimizing, then we have been asked to produce
+ an error about such constructs. For the purposes of this
+ test, subroutine calls are considered to be branches. */
+ write_1_short (opcode1, insn1, fx->next, false);
+ return 1;
+ }
+
+ /* Note: we do not have to worry about subroutine calls occuring
+ in the right hand container. The return address is always
+ aligned to the next 64 bit boundary, be that 64 or 32 bit away. */
+
+ switch (exec_type)
+ {
+ case EXEC_UNKNOWN: /* Order not specified. */
+ if (Optimizing
+ && parallel_ok (opcode1, insn1, opcode2, insn2, exec_type)
+ && ! ( (opcode1->op->unit == EITHER_BUT_PREFER_MU
+ || opcode1->op->unit == MU)
+ &&
+ ( opcode2->op->unit == EITHER_BUT_PREFER_MU
+ || opcode2->op->unit == MU)))
+ {
+ /* parallel */
+ exec_type = EXEC_PARALLEL;
+
+ if (opcode1->op->unit == IU
+ || opcode2->op->unit == MU
+ || opcode2->op->unit == EITHER_BUT_PREFER_MU)
+ insn = FM00 | (insn2 << 32) | insn1;
+ else
+ {
+ insn = FM00 | (insn1 << 32) | insn2;
+ fx = fx->next;
+ }
+ }
+ else if (opcode1->op->flags_used & (FLAG_JMP | FLAG_JSR)
+ && ((opcode1->op->flags_used & FLAG_DELAY) == 0)
+ && ((opcode1->ecc == ECC_AL) || ! Optimizing))
+ {
+ /* We must emit (non-delayed) branch type instructions
+ on their own with nothing in the right container. */
+ write_1_short (opcode1, insn1, fx->next, false);
+ return 1;
+ }
+ else if (prev_left_kills_right_p)
+ {
+ /* The left instruction kils the right slot, so we
+ must leave it empty. */
+ write_1_short (opcode1, insn1, fx->next, false);
+ return 1;
+ }
+ else if (opcode1->op->unit == IU
+ || (opcode1->op->unit == EITHER
+ && opcode2->op->unit == EITHER_BUT_PREFER_MU))
+ {
+ /* reverse sequential */
+ insn = FM10 | (insn2 << 32) | insn1;
+ exec_type = EXEC_REVSEQ;
+ }
+ else
+ {
+ /* sequential */
+ insn = FM01 | (insn1 << 32) | insn2;
+ fx = fx->next;
+ exec_type = EXEC_SEQ;
+ }
+ break;
+
+ case EXEC_PARALLEL: /* parallel */
+ flag_explicitly_parallel = flag_xp_state;
+ if (! parallel_ok (opcode1, insn1, opcode2, insn2, exec_type))
+ as_bad (_("Instructions may not be executed in parallel"));
+ else if (opcode1->op->unit == IU)
+ {
+ if (opcode2->op->unit == IU)
+ as_bad (_("Two IU instructions may not be executed in parallel"));
+ as_warn (_("Swapping instruction order"));
+ insn = FM00 | (insn2 << 32) | insn1;
+ }
+ else if (opcode2->op->unit == MU)
+ {
+ if (opcode1->op->unit == MU)
+ as_bad (_("Two MU instructions may not be executed in parallel"));
+ else if (opcode1->op->unit == EITHER_BUT_PREFER_MU)
+ as_warn (_("Executing %s in IU may not work"), opcode1->op->name);
+ as_warn (_("Swapping instruction order"));
+ insn = FM00 | (insn2 << 32) | insn1;
+ }
+ else
+ {
+ if (opcode2->op->unit == EITHER_BUT_PREFER_MU)
+ as_warn (_("Executing %s in IU may not work"), opcode2->op->name);
+
+ insn = FM00 | (insn1 << 32) | insn2;
+ fx = fx->next;
+ }
+ flag_explicitly_parallel = 0;
+ break;
+
+ case EXEC_SEQ: /* sequential */
+ if (opcode1->op->unit == IU)
+ as_bad (_("IU instruction may not be in the left container"));
+ if (prev_left_kills_right_p)
+ as_bad (_("special left instruction `%s' kills instruction "
+ "`%s' in right container"),
+ opcode1->op->name, opcode2->op->name);
+ if (opcode2->op->unit == EITHER_BUT_PREFER_MU)
+ as_warn (_("Executing %s in IU may not work"), opcode2->op->name);
+ insn = FM01 | (insn1 << 32) | insn2;
+ fx = fx->next;
+ break;
+
+ case EXEC_REVSEQ: /* reverse sequential */
+ if (opcode2->op->unit == MU)
+ as_bad (_("MU instruction may not be in the right container"));
+ if (opcode2->op->unit == EITHER_BUT_PREFER_MU)
+ as_warn (_("Executing %s in IU may not work"), opcode2->op->name);
+ insn = FM10 | (insn1 << 32) | insn2;
+ fx = fx->next;
+ break;
+
+ default:
+ as_fatal (_("unknown execution type passed to write_2_short()"));
+ }
+
+ /* printf ("writing out %llx\n",insn); */
+ f = frag_more (8);
+ d30v_number_to_chars (f, insn, 8);
+
+ /* If the previous instruction was a 32-bit multiply but it is put into a
+ parallel container, mark the current instruction as being a 32-bit
+ multiply. */
+ if (prev_mul32_p && exec_type == EXEC_PARALLEL)
+ cur_mul32_p = 1;
+
+ for (j=0; j<2; j++)
+ {
+ for (i=0; i < fx->fc; i++)
+ {
+ if (fx->fix[i].reloc)
+ {
+ where = (f - frag_now->fr_literal) + 4*j;
+
+ fix_new_exp (frag_now,
+ where,
+ fx->fix[i].size,
+ &(fx->fix[i].exp),
+ fx->fix[i].pcrel,
+ fx->fix[i].reloc);
+ }
+ }
+
+ fx->fc = 0;
+ fx = fx->next;
+ }
+
+ return 0;
+}
+
+
+/* Check 2 instructions and determine if they can be safely */
+/* executed in parallel. Returns 1 if they can be. */
+static int
+parallel_ok (op1, insn1, op2, insn2, exec_type)
+ struct d30v_insn *op1, *op2;
+ unsigned long insn1, insn2;
+ exec_type_enum exec_type;
+{
+ int i, j, shift, regno, bits, ecc;
+ unsigned long flags, mask, flags_set1, flags_set2, flags_used1, flags_used2;
+ unsigned long ins, mod_reg[2][3], used_reg[2][3], flag_reg[2];
+ struct d30v_format *f;
+ struct d30v_opcode *op;
+
+ /* section 4.3: both instructions must not be IU or MU only */
+ if ((op1->op->unit == IU && op2->op->unit == IU)
+ || (op1->op->unit == MU && op2->op->unit == MU))
+ return 0;
+
+ /* first instruction must not be a jump to safely optimize, unless this
+ is an explicit parallel operation. */
+ if (exec_type != EXEC_PARALLEL
+ && (op1->op->flags_used & (FLAG_JMP | FLAG_JSR)))
+ return 0;
+
+ /* If one instruction is /TX or /XT and the other is /FX or /XF respectively,
+ then it is safe to allow the two to be done as parallel ops, since only
+ one will ever be executed at a time. */
+ if ((op1->ecc == ECC_TX && op2->ecc == ECC_FX)
+ || (op1->ecc == ECC_FX && op2->ecc == ECC_TX)
+ || (op1->ecc == ECC_XT && op2->ecc == ECC_XF)
+ || (op1->ecc == ECC_XF && op2->ecc == ECC_XT))
+ return 1;
+
+ /* [0] r0-r31
+ [1] r32-r63
+ [2] a0, a1, flag registers */
+
+ for (j = 0; j < 2; j++)
+ {
+ if (j == 0)
+ {
+ f = op1->form;
+ op = op1->op;
+ ecc = op1->ecc;
+ ins = insn1;
+ }
+ else
+ {
+ f = op2->form;
+ op = op2->op;
+ ecc = op2->ecc;
+ ins = insn2;
+ }
+ flag_reg[j] = 0;
+ mod_reg[j][0] = mod_reg[j][1] = 0;
+ used_reg[j][0] = used_reg[j][1] = 0;
+
+ if (flag_explicitly_parallel)
+ {
+ /* For human specified parallel instructions we have been asked
+ to ignore the possibility that both instructions could modify
+ bits in the PSW, so we initialise the mod & used arrays to 0.
+ We have been asked, however, to refuse to allow parallel
+ instructions which explicitly set the same flag register,
+ eg "cmpne f0,r1,0x10 || cmpeq f0, r5, 0x2", so further on we test
+ for the use of a flag register and set a bit in the mod or used
+ array appropriately. */
+
+ mod_reg[j][2] = 0;
+ used_reg[j][2] = 0;
+ }
+ else
+ {
+ mod_reg[j][2] = (op->flags_set & FLAG_ALL);
+ used_reg[j][2] = (op->flags_used & FLAG_ALL);
+ }
+
+ /* BSR/JSR always sets R62 */
+ if (op->flags_used & FLAG_JSR)
+ mod_reg[j][1] = (1L << (62-32));
+
+ /* conditional execution affects the flags_used */
+ switch (ecc)
+ {
+ case ECC_TX:
+ case ECC_FX:
+ used_reg[j][2] |= flag_reg[j] = FLAG_0;
+ break;
+
+ case ECC_XT:
+ case ECC_XF:
+ used_reg[j][2] |= flag_reg[j] = FLAG_1;
+ break;
+
+ case ECC_TT:
+ case ECC_TF:
+ used_reg[j][2] |= flag_reg[j] = (FLAG_0 | FLAG_1);
+ break;
+ }
+
+ for (i = 0; f->operands[i]; i++)
+ {
+ flags = d30v_operand_table[f->operands[i]].flags;
+ shift = 12 - d30v_operand_table[f->operands[i]].position;
+ bits = d30v_operand_table[f->operands[i]].bits;
+ if (bits == 32)
+ mask = 0xffffffff;
+ else
+ mask = 0x7FFFFFFF >> (31 - bits);
+
+ if ((flags & OPERAND_PLUS) || (flags & OPERAND_MINUS))
+ {
+ /* this is a post-increment or post-decrement */
+ /* the previous register needs to be marked as modified */
+
+ shift = 12 - d30v_operand_table[f->operands[i-1]].position;
+ regno = (ins >> shift) & 0x3f;
+ if (regno >= 32)
+ mod_reg[j][1] |= 1L << (regno - 32);
+ else
+ mod_reg[j][0] |= 1L << regno;
+ }
+ else if (flags & OPERAND_REG)
+ {
+ regno = (ins >> shift) & mask;
+ /* the memory write functions don't have a destination register */
+ if ((flags & OPERAND_DEST) && !(op->flags_set & FLAG_MEM))
+ {
+ /* MODIFIED registers and flags */
+ if (flags & OPERAND_ACC)
+ {
+ if (regno == 0)
+ mod_reg[j][2] |= FLAG_A0;
+ else if (regno == 1)
+ mod_reg[j][2] |= FLAG_A1;
+ else
+ abort ();
+ }
+ else if (flags & OPERAND_FLAG)
+ mod_reg[j][2] |= 1L << regno;
+ else if (!(flags & OPERAND_CONTROL))
+ {
+ int r, z;
+
+ /* need to check if there are two destination */
+ /* registers, for example ld2w */
+ if (flags & OPERAND_2REG)
+ z = 1;
+ else
+ z = 0;
+
+ for (r = regno; r <= regno + z; r++)
+ {
+ if (r >= 32)
+ mod_reg[j][1] |= 1L << (r - 32);
+ else
+ mod_reg[j][0] |= 1L << r;
+ }
+ }
+ }
+ else
+ {
+ /* USED, but not modified registers and flags */
+ if (flags & OPERAND_ACC)
+ {
+ if (regno == 0)
+ used_reg[j][2] |= FLAG_A0;
+ else if (regno == 1)
+ used_reg[j][2] |= FLAG_A1;
+ else
+ abort ();
+ }
+ else if (flags & OPERAND_FLAG)
+ used_reg[j][2] |= 1L << regno;
+ else if (!(flags & OPERAND_CONTROL))
+ {
+ int r, z;
+
+ /* need to check if there are two source */
+ /* registers, for example st2w */
+ if (flags & OPERAND_2REG)
+ z = 1;
+ else
+ z = 0;
+
+ for (r = regno; r <= regno + z; r++)
+ {
+ if (r >= 32)
+ used_reg[j][1] |= 1L << (r - 32);
+ else
+ used_reg[j][0] |= 1L << r;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ flags_set1 = op1->op->flags_set;
+ flags_set2 = op2->op->flags_set;
+ flags_used1 = op1->op->flags_used;
+ flags_used2 = op2->op->flags_used;
+
+ /* ST2W/ST4HB combined with ADDppp/SUBppp is illegal. */
+ if (((flags_set1 & (FLAG_MEM | FLAG_2WORD)) == (FLAG_MEM | FLAG_2WORD)
+ && (flags_used2 & FLAG_ADDSUBppp) != 0)
+ || ((flags_set2 & (FLAG_MEM | FLAG_2WORD)) == (FLAG_MEM | FLAG_2WORD)
+ && (flags_used1 & FLAG_ADDSUBppp) != 0))
+ return 0;
+
+ /* Load instruction combined with half-word multiply is illegal. */
+ if (((flags_used1 & FLAG_MEM) != 0 && (flags_used2 & FLAG_MUL16))
+ || ((flags_used2 & FLAG_MEM) != 0 && (flags_used1 & FLAG_MUL16)))
+ return 0;
+
+ /* Specifically allow add || add by removing carry, overflow bits dependency.
+ This is safe, even if an addc follows since the IU takes the argument in
+ the right container, and it writes its results last.
+ However, don't paralellize add followed by addc or sub followed by
+ subb. */
+
+ if (mod_reg[0][2] == FLAG_CVVA && mod_reg[1][2] == FLAG_CVVA
+ && (used_reg[0][2] & ~flag_reg[0]) == 0
+ && (used_reg[1][2] & ~flag_reg[1]) == 0
+ && op1->op->unit == EITHER && op2->op->unit == EITHER)
+ {
+ mod_reg[0][2] = mod_reg[1][2] = 0;
+ }
+
+ for (j = 0; j < 3; j++)
+ {
+ /* If the second instruction depends on the first, we obviously
+ cannot parallelize. Note, the mod flag implies use, so
+ check that as well. */
+ /* If flag_explicitly_parallel is set, then the case of the
+ second instruction using a register the first instruction
+ modifies is assumed to be okay; we trust the human. We
+ don't trust the human if both instructions modify the same
+ register but we do trust the human if they modify the same
+ flags. */
+ /* We have now been requested not to trust the human if the
+ instructions modify the same flag registers either. */
+ if (flag_explicitly_parallel)
+ {
+ if ((mod_reg[0][j] & mod_reg[1][j]) != 0)
+ return 0;
+ }
+ else
+ if ((mod_reg[0][j] & (mod_reg[1][j] | used_reg[1][j])) != 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/* This is the main entry point for the machine-dependent assembler. str points to a
+ machine-dependent instruction. This function is supposed to emit the frags/bytes
+ it assembles to. For the D30V, it mostly handles the special VLIW parsing and packing
+ and leaves the difficult stuff to do_assemble(). */
+
+static long long prev_insn = -1;
+static struct d30v_insn prev_opcode;
+static subsegT prev_subseg;
+static segT prev_seg = 0;
+
+void
+md_assemble (str)
+ char *str;
+{
+ struct d30v_insn opcode;
+ long long insn;
+ exec_type_enum extype = EXEC_UNKNOWN; /* execution type; parallel, etc */
+ static exec_type_enum etype = EXEC_UNKNOWN; /* saved extype. used for multiline instructions */
+ char *str2;
+
+ if ((prev_insn != -1) && prev_seg
+ && ((prev_seg != now_seg) || (prev_subseg != now_subseg)))
+ d30v_cleanup (false);
+
+ if (d30v_current_align < 3)
+ d30v_align (3, NULL, d30v_last_label);
+ else if (d30v_current_align > 3)
+ d30v_current_align = 3;
+ d30v_last_label = NULL;
+
+ flag_explicitly_parallel = 0;
+ flag_xp_state = 0;
+ if (etype == EXEC_UNKNOWN)
+ {
+ /* look for the special multiple instruction separators */
+ str2 = strstr (str, "||");
+ if (str2)
+ {
+ extype = EXEC_PARALLEL;
+ flag_xp_state = 1;
+ }
+ else
+ {
+ str2 = strstr (str, "->");
+ if (str2)
+ extype = EXEC_SEQ;
+ else
+ {
+ str2 = strstr (str, "<-");
+ if (str2)
+ extype = EXEC_REVSEQ;
+ }
+ }
+ /* str2 points to the separator, if one */
+ if (str2)
+ {
+ *str2 = 0;
+
+ /* if two instructions are present and we already have one saved
+ then first write it out */
+ d30v_cleanup (false);
+
+ /* Assemble first instruction and save it. */
+ prev_insn = do_assemble (str, &prev_opcode, 1, 0);
+ if (prev_insn == -1)
+ as_bad (_("Cannot assemble instruction"));
+ if (prev_opcode.form != NULL && prev_opcode.form->form >= LONG)
+ as_bad (_("First opcode is long. Unable to mix instructions as specified."));
+ fixups = fixups->next;
+ str = str2 + 2;
+ prev_seg = now_seg;
+ prev_subseg = now_subseg;
+ }
+ }
+
+ insn = do_assemble (str, &opcode,
+ (extype != EXEC_UNKNOWN || etype != EXEC_UNKNOWN),
+ extype == EXEC_PARALLEL);
+ if (insn == -1)
+ {
+ if (extype != EXEC_UNKNOWN)
+ etype = extype;
+ as_bad (_("Cannot assemble instruction"));
+ return;
+ }
+
+ if (etype != EXEC_UNKNOWN)
+ {
+ extype = etype;
+ etype = EXEC_UNKNOWN;
+ }
+
+ /* Word multiply instructions must not be followed by either a load or a
+ 16-bit multiply instruction in the next cycle. */
+ if ( (extype != EXEC_REVSEQ)
+ && prev_mul32_p
+ && (opcode.op->flags_used & (FLAG_MEM | FLAG_MUL16)))
+ {
+ /* However, load and multiply should able to be combined in a parallel
+ operation, so check for that first. */
+ if (prev_insn != -1
+ && (opcode.op->flags_used & FLAG_MEM)
+ && opcode.form->form < LONG
+ && (extype == EXEC_PARALLEL || (Optimizing && extype == EXEC_UNKNOWN))
+ && parallel_ok (&prev_opcode, (long)prev_insn,
+ &opcode, (long)insn, extype)
+ && write_2_short (&prev_opcode, (long)prev_insn,
+ &opcode, (long)insn, extype, fixups) == 0)
+ {
+ /* no instructions saved */
+ prev_insn = -1;
+ return;
+ }
+ else
+ {
+ /* Can't parallelize, flush previous instruction and emit a word of NOPS,
+ unless the previous instruction is a NOP, in which case just flush it,
+ as this will generate a word of NOPs for us. */
+
+ if (prev_insn != -1 && (strcmp (prev_opcode.op->name, "nop") == 0))
+ d30v_cleanup (false);
+ else
+ {
+ char * f;
+
+ if (prev_insn != -1)
+ d30v_cleanup (true);
+ else
+ {
+ f = frag_more (8);
+ d30v_number_to_chars (f, NOP2, 8);
+
+ if (warn_nops == NOP_ALL || warn_nops == NOP_MULTIPLY)
+ {
+ if (opcode.op->flags_used & FLAG_MEM)
+ as_warn (_("word of NOPs added between word multiply and load"));
+ else
+ as_warn (_("word of NOPs added between word multiply and 16-bit multiply"));
+ }
+ }
+ }
+
+ extype = EXEC_UNKNOWN;
+ }
+ }
+ else if ( (extype == EXEC_REVSEQ)
+ && cur_mul32_p
+ && (prev_opcode.op->flags_used & (FLAG_MEM | FLAG_MUL16)))
+ {
+ /* Can't parallelize, flush current instruction and add a sequential NOP. */
+ write_1_short (& opcode, (long) insn, fixups->next->next, true);
+
+ /* Make the previous instruction the current one. */
+ extype = EXEC_UNKNOWN;
+ insn = prev_insn;
+ now_seg = prev_seg;
+ now_subseg = prev_subseg;
+ prev_insn = -1;
+ cur_mul32_p = prev_mul32_p;
+ prev_mul32_p = 0;
+ memcpy (&opcode, &prev_opcode, sizeof (prev_opcode));
+ }
+
+ /* If this is a long instruction, write it and any previous short instruction. */
+ if (opcode.form->form >= LONG)
+ {
+ if (extype != EXEC_UNKNOWN)
+ as_bad (_("Instruction uses long version, so it cannot be mixed as specified"));
+ d30v_cleanup (false);
+ write_long (& opcode, insn, fixups);
+ prev_insn = -1;
+ }
+ else if ((prev_insn != -1)
+ && (write_2_short
+ (& prev_opcode, (long) prev_insn, & opcode,
+ (long) insn, extype, fixups) == 0))
+ {
+ /* No instructions saved. */
+ prev_insn = -1;
+ }
+ else
+ {
+ if (extype != EXEC_UNKNOWN)
+ as_bad (_("Unable to mix instructions as specified"));
+
+ /* Save off last instruction so it may be packed on next pass. */
+ memcpy (&prev_opcode, &opcode, sizeof (prev_opcode));
+ prev_insn = insn;
+ prev_seg = now_seg;
+ prev_subseg = now_subseg;
+ fixups = fixups->next;
+ prev_mul32_p = cur_mul32_p;
+ }
+}
+
+
+/* do_assemble assembles a single instruction and returns an opcode */
+/* it returns -1 (an invalid opcode) on error */
+
+#define NAME_BUF_LEN 20
+
+static long long
+do_assemble (str, opcode, shortp, is_parallel)
+ char *str;
+ struct d30v_insn *opcode;
+ int shortp;
+ int is_parallel;
+{
+ unsigned char * op_start;
+ unsigned char * save;
+ unsigned char * op_end;
+ char name [NAME_BUF_LEN];
+ int cmp_hack;
+ int nlen = 0;
+ int fsize = (shortp ? FORCE_SHORT : 0);
+ expressionS myops [6];
+ long long insn;
+
+ /* Drop leading whitespace */
+ while (* str == ' ')
+ str ++;
+
+ /* find the opcode end */
+ for (op_start = op_end = (unsigned char *) (str);
+ * op_end
+ && nlen < (NAME_BUF_LEN - 1)
+ && * op_end != '/'
+ && !is_end_of_line[*op_end] && *op_end != ' ';
+ op_end++)
+ {
+ name[nlen] = tolower (op_start[nlen]);
+ nlen++;
+ }
+
+ if (nlen == 0)
+ return -1;
+
+ name[nlen] = 0;
+
+ /* if there is an execution condition code, handle it */
+ if (*op_end == '/')
+ {
+ int i = 0;
+ while ( (i < ECC_MAX) && strncasecmp (d30v_ecc_names[i], op_end + 1, 2))
+ i++;
+
+ if (i == ECC_MAX)
+ {
+ char tmp[4];
+ strncpy (tmp, op_end + 1, 2);
+ tmp[2] = 0;
+ as_bad (_("unknown condition code: %s"),tmp);
+ return -1;
+ }
+ /* printf ("condition code=%d\n",i); */
+ opcode->ecc = i;
+ op_end += 3;
+ }
+ else
+ opcode->ecc = ECC_AL;
+
+
+ /* CMP and CMPU change their name based on condition codes */
+ if (!strncmp (name, "cmp", 3))
+ {
+ int p,i;
+ char **str = (char **)d30v_cc_names;
+ if (name[3] == 'u')
+ p = 4;
+ else
+ p = 3;
+
+ for (i=1; *str && strncmp (*str, & name[p], 2); i++, str++)
+ ;
+
+ /* cmpu only supports some condition codes */
+ if (p == 4)
+ {
+ if (i < 3 || i > 6)
+ {
+ name[p+2]=0;
+ as_bad (_("cmpu doesn't support condition code %s"),&name[p]);
+ }
+ }
+
+ if (!*str)
+ {
+ name[p+2]=0;
+ as_bad (_("unknown condition code: %s"),&name[p]);
+ }
+
+ cmp_hack = i;
+ name[p] = 0;
+ }
+ else
+ cmp_hack = 0;
+
+ /* printf("cmp_hack=%d\n",cmp_hack); */
+
+ /* need to look for .s or .l */
+ if (name[nlen-2] == '.')
+ {
+ switch (name[nlen-1])
+ {
+ case 's':
+ fsize = FORCE_SHORT;
+ break;
+ case 'l':
+ fsize = FORCE_LONG;
+ break;
+ }
+ name[nlen-2] = 0;
+ }
+
+ /* find the first opcode with the proper name */
+ opcode->op = (struct d30v_opcode *)hash_find (d30v_hash, name);
+ if (opcode->op == NULL)
+ {
+ as_bad (_("unknown opcode: %s"),name);
+ return -1;
+ }
+
+ save = input_line_pointer;
+ input_line_pointer = op_end;
+ while (!(opcode->form = find_format (opcode->op, myops, fsize, cmp_hack)))
+ {
+ opcode->op++;
+ if (opcode->op->name == NULL || strcmp (opcode->op->name, name))
+ {
+ as_bad (_("operands for opcode `%s' do not match any valid format"), name);
+ return -1;
+ }
+ }
+ input_line_pointer = save;
+
+ insn = build_insn (opcode, myops);
+
+ /* Propigate multiply status */
+ if (insn != -1)
+ {
+ if (is_parallel && prev_mul32_p)
+ cur_mul32_p = 1;
+ else
+ {
+ prev_mul32_p = cur_mul32_p;
+ cur_mul32_p = (opcode->op->flags_used & FLAG_MUL32) != 0;
+ }
+ }
+
+ /* Propagate left_kills_right status */
+ if (insn != -1)
+ {
+ prev_left_kills_right_p = cur_left_kills_right_p;
+
+ if (opcode->op->flags_set & FLAG_LKR)
+ {
+ cur_left_kills_right_p = 1;
+
+ if (strcmp (opcode->op->name, "mvtsys") == 0)
+ {
+ /* Left kills right for only mvtsys only for PSW/PSWH/PSWL/flags target. */
+ if ((myops[0].X_op == O_register) &&
+ ((myops[0].X_add_number == OPERAND_CONTROL) || /* psw */
+ (myops[0].X_add_number == OPERAND_CONTROL+MAX_CONTROL_REG+2) || /* pswh */
+ (myops[0].X_add_number == OPERAND_CONTROL+MAX_CONTROL_REG+1) || /* pswl */
+ (myops[0].X_add_number == OPERAND_FLAG+0) || /* f0 */
+ (myops[0].X_add_number == OPERAND_FLAG+1) || /* f1 */
+ (myops[0].X_add_number == OPERAND_FLAG+2) || /* f2 */
+ (myops[0].X_add_number == OPERAND_FLAG+3) || /* f3 */
+ (myops[0].X_add_number == OPERAND_FLAG+4) || /* f4 */
+ (myops[0].X_add_number == OPERAND_FLAG+5) || /* f5 */
+ (myops[0].X_add_number == OPERAND_FLAG+6) || /* f6 */
+ (myops[0].X_add_number == OPERAND_FLAG+7))) /* f7 */
+ {
+ cur_left_kills_right_p = 1;
+ }
+ else
+ {
+ /* Other mvtsys target registers don't kill right instruction. */
+ cur_left_kills_right_p = 0;
+ }
+ } /* mvtsys */
+ }
+ else
+ cur_left_kills_right_p = 0;
+ }
+
+ return insn;
+}
+
+
+/* find_format() gets a pointer to an entry in the format table.
+ It must look at all formats for an opcode and use the operands
+ to choose the correct one. Returns NULL on error. */
+
+static struct d30v_format *
+find_format (opcode, myops, fsize, cmp_hack)
+ struct d30v_opcode *opcode;
+ expressionS myops[];
+ int fsize;
+ int cmp_hack;
+{
+ int numops, match, index, i=0, j, k;
+ struct d30v_format *fm;
+
+ if (opcode == NULL)
+ return NULL;
+
+ /* Get all the operands and save them as expressions. */
+ numops = get_operands (myops, cmp_hack);
+
+ while ((index = opcode->format[i++]) != 0)
+ {
+ if (fsize == FORCE_SHORT && index >= LONG)
+ continue;
+
+ if (fsize == FORCE_LONG && index < LONG)
+ continue;
+
+ fm = (struct d30v_format *)&d30v_format_table[index];
+ k = index;
+ while (fm->form == index)
+ {
+ match = 1;
+ /* Now check the operands for compatibility. */
+ for (j = 0; match && fm->operands[j]; j++)
+ {
+ int flags = d30v_operand_table[fm->operands[j]].flags;
+ int bits = d30v_operand_table[fm->operands[j]].bits;
+ int X_op = myops[j].X_op;
+ int num = myops[j].X_add_number;
+
+ if (flags & OPERAND_SPECIAL)
+ break;
+ else if (X_op == O_illegal)
+ match = 0;
+ else if (flags & OPERAND_REG)
+ {
+ if (X_op != O_register
+ || ((flags & OPERAND_ACC) && !(num & OPERAND_ACC))
+ || (!(flags & OPERAND_ACC) && (num & OPERAND_ACC))
+ || ((flags & OPERAND_FLAG) && !(num & OPERAND_FLAG))
+ || (!(flags & (OPERAND_FLAG | OPERAND_CONTROL)) && (num & OPERAND_FLAG))
+ || ((flags & OPERAND_CONTROL)
+ && !(num & (OPERAND_CONTROL | OPERAND_FLAG))))
+ {
+ match = 0;
+ }
+ }
+ else if (((flags & OPERAND_MINUS)
+ && (X_op != O_absent || num != OPERAND_MINUS))
+ || ((flags & OPERAND_PLUS)
+ && (X_op != O_absent || num != OPERAND_PLUS))
+ || ((flags & OPERAND_ATMINUS)
+ && (X_op != O_absent || num != OPERAND_ATMINUS))
+ || ((flags & OPERAND_ATPAR)
+ && (X_op != O_absent || num != OPERAND_ATPAR))
+ || ((flags & OPERAND_ATSIGN)
+ && (X_op != O_absent || num != OPERAND_ATSIGN)))
+ {
+ match=0;
+ }
+ else if (flags & OPERAND_NUM)
+ {
+ /* A number can be a constant or symbol expression. */
+
+ /* If we have found a register name, but that name also
+ matches a symbol, then re-parse the name as an expression. */
+ if (X_op == O_register
+ && symbol_find ((char *) myops[j].X_op_symbol))
+ {
+ input_line_pointer = (char *) myops[j].X_op_symbol;
+ expression (& myops[j]);
+ }
+
+ /* Turn an expression into a symbol for later resolution. */
+ if (X_op != O_absent && X_op != O_constant
+ && X_op != O_symbol && X_op != O_register
+ && X_op != O_big)
+ {
+ symbolS *sym = make_expr_symbol (&myops[j]);
+ myops[j].X_op = X_op = O_symbol;
+ myops[j].X_add_symbol = sym;
+ myops[j].X_add_number = num = 0;
+ }
+
+ if (fm->form >= LONG)
+ {
+ /* If we're testing for a LONG format, either fits. */
+ if (X_op != O_constant && X_op != O_symbol)
+ match = 0;
+ }
+ else if (fm->form < LONG
+ && ((fsize == FORCE_SHORT && X_op == O_symbol)
+ || (fm->form == SHORT_D2 && j == 0)))
+ match = 1;
+ /* This is the tricky part. Will the constant or symbol
+ fit into the space in the current format? */
+ else if (X_op == O_constant)
+ {
+ if (check_range (num, bits, flags))
+ match = 0;
+ }
+ else if (X_op == O_symbol
+ && S_IS_DEFINED (myops[j].X_add_symbol)
+ && S_GET_SEGMENT (myops[j].X_add_symbol) == now_seg
+ && opcode->reloc_flag == RELOC_PCREL)
+ {
+ /* If the symbol is defined, see if the value will fit
+ into the form we're considering. */
+ fragS *f;
+ long value;
+
+ /* Calculate the current address by running through the
+ previous frags and adding our current offset. */
+ value = 0;
+ for (f = frchain_now->frch_root; f; f = f->fr_next)
+ value += f->fr_fix + f->fr_offset;
+ value = (S_GET_VALUE (myops[j].X_add_symbol) - value
+ - (obstack_next_free (&frchain_now->frch_obstack)
+ - frag_now->fr_literal));
+ if (check_range (value, bits, flags))
+ match = 0;
+ }
+ else
+ match = 0;
+ }
+ }
+ /* printf("through the loop: match=%d\n",match); */
+ /* We're only done if the operands matched so far AND there
+ are no more to check. */
+ if (match && myops[j].X_op == 0)
+ {
+ /* Final check - issue a warning if an odd numbered register
+ is used as the first register in an instruction that reads
+ or writes 2 registers. */
+
+ for (j = 0; fm->operands[j]; j++)
+ if (myops[j].X_op == O_register
+ && (myops[j].X_add_number & 1)
+ && (d30v_operand_table[fm->operands[j]].flags & OPERAND_2REG))
+ as_warn (\
+_("Odd numbered register used as target of multi-register instruction"));
+
+ return fm;
+ }
+ fm = (struct d30v_format *)&d30v_format_table[++k];
+ }
+ /* printf("trying another format: i=%d\n",i); */
+ }
+ return NULL;
+}
+
+/* if while processing a fixup, a reloc really needs to be created */
+/* then it is done here */
+
+arelent *
+tc_gen_reloc (seg, fixp)
+ asection *seg;
+ fixS *fixp;
+{
+ arelent *reloc;
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("reloc %d not supported by object file format"), (int)fixp->fx_r_type);
+ return NULL;
+ }
+ reloc->addend = fixp->fx_addnumber;
+ return reloc;
+}
+
+int
+md_estimate_size_before_relax (fragp, seg)
+ fragS *fragp;
+ asection *seg;
+{
+ abort ();
+ return 0;
+}
+
+long
+md_pcrel_from_section (fixp, sec)
+ fixS *fixp;
+ segT sec;
+{
+ if (fixp->fx_addsy != (symbolS *)NULL && (!S_IS_DEFINED (fixp->fx_addsy) ||
+ (S_GET_SEGMENT (fixp->fx_addsy) != sec)))
+ return 0;
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+}
+
+int
+md_apply_fix3 (fixp, valuep, seg)
+ fixS * fixp;
+ valueT * valuep;
+ segT seg;
+{
+ char * where;
+ unsigned long insn, insn2;
+ long value;
+
+ if (fixp->fx_addsy == (symbolS *) NULL)
+ {
+ value = * valuep;
+ fixp->fx_done = 1;
+ }
+ else if (fixp->fx_pcrel)
+ value = * valuep;
+ else
+ {
+ value = fixp->fx_offset;
+
+ if (fixp->fx_subsy != (symbolS *) NULL)
+ {
+ if (S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
+ value -= S_GET_VALUE (fixp->fx_subsy);
+ else
+ {
+ /* We don't actually support subtracting a symbol. */
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("expression too complex"));
+ }
+ }
+ }
+
+ /* Fetch the instruction, insert the fully resolved operand
+ value, and stuff the instruction back again. */
+ where = fixp->fx_frag->fr_literal + fixp->fx_where;
+ insn = bfd_getb32 ((unsigned char *) where);
+
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_8: /* Check for a bad .byte directive. */
+ if (fixp->fx_addsy != NULL)
+ as_bad (_("line %d: unable to place address of symbol '%s' into a byte"),
+ fixp->fx_line, S_GET_NAME (fixp->fx_addsy));
+ else if (((unsigned)value) > 0xff)
+ as_bad (_("line %d: unable to place value %x into a byte"),
+ fixp->fx_line, value);
+ else
+ * (unsigned char *) where = value;
+ break;
+
+ case BFD_RELOC_16: /* Check for a bad .short directive. */
+ if (fixp->fx_addsy != NULL)
+ as_bad (_("line %d: unable to place address of symbol '%s' into a short"),
+ fixp->fx_line, S_GET_NAME (fixp->fx_addsy));
+ else if (((unsigned)value) > 0xffff)
+ as_bad (_("line %d: unable to place value %x into a short"),
+ fixp->fx_line, value);
+ else
+ bfd_putb16 ((bfd_vma) value, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_64: /* Check for a bad .quad directive. */
+ if (fixp->fx_addsy != NULL)
+ as_bad (_("line %d: unable to place address of symbol '%s' into a quad"),
+ fixp->fx_line, S_GET_NAME (fixp->fx_addsy));
+ else
+ {
+ bfd_putb32 ((bfd_vma) value, (unsigned char *) where);
+ bfd_putb32 (0, ((unsigned char *) where) + 4);
+ }
+ break;
+
+ case BFD_RELOC_D30V_6:
+ check_size (value, 6, fixp->fx_file, fixp->fx_line);
+ insn |= value & 0x3F;
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_D30V_9_PCREL:
+ if (fixp->fx_where & 0x7)
+ {
+ if (fixp->fx_done)
+ value += 4;
+ else
+ fixp->fx_r_type = BFD_RELOC_D30V_9_PCREL_R;
+ }
+ check_size (value, 9, fixp->fx_file, fixp->fx_line);
+ insn |= ((value >> 3) & 0x3F) << 12;
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_D30V_15:
+ check_size (value, 15, fixp->fx_file, fixp->fx_line);
+ insn |= (value >> 3) & 0xFFF;
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_D30V_15_PCREL:
+ if (fixp->fx_where & 0x7)
+ {
+ if (fixp->fx_done)
+ value += 4;
+ else
+ fixp->fx_r_type = BFD_RELOC_D30V_15_PCREL_R;
+ }
+ check_size (value, 15, fixp->fx_file, fixp->fx_line);
+ insn |= (value >> 3) & 0xFFF;
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_D30V_21:
+ check_size (value, 21, fixp->fx_file, fixp->fx_line);
+ insn |= (value >> 3) & 0x3FFFF;
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_D30V_21_PCREL:
+ if (fixp->fx_where & 0x7)
+ {
+ if (fixp->fx_done)
+ value += 4;
+ else
+ fixp->fx_r_type = BFD_RELOC_D30V_21_PCREL_R;
+ }
+ check_size (value, 21, fixp->fx_file, fixp->fx_line);
+ insn |= (value >> 3) & 0x3FFFF;
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_D30V_32:
+ insn2 = bfd_getb32 ((unsigned char *) where + 4);
+ insn |= (value >> 26) & 0x3F; /* top 6 bits */
+ insn2 |= ((value & 0x03FC0000) << 2); /* next 8 bits */
+ insn2 |= value & 0x0003FFFF; /* bottom 18 bits */
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ bfd_putb32 ((bfd_vma) insn2, (unsigned char *) where + 4);
+ break;
+
+ case BFD_RELOC_D30V_32_PCREL:
+ insn2 = bfd_getb32 ((unsigned char *) where + 4);
+ insn |= (value >> 26) & 0x3F; /* top 6 bits */
+ insn2 |= ((value & 0x03FC0000) << 2); /* next 8 bits */
+ insn2 |= value & 0x0003FFFF; /* bottom 18 bits */
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ bfd_putb32 ((bfd_vma) insn2, (unsigned char *) where + 4);
+ break;
+
+ case BFD_RELOC_32:
+ bfd_putb32 ((bfd_vma) value, (unsigned char *) where);
+ break;
+
+ default:
+ as_bad (_("line %d: unknown relocation type: 0x%x"),
+ fixp->fx_line,fixp->fx_r_type);
+ }
+
+ return 0;
+}
+
+
+/* d30v_cleanup() is called after the assembler has finished parsing the input
+ file or after a label is defined. Because the D30V assembler sometimes saves short
+ instructions to see if it can package them with the next instruction, there may
+ be a short instruction that still needs written. */
+int
+d30v_cleanup (use_sequential)
+ int use_sequential;
+{
+ segT seg;
+ subsegT subseg;
+
+ if (prev_insn != -1)
+ {
+ seg = now_seg;
+ subseg = now_subseg;
+ subseg_set (prev_seg, prev_subseg);
+ write_1_short (&prev_opcode, (long)prev_insn, fixups->next, use_sequential);
+ subseg_set (seg, subseg);
+ prev_insn = -1;
+ if (use_sequential)
+ prev_mul32_p = false;
+ }
+ return 1;
+}
+
+static void
+d30v_number_to_chars (buf, value, n)
+ char *buf; /* Return 'nbytes' of chars here. */
+ long long value; /* The value of the bits. */
+ int n; /* Number of bytes in the output. */
+{
+ while (n--)
+ {
+ buf[n] = value & 0xff;
+ value >>= 8;
+ }
+}
+
+
+/* This function is called at the start of every line. */
+/* it checks to see if the first character is a '.' */
+/* which indicates the start of a pseudo-op. If it is, */
+/* then write out any unwritten instructions */
+
+void
+d30v_start_line ()
+{
+ char *c = input_line_pointer;
+
+ while (isspace (*c))
+ c++;
+
+ if (*c == '.')
+ d30v_cleanup (false);
+}
+
+static void
+check_size (value, bits, file, line)
+ long value;
+ int bits;
+ char *file;
+ int line;
+{
+ int tmp, max;
+
+ if (value < 0)
+ tmp = ~value;
+ else
+ tmp = value;
+
+ max = (1 << (bits - 1)) - 1;
+
+ if (tmp > max)
+ as_bad_where (file, line, _("value too large to fit in %d bits"), bits);
+
+ return;
+}
+
+/* d30v_frob_label() is called when after a label is recognized. */
+
+void
+d30v_frob_label (lab)
+ symbolS *lab;
+{
+ /* Emit any pending instructions. */
+ d30v_cleanup (false);
+
+ /* Update the label's address with the current output pointer. */
+ lab->sy_frag = frag_now;
+ S_SET_VALUE (lab, (valueT) frag_now_fix ());
+
+ /* Record this label for future adjustment after we find out what
+ kind of data it references, and the required alignment therewith. */
+ d30v_last_label = lab;
+}
+
+/* Hook into cons for capturing alignment changes. */
+
+void
+d30v_cons_align (size)
+ int size;
+{
+ int log_size;
+
+ log_size = 0;
+ while ((size >>= 1) != 0)
+ ++log_size;
+
+ if (d30v_current_align < log_size)
+ d30v_align (log_size, (char *) NULL, NULL);
+ else if (d30v_current_align > log_size)
+ d30v_current_align = log_size;
+ d30v_last_label = NULL;
+}
+
+/* Called internally to handle all alignment needs. This takes care
+ of eliding calls to frag_align if'n the cached current alignment
+ says we've already got it, as well as taking care of the auto-aligning
+ labels wrt code. */
+
+static void
+d30v_align (n, pfill, label)
+ int n;
+ char *pfill;
+ symbolS *label;
+{
+ /* The front end is prone to changing segments out from under us
+ temporarily when -g is in effect. */
+ int switched_seg_p = (d30v_current_align_seg != now_seg);
+
+ /* Do not assume that if 'd30v_current_align >= n' and
+ '! switched_seg_p' that it is safe to avoid performing
+ this alignement request. The alignment of the current frag
+ can be changed under our feet, for example by a .ascii
+ directive in the source code. cf testsuite/gas/d30v/reloc.s */
+
+ d30v_cleanup (false);
+
+ if (pfill == NULL)
+ {
+ if (n > 2
+ && (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
+ {
+ static char const nop[4] = { 0x00, 0xf0, 0x00, 0x00 };
+
+ /* First, make sure we're on a four-byte boundary, in case
+ someone has been putting .byte values the text section. */
+ if (d30v_current_align < 2 || switched_seg_p)
+ frag_align (2, 0, 0);
+ frag_align_pattern (n, nop, sizeof nop, 0);
+ }
+ else
+ frag_align (n, 0, 0);
+ }
+ else
+ frag_align (n, *pfill, 0);
+
+ if (!switched_seg_p)
+ d30v_current_align = n;
+
+ if (label != NULL)
+ {
+ symbolS * sym;
+ int label_seen = false;
+ struct frag * old_frag;
+ valueT old_value;
+ valueT new_value;
+
+ assert (S_GET_SEGMENT (label) == now_seg);
+
+ old_frag = label->sy_frag;
+ old_value = S_GET_VALUE (label);
+ new_value = (valueT) frag_now_fix ();
+
+ /* It is possible to have more than one label at a particular
+ address, especially if debugging is enabled, so we must
+ take care to adjust all the labels at this address in this
+ fragment. To save time we search from the end of the symbol
+ list, backwards, since the symbols we are interested in are
+ almost certainly the ones that were most recently added.
+ Also to save time we stop searching once we have seen at least
+ one matching label, and we encounter a label that is no longer
+ in the target fragment. Note, this search is guaranteed to
+ find at least one match when sym == label, so no special case
+ code is necessary. */
+ for (sym = symbol_lastP; sym != NULL; sym = sym->sy_previous)
+ {
+ if (sym->sy_frag == old_frag && S_GET_VALUE (sym) == old_value)
+ {
+ label_seen = true;
+ sym->sy_frag = frag_now;
+ S_SET_VALUE (sym, new_value);
+ }
+ else if (label_seen && sym->sy_frag != old_frag)
+ break;
+ }
+ }
+
+ record_alignment (now_seg, n);
+}
+
+/* Handle the .align pseudo-op. This aligns to a power of two. We
+ hook here to latch the current alignment. */
+
+static void
+s_d30v_align (ignore)
+ int ignore;
+{
+ int align;
+ char fill, *pfill = NULL;
+ long max_alignment = 15;
+
+ align = get_absolute_expression ();
+ if (align > max_alignment)
+ {
+ align = max_alignment;
+ as_warn (_("Alignment too large: %d assumed"), align);
+ }
+ else if (align < 0)
+ {
+ as_warn (_("Alignment negative: 0 assumed"));
+ align = 0;
+ }
+
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ fill = get_absolute_expression ();
+ pfill = &fill;
+ }
+
+ d30v_last_label = NULL;
+ d30v_align (align, pfill, NULL);
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .text pseudo-op. This is like the usual one, but it
+ clears the saved last label and resets known alignment. */
+
+static void
+s_d30v_text (i)
+ int i;
+
+{
+ s_text (i);
+ d30v_last_label = NULL;
+ d30v_current_align = 0;
+ d30v_current_align_seg = now_seg;
+}
+
+/* Handle the .data pseudo-op. This is like the usual one, but it
+ clears the saved last label and resets known alignment. */
+
+static void
+s_d30v_data (i)
+ int i;
+{
+ s_data (i);
+ d30v_last_label = NULL;
+ d30v_current_align = 0;
+ d30v_current_align_seg = now_seg;
+}
+
+/* Handle the .section pseudo-op. This is like the usual one, but it
+ clears the saved last label and resets known alignment. */
+
+static void
+s_d30v_section (ignore)
+ int ignore;
+{
+ obj_elf_section (ignore);
+ d30v_last_label = NULL;
+ d30v_current_align = 0;
+ d30v_current_align_seg = now_seg;
+}
diff --git a/gas/config/tc-d30v.h b/gas/config/tc-d30v.h
new file mode 100644
index 0000000..acce285
--- /dev/null
+++ b/gas/config/tc-d30v.h
@@ -0,0 +1,59 @@
+/* tc-310v.h -- Header file for tc-d30v.c.
+ Copyright (C) 1997 Free Software Foundation, Inc.
+ Written by Martin Hunt, Cygnus Support.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#define TC_D30V
+
+#ifndef BFD_ASSEMBLER
+ #error D30V support requires BFD_ASSEMBLER
+#endif
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_d30v
+#define TARGET_FORMAT "elf32-d30v"
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+#define md_operand(x)
+
+#define MD_APPLY_FIX3
+
+/* call md_pcrel_from_section, not md_pcrel_from */
+#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section(FIXP, SEC)
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+#define DIFF_EXPR_OK /* .-foo gets turned into PC relative relocs */
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+#define md_number_to_chars number_to_chars_bigendian
+
+int d30v_cleanup PARAMS ((int));
+#define md_after_pass_hook() d30v_cleanup (false)
+#define md_cleanup() d30v_cleanup (false)
+#define TC_START_LABEL(ch, ptr) (ch == ':' && d30v_cleanup (false))
+#define md_start_line_hook() d30v_start_line (false)
+
+void d30v_frob_label PARAMS ((struct symbol *));
+#define tc_frob_label(sym) d30v_frob_label(sym)
+
+void d30v_cons_align PARAMS ((int));
+#define md_cons_align(nbytes) d30v_cons_align(nbytes)
diff --git a/gas/config/tc-fr30.c b/gas/config/tc-fr30.c
new file mode 100644
index 0000000..aa075b7
--- /dev/null
+++ b/gas/config/tc-fr30.c
@@ -0,0 +1,664 @@
+/* tc-fr30.c -- Assembler for the Fujitsu FR30.
+ Copyright (C) 1998, 1999 Free Software Foundation.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "as.h"
+#include "subsegs.h"
+#include "symcat.h"
+#include "opcodes/fr30-desc.h"
+#include "opcodes/fr30-opc.h"
+#include "cgen.h"
+
+/* Structure to hold all of the different components describing
+ an individual instruction. */
+typedef struct
+{
+ const CGEN_INSN * insn;
+ const CGEN_INSN * orig_insn;
+ CGEN_FIELDS fields;
+#if CGEN_INT_INSN_P
+ CGEN_INSN_INT buffer [1];
+#define INSN_VALUE(buf) (*(buf))
+#else
+ unsigned char buffer [CGEN_MAX_INSN_SIZE];
+#define INSN_VALUE(buf) (buf)
+#endif
+ char * addr;
+ fragS * frag;
+ int num_fixups;
+ fixS * fixups [GAS_CGEN_MAX_FIXUPS];
+ int indices [MAX_OPERAND_INSTANCES];
+}
+fr30_insn;
+
+const char comment_chars[] = ";";
+const char line_comment_chars[] = "#";
+const char line_separator_chars[] = "|";
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "dD";
+
+#define FR30_SHORTOPTS ""
+const char * md_shortopts = FR30_SHORTOPTS;
+
+struct option md_longopts[] =
+{
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (c, arg)
+ int c;
+ char * arg;
+{
+ switch (c)
+ {
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+void
+md_show_usage (stream)
+ FILE * stream;
+{
+ fprintf (stream, _(" FR30 specific command line options:\n"));
+}
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "word", cons, 4 },
+ { NULL, NULL, 0 }
+};
+
+
+void
+md_begin ()
+{
+ flagword applicable;
+ segT seg;
+ subsegT subseg;
+
+ /* Initialize the `cgen' interface. */
+
+ /* Set the machine number and endian. */
+ gas_cgen_cpu_desc = fr30_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, 0,
+ CGEN_CPU_OPEN_ENDIAN,
+ CGEN_ENDIAN_BIG,
+ CGEN_CPU_OPEN_END);
+ fr30_cgen_init_asm (gas_cgen_cpu_desc);
+
+ /* This is a callback from cgen to gas to parse operands. */
+ cgen_set_parse_operand_fn (gas_cgen_cpu_desc, gas_cgen_parse_operand);
+}
+
+void
+md_assemble (str)
+ char * str;
+{
+ static int last_insn_had_delay_slot = 0;
+ fr30_insn insn;
+ char * errmsg;
+ char * str2 = NULL;
+
+ /* Initialize GAS's cgen interface for a new instruction. */
+ gas_cgen_init_parse ();
+
+ insn.insn = fr30_cgen_assemble_insn
+ (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg);
+
+ if (!insn.insn)
+ {
+ as_bad (errmsg);
+ return;
+ }
+
+ /* Doesn't really matter what we pass for RELAX_P here. */
+ gas_cgen_finish_insn (insn.insn, insn.buffer,
+ CGEN_FIELDS_BITSIZE (& insn.fields), 1, NULL);
+
+ /* Warn about invalid insns in delay slots. */
+ if (last_insn_had_delay_slot
+ && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_NOT_IN_DELAY_SLOT))
+ as_warn (_("Instruction %s not allowed in a delay slot."),
+ CGEN_INSN_NAME (insn.insn));
+
+ last_insn_had_delay_slot
+ = CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_DELAY_SLOT);
+}
+
+/* The syntax in the manual says constants begin with '#'.
+ We just ignore it. */
+
+void
+md_operand (expressionP)
+ expressionS * expressionP;
+{
+ if (* input_line_pointer == '#')
+ {
+ input_line_pointer ++;
+ expression (expressionP);
+ }
+}
+
+valueT
+md_section_align (segment, size)
+ segT segment;
+ valueT size;
+{
+ int align = bfd_get_section_alignment (stdoutput, segment);
+ return ((size + (1 << align) - 1) & (-1 << align));
+}
+
+symbolS *
+md_undefined_symbol (name)
+ char * name;
+{
+ return 0;
+}
+
+/* Interface to relax_segment. */
+
+/* FIXME: Build table by hand, get it working, then machine generate. */
+
+const relax_typeS md_relax_table[] =
+{
+/* The fields are:
+ 1) most positive reach of this state,
+ 2) most negative reach of this state,
+ 3) how many bytes this mode will add to the size of the current frag
+ 4) which index into the table to try if we can't fit into this one. */
+
+ /* The first entry must be unused because an `rlx_more' value of zero ends
+ each list. */
+ {1, 1, 0, 0},
+
+ /* The displacement used by GAS is from the end of the 2 byte insn,
+ so we subtract 2 from the following. */
+ /* 16 bit insn, 8 bit disp -> 10 bit range.
+ This doesn't handle a branch in the right slot at the border:
+ the "& -4" isn't taken into account. It's not important enough to
+ complicate things over it, so we subtract an extra 2 (or + 2 in -ve
+ case). */
+ {511 - 2 - 2, -512 - 2 + 2, 0, 2 },
+ /* 32 bit insn, 24 bit disp -> 26 bit range. */
+ {0x2000000 - 1 - 2, -0x2000000 - 2, 2, 0 },
+ /* Same thing, but with leading nop for alignment. */
+ {0x2000000 - 1 - 2, -0x2000000 - 2, 4, 0 }
+};
+
+long
+fr30_relax_frag (fragP, stretch)
+ fragS * fragP;
+ long stretch;
+{
+ /* Address of branch insn. */
+ long address = fragP->fr_address + fragP->fr_fix - 2;
+ long growth = 0;
+
+ /* Keep 32 bit insns aligned on 32 bit boundaries. */
+ if (fragP->fr_subtype == 2)
+ {
+ if ((address & 3) != 0)
+ {
+ fragP->fr_subtype = 3;
+ growth = 2;
+ }
+ }
+ else if (fragP->fr_subtype == 3)
+ {
+ if ((address & 3) == 0)
+ {
+ fragP->fr_subtype = 2;
+ growth = -2;
+ }
+ }
+ else
+ {
+ growth = relax_frag (fragP, stretch);
+
+ /* Long jump on odd halfword boundary? */
+ if (fragP->fr_subtype == 2 && (address & 3) != 0)
+ {
+ fragP->fr_subtype = 3;
+ growth += 2;
+ }
+ }
+
+ return growth;
+}
+
+/* Return an initial guess of the length by which a fragment must grow to
+ hold a branch to reach its destination.
+ Also updates fr_type/fr_subtype as necessary.
+
+ Called just before doing relaxation.
+ Any symbol that is now undefined will not become defined.
+ The guess for fr_var is ACTUALLY the growth beyond fr_fix.
+ Whatever we do to grow fr_fix or fr_var contributes to our returned value.
+ Although it may not be explicit in the frag, pretend fr_var starts with a
+ 0 value. */
+
+int
+md_estimate_size_before_relax (fragP, segment)
+ fragS * fragP;
+ segT segment;
+{
+ int old_fr_fix = fragP->fr_fix;
+
+ /* The only thing we have to handle here are symbols outside of the
+ current segment. They may be undefined or in a different segment in
+ which case linker scripts may place them anywhere.
+ However, we can't finish the fragment here and emit the reloc as insn
+ alignment requirements may move the insn about. */
+
+ if (S_GET_SEGMENT (fragP->fr_symbol) != segment)
+ {
+ /* The symbol is undefined in this segment.
+ Change the relaxation subtype to the max allowable and leave
+ all further handling to md_convert_frag. */
+ fragP->fr_subtype = 2;
+
+#if 0 /* Can't use this, but leave in for illustration. */
+ /* Change 16 bit insn to 32 bit insn. */
+ fragP->fr_opcode[0] |= 0x80;
+
+ /* Increase known (fixed) size of fragment. */
+ fragP->fr_fix += 2;
+
+ /* Create a relocation for it. */
+ fix_new (fragP, old_fr_fix, 4,
+ fragP->fr_symbol,
+ fragP->fr_offset, 1 /* pcrel */,
+ /* FIXME: Can't use a real BFD reloc here.
+ gas_cgen_md_apply_fix3 can't handle it. */
+ BFD_RELOC_FR30_26_PCREL);
+
+ /* Mark this fragment as finished. */
+ frag_wane (fragP);
+#else
+ {
+ const CGEN_INSN * insn;
+ int i;
+
+ /* Update the recorded insn.
+ Fortunately we don't have to look very far.
+ FIXME: Change this to record in the instruction the next higher
+ relaxable insn to use. */
+ for (i = 0, insn = fragP->fr_cgen.insn; i < 4; i++, insn++)
+ {
+ if ((strcmp (CGEN_INSN_MNEMONIC (insn),
+ CGEN_INSN_MNEMONIC (fragP->fr_cgen.insn))
+ == 0)
+ && CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_RELAX))
+ break;
+ }
+ if (i == 4)
+ abort ();
+
+ fragP->fr_cgen.insn = insn;
+ return 2;
+ }
+#endif
+ }
+
+ return (fragP->fr_var + fragP->fr_fix - old_fr_fix);
+}
+
+/* *fragP has been relaxed to its final size, and now needs to have
+ the bytes inside it modified to conform to the new size.
+
+ Called after relaxation is finished.
+ fragP->fr_type == rs_machine_dependent.
+ fragP->fr_subtype is the subtype of what the address relaxed to. */
+
+void
+md_convert_frag (abfd, sec, fragP)
+ bfd * abfd;
+ segT sec;
+ fragS * fragP;
+{
+#if 0
+ char * opcode;
+ char * displacement;
+ int target_address;
+ int opcode_address;
+ int extension;
+ int addend;
+
+ opcode = fragP->fr_opcode;
+
+ /* Address opcode resides at in file space. */
+ opcode_address = fragP->fr_address + fragP->fr_fix - 2;
+
+ switch (fragP->fr_subtype)
+ {
+ case 1 :
+ extension = 0;
+ displacement = & opcode[1];
+ break;
+ case 2 :
+ opcode[0] |= 0x80;
+ extension = 2;
+ displacement = & opcode[1];
+ break;
+ case 3 :
+ opcode[2] = opcode[0] | 0x80;
+ md_number_to_chars (opcode, PAR_NOP_INSN, 2);
+ opcode_address += 2;
+ extension = 4;
+ displacement = & opcode[3];
+ break;
+ default :
+ abort ();
+ }
+
+ if (S_GET_SEGMENT (fragP->fr_symbol) != sec)
+ {
+ /* symbol must be resolved by linker */
+ if (fragP->fr_offset & 3)
+ as_warn (_("Addend to unresolved symbol not on word boundary."));
+ addend = fragP->fr_offset >> 2;
+ }
+ else
+ {
+ /* Address we want to reach in file space. */
+ target_address = S_GET_VALUE (fragP->fr_symbol) + fragP->fr_offset;
+ target_address += fragP->fr_symbol->sy_frag->fr_address;
+ addend = (target_address - (opcode_address & -4)) >> 2;
+ }
+
+ /* Create a relocation for symbols that must be resolved by the linker.
+ Otherwise output the completed insn. */
+
+ if (S_GET_SEGMENT (fragP->fr_symbol) != sec)
+ {
+ assert (fragP->fr_subtype != 1);
+ assert (fragP->fr_cgen.insn != 0);
+ gas_cgen_record_fixup (fragP,
+ /* Offset of branch insn in frag. */
+ fragP->fr_fix + extension - 4,
+ fragP->fr_cgen.insn,
+ 4 /*length*/,
+ /* FIXME: quick hack */
+#if 0
+ CGEN_OPERAND_ENTRY (fragP->fr_cgen.opindex),
+#else
+ CGEN_OPERAND_ENTRY (FR30_OPERAND_DISP24),
+#endif
+ fragP->fr_cgen.opinfo,
+ fragP->fr_symbol, fragP->fr_offset);
+ }
+
+#define SIZE_FROM_RELAX_STATE(n) ((n) == 1 ? 1 : 3)
+
+ md_number_to_chars (displacement, (valueT) addend,
+ SIZE_FROM_RELAX_STATE (fragP->fr_subtype));
+
+ fragP->fr_fix += extension;
+#endif
+}
+
+/* Functions concerning relocs. */
+
+/* The location from which a PC relative jump should be calculated,
+ given a PC relative reloc. */
+
+long
+md_pcrel_from_section (fixP, sec)
+ fixS * fixP;
+ segT sec;
+{
+ if (fixP->fx_addsy != (symbolS *) NULL
+ && (! S_IS_DEFINED (fixP->fx_addsy)
+ || S_GET_SEGMENT (fixP->fx_addsy) != sec))
+ {
+ /* The symbol is undefined (or is defined but not in this section).
+ Let the linker figure it out. */
+ return 0;
+ }
+
+ return (fixP->fx_frag->fr_address + fixP->fx_where) & ~1;
+}
+
+/* Return the bfd reloc type for OPERAND of INSN at fixup FIXP.
+ Returns BFD_RELOC_NONE if no reloc type can be found.
+ *FIXP may be modified if desired. */
+
+bfd_reloc_code_real_type
+md_cgen_lookup_reloc (insn, operand, fixP)
+ const CGEN_INSN * insn;
+ const CGEN_OPERAND * operand;
+ fixS * fixP;
+{
+ switch (operand->type)
+ {
+ case FR30_OPERAND_LABEL9: fixP->fx_pcrel = 1; return BFD_RELOC_FR30_9_PCREL;
+ case FR30_OPERAND_LABEL12: fixP->fx_pcrel = 1; return BFD_RELOC_FR30_12_PCREL;
+ case FR30_OPERAND_DISP10: return BFD_RELOC_FR30_10_IN_8;
+ case FR30_OPERAND_DISP9: return BFD_RELOC_FR30_9_IN_8;
+ case FR30_OPERAND_DISP8: return BFD_RELOC_FR30_8_IN_8;
+ case FR30_OPERAND_UDISP6: return BFD_RELOC_FR30_6_IN_4;
+ case FR30_OPERAND_I8: return BFD_RELOC_8;
+ case FR30_OPERAND_I32: return BFD_RELOC_FR30_48;
+ case FR30_OPERAND_I20: return BFD_RELOC_FR30_20;
+ default : /* avoid -Wall warning */
+ break;
+ }
+
+ return BFD_RELOC_NONE;
+}
+
+/* See whether we need to force a relocation into the output file.
+ This is used to force out switch and PC relative relocations when
+ relaxing. */
+
+int
+fr30_force_relocation (fix)
+ fixS * fix;
+{
+ if ( fix->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fix->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 1;
+
+ return 0;
+}
+
+/* Write a value out to the object file, using the appropriate endianness. */
+
+void
+md_number_to_chars (buf, val, n)
+ char * buf;
+ valueT val;
+ int n;
+{
+ number_to_chars_bigendian (buf, val, n);
+}
+
+/* Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP . An error message is returned, or NULL on OK.
+*/
+
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+char *
+md_atof (type, litP, sizeP)
+ char type;
+ char * litP;
+ int * sizeP;
+{
+ int i;
+ int prec;
+ LITTLENUM_TYPE words [MAX_LITTLENUMS];
+ char * t;
+ char * atof_ieee ();
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ /* FIXME: Some targets allow other format chars for bigger sizes here. */
+
+ default:
+ * sizeP = 0;
+ return _("Bad call to md_atof()");
+ }
+
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+ * sizeP = prec * sizeof (LITTLENUM_TYPE);
+
+ for (i = 0; i < prec; i++)
+ {
+ md_number_to_chars (litP, (valueT) words[i],
+ sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+
+ return 0;
+}
+
+/* Worker function for fr30_is_colon_insn(). */
+static char
+restore_colon (advance_i_l_p_by)
+ int advance_i_l_p_by;
+{
+ char c;
+
+ /* Restore the colon, and advance input_line_pointer to
+ the end of the new symbol. */
+ * input_line_pointer = ':';
+ input_line_pointer += advance_i_l_p_by;
+ c = * input_line_pointer;
+ * input_line_pointer = 0;
+
+ return c;
+}
+
+/* Determines if the symbol starting at START and ending in
+ a colon that was at the location pointed to by INPUT_LINE_POINTER
+ (but which has now been replaced bu a NUL) is in fact an
+ LDI:8, LDI:20, LDI:32, CALL:D. JMP:D, RET:D or Bcc:D instruction.
+ If it is, then it restores the colon, advances INPUT_LINE_POINTER
+ to the real end of the instruction/symbol, and returns the character
+ that really terminated the symbol. Otherwise it returns 0. */
+char
+fr30_is_colon_insn (start)
+ char * start;
+{
+ char * i_l_p = input_line_pointer;
+
+ /* Check to see if the symbol parsed so far is 'ldi' */
+ if ( (start[0] != 'l' && start[0] != 'L')
+ || (start[1] != 'd' && start[1] != 'D')
+ || (start[2] != 'i' && start[2] != 'I')
+ || start[3] != 0)
+ {
+ /* Nope - check to see a 'd' follows the colon. */
+ if ( (i_l_p[1] == 'd' || i_l_p[1] == 'D')
+ && (i_l_p[2] == ' ' || i_l_p[2] == '\t' || i_l_p[2] == '\n'))
+ {
+ /* Yup - it might be delay slot instruction. */
+ int i;
+ static char * delay_insns [] =
+ {
+ "call", "jmp", "ret", "bra", "bno",
+ "beq", "bne", "bc", "bnc", "bn",
+ "bp", "bv", "bnv", "blt", "bge",
+ "ble", "bgt", "bls", "bhi"
+ };
+
+ for (i = sizeof (delay_insns) / sizeof (delay_insns[0]); i--;)
+ {
+ char * insn = delay_insns[i];
+ int len = strlen (insn);
+
+ if (start [len] != 0)
+ continue;
+
+ while (len --)
+ if (tolower (start [len]) != insn [len])
+ break;
+
+ if (len == -1)
+ return restore_colon (1);
+ }
+ }
+
+ /* Nope - it is a normal label. */
+ return 0;
+ }
+
+ /* Check to see if the text following the colon is '8' */
+ if (i_l_p[1] == '8' && (i_l_p[2] == ' ' || i_l_p[2] == '\t'))
+ return restore_colon (2);
+
+ /* Check to see if the text following the colon is '20' */
+ else if (i_l_p[1] == '2' && i_l_p[2] =='0' && (i_l_p[3] == ' ' || i_l_p[3] == '\t'))
+ return restore_colon (3);
+
+ /* Check to see if the text following the colon is '32' */
+ else if (i_l_p[1] == '3' && i_l_p[2] =='2' && (i_l_p[3] == ' ' || i_l_p[3] == '\t'))
+ return restore_colon (3);
+
+ return 0;
+}
+
+boolean
+fr30_fix_adjustable (fixP)
+ fixS * fixP;
+{
+ if (fixP->fx_addsy == NULL)
+ return 1;
+
+#if 0
+ /* Prevent all adjustments to global symbols. */
+ if (S_IS_EXTERN (fixP->fx_addsy))
+ return 0;
+
+ if (S_IS_WEAK (fixP->fx_addsy))
+ return 0;
+#endif
+
+ /* We need the symbol name for the VTABLE entries */
+ if ( fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ return 1;
+}
diff --git a/gas/config/tc-fr30.h b/gas/config/tc-fr30.h
new file mode 100644
index 0000000..a672d8b
--- /dev/null
+++ b/gas/config/tc-fr30.h
@@ -0,0 +1,81 @@
+/* tc-fr30.h -- Header file for tc-fr30.c.
+ Copyright (C) 1998 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#define TC_FR30
+
+#ifndef BFD_ASSEMBLER
+/* leading space so will compile with cc */
+ #error FR30 support requires BFD_ASSEMBLER
+#endif
+
+#define LISTING_HEADER "FR30 GAS "
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_fr30
+
+#define TARGET_FORMAT "elf32-fr30"
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+/* call md_pcrel_from_section, not md_pcrel_from */
+long md_pcrel_from_section PARAMS ((struct fix *, segT));
+#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section (FIXP, SEC)
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+#define DIFF_EXPR_OK /* .-foo gets turned into PC relative relocs */
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+#define MD_APPLY_FIX3
+#define md_apply_fix3 gas_cgen_md_apply_fix3
+
+#define obj_fix_adjustable(fixP) fr30_fix_adjustable (fixP)
+extern boolean fr30_fix_adjustable PARAMS ((struct fix *));
+
+/* When relaxing, we need to emit various relocs we otherwise wouldn't. */
+#define TC_FORCE_RELOCATION(fix) fr30_force_relocation (fix)
+extern int fr30_force_relocation PARAMS ((struct fix *));
+
+#define TC_HANDLES_FX_DONE
+
+#define tc_gen_reloc gas_cgen_tc_gen_reloc
+
+/* Call md_pcrel_from_section(), not md_pcrel_from(). */
+#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section (FIXP, SEC)
+extern long md_pcrel_from_section PARAMS ((struct fix *, segT));
+
+/* For 8 vs 16 vs 32 bit branch selection. */
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+extern const struct relax_type md_relax_table[];
+
+/* We need a special version of the TC_START_LABEL macro so that we
+ allow the LDI:8, LDI:20, LDI:32 and delay slot instructions to be
+ parsed as such. Note - in a HORRIBLE HACK, we make use of the
+ knowledge that this marco is only ever evaluated in one place
+ (read_a_source_file in read.c) where we can access the local
+ variable 's' - the start of the symbol that was terminated by
+ 'character'. Also we need to be able to change the contents of
+ the local variable 'c' which is passed to this macro as 'character'. */
+#define TC_START_LABEL(character, i_l_p) \
+ ((character) != ':' ? 0 : (character = fr30_is_colon_insn (s)) ? 0 : ((character = ':'), 1))
+extern char fr30_is_colon_insn PARAMS ((char *));
diff --git a/gas/config/tc-generic.c b/gas/config/tc-generic.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gas/config/tc-generic.c
diff --git a/gas/config/tc-generic.h b/gas/config/tc-generic.h
new file mode 100644
index 0000000..72df020
--- /dev/null
+++ b/gas/config/tc-generic.h
@@ -0,0 +1,39 @@
+/* This file is tc-generic.h
+
+ Copyright (C) 1987, 91, 92, 95, 1997 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with GAS; see the file COPYING. If not, write to the Free Software
+ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/*
+ * This file is tc-generic.h and is intended to be a template for target cpu
+ * specific header files. It is my intent that this file compile. It is also
+ * my intent that this file grow into something that can be used as both a
+ * template for porting, and a stub for testing. xoxorich.
+ */
+
+#define TC_GENERIC 1
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of tc-generic.h */
diff --git a/gas/config/tc-h8300.c b/gas/config/tc-h8300.c
new file mode 100644
index 0000000..115ada1
--- /dev/null
+++ b/gas/config/tc-h8300.c
@@ -0,0 +1,1595 @@
+/* tc-h8300.c -- Assemble code for the Hitachi H8/300
+ Copyright (C) 1991, 92, 93, 94, 95, 96, 97, 1998 Free Software Foundation.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+
+/*
+ Written By Steve Chamberlain
+ sac@cygnus.com
+ */
+
+#include <stdio.h>
+#include "as.h"
+#include "subsegs.h"
+#include "bfd.h"
+#define DEFINE_TABLE
+#define h8_opcodes ops
+#include "opcode/h8300.h"
+#include <ctype.h>
+
+const char comment_chars[] =
+{';', 0};
+const char line_separator_chars[] =
+{0};
+const char line_comment_chars[] = "#";
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ Integer arg to pass to the function
+ */
+
+void cons ();
+
+int Hmode;
+int Smode;
+#define PSIZE (Hmode ? L_32 : L_16)
+#define DMODE (L_16)
+#define DSYMMODE (Hmode ? L_24 : L_16)
+int bsize = L_8; /* default branch displacement */
+
+
+void
+h8300hmode ()
+{
+ Hmode = 1;
+ Smode = 0;
+}
+
+void
+h8300smode ()
+{
+ Smode = 1;
+ Hmode = 1;
+}
+void
+sbranch (size)
+ int size;
+{
+ bsize = size;
+}
+
+static void pint ()
+{
+ cons (Hmode ? 4 : 2);
+}
+
+const pseudo_typeS md_pseudo_table[] =
+{
+
+ {"h8300h", h8300hmode, 0},
+ {"h8300s", h8300smode, 0},
+ {"sbranch", sbranch, L_8},
+ {"lbranch", sbranch, L_16},
+
+ {"int", pint, 0},
+ {"data.b", cons, 1},
+ {"data.w", cons, 2},
+ {"data.l", cons, 4},
+ {"form", listing_psize, 0},
+ {"heading", listing_title, 0},
+ {"import", s_ignore, 0},
+ {"page", listing_eject, 0},
+ {"program", s_ignore, 0},
+ {0, 0, 0}
+};
+
+const int md_reloc_size;
+
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+static struct hash_control *opcode_hash_control; /* Opcode mnemonics */
+
+/*
+ This function is called once, at assembler startup time. This should
+ set up all the tables, etc that the MD part of the assembler needs
+ */
+
+
+void
+md_begin ()
+{
+ struct h8_opcode *opcode;
+ char prev_buffer[100];
+ int idx = 0;
+
+ opcode_hash_control = hash_new ();
+ prev_buffer[0] = 0;
+
+ for (opcode = h8_opcodes; opcode->name; opcode++)
+ {
+ /* Strip off any . part when inserting the opcode and only enter
+ unique codes into the hash table
+ */
+ char *src = opcode->name;
+ unsigned int len = strlen (src);
+ char *dst = malloc (len + 1);
+ char *buffer = dst;
+
+ opcode->size = 0;
+ while (*src)
+ {
+ if (*src == '.')
+ {
+ src++;
+ opcode->size = *src;
+ break;
+ }
+ *dst++ = *src++;
+ }
+ *dst++ = 0;
+ if (strcmp (buffer, prev_buffer))
+ {
+ hash_insert (opcode_hash_control, buffer, (char *) opcode);
+ strcpy (prev_buffer, buffer);
+ idx++;
+ }
+ opcode->idx = idx;
+
+
+ /* Find the number of operands */
+ opcode->noperands = 0;
+ while (opcode->args.nib[opcode->noperands] != E)
+ opcode->noperands++;
+ /* Find the length of the opcode in bytes */
+ opcode->length = 0;
+ while (opcode->data.nib[opcode->length * 2] != E)
+ opcode->length++;
+ }
+
+ linkrelax = 1;
+}
+
+
+struct h8_exp
+{
+ char *e_beg;
+ char *e_end;
+ expressionS e_exp;
+};
+int dispreg;
+int opsize; /* Set when a register size is seen */
+
+
+struct h8_op
+{
+ op_type mode;
+ unsigned reg;
+ expressionS exp;
+};
+
+/*
+ parse operands
+ WREG r0,r1,r2,r3,r4,r5,r6,r7,fp,sp
+ r0l,r0h,..r7l,r7h
+ @WREG
+ @WREG+
+ @-WREG
+ #const
+ ccr
+*/
+
+/* try and parse a reg name, returns number of chars consumed */
+int
+parse_reg (src, mode, reg, direction)
+ char *src;
+ op_type *mode;
+ unsigned int *reg;
+ int direction;
+
+{
+ char *end;
+ int len;
+
+ /* Cribbed from get_symbol_end(). */
+ if (!is_name_beginner (*src) || *src == '\001')
+ return 0;
+ end = src+1;
+ while (is_part_of_name (*end) || *end == '\001')
+ end++;
+ len = end - src;
+
+ if (len == 2 && src[0] == 's' && src[1] == 'p')
+ {
+ *mode = PSIZE | REG | direction;
+ *reg = 7;
+ return len;
+ }
+ if (len == 3 && src[0] == 'c' && src[1] == 'c' && src[2] == 'r')
+ {
+ *mode = CCR;
+ *reg = 0;
+ return len;
+ }
+ if (len == 3 && src[0] == 'e' && src[1] == 'x' && src[2] == 'r')
+ {
+ *mode = EXR;
+ *reg = 0;
+ return len;
+ }
+ if (len == 2 && src[0] == 'f' && src[1] == 'p')
+ {
+ *mode = PSIZE | REG | direction;
+ *reg = 6;
+ return len;
+ }
+ if (len == 3 && src[0] == 'e' && src[1] == 'r'
+ && src[2] >= '0' && src[2] <= '7')
+ {
+ *mode = L_32 | REG | direction;
+ *reg = src[2] - '0';
+ if (!Hmode)
+ as_warn (_("Reg not valid for H8/300"));
+ return len;
+ }
+ if (len == 2 && src[0] == 'e' && src[1] >= '0' && src[1] <= '7')
+ {
+ *mode = L_16 | REG | direction;
+ *reg = src[1] - '0' + 8;
+ if (!Hmode)
+ as_warn (_("Reg not valid for H8/300"));
+ return len;
+ }
+
+ if (src[0] == 'r')
+ {
+ if (src[1] >= '0' && src[1] <= '7')
+ {
+ if (len == 3 && src[2] == 'l')
+ {
+ *mode = L_8 | REG | direction;
+ *reg = (src[1] - '0') + 8;
+ return len;
+ }
+ if (len == 3 && src[2] == 'h')
+ {
+ *mode = L_8 | REG | direction;
+ *reg = (src[1] - '0');
+ return len;
+ }
+ if (len == 2)
+ {
+ *mode = L_16 | REG | direction;
+ *reg = (src[1] - '0');
+ return len;
+ }
+ }
+ }
+
+ return 0;
+}
+
+char *
+parse_exp (s, op)
+ char *s;
+ expressionS * op;
+{
+ char *save = input_line_pointer;
+ char *new;
+
+ input_line_pointer = s;
+ expression (op);
+ if (op->X_op == O_absent)
+ as_bad (_("missing operand"));
+ new = input_line_pointer;
+ input_line_pointer = save;
+ return new;
+}
+
+static char *
+skip_colonthing (ptr, exp, mode)
+ char *ptr;
+ expressionS *exp;
+ int *mode;
+{
+ if (*ptr == ':')
+ {
+ ptr++;
+ *mode &= ~SIZE;
+ if (*ptr == '8')
+ {
+ ptr++;
+ /* ff fill any 8 bit quantity */
+ /* exp->X_add_number -= 0x100;*/
+ *mode |= L_8;
+ }
+ else
+ {
+ if (*ptr == '2')
+ {
+ *mode |= L_24;
+ }
+ else if (*ptr == '3')
+ {
+ *mode |= L_32;
+ }
+ else if (*ptr == '1')
+ {
+ *mode |= L_16;
+ }
+ while (isdigit (*ptr))
+ ptr++;
+ }
+ }
+ return ptr;
+}
+
+/* The many forms of operand:
+
+ Rn Register direct
+ @Rn Register indirect
+ @(exp[:16], Rn) Register indirect with displacement
+ @Rn+
+ @-Rn
+ @aa:8 absolute 8 bit
+ @aa:16 absolute 16 bit
+ @aa absolute 16 bit
+
+ #xx[:size] immediate data
+ @(exp:[8], pc) pc rel
+ @@aa[:8] memory indirect
+
+ */
+
+char *
+colonmod24 (op, src)
+ struct h8_op *op;
+ char *src;
+
+{
+ int mode = 0;
+ src = skip_colonthing (src, &op->exp, &mode);
+
+ if (!mode)
+ {
+ /* Choose a default mode */
+ if (op->exp.X_add_number < -32768
+ || op->exp.X_add_number > 32767)
+ {
+ if (Hmode)
+ mode = L_24;
+ else
+ mode = L_16;
+ }
+ else if (op->exp.X_add_symbol
+ || op->exp.X_op_symbol)
+ mode = DSYMMODE;
+ else
+ mode = DMODE;
+ }
+ op->mode |= mode;
+ return src;
+
+}
+
+
+static void
+get_operand (ptr, op, dst, direction)
+ char **ptr;
+ struct h8_op *op;
+ unsigned int dst;
+ int direction;
+{
+ char *src = *ptr;
+ op_type mode;
+ unsigned int num;
+ unsigned int len;
+
+ op->mode = E;
+
+ /* Gross. Gross. ldm and stm have a format not easily handled
+ by get_operand. We deal with it explicitly here. */
+ if (src[0] == 'e' && src[1] == 'r' && isdigit(src[2])
+ && src[3] == '-' && src[4] == 'e' && src[5] == 'r' && isdigit(src[6]))
+ {
+ int low, high;
+
+ low = src[2] - '0';
+ high = src[6] - '0';
+
+ if (high < low)
+ as_bad (_("Invalid register list for ldm/stm\n"));
+
+ if (low % 2)
+ as_bad (_("Invalid register list for ldm/stm\n"));
+
+ if (high - low > 3)
+ as_bad (_("Invalid register list for ldm/stm\n"));
+
+ if (high - low != 1
+ && low % 4)
+ as_bad (_("Invalid register list for ldm/stm\n"));
+
+ /* Even sicker. We encode two registers into op->reg. One
+ for the low register to save, the other for the high
+ register to save; we also set the high bit in op->reg
+ so we know this is "very special". */
+ op->reg = 0x80000000 | (high << 8) | low;
+ op->mode = REG;
+ *ptr = src + 7;
+ return;
+ }
+
+ len = parse_reg (src, &op->mode, &op->reg, direction);
+ if (len)
+ {
+ *ptr = src + len;
+ return;
+ }
+
+ if (*src == '@')
+ {
+ src++;
+ if (*src == '@')
+ {
+ src++;
+ src = parse_exp (src, &op->exp);
+
+ src = skip_colonthing (src, &op->exp, &op->mode);
+
+ *ptr = src;
+
+ op->mode = MEMIND;
+ return;
+
+ }
+
+
+ if (*src == '-')
+ {
+ src++;
+ len = parse_reg (src, &mode, &num, direction);
+ if (len == 0)
+ {
+ /* Oops, not a reg after all, must be ordinary exp */
+ src--;
+ /* must be a symbol */
+ op->mode = ABS | PSIZE | direction;
+ *ptr = skip_colonthing (parse_exp (src, &op->exp),
+ &op->exp, &op->mode);
+
+ return;
+
+
+ }
+
+
+ if ((mode & SIZE) != PSIZE)
+ as_bad (_("Wrong size pointer register for architecture."));
+ op->mode = RDDEC;
+ op->reg = num;
+ *ptr = src + len;
+ return;
+ }
+ if (*src == '(')
+ {
+ /* Disp */
+ src++;
+
+ /* Start off assuming a 16 bit offset */
+
+
+ src = parse_exp (src, &op->exp);
+
+ src = colonmod24 (op, src);
+
+ if (*src == ')')
+ {
+ src++;
+ op->mode |= ABS | direction;
+ *ptr = src;
+ return;
+ }
+
+ if (*src != ',')
+ {
+ as_bad (_("expected @(exp, reg16)"));
+ return;
+
+ }
+ src++;
+
+ len = parse_reg (src, &mode, &op->reg, direction);
+ if (len == 0 || !(mode & REG))
+ {
+ as_bad (_("expected @(exp, reg16)"));
+ return;
+ }
+ op->mode |= DISP | direction;
+ dispreg = op->reg;
+ src += len;
+ src = skip_colonthing (src, &op->exp, &op->mode);
+
+ if (*src != ')' && '(')
+ {
+ as_bad (_("expected @(exp, reg16)"));
+ return;
+ }
+ *ptr = src + 1;
+
+ return;
+ }
+ len = parse_reg (src, &mode, &num, direction);
+
+ if (len)
+ {
+ src += len;
+ if (*src == '+')
+ {
+ src++;
+ if ((mode & SIZE) != PSIZE)
+ as_bad (_("Wrong size pointer register for architecture."));
+ op->mode = RSINC;
+ op->reg = num;
+ *ptr = src;
+ return;
+ }
+ if ((mode & SIZE) != PSIZE)
+ as_bad (_("Wrong size pointer register for architecture."));
+
+ op->mode = direction | IND | PSIZE;
+ op->reg = num;
+ *ptr = src;
+
+ return;
+ }
+ else
+ {
+ /* must be a symbol */
+
+ op->mode = ABS | direction;
+ src = parse_exp (src, &op->exp);
+
+ *ptr = colonmod24 (op, src);
+
+ return;
+ }
+ }
+
+
+ if (*src == '#')
+ {
+ src++;
+ op->mode = IMM;
+ src = parse_exp (src, &op->exp);
+ *ptr = skip_colonthing (src, &op->exp, &op->mode);
+
+ return;
+ }
+ else if (strncmp (src, "mach", 4) == 0
+ || strncmp (src, "macl", 4) == 0)
+ {
+ op->reg = src[3] == 'l';
+ op->mode = MACREG;
+ *ptr = src + 4;
+ return;
+ }
+ else
+ {
+ src = parse_exp (src, &op->exp);
+ /* Trailing ':' size ? */
+ if (*src == ':')
+ {
+ if (src[1] == '1' && src[2] == '6')
+ {
+ op->mode = PCREL | L_16;
+ src += 3;
+ }
+ else if (src[1] == '8')
+ {
+ op->mode = PCREL | L_8;
+ src += 2;
+ }
+ else
+ {
+ as_bad (_("expect :8 or :16 here"));
+ }
+ }
+ else
+ {
+ op->mode = PCREL | bsize;
+ }
+ *ptr = src;
+ }
+}
+
+
+static
+char *
+get_operands (noperands, op_end, operand)
+ unsigned int noperands;
+ char *op_end;
+ struct h8_op *operand;
+{
+ char *ptr = op_end;
+
+ switch (noperands)
+ {
+ case 0:
+ operand[0].mode = 0;
+ operand[1].mode = 0;
+ break;
+
+ case 1:
+ ptr++;
+ get_operand (&ptr, operand + 0, 0, SRC);
+ if (*ptr == ',')
+ {
+ ptr++;
+ get_operand (&ptr, operand + 1, 1, DST);
+ }
+ else
+ {
+ operand[1].mode = 0;
+ }
+
+ break;
+ case 2:
+ ptr++;
+ get_operand (&ptr, operand + 0, 0, SRC);
+ if (*ptr == ',')
+ ptr++;
+ get_operand (&ptr, operand + 1, 1, DST);
+ break;
+
+ default:
+ abort ();
+ }
+
+
+ return ptr;
+}
+
+/* Passed a pointer to a list of opcodes which use different
+ addressing modes, return the opcode which matches the opcodes
+ provided
+ */
+static
+struct h8_opcode *
+get_specific (opcode, operands, size)
+ struct h8_opcode *opcode;
+ struct h8_op *operands;
+ int size;
+{
+ struct h8_opcode *this_try = opcode;
+ int found = 0;
+
+ unsigned int this_index = opcode->idx;
+
+ /* There's only one ldm/stm and it's easier to just
+ get out quick for them. */
+ if (strcmp (opcode->name, "stm.l") == 0
+ || strcmp (opcode->name, "ldm.l") == 0)
+ return this_try;
+
+ while (this_index == opcode->idx && !found)
+ {
+ found = 1;
+
+ this_try = opcode++;
+ if (this_try->noperands == 0)
+ {
+ int this_size;
+
+ this_size = this_try->how & SN;
+ if (this_size != size && (this_size != SB || size != SN))
+ found = 0;
+ }
+ else
+ {
+ unsigned int i;
+
+ for (i = 0; i < this_try->noperands && found; i++)
+ {
+ op_type op = this_try->args.nib[i];
+ int x = operands[i].mode;
+
+ if ((op & (DISP | REG)) == (DISP | REG)
+ && ((x & (DISP | REG)) == (DISP | REG)))
+ {
+ dispreg = operands[i].reg;
+ }
+ else if (op & REG)
+ {
+ if (!(x & REG))
+ found = 0;
+
+ if (x & L_P)
+ x = (x & ~L_P) | (Hmode ? L_32 : L_16);
+ if (op & L_P)
+ op = (op & ~L_P) | (Hmode ? L_32 : L_16);
+
+ opsize = op & SIZE;
+
+ /* The size of the reg is v important */
+ if ((op & SIZE) != (x & SIZE))
+ found = 0;
+ }
+ else if ((op & ABSJMP) && (x & ABS))
+ {
+ operands[i].mode &= ~ABS;
+ operands[i].mode |= ABSJMP;
+ /* But it may not be 24 bits long */
+ if (!Hmode)
+ {
+ operands[i].mode &= ~SIZE;
+ operands[i].mode |= L_16;
+ }
+ }
+ else if ((op & (KBIT | DBIT)) && (x & IMM))
+ {
+ /* This is ok if the immediate value is sensible */
+ }
+ else if (op & PCREL)
+ {
+ /* The size of the displacement is important */
+ if ((op & SIZE) != (x & SIZE))
+ found = 0;
+ }
+ else if ((op & (DISP | IMM | ABS))
+ && (op & (DISP | IMM | ABS)) == (x & (DISP | IMM | ABS)))
+ {
+ /* Promote a L_24 to L_32 if it makes us match. */
+ if ((x & L_24) && (op & L_32))
+ {
+ x &= ~L_24;
+ x |= L_32;
+ }
+ /* Promote an L8 to L_16 if it makes us match. */
+ if (op & ABS && op & L_8 && op & DISP)
+ {
+ if (x & L_16)
+ found= 1;
+ }
+ else if ((x & SIZE) != 0
+ && ((op & SIZE) != (x & SIZE)))
+ found = 0;
+ }
+ else if ((op & MACREG) != (x & MACREG))
+ {
+ found = 0;
+ }
+ else if ((op & MODE) != (x & MODE))
+ {
+ found = 0;
+ }
+ }
+ }
+ }
+ if (found)
+ return this_try;
+ else
+ return 0;
+}
+
+static void
+check_operand (operand, width, string)
+ struct h8_op *operand;
+ unsigned int width;
+ char *string;
+{
+ if (operand->exp.X_add_symbol == 0
+ && operand->exp.X_op_symbol == 0)
+ {
+
+ /* No symbol involved, let's look at offset, it's dangerous if any of
+ the high bits are not 0 or ff's, find out by oring or anding with
+ the width and seeing if the answer is 0 or all fs*/
+
+ if ((operand->exp.X_add_number & ~width) != 0 &&
+ (operand->exp.X_add_number | width) != (~0))
+ {
+ if (width == 255
+ && (operand->exp.X_add_number & 0xff00) == 0xff00)
+ {
+ /* Just ignore this one - which happens when trying to
+ fit a 16 bit address truncated into an 8 bit address
+ of something like bset. */
+ }
+ else
+ {
+ as_warn (_("operand %s0x%lx out of range."), string,
+ (unsigned long) operand->exp.X_add_number);
+ }
+ }
+ }
+
+}
+
+/* RELAXMODE has one of 3 values:
+
+ 0 Output a "normal" reloc, no relaxing possible for this insn/reloc
+
+ 1 Output a relaxable 24bit absolute mov.w address relocation
+ (may relax into a 16bit absolute address).
+
+ 2 Output a relaxable 16/24 absolute mov.b address relocation
+ (may relax into an 8bit absolute address). */
+
+static void
+do_a_fix_imm (offset, operand, relaxmode)
+ int offset;
+ struct h8_op *operand;
+ int relaxmode;
+{
+ int idx;
+ int size;
+ int where;
+
+
+ char *t = operand->mode & IMM ? "#" : "@";
+
+ if (operand->exp.X_add_symbol == 0)
+ {
+ char *bytes = frag_now->fr_literal + offset;
+ switch (operand->mode & SIZE)
+ {
+ case L_2:
+ check_operand (operand, 0x3, t);
+ bytes[0] |= (operand->exp.X_add_number) << 4;
+ break;
+ case L_3:
+ check_operand (operand, 0x7, t);
+ bytes[0] |= (operand->exp.X_add_number) << 4;
+ break;
+ case L_8:
+ check_operand (operand, 0xff, t);
+ bytes[0] = operand->exp.X_add_number;
+ break;
+ case L_16:
+ check_operand (operand, 0xffff, t);
+ bytes[0] = operand->exp.X_add_number >> 8;
+ bytes[1] = operand->exp.X_add_number >> 0;
+ break;
+ case L_24:
+ check_operand (operand, 0xffffff, t);
+ bytes[0] = operand->exp.X_add_number >> 16;
+ bytes[1] = operand->exp.X_add_number >> 8;
+ bytes[2] = operand->exp.X_add_number >> 0;
+ break;
+
+ case L_32:
+ /* This should be done with bfd */
+ bytes[0] = operand->exp.X_add_number >> 24;
+ bytes[1] = operand->exp.X_add_number >> 16;
+ bytes[2] = operand->exp.X_add_number >> 8;
+ bytes[3] = operand->exp.X_add_number >> 0;
+ break;
+ }
+
+ }
+ else
+ {
+ switch (operand->mode & SIZE)
+ {
+
+ case L_24:
+ case L_32:
+ size = 4;
+ where = (operand->mode & SIZE) == L_24 ? -1 : 0;
+ if (relaxmode == 2)
+ idx = R_MOV24B1;
+ else if (relaxmode == 1)
+ idx = R_MOVL1;
+ else
+ idx = R_RELLONG;
+ break;
+ default:
+ as_bad(_("Can't work out size of operand.\n"));
+ case L_16:
+ size = 2;
+ where = 0;
+ if (relaxmode == 2)
+ idx = R_MOV16B1;
+ else
+ idx = R_RELWORD;
+ operand->exp.X_add_number = (short)operand->exp.X_add_number;
+ break;
+ case L_8:
+ size = 1;
+ where = 0;
+ idx = R_RELBYTE;
+ /* This used to use a cast to char, but that fails if char is an
+ unsigned type. We can't use `signed char', as that isn't valid
+ K&R C. */
+ if (operand->exp.X_add_number & 0x80)
+ operand->exp.X_add_number |= ((offsetT) -1 << 8);
+ else
+ operand->exp.X_add_number &= 0xff;
+ }
+
+ fix_new_exp (frag_now,
+ offset + where,
+ size,
+ &operand->exp,
+ 0,
+ idx);
+ }
+
+}
+
+/* Now we know what sort of opcodes it is, lets build the bytes -
+ */
+static void
+build_bytes (this_try, operand)
+ struct h8_opcode *this_try;
+ struct h8_op *operand;
+{
+ unsigned int i;
+
+ char *output = frag_more (this_try->length);
+ op_type *nibble_ptr = this_try->data.nib;
+ op_type c;
+ unsigned int nibble_count = 0;
+ int absat;
+ int immat;
+ int nib;
+ int movb = 0;
+ char asnibbles[30];
+ char *p = asnibbles;
+
+ if (!(this_try->inbase || Hmode))
+ as_warn (_("Opcode `%s' with these operand types not available in H8/300 mode"),
+ this_try->name);
+
+ while (*nibble_ptr != E)
+ {
+ int d;
+ c = *nibble_ptr++;
+
+ d = (c & (DST | SRC_IN_DST)) != 0;
+
+ if (c < 16)
+ {
+ nib = c;
+ }
+ else
+ {
+
+ if (c & (REG | IND | INC | DEC))
+ {
+ nib = operand[d].reg;
+ }
+ else if ((c & DISPREG) == (DISPREG))
+ {
+ nib = dispreg;
+ }
+ else if (c & ABS )
+ {
+ operand[d].mode = c;
+ absat = nibble_count / 2;
+ nib = 0;
+ }
+ else if (c & (IMM | PCREL | ABS | ABSJMP | DISP))
+ {
+ operand[d].mode = c;
+ immat = nibble_count / 2;
+ nib = 0;
+ }
+ else if (c & IGNORE)
+ {
+ nib = 0;
+ }
+ else if (c & DBIT)
+ {
+ switch (operand[0].exp.X_add_number)
+ {
+ case 1:
+ nib = c;
+ break;
+ case 2:
+ nib = 0x8 | c;
+ break;
+ default:
+ as_bad (_("Need #1 or #2 here"));
+ }
+ }
+ else if (c & KBIT)
+ {
+ switch (operand[0].exp.X_add_number)
+ {
+ case 1:
+ nib = 0;
+ break;
+ case 2:
+ nib = 8;
+ break;
+ case 4:
+ if (!Hmode)
+ as_warn (_("#4 not valid on H8/300."));
+ nib = 9;
+ break;
+
+ default:
+ as_bad (_("Need #1 or #2 here"));
+ break;
+ }
+ /* stop it making a fix */
+ operand[0].mode = 0;
+ }
+
+ if (c & MEMRELAX)
+ {
+ operand[d].mode |= MEMRELAX;
+ }
+
+ if (c & B31)
+ {
+ nib |= 0x8;
+ }
+
+ if (c & MACREG)
+ {
+ nib = 2 + operand[d].reg;
+ }
+ }
+ nibble_count++;
+
+ *p++ = nib;
+ }
+
+ /* Disgusting. Why, oh why didn't someone ask us for advice
+ on the assembler format. */
+ if (strcmp (this_try->name, "stm.l") == 0
+ || strcmp (this_try->name, "ldm.l") == 0)
+ {
+ int high, low;
+ high = (operand[this_try->name[0] == 'l' ? 1 : 0].reg >> 8) & 0xf;
+ low = operand[this_try->name[0] == 'l' ? 1 : 0].reg & 0xf;
+
+ asnibbles[2] = high - low;
+ asnibbles[7] = (this_try->name[0] == 'l') ? high : low;
+ }
+
+ for (i = 0; i < this_try->length; i++)
+ {
+ output[i] = (asnibbles[i * 2] << 4) | asnibbles[i * 2 + 1];
+ }
+
+ /* Note if this is a movb instruction -- there's a special relaxation
+ which only applies to them. */
+ if (strcmp (this_try->name, "mov.b") == 0)
+ movb = 1;
+
+ /* output any fixes */
+ for (i = 0; i < 2; i++)
+ {
+ int x = operand[i].mode;
+
+ if (x & (IMM | DISP))
+ {
+ do_a_fix_imm (output - frag_now->fr_literal + immat,
+ operand + i, x & MEMRELAX != 0);
+ }
+ else if (x & ABS)
+ {
+ do_a_fix_imm (output - frag_now->fr_literal + absat,
+ operand + i, x & MEMRELAX ? movb + 1 : 0);
+ }
+ else if (x & PCREL)
+ {
+ int size16 = x & L_16;
+ int where = size16 ? 2 : 1;
+ int size = size16 ? 2 : 1;
+ int type = size16 ? R_PCRWORD : R_PCRBYTE;
+
+ check_operand (operand + i, size16 ? 0x7fff : 0x7f, "@");
+
+ if (operand[i].exp.X_add_number & 1)
+ {
+ as_warn (_("branch operand has odd offset (%lx)\n"),
+ (unsigned long) operand->exp.X_add_number);
+ }
+
+ operand[i].exp.X_add_number -= 1;
+ /* This used to use a cast to char, but that fails if char is an
+ unsigned type. We can't use `signed char', as that isn't valid
+ K&R C. */
+ if (operand[i].exp.X_add_number & 0x80)
+ operand[i].exp.X_add_number |= ((offsetT) -1 << 8);
+ else
+ operand[i].exp.X_add_number &= 0xff;
+
+ fix_new_exp (frag_now,
+ output - frag_now->fr_literal + where,
+ size,
+ &operand[i].exp,
+ 1,
+ type);
+ }
+ else if (x & MEMIND)
+ {
+
+ check_operand (operand + i, 0xff, "@@");
+ fix_new_exp (frag_now,
+ output - frag_now->fr_literal + 1,
+ 1,
+ &operand[i].exp,
+ 0,
+ R_MEM_INDIRECT);
+ }
+ else if (x & ABSJMP)
+ {
+ /* This jmp may be a jump or a branch */
+
+ check_operand (operand + i, Hmode ? 0xffffff : 0xffff, "@");
+ if (operand[i].exp.X_add_number & 1)
+ {
+ as_warn (_("branch operand has odd offset (%lx)\n"),
+ (unsigned long) operand->exp.X_add_number);
+ }
+ if (!Hmode)
+ operand[i].exp.X_add_number = (short) operand[i].exp.X_add_number;
+ fix_new_exp (frag_now,
+ output - frag_now->fr_literal,
+ 4,
+ &operand[i].exp,
+ 0,
+ R_JMPL1);
+ }
+ }
+
+}
+
+/*
+ try and give an intelligent error message for common and simple to
+ detect errors
+ */
+
+static void
+clever_message (opcode, operand)
+ struct h8_opcode *opcode;
+ struct h8_op *operand;
+{
+ /* Find out if there was more than one possible opccode */
+
+ if ((opcode + 1)->idx != opcode->idx)
+ {
+ unsigned int argn;
+
+ /* Only one opcode of this flavour, try and guess which operand
+ didn't match */
+ for (argn = 0; argn < opcode->noperands; argn++)
+ {
+ switch (opcode->args.nib[argn])
+ {
+ case RD16:
+ if (operand[argn].mode != RD16)
+ {
+ as_bad (_("destination operand must be 16 bit register"));
+ return;
+
+ }
+ break;
+
+ case RS8:
+
+ if (operand[argn].mode != RS8)
+ {
+ as_bad (_("source operand must be 8 bit register"));
+ return;
+ }
+ break;
+
+ case ABS16DST:
+ if (operand[argn].mode != ABS16DST)
+ {
+ as_bad (_("destination operand must be 16bit absolute address"));
+ return;
+ }
+ break;
+ case RD8:
+ if (operand[argn].mode != RD8)
+ {
+ as_bad (_("destination operand must be 8 bit register"));
+ return;
+ }
+ break;
+
+
+ case ABS16SRC:
+ if (operand[argn].mode != ABS16SRC)
+ {
+ as_bad (_("source operand must be 16bit absolute address"));
+ return;
+ }
+ break;
+
+ }
+ }
+ }
+ as_bad (_("invalid operands"));
+}
+
+/* This is the guts of the machine-dependent assembler. STR points to a
+ machine dependent instruction. This funciton is supposed to emit
+ the frags/bytes it assembles to.
+ */
+
+
+
+void
+md_assemble (str)
+ char *str;
+{
+ char *op_start;
+ char *op_end;
+ struct h8_op operand[2];
+ struct h8_opcode *opcode;
+ struct h8_opcode *prev_opcode;
+
+ char *dot = 0;
+ char c;
+ int size;
+
+ /* Drop leading whitespace */
+ while (*str == ' ')
+ str++;
+
+ /* find the op code end */
+ for (op_start = op_end = str;
+ *op_end != 0 && *op_end != ' ';
+ op_end++)
+ {
+ if (*op_end == '.')
+ {
+ dot = op_end + 1;
+ *op_end = 0;
+ op_end += 2;
+ break;
+ }
+ }
+
+ ;
+
+ if (op_end == op_start)
+ {
+ as_bad (_("can't find opcode "));
+ }
+ c = *op_end;
+
+ *op_end = 0;
+
+ opcode = (struct h8_opcode *) hash_find (opcode_hash_control,
+ op_start);
+
+ if (opcode == NULL)
+ {
+ as_bad (_("unknown opcode"));
+ return;
+ }
+
+ /* We use to set input_line_pointer to the result of get_operands,
+ but that is wrong. Our caller assumes we don't change it. */
+
+ (void) get_operands (opcode->noperands, op_end, operand);
+ *op_end = c;
+ prev_opcode = opcode;
+
+ size = SN;
+ if (dot)
+ {
+ switch (*dot)
+ {
+ case 'b':
+ size = SB;
+ break;
+
+ case 'w':
+ size = SW;
+ break;
+
+ case 'l':
+ size = SL;
+ break;
+ }
+ }
+ opcode = get_specific (opcode, operand, size);
+
+ if (opcode == 0)
+ {
+ /* Couldn't find an opcode which matched the operands */
+ char *where = frag_more (2);
+
+ where[0] = 0x0;
+ where[1] = 0x0;
+ clever_message (prev_opcode, operand);
+
+ return;
+ }
+ if (opcode->size && dot)
+ {
+ if (opcode->size != *dot)
+ {
+ as_warn (_("mismatch between opcode size and operand size"));
+ }
+ }
+
+ build_bytes (opcode, operand);
+
+}
+
+void
+tc_crawl_symbol_chain (headers)
+ object_headers * headers;
+{
+ printf (_("call to tc_crawl_symbol_chain \n"));
+}
+
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ return 0;
+}
+
+void
+tc_headers_hook (headers)
+ object_headers * headers;
+{
+ printf (_("call to tc_headers_hook \n"));
+}
+
+/* Various routines to kill one day */
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+/* Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP . An error message is returned, or NULL on OK.
+ */
+char *
+md_atof (type, litP, sizeP)
+ char type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+ char *atof_ieee ();
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("Bad call to MD_ATOF()");
+ }
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+
+ *sizeP = prec * sizeof (LITTLENUM_TYPE);
+ for (wordP = words; prec--;)
+ {
+ md_number_to_chars (litP, (long) (*wordP++), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ return 0;
+}
+
+CONST char *md_shortopts = "";
+struct option md_longopts[] = {
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ return 0;
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+}
+
+void
+tc_aout_fix_to_chars ()
+{
+ printf (_("call to tc_aout_fix_to_chars \n"));
+ abort ();
+}
+
+void
+md_convert_frag (headers, seg, fragP)
+ object_headers *headers;
+ segT seg;
+ fragS *fragP;
+{
+ printf (_("call to md_convert_frag \n"));
+ abort ();
+}
+
+valueT
+md_section_align (seg, size)
+ segT seg;
+ valueT size;
+{
+ return ((size + (1 << section_alignment[(int) seg]) - 1) & (-1 << section_alignment[(int) seg]));
+
+}
+
+void
+md_apply_fix (fixP, val)
+ fixS *fixP;
+ long val;
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ switch (fixP->fx_size)
+ {
+ case 1:
+ *buf++ = val;
+ break;
+ case 2:
+ *buf++ = (val >> 8);
+ *buf++ = val;
+ break;
+ case 4:
+ *buf++ = (val >> 24);
+ *buf++ = (val >> 16);
+ *buf++ = (val >> 8);
+ *buf++ = val;
+ break;
+ default:
+ abort ();
+ }
+}
+
+int
+md_estimate_size_before_relax (fragP, segment_type)
+ register fragS *fragP;
+ register segT segment_type;
+{
+ printf (_("call tomd_estimate_size_before_relax \n"));
+ abort ();
+}
+
+/* Put number into target byte order */
+
+void
+md_number_to_chars (ptr, use, nbytes)
+ char *ptr;
+ valueT use;
+ int nbytes;
+{
+ number_to_chars_bigendian (ptr, use, nbytes);
+}
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ abort ();
+}
+
+
+void
+tc_reloc_mangle (fix_ptr, intr, base)
+ fixS *fix_ptr;
+ struct internal_reloc *intr;
+ bfd_vma base;
+
+{
+ symbolS *symbol_ptr;
+
+ symbol_ptr = fix_ptr->fx_addsy;
+
+ /* If this relocation is attached to a symbol then it's ok
+ to output it */
+ if (fix_ptr->fx_r_type == TC_CONS_RELOC)
+ {
+ /* cons likes to create reloc32's whatever the size of the reloc..
+ */
+ switch (fix_ptr->fx_size)
+ {
+ case 4:
+ intr->r_type = R_RELLONG;
+ break;
+ case 2:
+ intr->r_type = R_RELWORD;
+ break;
+ case 1:
+ intr->r_type = R_RELBYTE;
+ break;
+ default:
+ abort ();
+
+ }
+
+ }
+ else
+ {
+ intr->r_type = fix_ptr->fx_r_type;
+ }
+
+ intr->r_vaddr = fix_ptr->fx_frag->fr_address + fix_ptr->fx_where + base;
+ intr->r_offset = fix_ptr->fx_offset;
+
+ if (symbol_ptr)
+ {
+ if (symbol_ptr->sy_number != -1)
+ intr->r_symndx = symbol_ptr->sy_number;
+ else
+ {
+ symbolS *segsym;
+
+ /* This case arises when a reference is made to `.'. */
+ segsym = seg_info (S_GET_SEGMENT (symbol_ptr))->dot;
+ if (segsym == NULL)
+ intr->r_symndx = -1;
+ else
+ {
+ intr->r_symndx = segsym->sy_number;
+ intr->r_offset += S_GET_VALUE (symbol_ptr);
+ }
+ }
+ }
+ else
+ intr->r_symndx = -1;
+
+
+}
+
+/* end of tc-h8300.c */
diff --git a/gas/config/tc-h8300.h b/gas/config/tc-h8300.h
new file mode 100644
index 0000000..2a2a704
--- /dev/null
+++ b/gas/config/tc-h8300.h
@@ -0,0 +1,58 @@
+/* This file is tc-h8300.h
+ Copyright (C) 1987-1992, 93, 94, 95, 96, 97, 1998
+ Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+
+#define TC_H8300
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+#if ANSI_PROTOTYPES
+struct internal_reloc;
+#endif
+
+#define WORKING_DOT_WORD
+
+/* This macro translates between an internal fix and an coff reloc type */
+#define TC_COFF_FIX2RTYPE(fixP) abort();
+
+#define BFD_ARCH bfd_arch_h8300
+#define COFF_MAGIC ( Smode ? 0x8302 : Hmode ? 0x8301 : 0x8300)
+#define TC_COUNT_RELOC(x) (1)
+#define IGNORE_NONSTANDARD_ESCAPES
+
+#define tc_coff_symbol_emit_hook(a) ; /* not used */
+#define TC_RELOC_MANGLE(s,a,b,c) tc_reloc_mangle(a,b,c)
+extern void tc_reloc_mangle
+ PARAMS ((struct fix *, struct internal_reloc *, bfd_vma));
+
+#define TC_CONS_RELOC (Hmode ? R_RELLONG: R_RELWORD)
+
+#define DO_NOT_STRIP 0
+#define LISTING_HEADER "Hitachi H8/300 GAS "
+#define NEED_FX_R_TYPE 1
+#define RELOC_32 1234
+
+extern int Hmode;
+extern int Smode;
+
+#define md_operand(x)
+
+/* end of tc-h8300.h */
diff --git a/gas/config/tc-h8500.c b/gas/config/tc-h8500.c
new file mode 100644
index 0000000..9dec3e2
--- /dev/null
+++ b/gas/config/tc-h8500.c
@@ -0,0 +1,1633 @@
+/* tc-h8500.c -- Assemble code for the Hitachi H8/500
+ Copyright (C) 1993, 94, 95, 1998 Free Software Foundation.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+/*
+ Written By Steve Chamberlain
+ sac@cygnus.com
+ */
+
+#include <stdio.h>
+#include "as.h"
+#include "bfd.h"
+#include "subsegs.h"
+#define DEFINE_TABLE
+#define ASSEMBLER_TABLE
+#include "opcodes/h8500-opc.h"
+#include <ctype.h>
+
+const char comment_chars[] = "!";
+const char line_separator_chars[] = ";";
+const char line_comment_chars[] = "!#";
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ Integer arg to pass to the function
+ */
+
+void cons ();
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"int", cons, 2},
+ {"data.b", cons, 1},
+ {"data.w", cons, 2},
+ {"data.l", cons, 4},
+ {"form", listing_psize, 0},
+ {"heading", listing_title, 0},
+ {"import", s_ignore, 0},
+ {"page", listing_eject, 0},
+ {"program", s_ignore, 0},
+ {0, 0, 0}
+};
+
+const int md_reloc_size;
+
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+#define C(a,b) ENCODE_RELAX(a,b)
+#define ENCODE_RELAX(what,length) (((what) << 2) + (length))
+
+#define GET_WHAT(x) ((x>>2))
+
+#define BYTE_DISP 1
+#define WORD_DISP 2
+#define UNDEF_BYTE_DISP 0
+#define UNDEF_WORD_DISP 3
+
+#define BRANCH 1
+#define SCB_F 2
+#define SCB_TST 3
+#define END 4
+
+#define BYTE_F 127
+#define BYTE_B -126
+#define WORD_F 32767
+#define WORD_B 32768
+
+relax_typeS md_relax_table[C (END, 0)];
+
+static struct hash_control *opcode_hash_control; /* Opcode mnemonics */
+
+/*
+ This function is called once, at assembler startup time. This should
+ set up all the tables, etc that the MD part of the assembler needs
+ */
+
+void
+md_begin ()
+{
+ h8500_opcode_info *opcode;
+ char prev_buffer[100];
+ int idx = 0;
+ register relax_typeS *table;
+
+ opcode_hash_control = hash_new ();
+ prev_buffer[0] = 0;
+
+ /* Insert unique names into hash table */
+ for (opcode = h8500_table; opcode->name; opcode++)
+ {
+ if (idx != opcode->idx)
+ {
+ hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
+ idx++;
+ }
+ }
+
+ /* Initialize the relax table. We use a local variable to avoid
+ warnings about modifying a supposedly const data structure. */
+ table = (relax_typeS *) md_relax_table;
+ table[C (BRANCH, BYTE_DISP)].rlx_forward = BYTE_F;
+ table[C (BRANCH, BYTE_DISP)].rlx_backward = BYTE_B;
+ table[C (BRANCH, BYTE_DISP)].rlx_length = 2;
+ table[C (BRANCH, BYTE_DISP)].rlx_more = C (BRANCH, WORD_DISP);
+
+ table[C (BRANCH, WORD_DISP)].rlx_forward = WORD_F;
+ table[C (BRANCH, WORD_DISP)].rlx_backward = WORD_B;
+ table[C (BRANCH, WORD_DISP)].rlx_length = 3;
+ table[C (BRANCH, WORD_DISP)].rlx_more = 0;
+
+ table[C (SCB_F, BYTE_DISP)].rlx_forward = BYTE_F;
+ table[C (SCB_F, BYTE_DISP)].rlx_backward = BYTE_B;
+ table[C (SCB_F, BYTE_DISP)].rlx_length = 3;
+ table[C (SCB_F, BYTE_DISP)].rlx_more = C (SCB_F, WORD_DISP);
+
+ table[C (SCB_F, WORD_DISP)].rlx_forward = WORD_F;
+ table[C (SCB_F, WORD_DISP)].rlx_backward = WORD_B;
+ table[C (SCB_F, WORD_DISP)].rlx_length = 8;
+ table[C (SCB_F, WORD_DISP)].rlx_more = 0;
+
+ table[C (SCB_TST, BYTE_DISP)].rlx_forward = BYTE_F;
+ table[C (SCB_TST, BYTE_DISP)].rlx_backward = BYTE_B;
+ table[C (SCB_TST, BYTE_DISP)].rlx_length = 3;
+ table[C (SCB_TST, BYTE_DISP)].rlx_more = C (SCB_TST, WORD_DISP);
+
+ table[C (SCB_TST, WORD_DISP)].rlx_forward = WORD_F;
+ table[C (SCB_TST, WORD_DISP)].rlx_backward = WORD_B;
+ table[C (SCB_TST, WORD_DISP)].rlx_length = 10;
+ table[C (SCB_TST, WORD_DISP)].rlx_more = 0;
+
+}
+
+static int rn; /* register number used by RN */
+static int rs; /* register number used by RS */
+static int rd; /* register number used by RD */
+static int crb; /* byte size cr */
+static int crw; /* word sized cr */
+static int cr; /* unknown size cr */
+
+static expressionS displacement;/* displacement expression */
+static int displacement_size; /* and size if given */
+
+static int immediate_inpage;
+static expressionS immediate; /* immediate expression */
+static int immediate_size; /* and size if given */
+
+static expressionS absolute; /* absolute expression */
+static int absolute_size; /* and size if given */
+
+typedef struct
+{
+ int type;
+ int reg;
+ expressionS exp;
+ int page;
+}
+
+h8500_operand_info;
+
+/* try and parse a reg name, returns number of chars consumed */
+static int
+parse_reg (src, mode, reg)
+ char *src;
+ int *mode;
+ int *reg;
+{
+ char *end;
+ int len;
+
+ /* Cribbed from get_symbol_end(). */
+ if (!is_name_beginner (*src) || *src == '\001')
+ return 0;
+ end = src+1;
+ while (is_part_of_name (*end) || *end == '\001')
+ end++;
+ len = end - src;
+
+ if (len == 2 && src[0] == 'r')
+ {
+ if (src[1] >= '0' && src[1] <= '7')
+ {
+ *mode = RN;
+ *reg = (src[1] - '0');
+ return len;
+ }
+ }
+ if (len == 2 && src[0] == 's' && src[1] == 'p')
+ {
+ *mode = RN;
+ *reg = 7;
+ return len;
+ }
+ if (len == 3 && src[0] == 'c' && src[1] == 'c' && src[2] == 'r')
+ {
+ *mode = CRB;
+ *reg = 1;
+ return len;
+ }
+ if (len == 2 && src[0] == 's' && src[1] == 'r')
+ {
+ *mode = CRW;
+ *reg = 0;
+ return len;
+ }
+ if (len == 2 && src[0] == 'b' && src[1] == 'r')
+ {
+ *mode = CRB;
+ *reg = 3;
+ return len;
+ }
+ if (len == 2 && src[0] == 'e' && src[1] == 'p')
+ {
+ *mode = CRB;
+ *reg = 4;
+ return len;
+ }
+ if (len == 2 && src[0] == 'd' && src[1] == 'p')
+ {
+ *mode = CRB;
+ *reg = 5;
+ return len;
+ }
+ if (len == 2 && src[0] == 't' && src[1] == 'p')
+ {
+ *mode = CRB;
+ *reg = 7;
+ return len;
+ }
+ if (len == 2 && src[0] == 'f' && src[1] == 'p')
+ {
+ *mode = RN;
+ *reg = 6;
+ return len;
+ }
+ return 0;
+}
+
+static
+char *
+parse_exp (s, op, page)
+ char *s;
+ expressionS *op;
+ int *page;
+{
+ char *save;
+ char *new;
+
+ save = input_line_pointer;
+
+ *page = 0;
+ if (s[0] == '%')
+ {
+ if (s[1] == 'p' && s[2] == 'a' && s[3] == 'g' && s[4] == 'e')
+ {
+ s += 5;
+ *page = 'p';
+ }
+ if (s[1] == 'h' && s[2] == 'i' && s[3] == '1' && s[4] == '6')
+ {
+ s += 5;
+ *page = 'h';
+ }
+ else if (s[1] == 'o' && s[2] == 'f' && s[3] == 'f')
+ {
+ s += 4;
+ *page = 'o';
+ }
+ }
+
+ input_line_pointer = s;
+
+ expression (op);
+ if (op->X_op == O_absent)
+ as_bad (_("missing operand"));
+ new = input_line_pointer;
+ input_line_pointer = save;
+ return new;
+}
+
+typedef enum
+ {
+ exp_signed, exp_unsigned, exp_sandu
+ } sign_type;
+
+
+static char *
+skip_colonthing (sign, ptr, exp, def, size8, size16, size24)
+ sign_type sign;
+ char *ptr;
+ h8500_operand_info *exp;
+ int def;
+ int size8;
+ int size16;
+ int size24;
+{
+ ptr = parse_exp (ptr, &exp->exp, &exp->page);
+ if (*ptr == ':')
+ {
+ ptr++;
+ if (*ptr == '8')
+ {
+ ptr++;
+ exp->type = size8;
+ }
+ else if (ptr[0] == '1' & ptr[1] == '6')
+ {
+ ptr += 2;
+ exp->type = size16;
+ }
+ else if (ptr[0] == '2' & ptr[1] == '4')
+ {
+ if (!size24)
+ {
+ as_bad (_(":24 not valid for this opcode"));
+ }
+ ptr += 2;
+ exp->type = size24;
+ }
+ else
+ {
+ as_bad (_("expect :8,:16 or :24"));
+ exp->type = size16;
+ }
+ }
+ else
+ {
+ if (exp->page == 'p')
+ {
+ exp->type = IMM8;
+ }
+ else if (exp->page == 'h')
+ {
+ exp->type = IMM16;
+ }
+ else
+ {
+ /* Let's work out the size from the context */
+ int n = exp->exp.X_add_number;
+ if (size8
+ && exp->exp.X_op == O_constant
+ && ((sign == exp_signed && (n >= -128 && n <= 127))
+ || (sign == exp_unsigned && (n >= 0 && (n <= 255)))
+ || (sign == exp_sandu && (n >= -128 && (n <= 255)))))
+ {
+ exp->type = size8;
+ }
+ else
+ {
+ exp->type = def;
+ }
+ }
+ }
+ return ptr;
+}
+
+static int
+parse_reglist (src, op)
+ char *src;
+ h8500_operand_info *op;
+{
+ int mode;
+ int rn;
+ int mask = 0;
+ int rm;
+ int idx = 1; /* skip ( */
+
+ while (src[idx] && src[idx] != ')')
+ {
+ int done = parse_reg (src + idx, &mode, &rn);
+
+ if (done)
+ {
+ idx += done;
+ mask |= 1 << rn;
+ }
+ else
+ {
+ as_bad (_("syntax error in reg list"));
+ return 0;
+ }
+ if (src[idx] == '-')
+ {
+ idx++;
+ done = parse_reg (src + idx, &mode, &rm);
+ if (done)
+ {
+ idx += done;
+ while (rn <= rm)
+ {
+ mask |= 1 << rn;
+ rn++;
+ }
+ }
+ else
+ {
+ as_bad (_("missing final register in range"));
+ }
+ }
+ if (src[idx] == ',')
+ idx++;
+ }
+ idx++;
+ op->exp.X_add_symbol = 0;
+ op->exp.X_op_symbol = 0;
+ op->exp.X_add_number = mask;
+ op->exp.X_op = O_constant;
+ op->exp.X_unsigned = 1;
+ op->type = IMM8;
+ return idx;
+
+}
+
+/* The many forms of operand:
+
+ Rn Register direct
+ @Rn Register indirect
+ @(disp[:size], Rn) Register indirect with displacement
+ @Rn+
+ @-Rn
+ @aa[:size] absolute
+ #xx[:size] immediate data
+
+ */
+
+static void
+get_operand (ptr, op, ispage)
+ char **ptr;
+ h8500_operand_info *op;
+ char ispage;
+{
+ char *src = *ptr;
+ int mode;
+ unsigned int num;
+ unsigned int len;
+ op->page = 0;
+ if (src[0] == '(' && src[1] == 'r')
+ {
+ /* This is a register list */
+ *ptr = src + parse_reglist (src, op);
+ return;
+ }
+
+ len = parse_reg (src, &op->type, &op->reg);
+
+ if (len)
+ {
+ *ptr = src + len;
+ return;
+ }
+
+ if (*src == '@')
+ {
+ src++;
+ if (*src == '-')
+ {
+ src++;
+ len = parse_reg (src, &mode, &num);
+ if (len == 0)
+ {
+ /* Oops, not a reg after all, must be ordinary exp */
+ src--;
+ /* must be a symbol */
+ *ptr = skip_colonthing (exp_unsigned, src,
+ op, ABS16, ABS8, ABS16, ABS24);
+ return;
+ }
+
+ op->type = RNDEC;
+ op->reg = num;
+ *ptr = src + len;
+ return;
+ }
+ if (*src == '(')
+ {
+ /* Disp */
+ src++;
+
+ src = skip_colonthing (exp_signed, src,
+ op, RNIND_D16, RNIND_D8, RNIND_D16, 0);
+
+ if (*src != ',')
+ {
+ as_bad (_("expected @(exp, Rn)"));
+ return;
+ }
+ src++;
+ len = parse_reg (src, &mode, &op->reg);
+ if (len == 0 || mode != RN)
+ {
+ as_bad (_("expected @(exp, Rn)"));
+ return;
+ }
+ src += len;
+ if (*src != ')')
+ {
+ as_bad (_("expected @(exp, Rn)"));
+ return;
+ }
+ *ptr = src + 1;
+ return;
+ }
+ len = parse_reg (src, &mode, &num);
+
+ if (len)
+ {
+ src += len;
+ if (*src == '+')
+ {
+ src++;
+ if (mode != RN)
+ {
+ as_bad (_("@Rn+ needs word register"));
+ return;
+ }
+ op->type = RNINC;
+ op->reg = num;
+ *ptr = src;
+ return;
+ }
+ if (mode != RN)
+ {
+ as_bad (_("@Rn needs word register"));
+ return;
+ }
+ op->type = RNIND;
+ op->reg = num;
+ *ptr = src;
+ return;
+ }
+ else
+ {
+ /* must be a symbol */
+ *ptr =
+ skip_colonthing (exp_unsigned, src, op,
+ ispage ? ABS24 : ABS16, ABS8, ABS16, ABS24);
+ return;
+ }
+ }
+
+ if (*src == '#')
+ {
+ src++;
+ *ptr = skip_colonthing (exp_sandu, src, op, IMM16, IMM8, IMM16, ABS24);
+ return;
+ }
+ else
+ {
+ *ptr = skip_colonthing (exp_signed, src, op,
+ ispage ? ABS24 : PCREL8, PCREL8, PCREL16, ABS24);
+ }
+}
+
+static
+char *
+get_operands (info, args, operand)
+ h8500_opcode_info *info;
+ char *args;
+ h8500_operand_info *operand;
+
+{
+ char *ptr = args;
+
+ switch (info->nargs)
+ {
+ case 0:
+ operand[0].type = 0;
+ operand[1].type = 0;
+ break;
+
+ case 1:
+ ptr++;
+ get_operand (&ptr, operand + 0, info->name[0] == 'p');
+ operand[1].type = 0;
+ break;
+
+ case 2:
+ ptr++;
+ get_operand (&ptr, operand + 0, 0);
+ if (*ptr == ',')
+ ptr++;
+ get_operand (&ptr, operand + 1, 0);
+ break;
+
+ default:
+ abort ();
+ }
+
+ return ptr;
+}
+
+/* Passed a pointer to a list of opcodes which use different
+ addressing modes, return the opcode which matches the opcodes
+ provided
+ */
+
+int pcrel8; /* Set when we've seen a pcrel operand */
+
+static
+h8500_opcode_info *
+get_specific (opcode, operands)
+ h8500_opcode_info *opcode;
+ h8500_operand_info *operands;
+{
+ h8500_opcode_info *this_try = opcode;
+ int found = 0;
+ unsigned int noperands = opcode->nargs;
+
+ unsigned int this_index = opcode->idx;
+
+ while (this_index == opcode->idx && !found)
+ {
+ unsigned int i;
+
+ this_try = opcode++;
+
+ /* look at both operands needed by the opcodes and provided by
+ the user*/
+ for (i = 0; i < noperands; i++)
+ {
+ h8500_operand_info *user = operands + i;
+
+ switch (this_try->arg_type[i])
+ {
+ case FPIND_D8:
+ /* Opcode needs (disp:8,fp) */
+ if (user->type == RNIND_D8 && user->reg == 6)
+ {
+ displacement = user->exp;
+ continue;
+ }
+ break;
+ case RDIND_D16:
+ if (user->type == RNIND_D16)
+ {
+ displacement = user->exp;
+ rd = user->reg;
+ continue;
+ }
+ break;
+ case RDIND_D8:
+ if (user->type == RNIND_D8)
+ {
+ displacement = user->exp;
+ rd = user->reg;
+ continue;
+ }
+ break;
+ case RNIND_D16:
+ case RNIND_D8:
+ if (user->type == this_try->arg_type[i])
+ {
+ displacement = user->exp;
+ rn = user->reg;
+ continue;
+ }
+ break;
+
+ case SPDEC:
+ if (user->type == RNDEC && user->reg == 7)
+ {
+ continue;
+ }
+ break;
+ case SPINC:
+ if (user->type == RNINC && user->reg == 7)
+ {
+ continue;
+ }
+ break;
+ case ABS16:
+ if (user->type == ABS16)
+ {
+ absolute = user->exp;
+ continue;
+ }
+ break;
+ case ABS8:
+ if (user->type == ABS8)
+ {
+ absolute = user->exp;
+ continue;
+ }
+ break;
+ case ABS24:
+ if (user->type == ABS24)
+ {
+ absolute = user->exp;
+ continue;
+ }
+ break;
+
+ case CRB:
+ if ((user->type == CRB || user->type == CR) && user->reg != 0)
+ {
+ crb = user->reg;
+ continue;
+ }
+ break;
+ case CRW:
+ if ((user->type == CRW || user->type == CR) && user->reg == 0)
+ {
+ crw = user->reg;
+ continue;
+ }
+ break;
+ case DISP16:
+ if (user->type == DISP16)
+ {
+ displacement = user->exp;
+ continue;
+ }
+ break;
+ case DISP8:
+ if (user->type == DISP8)
+ {
+ displacement = user->exp;
+ continue;
+ }
+ break;
+ case FP:
+ if (user->type == RN && user->reg == 6)
+ {
+ continue;
+ }
+ break;
+ case PCREL16:
+ if (user->type == PCREL16)
+ {
+ displacement = user->exp;
+ continue;
+ }
+ break;
+ case PCREL8:
+ if (user->type == PCREL8)
+ {
+ displacement = user->exp;
+ pcrel8 = 1;
+ continue;
+ }
+ break;
+
+ case IMM16:
+ if (user->type == IMM16
+ || user->type == IMM8)
+ {
+ immediate_inpage = user->page;
+ immediate = user->exp;
+ continue;
+ }
+ break;
+ case RLIST:
+ case IMM8:
+ if (user->type == IMM8)
+ {
+ immediate_inpage = user->page;
+ immediate = user->exp;
+ continue;
+ }
+ break;
+ case IMM4:
+ if (user->type == IMM8)
+ {
+ immediate_inpage = user->page;
+ immediate = user->exp;
+ continue;
+ }
+ break;
+ case QIM:
+ if (user->type == IMM8
+ && user->exp.X_op == O_constant
+ &&
+ (user->exp.X_add_number == -2
+ || user->exp.X_add_number == -1
+ || user->exp.X_add_number == 1
+ || user->exp.X_add_number == 2))
+ {
+ immediate_inpage = user->page;
+ immediate = user->exp;
+ continue;
+ }
+ break;
+ case RD:
+ if (user->type == RN)
+ {
+ rd = user->reg;
+ continue;
+ }
+ break;
+ case RS:
+ if (user->type == RN)
+ {
+ rs = user->reg;
+ continue;
+ }
+ break;
+ case RDIND:
+ if (user->type == RNIND)
+ {
+ rd = user->reg;
+ continue;
+
+ }
+ break;
+ case RNINC:
+ case RNIND:
+ case RNDEC:
+ case RN:
+
+ if (user->type == this_try->arg_type[i])
+ {
+ rn = user->reg;
+ continue;
+ }
+ break;
+ case SP:
+ if (user->type == RN && user->reg == 7)
+ {
+ continue;
+ }
+ break;
+ default:
+ printf (_("unhandled %d\n"), this_try->arg_type[i]);
+ break;
+ }
+
+ /* If we get here this didn't work out */
+ goto fail;
+ }
+ found = 1;
+ fail:;
+
+ }
+
+ if (found)
+ return this_try;
+ else
+ return 0;
+}
+
+int
+check (operand, low, high)
+ expressionS *operand;
+ int low;
+ int high;
+{
+ if (operand->X_op != O_constant
+ || operand->X_add_number < low
+ || operand->X_add_number > high)
+ {
+ as_bad (_("operand must be absolute in range %d..%d"), low, high);
+ }
+ return operand->X_add_number;
+}
+
+static
+void
+insert (output, index, exp, reloc, pcrel)
+ char *output;
+ int index;
+ expressionS *exp;
+ int reloc;
+ int pcrel;
+{
+ fix_new_exp (frag_now,
+ output - frag_now->fr_literal + index,
+ 4, /* always say size is 4, but we know better */
+ exp,
+ pcrel,
+ reloc);
+}
+
+void
+build_relaxable_instruction (opcode, operand)
+ h8500_opcode_info *opcode;
+ h8500_operand_info *operand;
+{
+ /* All relaxable instructions start life as two bytes but can become
+ three bytes long if a lonely branch and up to 9 bytes if long scb
+ */
+ char *p;
+ int len;
+ int type;
+
+ if (opcode->bytes[0].contents == 0x01)
+ {
+ type = SCB_F;
+ }
+ else if (opcode->bytes[0].contents == 0x06
+ || opcode->bytes[0].contents == 0x07)
+ {
+ type = SCB_TST;
+ }
+ else
+ {
+ type = BRANCH;
+ }
+
+ p = frag_var (rs_machine_dependent,
+ md_relax_table[C (type, WORD_DISP)].rlx_length,
+ len = md_relax_table[C (type, BYTE_DISP)].rlx_length,
+ C (type, UNDEF_BYTE_DISP),
+ displacement.X_add_symbol,
+ displacement.X_add_number,
+ 0);
+
+ p[0] = opcode->bytes[0].contents;
+ if (type != BRANCH)
+ {
+ p[1] = opcode->bytes[1].contents | rs;
+ }
+}
+
+/* Now we know what sort of opcodes it is, lets build the bytes -
+ */
+static void
+build_bytes (opcode, operand)
+ h8500_opcode_info *opcode;
+ h8500_operand_info *operand;
+
+{
+ int index;
+
+ if (pcrel8)
+ {
+ pcrel8 = 0;
+ build_relaxable_instruction (opcode, operand);
+ }
+ else
+ {
+ char *output = frag_more (opcode->length);
+
+ memset (output, 0, opcode->length);
+ for (index = 0; index < opcode->length; index++)
+ {
+ output[index] = opcode->bytes[index].contents;
+
+ switch (opcode->bytes[index].insert)
+ {
+ default:
+ printf (_("failed for %d\n"), opcode->bytes[index].insert);
+ break;
+ case 0:
+ break;
+ case RN:
+ output[index] |= rn;
+ break;
+ case RD:
+ case RDIND:
+
+ output[index] |= rd;
+ break;
+ case RS:
+ output[index] |= rs;
+ break;
+ case DISP16:
+ insert (output, index, &displacement, R_H8500_IMM16, 0);
+ index++;
+ break;
+ case DISP8:
+ case FPIND_D8:
+ insert (output, index, &displacement, R_H8500_IMM8, 0);
+ break;
+
+ case IMM16:
+ {
+ int p;
+ switch (immediate_inpage) {
+ case 'p':
+ p = R_H8500_HIGH16;
+ break;
+ case 'h':
+ p = R_H8500_HIGH16;
+ break;
+ default:
+ p = R_H8500_IMM16;
+ break;
+ }
+
+ insert (output, index, &immediate,p, 0);
+ }
+
+ index++;
+ break;
+ case RLIST:
+ case IMM8:
+ if (immediate_inpage)
+ {
+ insert (output, index, &immediate, R_H8500_HIGH8, 0);
+ }
+ else
+ {
+ insert (output, index, &immediate, R_H8500_IMM8, 0);
+ }
+ break;
+ case PCREL16:
+ insert (output, index, &displacement, R_H8500_PCREL16, 1);
+ index++;
+ break;
+ case PCREL8:
+ insert (output, index, &displacement, R_H8500_PCREL8, 1);
+ break;
+ case IMM4:
+ output[index] |= check (&immediate, 0, 15);
+ break;
+ case CR:
+
+ output[index] |= cr;
+ if (cr == 0)
+ {
+ output[0] |= 0x8;
+ }
+ else
+ {
+ output[0] &= ~0x8;
+ }
+
+ break;
+
+ case CRB:
+ output[index] |= crb;
+ output[0] &= ~0x8;
+ break;
+ case CRW:
+ output[index] |= crw;
+ output[0] |= 0x8;
+ break;
+ case ABS24:
+ insert (output, index, &absolute, R_H8500_IMM24, 0);
+ index += 2;
+ break;
+ case ABS16:
+ insert (output, index, &absolute, R_H8500_IMM16, 0);
+ index++;
+ break;
+ case ABS8:
+ insert (output, index, &absolute, R_H8500_IMM8, 0);
+ break;
+ case QIM:
+ switch (immediate.X_add_number)
+ {
+ case -2:
+ output[index] |= 0x5;
+ break;
+ case -1:
+ output[index] |= 0x4;
+ break;
+ case 1:
+ output[index] |= 0;
+ break;
+ case 2:
+ output[index] |= 1;
+ break;
+ }
+ break;
+ }
+ }
+ }
+}
+
+/* This is the guts of the machine-dependent assembler. STR points to a
+ machine dependent instruction. This funciton is supposed to emit
+ the frags/bytes it assembles to.
+ */
+
+void
+DEFUN (md_assemble, (str),
+ char *str)
+{
+ char *op_start;
+ char *op_end;
+ h8500_operand_info operand[2];
+ h8500_opcode_info *opcode;
+ h8500_opcode_info *prev_opcode;
+ char name[11];
+
+ int nlen = 0;
+
+ /* Drop leading whitespace */
+ while (*str == ' ')
+ str++;
+
+ /* find the op code end */
+ for (op_start = op_end = str;
+ *op_end &&
+ !is_end_of_line[*op_end] && *op_end != ' ';
+ op_end++)
+ {
+ if ( /**op_end != '.'
+ && *op_end != ':'
+ && */ nlen < 10)
+ {
+ name[nlen++] = *op_end;
+ }
+ }
+ name[nlen] = 0;
+
+ if (op_end == op_start)
+ {
+ as_bad (_("can't find opcode "));
+ }
+
+ opcode = (h8500_opcode_info *) hash_find (opcode_hash_control, name);
+
+ if (opcode == NULL)
+ {
+ as_bad (_("unknown opcode"));
+ return;
+ }
+
+ get_operands (opcode, op_end, operand);
+ prev_opcode = opcode;
+
+ opcode = get_specific (opcode, operand);
+
+ if (opcode == 0)
+ {
+ /* Couldn't find an opcode which matched the operands */
+ char *where = frag_more (2);
+
+ where[0] = 0x0;
+ where[1] = 0x0;
+ as_bad (_("invalid operands for opcode"));
+ return;
+ }
+
+ build_bytes (opcode, operand);
+
+}
+
+void
+DEFUN (tc_crawl_symbol_chain, (headers),
+ object_headers * headers)
+{
+ printf (_("call to tc_crawl_symbol_chain \n"));
+}
+
+symbolS *
+DEFUN (md_undefined_symbol, (name),
+ char *name)
+{
+ return 0;
+}
+
+void
+DEFUN (tc_headers_hook, (headers),
+ object_headers * headers)
+{
+ printf (_("call to tc_headers_hook \n"));
+}
+
+/* Various routines to kill one day */
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+/* Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP . An error message is returned, or NULL on OK.
+ */
+char *
+md_atof (type, litP, sizeP)
+ char type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+ char *atof_ieee ();
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("Bad call to MD_ATOF()");
+ }
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+
+ *sizeP = prec * sizeof (LITTLENUM_TYPE);
+ for (wordP = words; prec--;)
+ {
+ md_number_to_chars (litP, (long) (*wordP++), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ return 0;
+}
+
+CONST char *md_shortopts = "";
+struct option md_longopts[] = {
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ return 0;
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+}
+
+void
+tc_aout_fix_to_chars ()
+{
+ printf (_("call to tc_aout_fix_to_chars \n"));
+ abort ();
+}
+
+static
+void
+wordify_scb (buffer, disp_size, inst_size)
+ char *buffer;
+ int *disp_size;
+ int *inst_size;
+{
+ int rn = buffer[1] & 0x7;
+
+ switch (buffer[0])
+ {
+ case 0x0e: /* BSR */
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2a:
+ case 0x2b:
+ case 0x2c:
+ case 0x2d:
+ case 0x2e:
+ case 0x2f:
+ buffer[0] |= 0x10;
+ buffer[1] = 0;
+ buffer[2] = 0;
+ *disp_size = 2;
+ *inst_size = 1;
+ return;
+ default:
+ abort ();
+
+ case 0x01:
+ *inst_size = 6;
+ *disp_size = 2;
+ break;
+ case 0x06:
+ *inst_size = 8;
+ *disp_size = 2;
+
+ *buffer++ = 0x26; /* bne + 8 */
+ *buffer++ = 0x08;
+ break;
+ case 0x07:
+ *inst_size = 8;
+ *disp_size = 2;
+ *buffer++ = 0x27; /* bne + 8 */
+ *buffer++ = 0x08;
+ break;
+
+ }
+ *buffer++ = 0xa8 | rn; /* addq -1,rn */
+ *buffer++ = 0x0c;
+ *buffer++ = 0x04; /* cmp #0xff:8, rn */
+ *buffer++ = 0xff;
+ *buffer++ = 0x70 | rn;
+ *buffer++ = 0x36; /* bne ... */
+ *buffer++ = 0;
+ *buffer++ = 0;
+}
+
+/*
+called after relaxing, change the frags so they know how big they are
+*/
+void
+md_convert_frag (headers, seg, fragP)
+ object_headers *headers;
+ segT seg;
+ fragS *fragP;
+{
+ int disp_size = 0;
+ int inst_size = 0;
+ char *buffer = fragP->fr_fix + fragP->fr_literal;
+
+ switch (fragP->fr_subtype)
+ {
+ case C (BRANCH, BYTE_DISP):
+ disp_size = 1;
+ inst_size = 1;
+ break;
+
+ case C (SCB_F, BYTE_DISP):
+ case C (SCB_TST, BYTE_DISP):
+ disp_size = 1;
+ inst_size = 2;
+ break;
+
+ /* Branches to a known 16 bit displacement */
+
+ /* Turn on the 16bit bit */
+ case C (BRANCH, WORD_DISP):
+ case C (SCB_F, WORD_DISP):
+ case C (SCB_TST, WORD_DISP):
+ wordify_scb (buffer, &disp_size, &inst_size);
+ break;
+
+ case C (BRANCH, UNDEF_WORD_DISP):
+ case C (SCB_F, UNDEF_WORD_DISP):
+ case C (SCB_TST, UNDEF_WORD_DISP):
+ /* This tried to be relaxed, but didn't manage it, it now needs a
+ fix */
+ wordify_scb (buffer, &disp_size, &inst_size);
+
+ /* Make a reloc */
+ fix_new (fragP,
+ fragP->fr_fix + inst_size,
+ 4,
+ fragP->fr_symbol,
+ fragP->fr_offset,
+ 0,
+ R_H8500_PCREL16);
+
+ fragP->fr_fix += disp_size + inst_size;
+ fragP->fr_var = 0;
+ return;
+ break;
+ default:
+ abort ();
+ }
+ if (inst_size)
+ {
+ /* Get the address of the end of the instruction */
+ int next_inst = fragP->fr_fix + fragP->fr_address + disp_size + inst_size;
+ int targ_addr = (S_GET_VALUE (fragP->fr_symbol) +
+ fragP->fr_offset);
+ int disp = targ_addr - next_inst;
+
+ md_number_to_chars (buffer + inst_size, disp, disp_size);
+ fragP->fr_fix += disp_size + inst_size;
+ fragP->fr_var = 0;
+ }
+}
+
+valueT
+md_section_align (seg, size)
+ segT seg ;
+ valueT size;
+{
+ return ((size + (1 << section_alignment[(int) seg]) - 1)
+ & (-1 << section_alignment[(int) seg]));
+
+}
+
+void
+md_apply_fix (fixP, val)
+ fixS *fixP;
+ long val;
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ if (fixP->fx_r_type == 0)
+ {
+ fixP->fx_r_type = fixP->fx_size == 4 ? R_H8500_IMM32 : R_H8500_IMM16;
+ }
+
+ switch (fixP->fx_r_type)
+ {
+
+ case R_H8500_IMM8:
+ case R_H8500_PCREL8:
+ *buf++ = val;
+ break;
+ case R_H8500_IMM16:
+ case R_H8500_LOW16:
+ case R_H8500_PCREL16:
+ *buf++ = (val >> 8);
+ *buf++ = val;
+ break;
+ case R_H8500_HIGH8:
+ *buf++ = val >> 16;
+ break;
+ case R_H8500_HIGH16:
+ *buf++ = val >> 24;
+ *buf++ = val >> 16;
+ break;
+ case R_H8500_IMM24:
+ *buf++ = (val >> 16);
+ *buf++ = (val >> 8);
+ *buf++ = val;
+ break;
+ case R_H8500_IMM32:
+ *buf++ = (val >> 24);
+ *buf++ = (val >> 16);
+ *buf++ = (val >> 8);
+ *buf++ = val;
+ break;
+ default:
+ abort ();
+
+ }
+}
+
+/*
+called just before address relaxation, return the length
+by which a fragment must grow to reach it's destination
+*/
+int
+md_estimate_size_before_relax (fragP, segment_type)
+ register fragS *fragP;
+ register segT segment_type;
+{
+ int what = GET_WHAT (fragP->fr_subtype);
+
+ switch (fragP->fr_subtype)
+ {
+ default:
+ abort ();
+ case C (BRANCH, UNDEF_BYTE_DISP):
+ case C (SCB_F, UNDEF_BYTE_DISP):
+ case C (SCB_TST, UNDEF_BYTE_DISP):
+ /* used to be a branch to somewhere which was unknown */
+ if (S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
+ {
+ /* Got a symbol and it's defined in this segment, become byte
+ sized - maybe it will fix up */
+ fragP->fr_subtype = C (what, BYTE_DISP);
+ fragP->fr_var = md_relax_table[C (what, BYTE_DISP)].rlx_length;
+ }
+ else
+ {
+ /* Its got a segment, but its not ours, so it will always be long */
+ fragP->fr_subtype = C (what, UNDEF_WORD_DISP);
+ fragP->fr_var = md_relax_table[C (what, WORD_DISP)].rlx_length;
+ return md_relax_table[C (what, WORD_DISP)].rlx_length;
+ }
+ }
+ return fragP->fr_var;
+}
+
+/* Put number into target byte order */
+
+void
+md_number_to_chars (ptr, use, nbytes)
+ char *ptr;
+ valueT use;
+ int nbytes;
+{
+ number_to_chars_bigendian (ptr, use, nbytes);
+}
+
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+/*ARGSUSED*/
+void
+tc_coff_symbol_emit_hook (ignore)
+ symbolS *ignore;
+{
+}
+
+short
+tc_coff_fix2rtype (fix_ptr)
+ fixS *fix_ptr;
+{
+ if (fix_ptr->fx_r_type == RELOC_32)
+ {
+ /* cons likes to create reloc32's whatever the size of the reloc..
+ */
+ switch (fix_ptr->fx_size)
+ {
+ case 2:
+ return R_H8500_IMM16;
+ break;
+ case 1:
+ return R_H8500_IMM8;
+ break;
+ default:
+ abort ();
+ }
+ }
+ return fix_ptr->fx_r_type;
+}
+
+void
+tc_reloc_mangle (fix_ptr, intr, base)
+ fixS *fix_ptr;
+ struct internal_reloc *intr;
+ bfd_vma base;
+
+{
+ symbolS *symbol_ptr;
+
+ symbol_ptr = fix_ptr->fx_addsy;
+
+ /* If this relocation is attached to a symbol then it's ok
+ to output it */
+ if (fix_ptr->fx_r_type == RELOC_32)
+ {
+ /* cons likes to create reloc32's whatever the size of the reloc..
+ */
+ switch (fix_ptr->fx_size)
+ {
+ case 2:
+ intr->r_type = R_IMM16;
+ break;
+ case 1:
+ intr->r_type = R_IMM8;
+ break;
+ default:
+ abort ();
+ }
+ }
+ else
+ {
+ intr->r_type = fix_ptr->fx_r_type;
+ }
+
+ intr->r_vaddr = fix_ptr->fx_frag->fr_address + fix_ptr->fx_where + base;
+ intr->r_offset = fix_ptr->fx_offset;
+
+ /* Turn the segment of the symbol into an offset. */
+ if (symbol_ptr)
+ {
+ symbolS *dot;
+
+ dot = segment_info[S_GET_SEGMENT (symbol_ptr)].dot;
+ if (dot)
+ {
+ /* intr->r_offset -=
+ segment_info[S_GET_SEGMENT(symbol_ptr)].scnhdr.s_paddr;*/
+ intr->r_offset += S_GET_VALUE (symbol_ptr);
+ intr->r_symndx = dot->sy_number;
+ }
+ else
+ {
+ intr->r_symndx = symbol_ptr->sy_number;
+ }
+
+ }
+ else
+ {
+ intr->r_symndx = -1;
+ }
+
+}
+
+
+
+int
+start_label (ptr)
+ char *ptr;
+{
+ /* Check for :s.w */
+ if (isalpha (ptr[1]) && ptr[2] == '.')
+ return 0;
+ /* Check for :s */
+ if (isalpha (ptr[1]) && !isalpha (ptr[2]))
+ return 0;
+ return 1;
+}
+
+
+int
+tc_coff_sizemachdep (frag)
+ fragS *frag;
+{
+ return md_relax_table[frag->fr_subtype].rlx_length;
+}
+
+/* end of tc-h8500.c */
+
diff --git a/gas/config/tc-h8500.h b/gas/config/tc-h8500.h
new file mode 100644
index 0000000..2a53ec3
--- /dev/null
+++ b/gas/config/tc-h8500.h
@@ -0,0 +1,57 @@
+/* This file is tc-h8500.h
+ Copyright (C) 1993, 95, 97, 1998 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+
+#define TC_H8500
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+#if ANSI_PROTOTYPES
+struct internal_reloc;
+#endif
+
+#define WORKING_DOT_WORD
+
+/* This macro translates between an internal fix and an coff reloc type */
+#define TC_COFF_FIX2RTYPE(fixP) tc_coff_fix2rtype(fixP)
+
+#define BFD_ARCH bfd_arch_h8500
+#define COFF_MAGIC 0x8500
+#define TC_COUNT_RELOC(x) ((x)->fx_addsy||(x)->fx_subsy)
+#define IGNORE_NONSTANDARD_ESCAPES
+
+#define TC_RELOC_MANGLE(s,a,b,c) tc_reloc_mangle(a,b,c)
+extern void tc_reloc_mangle
+ PARAMS ((struct fix *, struct internal_reloc *, bfd_vma));
+
+#define DO_NOT_STRIP 0
+#define LISTING_HEADER "Hitachi H8/500 GAS "
+#define NEED_FX_R_TYPE 1
+#define RELOC_32 1234
+
+#define TC_START_LABEL(ch, ptr) (ch == ':' && start_label(ptr))
+#define TC_COFF_SIZEMACHDEP(frag) tc_coff_sizemachdep(frag)
+
+#define md_operand(x)
+
+extern struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+/* end of tc-h8500.h */
diff --git a/gas/config/tc-hppa.c b/gas/config/tc-hppa.c
new file mode 100644
index 0000000..8785de8
--- /dev/null
+++ b/gas/config/tc-hppa.c
@@ -0,0 +1,6716 @@
+/* tc-hppa.c -- Assemble for the PA
+ Copyright (C) 1989, 93, 94, 95, 96, 97, 1998 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+
+/* HP PA-RISC support was contributed by the Center for Software Science
+ at the University of Utah. */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "as.h"
+#include "subsegs.h"
+
+#include "bfd/libhppa.h"
+#include "bfd/libbfd.h"
+
+/* Be careful, this file includes data *declarations*. */
+#include "opcode/hppa.h"
+
+/* A "convient" place to put object file dependencies which do
+ not need to be seen outside of tc-hppa.c. */
+#ifdef OBJ_ELF
+/* Names of various debugging spaces/subspaces. */
+#define GDB_DEBUG_SPACE_NAME ".stab"
+#define GDB_STRINGS_SUBSPACE_NAME ".stabstr"
+#define GDB_SYMBOLS_SUBSPACE_NAME ".stab"
+#define UNWIND_SECTION_NAME ".PARISC.unwind"
+/* Nonzero if CODE is a fixup code needing further processing. */
+
+/* Object file formats specify relocation types. */
+typedef elf32_hppa_reloc_type reloc_type;
+
+/* Object file formats specify BFD symbol types. */
+typedef elf_symbol_type obj_symbol_type;
+
+/* How to generate a relocation. */
+#define hppa_gen_reloc_type hppa_elf_gen_reloc_type
+
+/* ELF objects can have versions, but apparently do not have anywhere
+ to store a copyright string. */
+#define obj_version obj_elf_version
+#define obj_copyright obj_elf_version
+
+/* Use space aliases. */
+#define USE_ALIASES 1
+#endif
+
+#ifdef OBJ_SOM
+/* Names of various debugging spaces/subspaces. */
+#define GDB_DEBUG_SPACE_NAME "$GDB_DEBUG$"
+#define GDB_STRINGS_SUBSPACE_NAME "$GDB_STRINGS$"
+#define GDB_SYMBOLS_SUBSPACE_NAME "$GDB_SYMBOLS$"
+#define UNWIND_SECTION_NAME "$UNWIND$"
+
+/* Object file formats specify relocation types. */
+typedef int reloc_type;
+
+/* SOM objects can have both a version string and a copyright string. */
+#define obj_version obj_som_version
+#define obj_copyright obj_som_copyright
+
+/* Do not use space aliases. */
+#define USE_ALIASES 0
+
+/* How to generate a relocation. */
+#define hppa_gen_reloc_type hppa_som_gen_reloc_type
+
+/* Object file formats specify BFD symbol types. */
+typedef som_symbol_type obj_symbol_type;
+
+/* This apparently isn't in older versions of hpux reloc.h. */
+#ifndef R_DLT_REL
+#define R_DLT_REL 0x78
+#endif
+#endif
+
+#ifndef R_N0SEL
+#define R_N0SEL 0xd8
+#endif
+
+#ifndef R_N1SEL
+#define R_N1SEL 0xd9
+#endif
+
+/* Various structures and types used internally in tc-hppa.c. */
+
+/* Unwind table and descriptor. FIXME: Sync this with GDB version. */
+
+struct unwind_desc
+ {
+ unsigned int cannot_unwind:1;
+ unsigned int millicode:1;
+ unsigned int millicode_save_rest:1;
+ unsigned int region_desc:2;
+ unsigned int save_sr:2;
+ unsigned int entry_fr:4;
+ unsigned int entry_gr:5;
+ unsigned int args_stored:1;
+ unsigned int call_fr:5;
+ unsigned int call_gr:5;
+ unsigned int save_sp:1;
+ unsigned int save_rp:1;
+ unsigned int save_rp_in_frame:1;
+ unsigned int extn_ptr_defined:1;
+ unsigned int cleanup_defined:1;
+
+ unsigned int hpe_interrupt_marker:1;
+ unsigned int hpux_interrupt_marker:1;
+ unsigned int reserved:3;
+ unsigned int frame_size:27;
+ };
+
+struct unwind_table
+ {
+ /* Starting and ending offsets of the region described by
+ descriptor. */
+ unsigned int start_offset;
+ unsigned int end_offset;
+ struct unwind_desc descriptor;
+ };
+
+/* This structure is used by the .callinfo, .enter, .leave pseudo-ops to
+ control the entry and exit code they generate. It is also used in
+ creation of the correct stack unwind descriptors.
+
+ NOTE: GAS does not support .enter and .leave for the generation of
+ prologues and epilogues. FIXME.
+
+ The fields in structure roughly correspond to the arguments available on the
+ .callinfo pseudo-op. */
+
+struct call_info
+ {
+ /* The unwind descriptor being built. */
+ struct unwind_table ci_unwind;
+
+ /* Name of this function. */
+ symbolS *start_symbol;
+
+ /* (temporary) symbol used to mark the end of this function. */
+ symbolS *end_symbol;
+
+ /* Next entry in the chain. */
+ struct call_info *ci_next;
+ };
+
+/* Operand formats for FP instructions. Note not all FP instructions
+ allow all four formats to be used (for example fmpysub only allows
+ SGL and DBL). */
+typedef enum
+ {
+ SGL, DBL, ILLEGAL_FMT, QUAD, W, UW, DW, UDW, QW, UQW
+ }
+fp_operand_format;
+
+/* This fully describes the symbol types which may be attached to
+ an EXPORT or IMPORT directive. Only SOM uses this formation
+ (ELF has no need for it). */
+typedef enum
+ {
+ SYMBOL_TYPE_UNKNOWN,
+ SYMBOL_TYPE_ABSOLUTE,
+ SYMBOL_TYPE_CODE,
+ SYMBOL_TYPE_DATA,
+ SYMBOL_TYPE_ENTRY,
+ SYMBOL_TYPE_MILLICODE,
+ SYMBOL_TYPE_PLABEL,
+ SYMBOL_TYPE_PRI_PROG,
+ SYMBOL_TYPE_SEC_PROG,
+ }
+pa_symbol_type;
+
+/* This structure contains information needed to assemble
+ individual instructions. */
+struct pa_it
+ {
+ /* Holds the opcode after parsing by pa_ip. */
+ unsigned long opcode;
+
+ /* Holds an expression associated with the current instruction. */
+ expressionS exp;
+
+ /* Does this instruction use PC-relative addressing. */
+ int pcrel;
+
+ /* Floating point formats for operand1 and operand2. */
+ fp_operand_format fpof1;
+ fp_operand_format fpof2;
+
+
+ /* Holds the field selector for this instruction
+ (for example L%, LR%, etc). */
+ long field_selector;
+
+ /* Holds any argument relocation bits associated with this
+ instruction. (instruction should be some sort of call). */
+ long arg_reloc;
+
+ /* The format specification for this instruction. */
+ int format;
+
+ /* The relocation (if any) associated with this instruction. */
+ reloc_type reloc;
+ };
+
+/* PA-89 floating point registers are arranged like this:
+
+
+ +--------------+--------------+
+ | 0 or 16L | 16 or 16R |
+ +--------------+--------------+
+ | 1 or 17L | 17 or 17R |
+ +--------------+--------------+
+ | | |
+
+ . . .
+ . . .
+ . . .
+
+ | | |
+ +--------------+--------------+
+ | 14 or 30L | 30 or 30R |
+ +--------------+--------------+
+ | 15 or 31L | 31 or 31R |
+ +--------------+--------------+
+
+
+ The following is a version of pa_parse_number that
+ handles the L/R notation and returns the correct
+ value to put into the instruction register field.
+ The correct value to put into the instruction is
+ encoded in the structure 'pa_11_fp_reg_struct'. */
+
+struct pa_11_fp_reg_struct
+ {
+ /* The register number. */
+ char number_part;
+
+ /* L/R selector. */
+ char l_r_select;
+ };
+
+/* Additional information needed to build argument relocation stubs. */
+struct call_desc
+ {
+ /* The argument relocation specification. */
+ unsigned int arg_reloc;
+
+ /* Number of arguments. */
+ unsigned int arg_count;
+ };
+
+/* This structure defines an entry in the subspace dictionary
+ chain. */
+
+struct subspace_dictionary_chain
+ {
+ /* Nonzero if this space has been defined by the user code. */
+ unsigned int ssd_defined;
+
+ /* Name of this subspace. */
+ char *ssd_name;
+
+ /* GAS segment and subsegment associated with this subspace. */
+ asection *ssd_seg;
+ int ssd_subseg;
+
+ /* Next space in the subspace dictionary chain. */
+ struct subspace_dictionary_chain *ssd_next;
+ };
+
+typedef struct subspace_dictionary_chain ssd_chain_struct;
+
+/* This structure defines an entry in the subspace dictionary
+ chain. */
+
+struct space_dictionary_chain
+ {
+ /* Nonzero if this space has been defined by the user code or
+ as a default space. */
+ unsigned int sd_defined;
+
+ /* Nonzero if this spaces has been defined by the user code. */
+ unsigned int sd_user_defined;
+
+ /* The space number (or index). */
+ unsigned int sd_spnum;
+
+ /* The name of this subspace. */
+ char *sd_name;
+
+ /* GAS segment to which this subspace corresponds. */
+ asection *sd_seg;
+
+ /* Current subsegment number being used. */
+ int sd_last_subseg;
+
+ /* The chain of subspaces contained within this space. */
+ ssd_chain_struct *sd_subspaces;
+
+ /* The next entry in the space dictionary chain. */
+ struct space_dictionary_chain *sd_next;
+ };
+
+typedef struct space_dictionary_chain sd_chain_struct;
+
+/* Structure for previous label tracking. Needed so that alignments,
+ callinfo declarations, etc can be easily attached to a particular
+ label. */
+typedef struct label_symbol_struct
+ {
+ struct symbol *lss_label;
+ sd_chain_struct *lss_space;
+ struct label_symbol_struct *lss_next;
+ }
+label_symbol_struct;
+
+/* This structure defines attributes of the default subspace
+ dictionary entries. */
+
+struct default_subspace_dict
+ {
+ /* Name of the subspace. */
+ char *name;
+
+ /* FIXME. Is this still needed? */
+ char defined;
+
+ /* Nonzero if this subspace is loadable. */
+ char loadable;
+
+ /* Nonzero if this subspace contains only code. */
+ char code_only;
+
+ /* Nonzero if this is a common subspace. */
+ char common;
+
+ /* Nonzero if this is a common subspace which allows symbols
+ to be multiply defined. */
+ char dup_common;
+
+ /* Nonzero if this subspace should be zero filled. */
+ char zero;
+
+ /* Sort key for this subspace. */
+ unsigned char sort;
+
+ /* Access control bits for this subspace. Can represent RWX access
+ as well as privilege level changes for gateways. */
+ int access;
+
+ /* Index of containing space. */
+ int space_index;
+
+ /* Alignment (in bytes) of this subspace. */
+ int alignment;
+
+ /* Quadrant within space where this subspace should be loaded. */
+ int quadrant;
+
+ /* An index into the default spaces array. */
+ int def_space_index;
+
+ /* An alias for this section (or NULL if no alias exists). */
+ char *alias;
+
+ /* Subsegment associated with this subspace. */
+ subsegT subsegment;
+ };
+
+/* This structure defines attributes of the default space
+ dictionary entries. */
+
+struct default_space_dict
+ {
+ /* Name of the space. */
+ char *name;
+
+ /* Space number. It is possible to identify spaces within
+ assembly code numerically! */
+ int spnum;
+
+ /* Nonzero if this space is loadable. */
+ char loadable;
+
+ /* Nonzero if this space is "defined". FIXME is still needed */
+ char defined;
+
+ /* Nonzero if this space can not be shared. */
+ char private;
+
+ /* Sort key for this space. */
+ unsigned char sort;
+
+ /* Segment associated with this space. */
+ asection *segment;
+
+ /* An alias for this section (or NULL if no alias exists). */
+ char *alias;
+ };
+
+/* Extra information needed to perform fixups (relocations) on the PA. */
+struct hppa_fix_struct
+ {
+ /* The field selector. */
+ enum hppa_reloc_field_selector_type_alt fx_r_field;
+
+ /* Type of fixup. */
+ int fx_r_type;
+
+ /* Format of fixup. */
+ int fx_r_format;
+
+ /* Argument relocation bits. */
+ long fx_arg_reloc;
+
+ /* The segment this fixup appears in. */
+ segT segment;
+ };
+
+/* Structure to hold information about predefined registers. */
+
+struct pd_reg
+ {
+ char *name;
+ int value;
+ };
+
+/* This structure defines the mapping from a FP condition string
+ to a condition number which can be recorded in an instruction. */
+struct fp_cond_map
+ {
+ char *string;
+ int cond;
+ };
+
+/* This structure defines a mapping from a field selector
+ string to a field selector type. */
+struct selector_entry
+ {
+ char *prefix;
+ int field_selector;
+ };
+
+/* Prototypes for functions local to tc-hppa.c. */
+
+static void pa_check_current_space_and_subspace PARAMS ((void));
+static fp_operand_format pa_parse_fp_format PARAMS ((char **s));
+static void pa_cons PARAMS ((int));
+static void pa_data PARAMS ((int));
+static void pa_float_cons PARAMS ((int));
+static void pa_fill PARAMS ((int));
+static void pa_lcomm PARAMS ((int));
+static void pa_lsym PARAMS ((int));
+static void pa_stringer PARAMS ((int));
+static void pa_text PARAMS ((int));
+static void pa_version PARAMS ((int));
+static int pa_parse_fp_cmp_cond PARAMS ((char **));
+static int get_expression PARAMS ((char *));
+static int pa_get_absolute_expression PARAMS ((struct pa_it *, char **));
+static int evaluate_absolute PARAMS ((struct pa_it *));
+static unsigned int pa_build_arg_reloc PARAMS ((char *));
+static unsigned int pa_align_arg_reloc PARAMS ((unsigned int, unsigned int));
+static int pa_parse_nullif PARAMS ((char **));
+static int pa_parse_nonneg_cmpsub_cmpltr PARAMS ((char **, int));
+static int pa_parse_neg_cmpsub_cmpltr PARAMS ((char **, int));
+static int pa_parse_neg_add_cmpltr PARAMS ((char **, int));
+static int pa_parse_nonneg_add_cmpltr PARAMS ((char **, int));
+static void pa_align PARAMS ((int));
+static void pa_block PARAMS ((int));
+static void pa_brtab PARAMS ((int));
+static void pa_try PARAMS ((int));
+static void pa_call PARAMS ((int));
+static void pa_call_args PARAMS ((struct call_desc *));
+static void pa_callinfo PARAMS ((int));
+static void pa_code PARAMS ((int));
+static void pa_comm PARAMS ((int));
+#ifdef OBJ_SOM
+static void pa_compiler PARAMS ((int));
+#endif
+static void pa_copyright PARAMS ((int));
+static void pa_end PARAMS ((int));
+static void pa_enter PARAMS ((int));
+static void pa_entry PARAMS ((int));
+static void pa_equ PARAMS ((int));
+static void pa_exit PARAMS ((int));
+static void pa_export PARAMS ((int));
+static void pa_type_args PARAMS ((symbolS *, int));
+static void pa_import PARAMS ((int));
+static void pa_label PARAMS ((int));
+static void pa_leave PARAMS ((int));
+static void pa_level PARAMS ((int));
+static void pa_origin PARAMS ((int));
+static void pa_proc PARAMS ((int));
+static void pa_procend PARAMS ((int));
+static void pa_space PARAMS ((int));
+static void pa_spnum PARAMS ((int));
+static void pa_subspace PARAMS ((int));
+static void pa_param PARAMS ((int));
+static void pa_undefine_label PARAMS ((void));
+static int need_pa11_opcode PARAMS ((struct pa_it *,
+ struct pa_11_fp_reg_struct *));
+static int pa_parse_number PARAMS ((char **, struct pa_11_fp_reg_struct *));
+static label_symbol_struct *pa_get_label PARAMS ((void));
+static sd_chain_struct *create_new_space PARAMS ((char *, int, int,
+ int, int, int,
+ asection *, int));
+static ssd_chain_struct *create_new_subspace PARAMS ((sd_chain_struct *,
+ char *, int, int,
+ int, int, int,
+ int, int, int, int,
+ int, asection *));
+static ssd_chain_struct *update_subspace PARAMS ((sd_chain_struct *,
+ char *, int, int, int,
+ int, int, int, int,
+ int, int, int,
+ asection *));
+static sd_chain_struct *is_defined_space PARAMS ((char *));
+static ssd_chain_struct *is_defined_subspace PARAMS ((char *));
+static sd_chain_struct *pa_segment_to_space PARAMS ((asection *));
+static ssd_chain_struct *pa_subsegment_to_subspace PARAMS ((asection *,
+ subsegT));
+static sd_chain_struct *pa_find_space_by_number PARAMS ((int));
+static unsigned int pa_subspace_start PARAMS ((sd_chain_struct *, int));
+static void pa_ip PARAMS ((char *));
+static void fix_new_hppa PARAMS ((fragS *, int, int, symbolS *,
+ long, expressionS *, int,
+ bfd_reloc_code_real_type,
+ enum hppa_reloc_field_selector_type_alt,
+ int, long, int *));
+static int is_end_of_statement PARAMS ((void));
+static int reg_name_search PARAMS ((char *));
+static int pa_chk_field_selector PARAMS ((char **));
+static int is_same_frag PARAMS ((fragS *, fragS *));
+static void process_exit PARAMS ((void));
+static sd_chain_struct *pa_parse_space_stmt PARAMS ((char *, int));
+static int log2 PARAMS ((int));
+static int pa_next_subseg PARAMS ((sd_chain_struct *));
+static unsigned int pa_stringer_aux PARAMS ((char *));
+static void pa_spaces_begin PARAMS ((void));
+
+#ifdef OBJ_ELF
+static void hppa_elf_mark_end_of_function PARAMS ((void));
+static void pa_build_unwind_subspace PARAMS ((struct call_info *));
+#endif
+
+/* File and gloally scoped variable declarations. */
+
+/* Root and final entry in the space chain. */
+static sd_chain_struct *space_dict_root;
+static sd_chain_struct *space_dict_last;
+
+/* The current space and subspace. */
+static sd_chain_struct *current_space;
+static ssd_chain_struct *current_subspace;
+
+/* Root of the call_info chain. */
+static struct call_info *call_info_root;
+
+/* The last call_info (for functions) structure
+ seen so it can be associated with fixups and
+ function labels. */
+static struct call_info *last_call_info;
+
+/* The last call description (for actual calls). */
+static struct call_desc last_call_desc;
+
+/* handle of the OPCODE hash table */
+static struct hash_control *op_hash = NULL;
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful. */
+const char comment_chars[] = ";";
+
+/* Table of pseudo ops for the PA. FIXME -- how many of these
+ are now redundant with the overall GAS and the object file
+ dependent tables? */
+const pseudo_typeS md_pseudo_table[] =
+{
+ /* align pseudo-ops on the PA specify the actual alignment requested,
+ not the log2 of the requested alignment. */
+ {"align", pa_align, 8},
+ {"begin_brtab", pa_brtab, 1},
+ {"begin_try", pa_try, 1},
+ {"block", pa_block, 1},
+ {"blockz", pa_block, 0},
+ {"byte", pa_cons, 1},
+ {"call", pa_call, 0},
+ {"callinfo", pa_callinfo, 0},
+ {"code", pa_code, 0},
+ {"comm", pa_comm, 0},
+#ifdef OBJ_SOM
+ {"compiler", pa_compiler, 0},
+#endif
+ {"copyright", pa_copyright, 0},
+ {"data", pa_data, 0},
+ {"double", pa_float_cons, 'd'},
+ {"end", pa_end, 0},
+ {"end_brtab", pa_brtab, 0},
+ {"end_try", pa_try, 0},
+ {"enter", pa_enter, 0},
+ {"entry", pa_entry, 0},
+ {"equ", pa_equ, 0},
+ {"exit", pa_exit, 0},
+ {"export", pa_export, 0},
+ {"fill", pa_fill, 0},
+ {"float", pa_float_cons, 'f'},
+ {"half", pa_cons, 2},
+ {"import", pa_import, 0},
+ {"int", pa_cons, 4},
+ {"label", pa_label, 0},
+ {"lcomm", pa_lcomm, 0},
+ {"leave", pa_leave, 0},
+ {"level", pa_level, 0},
+ {"long", pa_cons, 4},
+ {"lsym", pa_lsym, 0},
+ {"nsubspa", pa_subspace, 1},
+ {"octa", pa_cons, 16},
+ {"org", pa_origin, 0},
+ {"origin", pa_origin, 0},
+ {"param", pa_param, 0},
+ {"proc", pa_proc, 0},
+ {"procend", pa_procend, 0},
+ {"quad", pa_cons, 8},
+ {"reg", pa_equ, 1},
+ {"short", pa_cons, 2},
+ {"single", pa_float_cons, 'f'},
+ {"space", pa_space, 0},
+ {"spnum", pa_spnum, 0},
+ {"string", pa_stringer, 0},
+ {"stringz", pa_stringer, 1},
+ {"subspa", pa_subspace, 0},
+ {"text", pa_text, 0},
+ {"version", pa_version, 0},
+ {"word", pa_cons, 4},
+ {NULL, 0, 0}
+};
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output.
+
+ Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output.
+
+ Also note that C style comments will always work. */
+const char line_comment_chars[] = "#";
+
+/* This array holds the characters which act as line separators. */
+const char line_separator_chars[] = "!";
+
+/* Chars that can be used to separate mant from exp in floating point nums. */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant.
+ As in 0f12.456 or 0d1.2345e12.
+
+ Be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c. Ideally it shouldn't hae to know abou it at
+ all, but nothing is ideal around here. */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+static struct pa_it the_insn;
+
+/* Points to the end of an expression just parsed by get_expressoin
+ and friends. FIXME. This shouldn't be handled with a file-global
+ variable. */
+static char *expr_end;
+
+/* Nonzero if a .callinfo appeared within the current procedure. */
+static int callinfo_found;
+
+/* Nonzero if the assembler is currently within a .entry/.exit pair. */
+static int within_entry_exit;
+
+/* Nonzero if the assembler is currently within a procedure definition. */
+static int within_procedure;
+
+/* Handle on strucutre which keep track of the last symbol
+ seen in each subspace. */
+static label_symbol_struct *label_symbols_rootp = NULL;
+
+/* Holds the last field selector. */
+static int hppa_field_selector;
+
+
+/* A dummy bfd symbol so that all relocations have symbols of some kind. */
+static symbolS *dummy_symbol;
+
+/* Nonzero if errors are to be printed. */
+static int print_errors = 1;
+
+/* List of registers that are pre-defined:
+
+ Each general register has one predefined name of the form
+ %r<REGNUM> which has the value <REGNUM>.
+
+ Space and control registers are handled in a similar manner,
+ but use %sr<REGNUM> and %cr<REGNUM> as their predefined names.
+
+ Likewise for the floating point registers, but of the form
+ %fr<REGNUM>. Floating point registers have additional predefined
+ names with 'L' and 'R' suffixes (e.g. %fr19L, %fr19R) which
+ again have the value <REGNUM>.
+
+ Many registers also have synonyms:
+
+ %r26 - %r23 have %arg0 - %arg3 as synonyms
+ %r28 - %r29 have %ret0 - %ret1 as synonyms
+ %r30 has %sp as a synonym
+ %r27 has %dp as a synonym
+ %r2 has %rp as a synonym
+
+ Almost every control register has a synonym; they are not listed
+ here for brevity.
+
+ The table is sorted. Suitable for searching by a binary search. */
+
+static const struct pd_reg pre_defined_registers[] =
+{
+ {"%arg0", 26},
+ {"%arg1", 25},
+ {"%arg2", 24},
+ {"%arg3", 23},
+ {"%cr0", 0},
+ {"%cr10", 10},
+ {"%cr11", 11},
+ {"%cr12", 12},
+ {"%cr13", 13},
+ {"%cr14", 14},
+ {"%cr15", 15},
+ {"%cr16", 16},
+ {"%cr17", 17},
+ {"%cr18", 18},
+ {"%cr19", 19},
+ {"%cr20", 20},
+ {"%cr21", 21},
+ {"%cr22", 22},
+ {"%cr23", 23},
+ {"%cr24", 24},
+ {"%cr25", 25},
+ {"%cr26", 26},
+ {"%cr27", 27},
+ {"%cr28", 28},
+ {"%cr29", 29},
+ {"%cr30", 30},
+ {"%cr31", 31},
+ {"%cr8", 8},
+ {"%cr9", 9},
+ {"%dp", 27},
+ {"%eiem", 15},
+ {"%eirr", 23},
+ {"%fr0", 0},
+ {"%fr0l", 0},
+ {"%fr0r", 0},
+ {"%fr1", 1},
+ {"%fr10", 10},
+ {"%fr10l", 10},
+ {"%fr10r", 10},
+ {"%fr11", 11},
+ {"%fr11l", 11},
+ {"%fr11r", 11},
+ {"%fr12", 12},
+ {"%fr12l", 12},
+ {"%fr12r", 12},
+ {"%fr13", 13},
+ {"%fr13l", 13},
+ {"%fr13r", 13},
+ {"%fr14", 14},
+ {"%fr14l", 14},
+ {"%fr14r", 14},
+ {"%fr15", 15},
+ {"%fr15l", 15},
+ {"%fr15r", 15},
+ {"%fr16", 16},
+ {"%fr16l", 16},
+ {"%fr16r", 16},
+ {"%fr17", 17},
+ {"%fr17l", 17},
+ {"%fr17r", 17},
+ {"%fr18", 18},
+ {"%fr18l", 18},
+ {"%fr18r", 18},
+ {"%fr19", 19},
+ {"%fr19l", 19},
+ {"%fr19r", 19},
+ {"%fr1l", 1},
+ {"%fr1r", 1},
+ {"%fr2", 2},
+ {"%fr20", 20},
+ {"%fr20l", 20},
+ {"%fr20r", 20},
+ {"%fr21", 21},
+ {"%fr21l", 21},
+ {"%fr21r", 21},
+ {"%fr22", 22},
+ {"%fr22l", 22},
+ {"%fr22r", 22},
+ {"%fr23", 23},
+ {"%fr23l", 23},
+ {"%fr23r", 23},
+ {"%fr24", 24},
+ {"%fr24l", 24},
+ {"%fr24r", 24},
+ {"%fr25", 25},
+ {"%fr25l", 25},
+ {"%fr25r", 25},
+ {"%fr26", 26},
+ {"%fr26l", 26},
+ {"%fr26r", 26},
+ {"%fr27", 27},
+ {"%fr27l", 27},
+ {"%fr27r", 27},
+ {"%fr28", 28},
+ {"%fr28l", 28},
+ {"%fr28r", 28},
+ {"%fr29", 29},
+ {"%fr29l", 29},
+ {"%fr29r", 29},
+ {"%fr2l", 2},
+ {"%fr2r", 2},
+ {"%fr3", 3},
+ {"%fr30", 30},
+ {"%fr30l", 30},
+ {"%fr30r", 30},
+ {"%fr31", 31},
+ {"%fr31l", 31},
+ {"%fr31r", 31},
+ {"%fr3l", 3},
+ {"%fr3r", 3},
+ {"%fr4", 4},
+ {"%fr4l", 4},
+ {"%fr4r", 4},
+ {"%fr5", 5},
+ {"%fr5l", 5},
+ {"%fr5r", 5},
+ {"%fr6", 6},
+ {"%fr6l", 6},
+ {"%fr6r", 6},
+ {"%fr7", 7},
+ {"%fr7l", 7},
+ {"%fr7r", 7},
+ {"%fr8", 8},
+ {"%fr8l", 8},
+ {"%fr8r", 8},
+ {"%fr9", 9},
+ {"%fr9l", 9},
+ {"%fr9r", 9},
+ {"%hta", 25},
+ {"%iir", 19},
+ {"%ior", 21},
+ {"%ipsw", 22},
+ {"%isr", 20},
+ {"%itmr", 16},
+ {"%iva", 14},
+ {"%pcoq", 18},
+ {"%pcsq", 17},
+ {"%pidr1", 8},
+ {"%pidr2", 9},
+ {"%pidr3", 12},
+ {"%pidr4", 13},
+ {"%ppda", 24},
+ {"%r0", 0},
+ {"%r1", 1},
+ {"%r10", 10},
+ {"%r11", 11},
+ {"%r12", 12},
+ {"%r13", 13},
+ {"%r14", 14},
+ {"%r15", 15},
+ {"%r16", 16},
+ {"%r17", 17},
+ {"%r18", 18},
+ {"%r19", 19},
+ {"%r2", 2},
+ {"%r20", 20},
+ {"%r21", 21},
+ {"%r22", 22},
+ {"%r23", 23},
+ {"%r24", 24},
+ {"%r25", 25},
+ {"%r26", 26},
+ {"%r27", 27},
+ {"%r28", 28},
+ {"%r29", 29},
+ {"%r3", 3},
+ {"%r30", 30},
+ {"%r31", 31},
+ {"%r4", 4},
+ {"%r5", 5},
+ {"%r6", 6},
+ {"%r7", 7},
+ {"%r8", 8},
+ {"%r9", 9},
+ {"%rctr", 0},
+ {"%ret0", 28},
+ {"%ret1", 29},
+ {"%rp", 2},
+ {"%sar", 11},
+ {"%sp", 30},
+ {"%sr0", 0},
+ {"%sr1", 1},
+ {"%sr2", 2},
+ {"%sr3", 3},
+ {"%sr4", 4},
+ {"%sr5", 5},
+ {"%sr6", 6},
+ {"%sr7", 7},
+ {"%tr0", 24},
+ {"%tr1", 25},
+ {"%tr2", 26},
+ {"%tr3", 27},
+ {"%tr4", 28},
+ {"%tr5", 29},
+ {"%tr6", 30},
+ {"%tr7", 31}
+};
+
+/* This table is sorted by order of the length of the string. This is
+ so we check for <> before we check for <. If we had a <> and checked
+ for < first, we would get a false match. */
+static const struct fp_cond_map fp_cond_map[] =
+{
+ {"false?", 0},
+ {"false", 1},
+ {"true?", 30},
+ {"true", 31},
+ {"!<=>", 3},
+ {"!?>=", 8},
+ {"!?<=", 16},
+ {"!<>", 7},
+ {"!>=", 11},
+ {"!?>", 12},
+ {"?<=", 14},
+ {"!<=", 19},
+ {"!?<", 20},
+ {"?>=", 22},
+ {"!?=", 24},
+ {"!=t", 27},
+ {"<=>", 29},
+ {"=t", 5},
+ {"?=", 6},
+ {"?<", 10},
+ {"<=", 13},
+ {"!>", 15},
+ {"?>", 18},
+ {">=", 21},
+ {"!<", 23},
+ {"<>", 25},
+ {"!=", 26},
+ {"!?", 28},
+ {"?", 2},
+ {"=", 4},
+ {"<", 9},
+ {">", 17}
+};
+
+static const struct selector_entry selector_table[] =
+{
+ {"f", e_fsel},
+ {"l", e_lsel},
+ {"ld", e_ldsel},
+ {"lp", e_lpsel},
+ {"lr", e_lrsel},
+ {"ls", e_lssel},
+ {"lt", e_ltsel},
+ {"n", e_nsel},
+ {"nl", e_nlsel},
+ {"nlr", e_nlrsel},
+ {"p", e_psel},
+ {"r", e_rsel},
+ {"rd", e_rdsel},
+ {"rp", e_rpsel},
+ {"rr", e_rrsel},
+ {"rs", e_rssel},
+ {"rt", e_rtsel},
+ {"t", e_tsel},
+};
+
+/* default space and subspace dictionaries */
+
+#define GDB_SYMBOLS GDB_SYMBOLS_SUBSPACE_NAME
+#define GDB_STRINGS GDB_STRINGS_SUBSPACE_NAME
+
+/* pre-defined subsegments (subspaces) for the HPPA. */
+#define SUBSEG_CODE 0
+#define SUBSEG_LIT 1
+#define SUBSEG_MILLI 2
+#define SUBSEG_DATA 0
+#define SUBSEG_BSS 2
+#define SUBSEG_UNWIND 3
+#define SUBSEG_GDB_STRINGS 0
+#define SUBSEG_GDB_SYMBOLS 1
+
+static struct default_subspace_dict pa_def_subspaces[] =
+{
+ {"$CODE$", 1, 1, 1, 0, 0, 0, 24, 0x2c, 0, 8, 0, 0, ".text", SUBSEG_CODE},
+ {"$DATA$", 1, 1, 0, 0, 0, 0, 24, 0x1f, 1, 8, 1, 1, ".data", SUBSEG_DATA},
+ {"$LIT$", 1, 1, 0, 0, 0, 0, 16, 0x2c, 0, 8, 0, 0, ".text", SUBSEG_LIT},
+ {"$MILLICODE$", 1, 1, 0, 0, 0, 0, 8, 0x2c, 0, 8, 0, 0, ".text", SUBSEG_MILLI},
+ {"$BSS$", 1, 1, 0, 0, 0, 1, 80, 0x1f, 1, 8, 1, 1, ".bss", SUBSEG_BSS},
+#ifdef OBJ_ELF
+ {"$UNWIND$", 1, 1, 0, 0, 0, 0, 64, 0x2c, 0, 4, 0, 0, ".PARISC.unwind", SUBSEG_UNWIND},
+#endif
+ {NULL, 0, 1, 0, 0, 0, 0, 255, 0x1f, 0, 4, 0, 0, 0}
+};
+
+static struct default_space_dict pa_def_spaces[] =
+{
+ {"$TEXT$", 0, 1, 1, 0, 8, ASEC_NULL, ".text"},
+ {"$PRIVATE$", 1, 1, 1, 1, 16, ASEC_NULL, ".data"},
+ {NULL, 0, 0, 0, 0, 0, ASEC_NULL, NULL}
+};
+
+/* Misc local definitions used by the assembler. */
+
+/* Return nonzero if the string pointed to by S potentially represents
+ a right or left half of a FP register */
+#define IS_R_SELECT(S) (*(S) == 'R' || *(S) == 'r')
+#define IS_L_SELECT(S) (*(S) == 'L' || *(S) == 'l')
+
+/* These macros are used to maintain spaces/subspaces. */
+#define SPACE_DEFINED(space_chain) (space_chain)->sd_defined
+#define SPACE_USER_DEFINED(space_chain) (space_chain)->sd_user_defined
+#define SPACE_SPNUM(space_chain) (space_chain)->sd_spnum
+#define SPACE_NAME(space_chain) (space_chain)->sd_name
+
+#define SUBSPACE_DEFINED(ss_chain) (ss_chain)->ssd_defined
+#define SUBSPACE_NAME(ss_chain) (ss_chain)->ssd_name
+
+/* Insert FIELD into OPCODE starting at bit START. Continue pa_ip
+ main loop after insertion. */
+
+#define INSERT_FIELD_AND_CONTINUE(OPCODE, FIELD, START) \
+ { \
+ ((OPCODE) |= (FIELD) << (START)); \
+ continue; \
+ }
+
+/* Simple range checking for FIELD againt HIGH and LOW bounds.
+ IGNORE is used to suppress the error message. */
+
+#define CHECK_FIELD(FIELD, HIGH, LOW, IGNORE) \
+ { \
+ if ((FIELD) > (HIGH) || (FIELD) < (LOW)) \
+ { \
+ if (! IGNORE) \
+ as_bad (_("Field out of range [%d..%d] (%d)."), (LOW), (HIGH), \
+ (int) (FIELD));\
+ break; \
+ } \
+ }
+
+#define is_DP_relative(exp) \
+ ((exp).X_op == O_subtract \
+ && strcmp((exp).X_op_symbol->bsym->name, "$global$") == 0)
+
+#define is_PC_relative(exp) \
+ ((exp).X_op == O_subtract \
+ && strcmp((exp).X_op_symbol->bsym->name, "$PIC_pcrel$0") == 0)
+
+/* We need some complex handling for stabs (sym1 - sym2). Luckily, we'll
+ always be able to reduce the expression to a constant, so we don't
+ need real complex handling yet. */
+#define is_complex(exp) \
+ ((exp).X_op != O_constant && (exp).X_op != O_symbol)
+
+/* Actual functions to implement the PA specific code for the assembler. */
+
+/* Called before writing the object file. Make sure entry/exit and
+ proc/procend pairs match. */
+
+void
+pa_check_eof ()
+{
+ if (within_entry_exit)
+ as_fatal (_("Missing .exit\n"));
+
+ if (within_procedure)
+ as_fatal (_("Missing .procend\n"));
+}
+
+/* Check to make sure we have a valid space and subspace. */
+
+static void
+pa_check_current_space_and_subspace ()
+{
+ if (current_space == NULL)
+ as_fatal (_("Not in a space.\n"));
+
+ if (current_subspace == NULL)
+ as_fatal (_("Not in a subspace.\n"));
+}
+
+/* Returns a pointer to the label_symbol_struct for the current space.
+ or NULL if no label_symbol_struct exists for the current space. */
+
+static label_symbol_struct *
+pa_get_label ()
+{
+ label_symbol_struct *label_chain;
+ sd_chain_struct *space_chain = current_space;
+
+ for (label_chain = label_symbols_rootp;
+ label_chain;
+ label_chain = label_chain->lss_next)
+ if (space_chain == label_chain->lss_space && label_chain->lss_label)
+ return label_chain;
+
+ return NULL;
+}
+
+/* Defines a label for the current space. If one is already defined,
+ this function will replace it with the new label. */
+
+void
+pa_define_label (symbol)
+ symbolS *symbol;
+{
+ label_symbol_struct *label_chain = pa_get_label ();
+ sd_chain_struct *space_chain = current_space;
+
+ if (label_chain)
+ label_chain->lss_label = symbol;
+ else
+ {
+ /* Create a new label entry and add it to the head of the chain. */
+ label_chain
+ = (label_symbol_struct *) xmalloc (sizeof (label_symbol_struct));
+ label_chain->lss_label = symbol;
+ label_chain->lss_space = space_chain;
+ label_chain->lss_next = NULL;
+
+ if (label_symbols_rootp)
+ label_chain->lss_next = label_symbols_rootp;
+
+ label_symbols_rootp = label_chain;
+ }
+}
+
+/* Removes a label definition for the current space.
+ If there is no label_symbol_struct entry, then no action is taken. */
+
+static void
+pa_undefine_label ()
+{
+ label_symbol_struct *label_chain;
+ label_symbol_struct *prev_label_chain = NULL;
+ sd_chain_struct *space_chain = current_space;
+
+ for (label_chain = label_symbols_rootp;
+ label_chain;
+ label_chain = label_chain->lss_next)
+ {
+ if (space_chain == label_chain->lss_space && label_chain->lss_label)
+ {
+ /* Remove the label from the chain and free its memory. */
+ if (prev_label_chain)
+ prev_label_chain->lss_next = label_chain->lss_next;
+ else
+ label_symbols_rootp = label_chain->lss_next;
+
+ free (label_chain);
+ break;
+ }
+ prev_label_chain = label_chain;
+ }
+}
+
+
+/* An HPPA-specific version of fix_new. This is required because the HPPA
+ code needs to keep track of some extra stuff. Each call to fix_new_hppa
+ results in the creation of an instance of an hppa_fix_struct. An
+ hppa_fix_struct stores the extra information along with a pointer to the
+ original fixS. This is attached to the original fixup via the
+ tc_fix_data field. */
+
+static void
+fix_new_hppa (frag, where, size, add_symbol, offset, exp, pcrel,
+ r_type, r_field, r_format, arg_reloc, unwind_bits)
+ fragS *frag;
+ int where;
+ int size;
+ symbolS *add_symbol;
+ long offset;
+ expressionS *exp;
+ int pcrel;
+ bfd_reloc_code_real_type r_type;
+ enum hppa_reloc_field_selector_type_alt r_field;
+ int r_format;
+ long arg_reloc;
+ int* unwind_bits;
+{
+ fixS *new_fix;
+
+ struct hppa_fix_struct *hppa_fix = (struct hppa_fix_struct *)
+ obstack_alloc (&notes, sizeof (struct hppa_fix_struct));
+
+ if (exp != NULL)
+ new_fix = fix_new_exp (frag, where, size, exp, pcrel, r_type);
+ else
+ new_fix = fix_new (frag, where, size, add_symbol, offset, pcrel, r_type);
+ new_fix->tc_fix_data = (void *) hppa_fix;
+ hppa_fix->fx_r_type = r_type;
+ hppa_fix->fx_r_field = r_field;
+ hppa_fix->fx_r_format = r_format;
+ hppa_fix->fx_arg_reloc = arg_reloc;
+ hppa_fix->segment = now_seg;
+#ifdef OBJ_SOM
+ if (r_type == R_ENTRY || r_type == R_EXIT)
+ new_fix->fx_offset = *unwind_bits;
+#endif
+
+ /* foo-$global$ is used to access non-automatic storage. $global$
+ is really just a marker and has served its purpose, so eliminate
+ it now so as not to confuse write.c. */
+ if (new_fix->fx_subsy
+ && !strcmp (S_GET_NAME (new_fix->fx_subsy), "$global$"))
+ new_fix->fx_subsy = NULL;
+}
+
+/* Parse a .byte, .word, .long expression for the HPPA. Called by
+ cons via the TC_PARSE_CONS_EXPRESSION macro. */
+
+void
+parse_cons_expression_hppa (exp)
+ expressionS *exp;
+{
+ hppa_field_selector = pa_chk_field_selector (&input_line_pointer);
+ expression (exp);
+}
+
+/* This fix_new is called by cons via TC_CONS_FIX_NEW.
+ hppa_field_selector is set by the parse_cons_expression_hppa. */
+
+void
+cons_fix_new_hppa (frag, where, size, exp)
+ fragS *frag;
+ int where;
+ int size;
+ expressionS *exp;
+{
+ unsigned int rel_type;
+
+ /* Get a base relocation type. */
+ if (is_DP_relative (*exp))
+ rel_type = R_HPPA_GOTOFF;
+ else if (is_complex (*exp))
+ rel_type = R_HPPA_COMPLEX;
+ else
+ rel_type = R_HPPA;
+
+ if (hppa_field_selector != e_psel && hppa_field_selector != e_fsel)
+ as_warn (_("Invalid field selector. Assuming F%%."));
+
+ fix_new_hppa (frag, where, size,
+ (symbolS *) NULL, (offsetT) 0, exp, 0, rel_type,
+ hppa_field_selector, 32, 0, NULL);
+
+ /* Reset field selector to its default state. */
+ hppa_field_selector = 0;
+}
+
+/* This function is called once, at assembler startup time. It should
+ set up all the tables, etc. that the MD part of the assembler will need. */
+
+void
+md_begin ()
+{
+ const char *retval = NULL;
+ int lose = 0;
+ unsigned int i = 0;
+
+ last_call_info = NULL;
+ call_info_root = NULL;
+
+ /* Set the default machine type. */
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_hppa, 10))
+ as_warn (_("could not set architecture and machine"));
+
+ /* Folding of text and data segments fails miserably on the PA.
+ Warn user and disable "-R" option. */
+ if (flag_readonly_data_in_text)
+ {
+ as_warn (_("-R option not supported on this target."));
+ flag_readonly_data_in_text = 0;
+ }
+
+ pa_spaces_begin ();
+
+ op_hash = hash_new ();
+
+ while (i < NUMOPCODES)
+ {
+ const char *name = pa_opcodes[i].name;
+ retval = hash_insert (op_hash, name, (struct pa_opcode *) &pa_opcodes[i]);
+ if (retval != NULL && *retval != '\0')
+ {
+ as_fatal (_("Internal error: can't hash `%s': %s\n"), name, retval);
+ lose = 1;
+ }
+ do
+ {
+ if ((pa_opcodes[i].match & pa_opcodes[i].mask)
+ != pa_opcodes[i].match)
+ {
+ fprintf (stderr, _("internal error: losing opcode: `%s' \"%s\"\n"),
+ pa_opcodes[i].name, pa_opcodes[i].args);
+ lose = 1;
+ }
+ ++i;
+ }
+ while (i < NUMOPCODES && !strcmp (pa_opcodes[i].name, name));
+ }
+
+ if (lose)
+ as_fatal (_("Broken assembler. No assembly attempted."));
+
+ /* SOM will change text_section. To make sure we never put
+ anything into the old one switch to the new one now. */
+ subseg_set (text_section, 0);
+
+ dummy_symbol = symbol_find_or_make ("L$dummy");
+ S_SET_SEGMENT (dummy_symbol, text_section);
+}
+
+/* Assemble a single instruction storing it into a frag. */
+void
+md_assemble (str)
+ char *str;
+{
+ char *to;
+
+ /* The had better be something to assemble. */
+ assert (str);
+
+ /* If we are within a procedure definition, make sure we've
+ defined a label for the procedure; handle case where the
+ label was defined after the .PROC directive.
+
+ Note there's not need to diddle with the segment or fragment
+ for the label symbol in this case. We have already switched
+ into the new $CODE$ subspace at this point. */
+ if (within_procedure && last_call_info->start_symbol == NULL)
+ {
+ label_symbol_struct *label_symbol = pa_get_label ();
+
+ if (label_symbol)
+ {
+ if (label_symbol->lss_label)
+ {
+ last_call_info->start_symbol = label_symbol->lss_label;
+ label_symbol->lss_label->bsym->flags |= BSF_FUNCTION;
+#ifdef OBJ_SOM
+ /* Also handle allocation of a fixup to hold the unwind
+ information when the label appears after the proc/procend. */
+ if (within_entry_exit)
+ {
+ char *where = frag_more (0);
+
+ fix_new_hppa (frag_now, where - frag_now->fr_literal, 0,
+ NULL, (offsetT) 0, NULL,
+ 0, R_HPPA_ENTRY, e_fsel, 0, 0,
+ (int *)&last_call_info->ci_unwind.descriptor);
+ }
+#endif
+ }
+ else
+ as_bad (_("Missing function name for .PROC (corrupted label chain)"));
+ }
+ else
+ as_bad (_("Missing function name for .PROC"));
+ }
+
+ /* Assemble the instruction. Results are saved into "the_insn". */
+ pa_ip (str);
+
+ /* Get somewhere to put the assembled instrution. */
+ to = frag_more (4);
+
+ /* Output the opcode. */
+ md_number_to_chars (to, the_insn.opcode, 4);
+
+ /* If necessary output more stuff. */
+ if (the_insn.reloc != R_HPPA_NONE)
+ fix_new_hppa (frag_now, (to - frag_now->fr_literal), 4, NULL,
+ (offsetT) 0, &the_insn.exp, the_insn.pcrel,
+ the_insn.reloc, the_insn.field_selector,
+ the_insn.format, the_insn.arg_reloc, NULL);
+}
+
+/* Do the real work for assembling a single instruction. Store results
+ into the global "the_insn" variable. */
+
+static void
+pa_ip (str)
+ char *str;
+{
+ char *error_message = "";
+ char *s, c, *argstart, *name, *save_s;
+ const char *args;
+ int match = FALSE;
+ int comma = 0;
+ int cmpltr, nullif, flag, cond, num;
+ unsigned long opcode;
+ struct pa_opcode *insn;
+
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+
+ /* Skip to something interesting. */
+ for (s = str; isupper (*s) || islower (*s) || (*s >= '0' && *s <= '3'); ++s)
+ ;
+
+ switch (*s)
+ {
+
+ case '\0':
+ break;
+
+ case ',':
+ comma = 1;
+
+ /*FALLTHROUGH */
+
+ case ' ':
+ *s++ = '\0';
+ break;
+
+ default:
+ as_fatal (_("Unknown opcode: `%s'"), str);
+ }
+
+ save_s = str;
+
+ /* Convert everything into lower case. */
+ while (*save_s)
+ {
+ if (isupper (*save_s))
+ *save_s = tolower (*save_s);
+ save_s++;
+ }
+
+ /* Look up the opcode in the has table. */
+ if ((insn = (struct pa_opcode *) hash_find (op_hash, str)) == NULL)
+ {
+ as_bad ("Unknown opcode: `%s'", str);
+ return;
+ }
+
+ if (comma)
+ {
+ *--s = ',';
+ }
+
+ /* Mark the location where arguments for the instruction start, then
+ start processing them. */
+ argstart = s;
+ for (;;)
+ {
+ /* Do some initialization. */
+ opcode = insn->match;
+ memset (&the_insn, 0, sizeof (the_insn));
+
+ the_insn.reloc = R_HPPA_NONE;
+
+ /* If this instruction is specific to a particular architecture,
+ then set a new architecture. */
+ /* But do not automatically promote to pa2.0. The automatic promotion
+ crud is for compatability with HP's old assemblers only. */
+ if (insn->arch < 20
+ && bfd_get_mach (stdoutput) < insn->arch)
+ {
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_hppa, insn->arch))
+ as_warn (_("could not update architecture and machine"));
+ }
+ else if (bfd_get_mach (stdoutput) < insn->arch)
+ {
+ match = FALSE;
+ goto failed;
+ }
+
+ /* Build the opcode, checking as we go to make
+ sure that the operands match. */
+ for (args = insn->args;; ++args)
+ {
+ switch (*args)
+ {
+
+ /* End of arguments. */
+ case '\0':
+ if (*s == '\0')
+ match = TRUE;
+ break;
+
+ case '+':
+ if (*s == '+')
+ {
+ ++s;
+ continue;
+ }
+ if (*s == '-')
+ continue;
+ break;
+
+ /* These must match exactly. */
+ case '(':
+ case ')':
+ case ',':
+ case ' ':
+ if (*s++ == *args)
+ continue;
+ break;
+
+ /* Handle a 5 bit register or control register field at 10. */
+ case 'b':
+ case '^':
+ num = pa_parse_number (&s, 0);
+ CHECK_FIELD (num, 31, 0, 0);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 21);
+
+ /* Handle a 5 bit register field at 15. */
+ case 'x':
+ num = pa_parse_number (&s, 0);
+ CHECK_FIELD (num, 31, 0, 0);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 16);
+
+ /* Handle a 5 bit register field at 31. */
+ case 'y':
+ case 't':
+ num = pa_parse_number (&s, 0);
+ CHECK_FIELD (num, 31, 0, 0);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+
+ /* Handle a 5 bit field length at 31. */
+ case 'T':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ s = expr_end;
+ CHECK_FIELD (num, 32, 1, 0);
+ INSERT_FIELD_AND_CONTINUE (opcode, 32 - num, 0);
+
+ /* Handle a 5 bit immediate at 15. */
+ case '5':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ s = expr_end;
+ CHECK_FIELD (num, 15, -16, 0);
+ low_sign_unext (num, 5, &num);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 16);
+
+ /* Handle a 5 bit immediate at 31. */
+ case 'V':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ s = expr_end;
+ CHECK_FIELD (num, 15, -16, 0)
+ low_sign_unext (num, 5, &num);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+
+ /* Handle an unsigned 5 bit immediate at 31. */
+ case 'r':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ s = expr_end;
+ CHECK_FIELD (num, 31, 0, 0);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+
+ /* Handle an unsigned 5 bit immediate at 15. */
+ case 'R':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ s = expr_end;
+ CHECK_FIELD (num, 31, 0, 0);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 16);
+
+ /* Handle a 2 bit space identifier at 17. */
+ case 's':
+ num = pa_parse_number (&s, 0);
+ CHECK_FIELD (num, 3, 0, 1);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 14);
+
+ /* Handle a 3 bit space identifier at 18. */
+ case 'S':
+ num = pa_parse_number (&s, 0);
+ CHECK_FIELD (num, 7, 0, 1);
+ dis_assemble_3 (num, &num);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 13);
+
+ /* Handle a completer for an indexing load or store. */
+ case 'c':
+ {
+ int uu = 0;
+ int m = 0;
+ int i = 0;
+ while (*s == ',' && i < 2)
+ {
+ s++;
+ if (strncasecmp (s, "sm", 2) == 0)
+ {
+ uu = 1;
+ m = 1;
+ s++;
+ i++;
+ }
+ else if (strncasecmp (s, "m", 1) == 0)
+ m = 1;
+ else if (strncasecmp (s, "s", 1) == 0)
+ uu = 1;
+ else
+ as_bad (_("Invalid Indexed Load Completer."));
+ s++;
+ i++;
+ }
+ if (i > 2)
+ as_bad (_("Invalid Indexed Load Completer Syntax."));
+ opcode |= m << 5;
+ INSERT_FIELD_AND_CONTINUE (opcode, uu, 13);
+ }
+
+ /* Handle a short load/store completer. */
+ case 'C':
+ {
+ int a = 0;
+ int m = 0;
+ if (*s == ',')
+ {
+ s++;
+ if (strncasecmp (s, "ma", 2) == 0)
+ {
+ a = 0;
+ m = 1;
+ }
+ else if (strncasecmp (s, "mb", 2) == 0)
+ {
+ a = 1;
+ m = 1;
+ }
+ else
+ as_bad (_("Invalid Short Load/Store Completer."));
+ s += 2;
+ }
+
+ if (*args == 'C')
+ {
+ opcode |= m << 5;
+ INSERT_FIELD_AND_CONTINUE (opcode, a, 13);
+ }
+ }
+
+ /* Handle a stbys completer. */
+ case 'Y':
+ {
+ int a = 0;
+ int m = 0;
+ int i = 0;
+ while (*s == ',' && i < 2)
+ {
+ s++;
+ if (strncasecmp (s, "m", 1) == 0)
+ m = 1;
+ else if (strncasecmp (s, "b", 1) == 0)
+ a = 0;
+ else if (strncasecmp (s, "e", 1) == 0)
+ a = 1;
+ else
+ as_bad (_("Invalid Store Bytes Short Completer"));
+ s++;
+ i++;
+ }
+ if (i > 2)
+ as_bad (_("Invalid Store Bytes Short Completer"));
+ opcode |= m << 5;
+ INSERT_FIELD_AND_CONTINUE (opcode, a, 13);
+ }
+
+ /* Handle a non-negated compare/stubtract condition. */
+ case '<':
+ cmpltr = pa_parse_nonneg_cmpsub_cmpltr (&s, 1);
+ if (cmpltr < 0)
+ {
+ as_bad (_("Invalid Compare/Subtract Condition: %c"), *s);
+ cmpltr = 0;
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, cmpltr, 13);
+
+ /* Handle a negated or non-negated compare/subtract condition. */
+ case '?':
+ save_s = s;
+ cmpltr = pa_parse_nonneg_cmpsub_cmpltr (&s, 1);
+ if (cmpltr < 0)
+ {
+ s = save_s;
+ cmpltr = pa_parse_neg_cmpsub_cmpltr (&s, 1);
+ if (cmpltr < 0)
+ {
+ as_bad (_("Invalid Compare/Subtract Condition."));
+ cmpltr = 0;
+ }
+ else
+ {
+ /* Negated condition requires an opcode change. */
+ opcode |= 1 << 27;
+ }
+ }
+
+ INSERT_FIELD_AND_CONTINUE (opcode, cmpltr, 13);
+
+ /* Handle non-negated add condition. */
+ case '!':
+ cmpltr = pa_parse_nonneg_add_cmpltr (&s, 1);
+ if (cmpltr < 0)
+ {
+ as_bad (_("Invalid Compare/Subtract Condition: %c"), *s);
+ cmpltr = 0;
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, cmpltr, 13);
+
+ /* Handle a negated or non-negated add condition. */
+ case '@':
+ save_s = s;
+ cmpltr = pa_parse_nonneg_add_cmpltr (&s, 1);
+ if (cmpltr < 0)
+ {
+ s = save_s;
+ cmpltr = pa_parse_neg_add_cmpltr (&s, 1);
+ if (cmpltr < 0)
+ {
+ as_bad (_("Invalid Compare/Subtract Condition"));
+ cmpltr = 0;
+ }
+ else
+ {
+ /* Negated condition requires an opcode change. */
+ opcode |= 1 << 27;
+ }
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, cmpltr, 13);
+
+ /* Handle a compare/subtract condition. */
+ case 'a':
+ cmpltr = 0;
+ flag = 0;
+ if (*s == ',')
+ {
+ s++;
+ name = s;
+ while (*s != ',' && *s != ' ' && *s != '\t')
+ s += 1;
+ c = *s;
+ *s = 0x00;
+ if (strcmp (name, "=") == 0)
+ cmpltr = 1;
+ else if (strcmp (name, "<") == 0)
+ cmpltr = 2;
+ else if (strcmp (name, "<=") == 0)
+ cmpltr = 3;
+ else if (strcasecmp (name, "<<") == 0)
+ cmpltr = 4;
+ else if (strcasecmp (name, "<<=") == 0)
+ cmpltr = 5;
+ else if (strcasecmp (name, "sv") == 0)
+ cmpltr = 6;
+ else if (strcasecmp (name, "od") == 0)
+ cmpltr = 7;
+ else if (strcasecmp (name, "tr") == 0)
+ {
+ cmpltr = 0;
+ flag = 1;
+ }
+ else if (strcmp (name, "<>") == 0)
+ {
+ cmpltr = 1;
+ flag = 1;
+ }
+ else if (strcmp (name, ">=") == 0)
+ {
+ cmpltr = 2;
+ flag = 1;
+ }
+ else if (strcmp (name, ">") == 0)
+ {
+ cmpltr = 3;
+ flag = 1;
+ }
+ else if (strcasecmp (name, ">>=") == 0)
+ {
+ cmpltr = 4;
+ flag = 1;
+ }
+ else if (strcasecmp (name, ">>") == 0)
+ {
+ cmpltr = 5;
+ flag = 1;
+ }
+ else if (strcasecmp (name, "nsv") == 0)
+ {
+ cmpltr = 6;
+ flag = 1;
+ }
+ else if (strcasecmp (name, "ev") == 0)
+ {
+ cmpltr = 7;
+ flag = 1;
+ }
+ else
+ as_bad (_("Invalid Add Condition: %s"), name);
+ *s = c;
+ }
+ opcode |= cmpltr << 13;
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 12);
+
+ /* Handle a non-negated add condition. */
+ case 'd':
+ cmpltr = 0;
+ flag = 0;
+ if (*s == ',')
+ {
+ s++;
+ name = s;
+ while (*s != ',' && *s != ' ' && *s != '\t')
+ s += 1;
+ c = *s;
+ *s = 0x00;
+ if (strcmp (name, "=") == 0)
+ cmpltr = 1;
+ else if (strcmp (name, "<") == 0)
+ cmpltr = 2;
+ else if (strcmp (name, "<=") == 0)
+ cmpltr = 3;
+ else if (strcasecmp (name, "nuv") == 0)
+ cmpltr = 4;
+ else if (strcasecmp (name, "znv") == 0)
+ cmpltr = 5;
+ else if (strcasecmp (name, "sv") == 0)
+ cmpltr = 6;
+ else if (strcasecmp (name, "od") == 0)
+ cmpltr = 7;
+ else if (strcasecmp (name, "tr") == 0)
+ {
+ cmpltr = 0;
+ flag = 1;
+ }
+ else if (strcmp (name, "<>") == 0)
+ {
+ cmpltr = 1;
+ flag = 1;
+ }
+ else if (strcmp (name, ">=") == 0)
+ {
+ cmpltr = 2;
+ flag = 1;
+ }
+ else if (strcmp (name, ">") == 0)
+ {
+ cmpltr = 3;
+ flag = 1;
+ }
+ else if (strcasecmp (name, "uv") == 0)
+ {
+ cmpltr = 4;
+ flag = 1;
+ }
+ else if (strcasecmp (name, "vnz") == 0)
+ {
+ cmpltr = 5;
+ flag = 1;
+ }
+ else if (strcasecmp (name, "nsv") == 0)
+ {
+ cmpltr = 6;
+ flag = 1;
+ }
+ else if (strcasecmp (name, "ev") == 0)
+ {
+ cmpltr = 7;
+ flag = 1;
+ }
+ else
+ as_bad (_("Invalid Add Condition: %s"), name);
+ *s = c;
+ }
+ opcode |= cmpltr << 13;
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 12);
+
+ /* HANDLE a logical instruction condition. */
+ case '&':
+ cmpltr = 0;
+ flag = 0;
+ if (*s == ',')
+ {
+ s++;
+ name = s;
+ while (*s != ',' && *s != ' ' && *s != '\t')
+ s += 1;
+ c = *s;
+ *s = 0x00;
+
+
+ if (strcmp (name, "=") == 0)
+ cmpltr = 1;
+ else if (strcmp (name, "<") == 0)
+ cmpltr = 2;
+ else if (strcmp (name, "<=") == 0)
+ cmpltr = 3;
+ else if (strcasecmp (name, "od") == 0)
+ cmpltr = 7;
+ else if (strcasecmp (name, "tr") == 0)
+ {
+ cmpltr = 0;
+ flag = 1;
+ }
+ else if (strcmp (name, "<>") == 0)
+ {
+ cmpltr = 1;
+ flag = 1;
+ }
+ else if (strcmp (name, ">=") == 0)
+ {
+ cmpltr = 2;
+ flag = 1;
+ }
+ else if (strcmp (name, ">") == 0)
+ {
+ cmpltr = 3;
+ flag = 1;
+ }
+ else if (strcasecmp (name, "ev") == 0)
+ {
+ cmpltr = 7;
+ flag = 1;
+ }
+ else
+ as_bad (_("Invalid Logical Instruction Condition."));
+ *s = c;
+ }
+ opcode |= cmpltr << 13;
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 12);
+
+ /* Handle a unit instruction condition. */
+ case 'U':
+ cmpltr = 0;
+ flag = 0;
+ if (*s == ',')
+ {
+ s++;
+
+
+ if (strncasecmp (s, "sbz", 3) == 0)
+ {
+ cmpltr = 2;
+ s += 3;
+ }
+ else if (strncasecmp (s, "shz", 3) == 0)
+ {
+ cmpltr = 3;
+ s += 3;
+ }
+ else if (strncasecmp (s, "sdc", 3) == 0)
+ {
+ cmpltr = 4;
+ s += 3;
+ }
+ else if (strncasecmp (s, "sbc", 3) == 0)
+ {
+ cmpltr = 6;
+ s += 3;
+ }
+ else if (strncasecmp (s, "shc", 3) == 0)
+ {
+ cmpltr = 7;
+ s += 3;
+ }
+ else if (strncasecmp (s, "tr", 2) == 0)
+ {
+ cmpltr = 0;
+ flag = 1;
+ s += 2;
+ }
+ else if (strncasecmp (s, "nbz", 3) == 0)
+ {
+ cmpltr = 2;
+ flag = 1;
+ s += 3;
+ }
+ else if (strncasecmp (s, "nhz", 3) == 0)
+ {
+ cmpltr = 3;
+ flag = 1;
+ s += 3;
+ }
+ else if (strncasecmp (s, "ndc", 3) == 0)
+ {
+ cmpltr = 4;
+ flag = 1;
+ s += 3;
+ }
+ else if (strncasecmp (s, "nbc", 3) == 0)
+ {
+ cmpltr = 6;
+ flag = 1;
+ s += 3;
+ }
+ else if (strncasecmp (s, "nhc", 3) == 0)
+ {
+ cmpltr = 7;
+ flag = 1;
+ s += 3;
+ }
+ else
+ as_bad (_("Invalid Logical Instruction Condition."));
+ }
+ opcode |= cmpltr << 13;
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 12);
+
+ /* Handle a shift/extract/deposit condition. */
+ case '|':
+ case '>':
+ cmpltr = 0;
+ if (*s == ',')
+ {
+ save_s = s++;
+
+
+ name = s;
+ while (*s != ',' && *s != ' ' && *s != '\t')
+ s += 1;
+ c = *s;
+ *s = 0x00;
+ if (strcmp (name, "=") == 0)
+ cmpltr = 1;
+ else if (strcmp (name, "<") == 0)
+ cmpltr = 2;
+ else if (strcasecmp (name, "od") == 0)
+ cmpltr = 3;
+ else if (strcasecmp (name, "tr") == 0)
+ cmpltr = 4;
+ else if (strcmp (name, "<>") == 0)
+ cmpltr = 5;
+ else if (strcmp (name, ">=") == 0)
+ cmpltr = 6;
+ else if (strcasecmp (name, "ev") == 0)
+ cmpltr = 7;
+ /* Handle movb,n. Put things back the way they were.
+ This includes moving s back to where it started. */
+ else if (strcasecmp (name, "n") == 0 && *args == '|')
+ {
+ *s = c;
+ s = save_s;
+ continue;
+ }
+ else
+ as_bad (_("Invalid Shift/Extract/Deposit Condition."));
+ *s = c;
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, cmpltr, 13);
+
+ /* Handle bvb and bb conditions. */
+ case '~':
+ cmpltr = 0;
+ if (*s == ',')
+ {
+ s++;
+ if (strncmp (s, "<", 1) == 0)
+ {
+ cmpltr = 0;
+ s++;
+ }
+ else if (strncmp (s, ">=", 2) == 0)
+ {
+ cmpltr = 1;
+ s += 2;
+ }
+ else
+ as_bad (_("Invalid Bit Branch Condition: %c"), *s);
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, cmpltr, 15);
+
+ /* Handle a system control completer. */
+ case 'Z':
+ if (*s == ',' && (*(s + 1) == 'm' || *(s + 1) == 'M'))
+ {
+ flag = 1;
+ s += 2;
+ }
+ else
+ flag = 0;
+
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 5);
+
+ /* Handle a nullification completer for branch instructions. */
+ case 'n':
+ nullif = pa_parse_nullif (&s);
+ INSERT_FIELD_AND_CONTINUE (opcode, nullif, 1);
+
+ /* Handle a nullification completer for copr and spop insns. */
+ case 'N':
+ nullif = pa_parse_nullif (&s);
+ INSERT_FIELD_AND_CONTINUE (opcode, nullif, 5);
+
+
+ /* Handle a 11 bit immediate at 31. */
+ case 'i':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ if (the_insn.exp.X_op == O_constant)
+ {
+ num = evaluate_absolute (&the_insn);
+ CHECK_FIELD (num, 1023, -1024, 0);
+ low_sign_unext (num, 11, &num);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+ }
+ else
+ {
+ if (is_DP_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_GOTOFF;
+ else if (is_PC_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_PCREL_CALL;
+ else
+ the_insn.reloc = R_HPPA;
+ the_insn.format = 11;
+ continue;
+ }
+
+
+ /* Handle a 14 bit immediate at 31. */
+ case 'j':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ if (the_insn.exp.X_op == O_constant)
+ {
+ num = evaluate_absolute (&the_insn);
+ CHECK_FIELD (num, 8191, -8192, 0);
+ low_sign_unext (num, 14, &num);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+ }
+ else
+ {
+ if (is_DP_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_GOTOFF;
+ else if (is_PC_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_PCREL_CALL;
+ else
+ the_insn.reloc = R_HPPA;
+ the_insn.format = 14;
+ continue;
+ }
+
+ /* Handle a 21 bit immediate at 31. */
+ case 'k':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ if (the_insn.exp.X_op == O_constant)
+ {
+ num = evaluate_absolute (&the_insn);
+ CHECK_FIELD (num >> 11, 1048575, -1048576, 0);
+ dis_assemble_21 (num, &num);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+ }
+ else
+ {
+ if (is_DP_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_GOTOFF;
+ else if (is_PC_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_PCREL_CALL;
+ else
+ the_insn.reloc = R_HPPA;
+ the_insn.format = 21;
+ continue;
+ }
+
+ /* Handle a 12 bit branch displacement. */
+ case 'w':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ the_insn.pcrel = 1;
+ if (!strcmp (S_GET_NAME (the_insn.exp.X_add_symbol), "L$0\001"))
+ {
+ unsigned int w1, w, result;
+
+ num = evaluate_absolute (&the_insn);
+ if (num % 4)
+ {
+ as_bad (_("Branch to unaligned address"));
+ break;
+ }
+ CHECK_FIELD (num, 8199, -8184, 0);
+ sign_unext ((num - 8) >> 2, 12, &result);
+ dis_assemble_12 (result, &w1, &w);
+ INSERT_FIELD_AND_CONTINUE (opcode, ((w1 << 2) | w), 0);
+ }
+ else
+ {
+ the_insn.reloc = R_HPPA_PCREL_CALL;
+ the_insn.format = 12;
+ the_insn.arg_reloc = last_call_desc.arg_reloc;
+ memset (&last_call_desc, 0, sizeof (struct call_desc));
+ s = expr_end;
+ continue;
+ }
+
+ /* Handle a 17 bit branch displacement. */
+ case 'W':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ the_insn.pcrel = 1;
+ if (!the_insn.exp.X_add_symbol
+ || !strcmp (S_GET_NAME (the_insn.exp.X_add_symbol),
+ "L$0\001"))
+ {
+ unsigned int w2, w1, w, result;
+
+ num = evaluate_absolute (&the_insn);
+ if (num % 4)
+ {
+ as_bad (_("Branch to unaligned address"));
+ break;
+ }
+ CHECK_FIELD (num, 262143, -262144, 0);
+
+ if (the_insn.exp.X_add_symbol)
+ num -= 8;
+
+ sign_unext (num >> 2, 17, &result);
+ dis_assemble_17 (result, &w1, &w2, &w);
+ INSERT_FIELD_AND_CONTINUE (opcode,
+ ((w2 << 2) | (w1 << 16) | w), 0);
+ }
+ else
+ {
+ the_insn.reloc = R_HPPA_PCREL_CALL;
+ the_insn.format = 17;
+ the_insn.arg_reloc = last_call_desc.arg_reloc;
+ memset (&last_call_desc, 0, sizeof (struct call_desc));
+ continue;
+ }
+
+ /* Handle an absolute 17 bit branch target. */
+ case 'z':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ the_insn.pcrel = 0;
+ if (!the_insn.exp.X_add_symbol
+ || !strcmp (S_GET_NAME (the_insn.exp.X_add_symbol),
+ "L$0\001"))
+ {
+ unsigned int w2, w1, w, result;
+
+ num = evaluate_absolute (&the_insn);
+ if (num % 4)
+ {
+ as_bad (_("Branch to unaligned address"));
+ break;
+ }
+ CHECK_FIELD (num, 262143, -262144, 0);
+
+ if (the_insn.exp.X_add_symbol)
+ num -= 8;
+
+ sign_unext (num >> 2, 17, &result);
+ dis_assemble_17 (result, &w1, &w2, &w);
+ INSERT_FIELD_AND_CONTINUE (opcode,
+ ((w2 << 2) | (w1 << 16) | w), 0);
+ }
+ else
+ {
+ the_insn.reloc = R_HPPA_ABS_CALL;
+ the_insn.format = 17;
+ the_insn.arg_reloc = last_call_desc.arg_reloc;
+ memset (&last_call_desc, 0, sizeof (struct call_desc));
+ continue;
+ }
+
+ /* Handle a 5 bit shift count at 26. */
+ case 'p':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ s = expr_end;
+ CHECK_FIELD (num, 31, 0, 0);
+ INSERT_FIELD_AND_CONTINUE (opcode, 31 - num, 5);
+
+ /* Handle a 5 bit bit position at 26. */
+ case 'P':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ s = expr_end;
+ CHECK_FIELD (num, 31, 0, 0);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 5);
+
+ /* Handle a 5 bit immediate at 10. */
+ case 'Q':
+
+ num = pa_get_absolute_expression (&the_insn, &s);
+ if (the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 31, 0, 0);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 21);
+
+ /* Handle a 13 bit immediate at 18. */
+ case 'A':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ s = expr_end;
+ CHECK_FIELD (num, 8191, 0, 0);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 13);
+
+ /* Handle a 26 bit immediate at 31. */
+ case 'D':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ s = expr_end;
+ CHECK_FIELD (num, 671108864, 0, 0);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+
+ /* Handle a 3 bit SFU identifier at 25. */
+ case 'f':
+ if (*s++ != ',')
+ as_bad (_("Invalid SFU identifier"));
+ num = pa_get_absolute_expression (&the_insn, &s);
+ s = expr_end;
+ CHECK_FIELD (num, 7, 0, 0);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 6);
+
+ /* Handle a 20 bit SOP field for spop0. */
+ case 'O':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ s = expr_end;
+ CHECK_FIELD (num, 1048575, 0, 0);
+ num = (num & 0x1f) | ((num & 0x000fffe0) << 6);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+
+ /* Handle a 15bit SOP field for spop1. */
+ case 'o':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ s = expr_end;
+ CHECK_FIELD (num, 32767, 0, 0);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 11);
+
+ /* Handle a 10bit SOP field for spop3. */
+ case '0':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ s = expr_end;
+ CHECK_FIELD (num, 1023, 0, 0);
+ num = (num & 0x1f) | ((num & 0x000003e0) << 6);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+
+ /* Handle a 15 bit SOP field for spop2. */
+ case '1':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ s = expr_end;
+ CHECK_FIELD (num, 32767, 0, 0);
+ num = (num & 0x1f) | ((num & 0x00007fe0) << 6);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+
+ /* Handle a 3-bit co-processor ID field. */
+ case 'u':
+ if (*s++ != ',')
+ as_bad (_("Invalid COPR identifier"));
+ num = pa_get_absolute_expression (&the_insn, &s);
+ s = expr_end;
+ CHECK_FIELD (num, 7, 0, 0);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 6);
+
+ /* Handle a 22bit SOP field for copr. */
+ case '2':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ s = expr_end;
+ CHECK_FIELD (num, 4194303, 0, 0);
+ num = (num & 0x1f) | ((num & 0x003fffe0) << 4);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+
+
+ /* Handle a source FP operand format completer. */
+ case 'F':
+ flag = pa_parse_fp_format (&s);
+ the_insn.fpof1 = flag;
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 11);
+
+ /* Handle a destination FP operand format completer. */
+ case 'G':
+ /* pa_parse_format needs the ',' prefix. */
+ s--;
+ flag = pa_parse_fp_format (&s);
+ the_insn.fpof2 = flag;
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 13);
+
+ /* Handle FP compare conditions. */
+ case 'M':
+ cond = pa_parse_fp_cmp_cond (&s);
+ INSERT_FIELD_AND_CONTINUE (opcode, cond, 0);
+
+ /* Handle L/R register halves like 't'. */
+ case 'v':
+ {
+ struct pa_11_fp_reg_struct result;
+
+ pa_parse_number (&s, &result);
+ CHECK_FIELD (result.number_part, 31, 0, 0);
+ opcode |= result.number_part;
+
+ /* 0x30 opcodes are FP arithmetic operation opcodes
+ and need to be turned into 0x38 opcodes. This
+ is not necessary for loads/stores. */
+ if (need_pa11_opcode (&the_insn, &result)
+ && ((opcode & 0xfc000000) == 0x30000000))
+ opcode |= 1 << 27;
+
+ INSERT_FIELD_AND_CONTINUE (opcode, result.l_r_select & 1, 6);
+ }
+
+ /* Handle L/R register halves like 'b'. */
+ case 'E':
+ {
+ struct pa_11_fp_reg_struct result;
+
+ pa_parse_number (&s, &result);
+ CHECK_FIELD (result.number_part, 31, 0, 0);
+ opcode |= result.number_part << 21;
+ if (need_pa11_opcode (&the_insn, &result))
+ {
+ opcode |= (result.l_r_select & 1) << 7;
+ opcode |= 1 << 27;
+ }
+ continue;
+ }
+
+ /* Handle L/R register halves like 'b'. */
+ case '3':
+ {
+ struct pa_11_fp_reg_struct result;
+ int regnum;
+
+ pa_parse_number (&s, &result);
+ CHECK_FIELD (result.number_part, 31, 0, 0);
+ opcode |= (result.number_part & 0x1c) << 11;
+ opcode |= (result.number_part & 0x3) << 9;
+ opcode |= (result.l_r_select & 1) << 8;
+ continue;
+ }
+
+ /* Handle L/R register halves like 'x'. */
+ case 'e':
+ {
+ struct pa_11_fp_reg_struct result;
+
+ pa_parse_number (&s, &result);
+ CHECK_FIELD (result.number_part, 31, 0, 0);
+ opcode |= (result.number_part & 0x1f) << 16;
+ if (need_pa11_opcode (&the_insn, &result))
+ {
+ opcode |= (result.l_r_select & 1) << 1;
+ }
+ continue;
+ }
+
+ /* Handle L/R register halves like 'x'. */
+ case 'X':
+ {
+ struct pa_11_fp_reg_struct result;
+
+ pa_parse_number (&s, &result);
+ CHECK_FIELD (result.number_part, 31, 0, 0);
+ opcode |= (result.number_part & 0x1f) << 16;
+ if (need_pa11_opcode (&the_insn, &result))
+ {
+ opcode |= (result.l_r_select & 1) << 12;
+ opcode |= 1 << 27;
+ }
+ continue;
+ }
+
+ /* Handle a 5 bit register field at 10. */
+ case '4':
+ {
+ struct pa_11_fp_reg_struct result;
+
+ pa_parse_number (&s, &result);
+ CHECK_FIELD (result.number_part, 31, 0, 0);
+ if (the_insn.fpof1 == SGL)
+ {
+ if (result.number_part < 16)
+ {
+ as_bad (_("Invalid register for single precision fmpyadd or fmpysub"));
+ break;
+ }
+
+ result.number_part &= 0xF;
+ result.number_part |= (result.l_r_select & 1) << 4;
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, result.number_part, 21);
+ }
+
+ /* Handle a 5 bit register field at 15. */
+ case '6':
+ {
+ struct pa_11_fp_reg_struct result;
+
+ pa_parse_number (&s, &result);
+ CHECK_FIELD (result.number_part, 31, 0, 0);
+ if (the_insn.fpof1 == SGL)
+ {
+ if (result.number_part < 16)
+ {
+ as_bad (_("Invalid register for single precision fmpyadd or fmpysub"));
+ break;
+ }
+ result.number_part &= 0xF;
+ result.number_part |= (result.l_r_select & 1) << 4;
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, result.number_part, 16);
+ }
+
+ /* Handle a 5 bit register field at 31. */
+ case '7':
+ {
+ struct pa_11_fp_reg_struct result;
+
+ pa_parse_number (&s, &result);
+ CHECK_FIELD (result.number_part, 31, 0, 0);
+ if (the_insn.fpof1 == SGL)
+ {
+ if (result.number_part < 16)
+ {
+ as_bad (_("Invalid register for single precision fmpyadd or fmpysub"));
+ break;
+ }
+ result.number_part &= 0xF;
+ result.number_part |= (result.l_r_select & 1) << 4;
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, result.number_part, 0);
+ }
+
+ /* Handle a 5 bit register field at 20. */
+ case '8':
+ {
+ struct pa_11_fp_reg_struct result;
+
+ pa_parse_number (&s, &result);
+ CHECK_FIELD (result.number_part, 31, 0, 0);
+ if (the_insn.fpof1 == SGL)
+ {
+ if (result.number_part < 16)
+ {
+ as_bad (_("Invalid register for single precision fmpyadd or fmpysub"));
+ break;
+ }
+ result.number_part &= 0xF;
+ result.number_part |= (result.l_r_select & 1) << 4;
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, result.number_part, 11);
+ }
+
+ /* Handle a 5 bit register field at 25. */
+ case '9':
+ {
+ struct pa_11_fp_reg_struct result;
+
+ pa_parse_number (&s, &result);
+ CHECK_FIELD (result.number_part, 31, 0, 0);
+ if (the_insn.fpof1 == SGL)
+ {
+ if (result.number_part < 16)
+ {
+ as_bad (_("Invalid register for single precision fmpyadd or fmpysub"));
+ break;
+ }
+ result.number_part &= 0xF;
+ result.number_part |= (result.l_r_select & 1) << 4;
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, result.number_part, 6);
+ }
+
+ /* Handle a floating point operand format at 26.
+ Only allows single and double precision. */
+ case 'H':
+ flag = pa_parse_fp_format (&s);
+ switch (flag)
+ {
+ case SGL:
+ opcode |= 0x20;
+ case DBL:
+ the_insn.fpof1 = flag;
+ continue;
+
+ case QUAD:
+ case ILLEGAL_FMT:
+ default:
+ as_bad (_("Invalid Floating Point Operand Format."));
+ }
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+ }
+
+ failed:
+ /* Check if the args matched. */
+ if (match == FALSE)
+ {
+ if (&insn[1] - pa_opcodes < (int) NUMOPCODES
+ && !strcmp (insn->name, insn[1].name))
+ {
+ ++insn;
+ s = argstart;
+ continue;
+ }
+ else
+ {
+ as_bad (_("Invalid operands %s"), error_message);
+ return;
+ }
+ }
+ break;
+ }
+
+ the_insn.opcode = opcode;
+}
+
+/* Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP . An error message or NULL is returned. */
+
+#define MAX_LITTLENUMS 6
+
+char *
+md_atof (type, litP, sizeP)
+ char type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+
+ switch (type)
+ {
+
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("Bad call to MD_ATOF()");
+ }
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+ *sizeP = prec * sizeof (LITTLENUM_TYPE);
+ for (wordP = words; prec--;)
+ {
+ md_number_to_chars (litP, (valueT) (*wordP++), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ return NULL;
+}
+
+/* Write out big-endian. */
+
+void
+md_number_to_chars (buf, val, n)
+ char *buf;
+ valueT val;
+ int n;
+{
+ number_to_chars_bigendian (buf, val, n);
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format. */
+
+arelent **
+tc_gen_reloc (section, fixp)
+ asection *section;
+ fixS *fixp;
+{
+ arelent *reloc;
+ struct hppa_fix_struct *hppa_fixp;
+ bfd_reloc_code_real_type code;
+ static arelent *no_relocs = NULL;
+ arelent **relocs;
+ bfd_reloc_code_real_type **codes;
+ int n_relocs;
+ int i;
+
+ hppa_fixp = (struct hppa_fix_struct *) fixp->tc_fix_data;
+ if (fixp->fx_addsy == 0)
+ return &no_relocs;
+ assert (hppa_fixp != 0);
+ assert (section != 0);
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+
+ reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+ codes = (bfd_reloc_code_real_type **) hppa_gen_reloc_type (stdoutput,
+ fixp->fx_r_type,
+ hppa_fixp->fx_r_format,
+ hppa_fixp->fx_r_field,
+ fixp->fx_subsy != NULL,
+ fixp->fx_addsy->bsym);
+
+ if (codes == NULL)
+ abort ();
+
+ for (n_relocs = 0; codes[n_relocs]; n_relocs++)
+ ;
+
+ relocs = (arelent **) xmalloc (sizeof (arelent *) * n_relocs + 1);
+ reloc = (arelent *) xmalloc (sizeof (arelent) * n_relocs);
+ for (i = 0; i < n_relocs; i++)
+ relocs[i] = &reloc[i];
+
+ relocs[n_relocs] = NULL;
+
+#ifdef OBJ_ELF
+ switch (fixp->fx_r_type)
+ {
+ default:
+ assert (n_relocs == 1);
+
+ code = *codes[0];
+
+ reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->addend = 0; /* default */
+
+ assert (reloc->howto && code == reloc->howto->type);
+
+ /* Now, do any processing that is dependent on the relocation type. */
+ switch (code)
+ {
+ case R_PARISC_DLTREL21L:
+ case R_PARISC_DLTREL14R:
+ case R_PARISC_DLTREL14F:
+ case R_PARISC_PLABEL32:
+ case R_PARISC_PLABEL21L:
+ case R_PARISC_PLABEL14R:
+ /* For plabel relocations, the addend of the
+ relocation should be either 0 (no static link) or 2
+ (static link required).
+
+ FIXME: We always assume no static link!
+
+ We also slam a zero addend into the DLT relative relocs;
+ it doesn't make a lot of sense to use any addend since
+ it gets you a different (eg unknown) DLT entry. */
+ reloc->addend = 0;
+ break;
+
+ case R_PARISC_PCREL21L:
+ case R_PARISC_PCREL17R:
+ case R_PARISC_PCREL17F:
+ case R_PARISC_PCREL17C:
+ case R_PARISC_PCREL14R:
+ case R_PARISC_PCREL14F:
+ /* The constant is stored in the instruction. */
+ reloc->addend = HPPA_R_ADDEND (hppa_fixp->fx_arg_reloc, 0);
+ break;
+ default:
+ reloc->addend = fixp->fx_offset;
+ break;
+ }
+ break;
+ }
+#else /* OBJ_SOM */
+
+ /* Walk over reach relocation returned by the BFD backend. */
+ for (i = 0; i < n_relocs; i++)
+ {
+ code = *codes[i];
+
+ relocs[i]->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+ relocs[i]->howto = bfd_reloc_type_lookup (stdoutput, code);
+ relocs[i]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ switch (code)
+ {
+ case R_COMP2:
+ /* The only time we ever use a R_COMP2 fixup is for the difference
+ of two symbols. With that in mind we fill in all four
+ relocs now and break out of the loop. */
+ assert (i == 1);
+ relocs[0]->sym_ptr_ptr = (asymbol **) &bfd_abs_symbol;
+ relocs[0]->howto = bfd_reloc_type_lookup (stdoutput, *codes[0]);
+ relocs[0]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ relocs[0]->addend = 0;
+ relocs[1]->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+ relocs[1]->howto = bfd_reloc_type_lookup (stdoutput, *codes[1]);
+ relocs[1]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ relocs[1]->addend = 0;
+ relocs[2]->sym_ptr_ptr = &fixp->fx_subsy->bsym;
+ relocs[2]->howto = bfd_reloc_type_lookup (stdoutput, *codes[2]);
+ relocs[2]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ relocs[2]->addend = 0;
+ relocs[3]->sym_ptr_ptr = (asymbol **) &bfd_abs_symbol;
+ relocs[3]->howto = bfd_reloc_type_lookup (stdoutput, *codes[3]);
+ relocs[3]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ relocs[3]->addend = 0;
+ relocs[4]->sym_ptr_ptr = (asymbol **) &bfd_abs_symbol;
+ relocs[4]->howto = bfd_reloc_type_lookup (stdoutput, *codes[4]);
+ relocs[4]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ relocs[4]->addend = 0;
+ goto done;
+ case R_PCREL_CALL:
+ case R_ABS_CALL:
+ relocs[i]->addend = HPPA_R_ADDEND (hppa_fixp->fx_arg_reloc, 0);
+ break;
+
+ case R_DLT_REL:
+ case R_DATA_PLABEL:
+ case R_CODE_PLABEL:
+ /* For plabel relocations, the addend of the
+ relocation should be either 0 (no static link) or 2
+ (static link required).
+
+ FIXME: We always assume no static link!
+
+ We also slam a zero addend into the DLT relative relocs;
+ it doesn't make a lot of sense to use any addend since
+ it gets you a different (eg unknown) DLT entry. */
+ relocs[i]->addend = 0;
+ break;
+
+ case R_N_MODE:
+ case R_S_MODE:
+ case R_D_MODE:
+ case R_R_MODE:
+ case R_FSEL:
+ case R_LSEL:
+ case R_RSEL:
+ case R_BEGIN_BRTAB:
+ case R_END_BRTAB:
+ case R_BEGIN_TRY:
+ case R_N0SEL:
+ case R_N1SEL:
+ /* There is no symbol or addend associated with these fixups. */
+ relocs[i]->sym_ptr_ptr = &dummy_symbol->bsym;
+ relocs[i]->addend = 0;
+ break;
+
+ case R_END_TRY:
+ case R_ENTRY:
+ case R_EXIT:
+ /* There is no symbol associated with these fixups. */
+ relocs[i]->sym_ptr_ptr = &dummy_symbol->bsym;
+ relocs[i]->addend = fixp->fx_offset;
+ break;
+
+ default:
+ relocs[i]->addend = fixp->fx_offset;
+ }
+ }
+
+ done:
+#endif
+
+ return relocs;
+}
+
+/* Process any machine dependent frag types. */
+
+void
+md_convert_frag (abfd, sec, fragP)
+ register bfd *abfd;
+ register asection *sec;
+ register fragS *fragP;
+{
+ unsigned int address;
+
+ if (fragP->fr_type == rs_machine_dependent)
+ {
+ switch ((int) fragP->fr_subtype)
+ {
+ case 0:
+ fragP->fr_type = rs_fill;
+ know (fragP->fr_var == 1);
+ know (fragP->fr_next);
+ address = fragP->fr_address + fragP->fr_fix;
+ if (address % fragP->fr_offset)
+ {
+ fragP->fr_offset =
+ fragP->fr_next->fr_address
+ - fragP->fr_address
+ - fragP->fr_fix;
+ }
+ else
+ fragP->fr_offset = 0;
+ break;
+ }
+ }
+}
+
+/* Round up a section size to the appropriate boundary. */
+
+valueT
+md_section_align (segment, size)
+ asection *segment;
+ valueT size;
+{
+ int align = bfd_get_section_alignment (stdoutput, segment);
+ int align2 = (1 << align) - 1;
+
+ return (size + align2) & ~align2;
+}
+
+/* Return the approximate size of a frag before relaxation has occurred. */
+int
+md_estimate_size_before_relax (fragP, segment)
+ register fragS *fragP;
+ asection *segment;
+{
+ int size;
+
+ size = 0;
+
+ while ((fragP->fr_fix + size) % fragP->fr_offset)
+ size++;
+
+ return size;
+}
+
+CONST char *md_shortopts = "";
+struct option md_longopts[] = {
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ return 0;
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+}
+
+/* We have no need to default values of symbols. */
+
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ return 0;
+}
+
+/* Apply a fixup to an instruction. */
+
+int
+md_apply_fix (fixP, valp)
+ fixS *fixP;
+ valueT *valp;
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ struct hppa_fix_struct *hppa_fixP;
+ long new_val, result = 0;
+ unsigned int w1, w2, w, resulti;
+
+ hppa_fixP = (struct hppa_fix_struct *) fixP->tc_fix_data;
+ /* SOM uses R_HPPA_ENTRY and R_HPPA_EXIT relocations which can
+ never be "applied" (they are just markers). Likewise for
+ R_HPPA_BEGIN_BRTAB and R_HPPA_END_BRTAB. */
+#ifdef OBJ_SOM
+ if (fixP->fx_r_type == R_HPPA_ENTRY
+ || fixP->fx_r_type == R_HPPA_EXIT
+ || fixP->fx_r_type == R_HPPA_BEGIN_BRTAB
+ || fixP->fx_r_type == R_HPPA_END_BRTAB
+ || fixP->fx_r_type == R_HPPA_BEGIN_TRY)
+ return 1;
+
+ /* Disgusting. We must set fx_offset ourselves -- R_HPPA_END_TRY
+ fixups are considered not adjustable, which in turn causes
+ adjust_reloc_syms to not set fx_offset. Ugh. */
+ if (fixP->fx_r_type == R_HPPA_END_TRY)
+ {
+ fixP->fx_offset = *valp;
+ return 1;
+ }
+#endif
+
+ /* There should have been an HPPA specific fixup associated
+ with the GAS fixup. */
+ if (hppa_fixP)
+ {
+ unsigned long buf_wd = bfd_get_32 (stdoutput, buf);
+ unsigned char fmt = bfd_hppa_insn2fmt (buf_wd);
+
+ /* If there is a symbol associated with this fixup, then it's something
+ which will need a SOM relocation (except for some PC-relative relocs).
+ In such cases we should treat the "val" or "addend" as zero since it
+ will be added in as needed from fx_offset in tc_gen_reloc. */
+ if ((fixP->fx_addsy != NULL
+ || fixP->fx_r_type == R_HPPA_NONE)
+#ifdef OBJ_SOM
+ && fmt != 32
+#endif
+ )
+ new_val = ((fmt == 12 || fmt == 17) ? 8 : 0);
+#ifdef OBJ_SOM
+ /* These field selectors imply that we do not want an addend. */
+ else if (hppa_fixP->fx_r_field == e_psel
+ || hppa_fixP->fx_r_field == e_rpsel
+ || hppa_fixP->fx_r_field == e_lpsel
+ || hppa_fixP->fx_r_field == e_tsel
+ || hppa_fixP->fx_r_field == e_rtsel
+ || hppa_fixP->fx_r_field == e_ltsel)
+ new_val = ((fmt == 12 || fmt == 17) ? 8 : 0);
+ /* This is truely disgusting. The machine independent code blindly
+ adds in the value of the symbol being relocated against. Damn! */
+ else if (fmt == 32
+ && fixP->fx_addsy != NULL
+ && S_GET_SEGMENT (fixP->fx_addsy) != bfd_com_section_ptr)
+ new_val = hppa_field_adjust (*valp - S_GET_VALUE (fixP->fx_addsy),
+ 0, hppa_fixP->fx_r_field);
+#endif
+ else
+ new_val = hppa_field_adjust (*valp, 0, hppa_fixP->fx_r_field);
+
+ /* Handle pc-relative exceptions from above. */
+#define arg_reloc_stub_needed(CALLER, CALLEE) \
+ ((CALLEE) && (CALLER) && ((CALLEE) != (CALLER)))
+ if ((fmt == 12 || fmt == 17)
+ && fixP->fx_addsy
+ && fixP->fx_pcrel
+ && !arg_reloc_stub_needed ((long) ((obj_symbol_type *)
+ fixP->fx_addsy->bsym)->tc_data.ap.hppa_arg_reloc,
+ hppa_fixP->fx_arg_reloc)
+ && ((int)(*valp) > -262144 && (int)(*valp) < 262143)
+ && S_GET_SEGMENT (fixP->fx_addsy) == hppa_fixP->segment
+ && !(fixP->fx_subsy
+ && S_GET_SEGMENT (fixP->fx_subsy) != hppa_fixP->segment))
+
+ new_val = hppa_field_adjust (*valp, 0, hppa_fixP->fx_r_field);
+#undef arg_reloc_stub_needed
+
+ switch (fmt)
+ {
+ /* Handle all opcodes with the 'j' operand type. */
+ case 14:
+ CHECK_FIELD (new_val, 8191, -8192, 0);
+
+ /* Mask off 14 bits to be changed. */
+ bfd_put_32 (stdoutput,
+ bfd_get_32 (stdoutput, buf) & 0xffffc000,
+ buf);
+ low_sign_unext (new_val, 14, &resulti);
+ result = resulti;
+ break;
+
+ /* Handle all opcodes with the 'k' operand type. */
+ case 21:
+ CHECK_FIELD (new_val, 2097152, 0, 0);
+
+ /* Mask off 21 bits to be changed. */
+ bfd_put_32 (stdoutput,
+ bfd_get_32 (stdoutput, buf) & 0xffe00000,
+ buf);
+ dis_assemble_21 (new_val, &resulti);
+ result = resulti;
+ break;
+
+ /* Handle all the opcodes with the 'i' operand type. */
+ case 11:
+ CHECK_FIELD (new_val, 1023, -1023, 0);
+
+ /* Mask off 11 bits to be changed. */
+ bfd_put_32 (stdoutput,
+ bfd_get_32 (stdoutput, buf) & 0xffff800,
+ buf);
+ low_sign_unext (new_val, 11, &resulti);
+ result = resulti;
+ break;
+
+ /* Handle all the opcodes with the 'w' operand type. */
+ case 12:
+ CHECK_FIELD (new_val, 8199, -8184, 0);
+
+ /* Mask off 11 bits to be changed. */
+ sign_unext ((new_val - 8) >> 2, 12, &resulti);
+ bfd_put_32 (stdoutput,
+ bfd_get_32 (stdoutput, buf) & 0xffffe002,
+ buf);
+
+ dis_assemble_12 (resulti, &w1, &w);
+ result = ((w1 << 2) | w);
+ break;
+
+ /* Handle some of the opcodes with the 'W' operand type. */
+ case 17:
+ {
+ int distance = *valp;
+
+ CHECK_FIELD (new_val, 262143, -262144, 0);
+
+ /* If this is an absolute branch (ie no link) with an out of
+ range target, then we want to complain. */
+ if (fixP->fx_r_type == R_HPPA_PCREL_CALL
+ && (distance > 262143 || distance < -262144)
+ && (bfd_get_32 (stdoutput, buf) & 0xffe00000) == 0xe8000000)
+ CHECK_FIELD (distance, 262143, -262144, 0);
+
+ /* Mask off 17 bits to be changed. */
+ bfd_put_32 (stdoutput,
+ bfd_get_32 (stdoutput, buf) & 0xffe0e002,
+ buf);
+ sign_unext ((new_val - 8) >> 2, 17, &resulti);
+ dis_assemble_17 (resulti, &w1, &w2, &w);
+ result = ((w2 << 2) | (w1 << 16) | w);
+ break;
+ }
+
+ case 32:
+ result = 0;
+ bfd_put_32 (stdoutput, new_val, buf);
+ break;
+
+ default:
+ as_bad (_("Unknown relocation encountered in md_apply_fix."));
+ return 0;
+ }
+
+ /* Insert the relocation. */
+ bfd_put_32 (stdoutput, bfd_get_32 (stdoutput, buf) | result, buf);
+ return 1;
+ }
+ else
+ {
+ printf (_("no hppa_fixup entry for this fixup (fixP = 0x%x, type = 0x%x)\n"),
+ (unsigned int) fixP, fixP->fx_r_type);
+ return 0;
+ }
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the PA, they're relative to the address of the offset. */
+
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ return fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+/* Return nonzero if the input line pointer is at the end of
+ a statement. */
+
+static int
+is_end_of_statement ()
+{
+ return ((*input_line_pointer == '\n')
+ || (*input_line_pointer == ';')
+ || (*input_line_pointer == '!'));
+}
+
+/* Read a number from S. The number might come in one of many forms,
+ the most common will be a hex or decimal constant, but it could be
+ a pre-defined register (Yuk!), or an absolute symbol.
+
+ Return a number or -1 for failure.
+
+ When parsing PA-89 FP register numbers RESULT will be
+ the address of a structure to return information about
+ L/R half of FP registers, store results there as appropriate.
+
+ pa_parse_number can not handle negative constants and will fail
+ horribly if it is passed such a constant. */
+
+static int
+pa_parse_number (s, result)
+ char **s;
+ struct pa_11_fp_reg_struct *result;
+{
+ int num;
+ char *name;
+ char c;
+ symbolS *sym;
+ int status;
+ char *p = *s;
+
+ /* Skip whitespace before the number. */
+ while (*p == ' ' || *p == '\t')
+ p = p + 1;
+
+ /* Store info in RESULT if requested by caller. */
+ if (result)
+ {
+ result->number_part = -1;
+ result->l_r_select = -1;
+ }
+ num = -1;
+
+ if (isdigit (*p))
+ {
+ /* Looks like a number. */
+ num = 0;
+
+ if (*p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X'))
+ {
+ /* The number is specified in hex. */
+ p += 2;
+ while (isdigit (*p) || ((*p >= 'a') && (*p <= 'f'))
+ || ((*p >= 'A') && (*p <= 'F')))
+ {
+ if (isdigit (*p))
+ num = num * 16 + *p - '0';
+ else if (*p >= 'a' && *p <= 'f')
+ num = num * 16 + *p - 'a' + 10;
+ else
+ num = num * 16 + *p - 'A' + 10;
+ ++p;
+ }
+ }
+ else
+ {
+ /* The number is specified in decimal. */
+ while (isdigit (*p))
+ {
+ num = num * 10 + *p - '0';
+ ++p;
+ }
+ }
+
+ /* Store info in RESULT if requested by the caller. */
+ if (result)
+ {
+ result->number_part = num;
+
+ if (IS_R_SELECT (p))
+ {
+ result->l_r_select = 1;
+ ++p;
+ }
+ else if (IS_L_SELECT (p))
+ {
+ result->l_r_select = 0;
+ ++p;
+ }
+ else
+ result->l_r_select = 0;
+ }
+ }
+ else if (*p == '%')
+ {
+ /* The number might be a predefined register. */
+ num = 0;
+ name = p;
+ p++;
+ c = *p;
+ /* Tege hack: Special case for general registers as the general
+ code makes a binary search with case translation, and is VERY
+ slow. */
+ if (c == 'r')
+ {
+ p++;
+ if (*p == 'e' && *(p + 1) == 't'
+ && (*(p + 2) == '0' || *(p + 2) == '1'))
+ {
+ p += 2;
+ num = *p - '0' + 28;
+ p++;
+ }
+ else if (*p == 'p')
+ {
+ num = 2;
+ p++;
+ }
+ else if (!isdigit (*p))
+ {
+ if (print_errors)
+ as_bad (_("Undefined register: '%s'."), name);
+ num = -1;
+ }
+ else
+ {
+ do
+ num = num * 10 + *p++ - '0';
+ while (isdigit (*p));
+ }
+ }
+ else
+ {
+ /* Do a normal register search. */
+ while (is_part_of_name (c))
+ {
+ p = p + 1;
+ c = *p;
+ }
+ *p = 0;
+ status = reg_name_search (name);
+ if (status >= 0)
+ num = status;
+ else
+ {
+ if (print_errors)
+ as_bad (_("Undefined register: '%s'."), name);
+ num = -1;
+ }
+ *p = c;
+ }
+
+ /* Store info in RESULT if requested by caller. */
+ if (result)
+ {
+ result->number_part = num;
+ if (IS_R_SELECT (p - 1))
+ result->l_r_select = 1;
+ else if (IS_L_SELECT (p - 1))
+ result->l_r_select = 0;
+ else
+ result->l_r_select = 0;
+ }
+ }
+ else
+ {
+ /* And finally, it could be a symbol in the absolute section which
+ is effectively a constant. */
+ num = 0;
+ name = p;
+ c = *p;
+ while (is_part_of_name (c))
+ {
+ p = p + 1;
+ c = *p;
+ }
+ *p = 0;
+ if ((sym = symbol_find (name)) != NULL)
+ {
+ if (S_GET_SEGMENT (sym) == &bfd_abs_section)
+ num = S_GET_VALUE (sym);
+ else
+ {
+ if (print_errors)
+ as_bad (_("Non-absolute symbol: '%s'."), name);
+ num = -1;
+ }
+ }
+ else
+ {
+ /* There is where we'd come for an undefined symbol
+ or for an empty string. For an empty string we
+ will return zero. That's a concession made for
+ compatability with the braindamaged HP assemblers. */
+ if (*name == 0)
+ num = 0;
+ else
+ {
+ if (print_errors)
+ as_bad (_("Undefined absolute constant: '%s'."), name);
+ num = -1;
+ }
+ }
+ *p = c;
+
+ /* Store info in RESULT if requested by caller. */
+ if (result)
+ {
+ result->number_part = num;
+ if (IS_R_SELECT (p - 1))
+ result->l_r_select = 1;
+ else if (IS_L_SELECT (p - 1))
+ result->l_r_select = 0;
+ else
+ result->l_r_select = 0;
+ }
+ }
+
+ *s = p;
+ return num;
+}
+
+#define REG_NAME_CNT (sizeof(pre_defined_registers) / sizeof(struct pd_reg))
+
+/* Given NAME, find the register number associated with that name, return
+ the integer value associated with the given name or -1 on failure. */
+
+static int
+reg_name_search (name)
+ char *name;
+{
+ int middle, low, high;
+ int cmp;
+
+ low = 0;
+ high = REG_NAME_CNT - 1;
+
+ do
+ {
+ middle = (low + high) / 2;
+ cmp = strcasecmp (name, pre_defined_registers[middle].name);
+ if (cmp < 0)
+ high = middle - 1;
+ else if (cmp > 0)
+ low = middle + 1;
+ else
+ return pre_defined_registers[middle].value;
+ }
+ while (low <= high);
+
+ return -1;
+}
+
+
+/* Return nonzero if the given INSN and L/R information will require
+ a new PA-1.1 opcode. */
+
+static int
+need_pa11_opcode (insn, result)
+ struct pa_it *insn;
+ struct pa_11_fp_reg_struct *result;
+{
+ if (result->l_r_select == 1 && !(insn->fpof1 == DBL && insn->fpof2 == DBL))
+ {
+ /* If this instruction is specific to a particular architecture,
+ then set a new architecture. */
+ if (bfd_get_mach (stdoutput) < pa11)
+ {
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_hppa, pa11))
+ as_warn (_("could not update architecture and machine"));
+ }
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/* Parse a condition for a fcmp instruction. Return the numerical
+ code associated with the condition. */
+
+static int
+pa_parse_fp_cmp_cond (s)
+ char **s;
+{
+ int cond, i;
+
+ cond = 0;
+
+ for (i = 0; i < 32; i++)
+ {
+ if (strncasecmp (*s, fp_cond_map[i].string,
+ strlen (fp_cond_map[i].string)) == 0)
+ {
+ cond = fp_cond_map[i].cond;
+ *s += strlen (fp_cond_map[i].string);
+ /* If not a complete match, back up the input string and
+ report an error. */
+ if (**s != ' ' && **s != '\t')
+ {
+ *s -= strlen (fp_cond_map[i].string);
+ break;
+ }
+ while (**s == ' ' || **s == '\t')
+ *s = *s + 1;
+ return cond;
+ }
+ }
+
+ as_bad (_("Invalid FP Compare Condition: %s"), *s);
+
+ /* Advance over the bogus completer. */
+ while (**s != ',' && **s != ' ' && **s != '\t')
+ *s += 1;
+
+ return 0;
+}
+
+
+/* Parse an FP operand format completer returning the completer
+ type. */
+
+static fp_operand_format
+pa_parse_fp_format (s)
+ char **s;
+{
+ int format;
+
+ format = SGL;
+ if (**s == ',')
+ {
+ *s += 1;
+ if (strncasecmp (*s, "sgl", 3) == 0)
+ {
+ format = SGL;
+ *s += 4;
+ }
+ else if (strncasecmp (*s, "dbl", 3) == 0)
+ {
+ format = DBL;
+ *s += 4;
+ }
+ else if (strncasecmp (*s, "quad", 4) == 0)
+ {
+ format = QUAD;
+ *s += 5;
+ }
+ else
+ {
+ format = ILLEGAL_FMT;
+ as_bad (_("Invalid FP Operand Format: %3s"), *s);
+ }
+ }
+
+ return format;
+}
+
+/* Convert from a selector string into a selector type. */
+
+static int
+pa_chk_field_selector (str)
+ char **str;
+{
+ int middle, low, high;
+ int cmp;
+ char name[4];
+
+ /* Read past any whitespace. */
+ /* FIXME: should we read past newlines and formfeeds??? */
+ while (**str == ' ' || **str == '\t' || **str == '\n' || **str == '\f')
+ *str = *str + 1;
+
+ if ((*str)[1] == '\'' || (*str)[1] == '%')
+ name[0] = tolower ((*str)[0]),
+ name[1] = 0;
+ else if ((*str)[2] == '\'' || (*str)[2] == '%')
+ name[0] = tolower ((*str)[0]),
+ name[1] = tolower ((*str)[1]),
+ name[2] = 0;
+#ifdef OBJ_SOM
+ else if ((*str)[3] == '\'' || (*str)[3] == '%')
+ name[0] = tolower ((*str)[0]),
+ name[1] = tolower ((*str)[1]),
+ name[2] = tolower ((*str)[2]),
+ name[3] = 0;
+#endif
+ else
+ return e_fsel;
+
+ low = 0;
+ high = sizeof (selector_table) / sizeof (struct selector_entry) - 1;
+
+ do
+ {
+ middle = (low + high) / 2;
+ cmp = strcmp (name, selector_table[middle].prefix);
+ if (cmp < 0)
+ high = middle - 1;
+ else if (cmp > 0)
+ low = middle + 1;
+ else
+ {
+ *str += strlen (name) + 1;
+#ifndef OBJ_SOM
+ if (selector_table[middle].field_selector == e_nsel)
+ return e_fsel;
+#endif
+ return selector_table[middle].field_selector;
+ }
+ }
+ while (low <= high);
+
+ return e_fsel;
+}
+
+/* Mark (via expr_end) the end of an expression (I think). FIXME. */
+
+static int
+get_expression (str)
+ char *str;
+{
+ char *save_in;
+ asection *seg;
+
+ save_in = input_line_pointer;
+ input_line_pointer = str;
+ seg = expression (&the_insn.exp);
+ if (!(seg == absolute_section
+ || seg == undefined_section
+ || SEG_NORMAL (seg)))
+ {
+ as_warn (_("Bad segment in expression."));
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+ return 1;
+ }
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+ return 0;
+}
+
+/* Mark (via expr_end) the end of an absolute expression. FIXME. */
+static int
+pa_get_absolute_expression (insn, strp)
+ struct pa_it *insn;
+ char **strp;
+{
+ char *save_in;
+
+ insn->field_selector = pa_chk_field_selector (strp);
+ save_in = input_line_pointer;
+ input_line_pointer = *strp;
+ expression (&insn->exp);
+ /* This is not perfect, but is a huge improvement over doing nothing.
+
+ The PA assembly syntax is ambigious in a variety of ways. Consider
+ this string "4 %r5" Is that the number 4 followed by the register
+ r5, or is that 4 MOD 5?
+
+ If we get a modulo expresion When looking for an absolute, we try
+ again cutting off the input string at the first whitespace character. */
+ if (insn->exp.X_op == O_modulus)
+ {
+ char *s, c;
+ int retval;
+
+ input_line_pointer = *strp;
+ s = *strp;
+ while (*s != ',' && *s != ' ' && *s != '\t')
+ s++;
+
+ c = *s;
+ *s = 0;
+
+ retval = pa_get_absolute_expression (insn, strp);
+
+ input_line_pointer = save_in;
+ *s = c;
+ return evaluate_absolute (insn);
+ }
+ if (insn->exp.X_op != O_constant)
+ {
+ as_bad (_("Bad segment (should be absolute)."));
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+ return 0;
+ }
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+ return evaluate_absolute (insn);
+}
+
+/* Evaluate an absolute expression EXP which may be modified by
+ the selector FIELD_SELECTOR. Return the value of the expression. */
+static int
+evaluate_absolute (insn)
+ struct pa_it *insn;
+{
+ int value;
+ expressionS exp;
+ int field_selector = insn->field_selector;
+
+ exp = insn->exp;
+ value = exp.X_add_number;
+
+ switch (field_selector)
+ {
+ /* No change. */
+ case e_fsel:
+ break;
+
+ /* If bit 21 is on then add 0x800 and arithmetic shift right 11 bits. */
+ case e_lssel:
+ if (value & 0x00000400)
+ value += 0x800;
+ value = (value & 0xfffff800) >> 11;
+ break;
+
+ /* Sign extend from bit 21. */
+ case e_rssel:
+ if (value & 0x00000400)
+ value |= 0xfffff800;
+ else
+ value &= 0x7ff;
+ break;
+
+ /* Arithmetic shift right 11 bits. */
+ case e_lsel:
+ value = (value & 0xfffff800) >> 11;
+ break;
+
+ /* Set bits 0-20 to zero. */
+ case e_rsel:
+ value = value & 0x7ff;
+ break;
+
+ /* Add 0x800 and arithmetic shift right 11 bits. */
+ case e_ldsel:
+ value += 0x800;
+ value = (value & 0xfffff800) >> 11;
+ break;
+
+ /* Set bitgs 0-21 to one. */
+ case e_rdsel:
+ value |= 0xfffff800;
+ break;
+
+#define RSEL_ROUND(c) (((c) + 0x1000) & ~0x1fff)
+ case e_rrsel:
+ value = (RSEL_ROUND (value) & 0x7ff) + (value - RSEL_ROUND (value));
+ break;
+
+ case e_lrsel:
+ value = (RSEL_ROUND (value) >> 11) & 0x1fffff;
+ break;
+#undef RSEL_ROUND
+
+ default:
+ BAD_CASE (field_selector);
+ break;
+ }
+ return value;
+}
+
+/* Given an argument location specification return the associated
+ argument location number. */
+
+static unsigned int
+pa_build_arg_reloc (type_name)
+ char *type_name;
+{
+
+ if (strncasecmp (type_name, "no", 2) == 0)
+ return 0;
+ if (strncasecmp (type_name, "gr", 2) == 0)
+ return 1;
+ else if (strncasecmp (type_name, "fr", 2) == 0)
+ return 2;
+ else if (strncasecmp (type_name, "fu", 2) == 0)
+ return 3;
+ else
+ as_bad (_("Invalid argument location: %s\n"), type_name);
+
+ return 0;
+}
+
+/* Encode and return an argument relocation specification for
+ the given register in the location specified by arg_reloc. */
+
+static unsigned int
+pa_align_arg_reloc (reg, arg_reloc)
+ unsigned int reg;
+ unsigned int arg_reloc;
+{
+ unsigned int new_reloc;
+
+ new_reloc = arg_reloc;
+ switch (reg)
+ {
+ case 0:
+ new_reloc <<= 8;
+ break;
+ case 1:
+ new_reloc <<= 6;
+ break;
+ case 2:
+ new_reloc <<= 4;
+ break;
+ case 3:
+ new_reloc <<= 2;
+ break;
+ default:
+ as_bad (_("Invalid argument description: %d"), reg);
+ }
+
+ return new_reloc;
+}
+
+/* Parse a PA nullification completer (,n). Return nonzero if the
+ completer was found; return zero if no completer was found. */
+
+static int
+pa_parse_nullif (s)
+ char **s;
+{
+ int nullif;
+
+ nullif = 0;
+ if (**s == ',')
+ {
+ *s = *s + 1;
+ if (strncasecmp (*s, "n", 1) == 0)
+ nullif = 1;
+ else
+ {
+ as_bad (_("Invalid Nullification: (%c)"), **s);
+ nullif = 0;
+ }
+ *s = *s + 1;
+ }
+
+ return nullif;
+}
+
+/* Parse a non-negated compare/subtract completer returning the
+ number (for encoding in instrutions) of the given completer.
+
+ ISBRANCH specifies whether or not this is parsing a condition
+ completer for a branch (vs a nullification completer for a
+ computational instruction. */
+
+static int
+pa_parse_nonneg_cmpsub_cmpltr (s, isbranch)
+ char **s;
+ int isbranch;
+{
+ int cmpltr;
+ char *name = *s + 1;
+ char c;
+ char *save_s = *s;
+ int nullify = 0;
+
+ cmpltr = 0;
+ if (**s == ',')
+ {
+ *s += 1;
+ while (**s != ',' && **s != ' ' && **s != '\t')
+ *s += 1;
+ c = **s;
+ **s = 0x00;
+
+
+ if (strcmp (name, "=") == 0)
+ {
+ cmpltr = 1;
+ }
+ else if (strcmp (name, "<") == 0)
+ {
+ cmpltr = 2;
+ }
+ else if (strcmp (name, "<=") == 0)
+ {
+ cmpltr = 3;
+ }
+ else if (strcmp (name, "<<") == 0)
+ {
+ cmpltr = 4;
+ }
+ else if (strcmp (name, "<<=") == 0)
+ {
+ cmpltr = 5;
+ }
+ else if (strcasecmp (name, "sv") == 0)
+ {
+ cmpltr = 6;
+ }
+ else if (strcasecmp (name, "od") == 0)
+ {
+ cmpltr = 7;
+ }
+ /* If we have something like addb,n then there is no condition
+ completer. */
+ else if (strcasecmp (name, "n") == 0 && isbranch)
+ {
+ cmpltr = 0;
+ nullify = 1;
+ }
+ else
+ {
+ cmpltr = -1;
+ }
+ **s = c;
+ }
+
+ /* Reset pointers if this was really a ,n for a branch instruction. */
+ if (nullify)
+ *s = save_s;
+
+
+ return cmpltr;
+}
+
+/* Parse a negated compare/subtract completer returning the
+ number (for encoding in instrutions) of the given completer.
+
+ ISBRANCH specifies whether or not this is parsing a condition
+ completer for a branch (vs a nullification completer for a
+ computational instruction. */
+
+static int
+pa_parse_neg_cmpsub_cmpltr (s, isbranch)
+ char **s;
+ int isbranch;
+{
+ int cmpltr;
+ char *name = *s + 1;
+ char c;
+ char *save_s = *s;
+ int nullify = 0;
+
+ cmpltr = 0;
+ if (**s == ',')
+ {
+ *s += 1;
+ while (**s != ',' && **s != ' ' && **s != '\t')
+ *s += 1;
+ c = **s;
+ **s = 0x00;
+
+
+ if (strcasecmp (name, "tr") == 0)
+ {
+ cmpltr = 0;
+ }
+ else if (strcmp (name, "<>") == 0)
+ {
+ cmpltr = 1;
+ }
+ else if (strcmp (name, ">=") == 0)
+ {
+ cmpltr = 2;
+ }
+ else if (strcmp (name, ">") == 0)
+ {
+ cmpltr = 3;
+ }
+ else if (strcmp (name, ">>=") == 0)
+ {
+ cmpltr = 4;
+ }
+ else if (strcmp (name, ">>") == 0)
+ {
+ cmpltr = 5;
+ }
+ else if (strcasecmp (name, "nsv") == 0)
+ {
+ cmpltr = 6;
+ }
+ else if (strcasecmp (name, "ev") == 0)
+ {
+ cmpltr = 7;
+ }
+ /* If we have something like addb,n then there is no condition
+ completer. */
+ else if (strcasecmp (name, "n") == 0 && isbranch)
+ {
+ cmpltr = 0;
+ nullify = 1;
+ }
+ else
+ {
+ cmpltr = -1;
+ }
+ **s = c;
+ }
+
+ /* Reset pointers if this was really a ,n for a branch instruction. */
+ if (nullify)
+ *s = save_s;
+
+
+ return cmpltr;
+}
+
+
+/* Parse a non-negated addition completer returning the number
+ (for encoding in instrutions) of the given completer.
+
+ ISBRANCH specifies whether or not this is parsing a condition
+ completer for a branch (vs a nullification completer for a
+ computational instruction. */
+
+static int
+pa_parse_nonneg_add_cmpltr (s, isbranch)
+ char **s;
+ int isbranch;
+{
+ int cmpltr;
+ char *name = *s + 1;
+ char c;
+ char *save_s = *s;
+
+ cmpltr = 0;
+ if (**s == ',')
+ {
+ *s += 1;
+ while (**s != ',' && **s != ' ' && **s != '\t')
+ *s += 1;
+ c = **s;
+ **s = 0x00;
+ if (strcmp (name, "=") == 0)
+ {
+ cmpltr = 1;
+ }
+ else if (strcmp (name, "<") == 0)
+ {
+ cmpltr = 2;
+ }
+ else if (strcmp (name, "<=") == 0)
+ {
+ cmpltr = 3;
+ }
+ else if (strcasecmp (name, "nuv") == 0)
+ {
+ cmpltr = 4;
+ }
+ else if (strcasecmp (name, "znv") == 0)
+ {
+ cmpltr = 5;
+ }
+ else if (strcasecmp (name, "sv") == 0)
+ {
+ cmpltr = 6;
+ }
+ else if (strcasecmp (name, "od") == 0)
+ {
+ cmpltr = 7;
+ }
+ /* If we have something like addb,n then there is no condition
+ completer. */
+ else if (strcasecmp (name, "n") == 0 && isbranch)
+ {
+ cmpltr = 0;
+ }
+ else
+ {
+ cmpltr = -1;
+ }
+ **s = c;
+ }
+
+ /* Reset pointers if this was really a ,n for a branch instruction. */
+ if (cmpltr == 0 && *name == 'n' && isbranch)
+ *s = save_s;
+
+ return cmpltr;
+}
+
+/* Parse a negated addition completer returning the number
+ (for encoding in instrutions) of the given completer.
+
+ ISBRANCH specifies whether or not this is parsing a condition
+ completer for a branch (vs a nullification completer for a
+ computational instruction). */
+
+static int
+pa_parse_neg_add_cmpltr (s, isbranch)
+ char **s;
+ int isbranch;
+{
+ int cmpltr;
+ char *name = *s + 1;
+ char c;
+ char *save_s = *s;
+
+ cmpltr = 0;
+ if (**s == ',')
+ {
+ *s += 1;
+ while (**s != ',' && **s != ' ' && **s != '\t')
+ *s += 1;
+ c = **s;
+ **s = 0x00;
+ if (strcasecmp (name, "tr") == 0)
+ {
+ cmpltr = 0;
+ }
+ else if (strcmp (name, "<>") == 0)
+ {
+ cmpltr = 1;
+ }
+ else if (strcmp (name, ">=") == 0)
+ {
+ cmpltr = 2;
+ }
+ else if (strcmp (name, ">") == 0)
+ {
+ cmpltr = 3;
+ }
+ else if (strcasecmp (name, "uv") == 0)
+ {
+ cmpltr = 4;
+ }
+ else if (strcasecmp (name, "vnz") == 0)
+ {
+ cmpltr = 5;
+ }
+ else if (strcasecmp (name, "nsv") == 0)
+ {
+ cmpltr = 6;
+ }
+ else if (strcasecmp (name, "ev") == 0)
+ {
+ cmpltr = 7;
+ }
+ /* If we have something like addb,n then there is no condition
+ completer. */
+ else if (strcasecmp (name, "n") == 0 && isbranch)
+ {
+ cmpltr = 0;
+ }
+ else
+ {
+ cmpltr = -1;
+ }
+ **s = c;
+ }
+
+ /* Reset pointers if this was really a ,n for a branch instruction. */
+ if (cmpltr == 0 && *name == 'n' && isbranch)
+ *s = save_s;
+
+ return cmpltr;
+}
+
+/* Handle an alignment directive. Special so that we can update the
+ alignment of the subspace if necessary. */
+static void
+pa_align (bytes)
+{
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+
+ /* Let the generic gas code do most of the work. */
+ s_align_bytes (bytes);
+
+ /* If bytes is a power of 2, then update the current subspace's
+ alignment if necessary. */
+ if (log2 (bytes) != -1)
+ record_alignment (current_subspace->ssd_seg, log2 (bytes));
+}
+
+/* Handle a .BLOCK type pseudo-op. */
+
+static void
+pa_block (z)
+ int z;
+{
+ char *p;
+ long int temp_fill;
+ unsigned int temp_size;
+ unsigned int i;
+
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+
+ temp_size = get_absolute_expression ();
+
+ /* Always fill with zeros, that's what the HP assembler does. */
+ temp_fill = 0;
+
+ p = frag_var (rs_fill, (int) temp_size, (int) temp_size,
+ (relax_substateT) 0, (symbolS *) 0, (offsetT) 1, NULL);
+ memset (p, 0, temp_size);
+
+ /* Convert 2 bytes at a time. */
+
+ for (i = 0; i < temp_size; i += 2)
+ {
+ md_number_to_chars (p + i,
+ (valueT) temp_fill,
+ (int) ((temp_size - i) > 2 ? 2 : (temp_size - i)));
+ }
+
+ pa_undefine_label ();
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .begin_brtab and .end_brtab pseudo-op. */
+
+static void
+pa_brtab (begin)
+ int begin;
+{
+
+#ifdef OBJ_SOM
+ /* The BRTAB relocations are only availble in SOM (to denote
+ the beginning and end of branch tables). */
+ char *where = frag_more (0);
+
+ fix_new_hppa (frag_now, where - frag_now->fr_literal, 0,
+ NULL, (offsetT) 0, NULL,
+ 0, begin ? R_HPPA_BEGIN_BRTAB : R_HPPA_END_BRTAB,
+ e_fsel, 0, 0, NULL);
+#endif
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .begin_try and .end_try pseudo-op. */
+
+static void
+pa_try (begin)
+ int begin;
+{
+#ifdef OBJ_SOM
+ expressionS exp;
+ char *where = frag_more (0);
+
+ if (! begin)
+ expression (&exp);
+
+ /* The TRY relocations are only availble in SOM (to denote
+ the beginning and end of exception handling regions). */
+
+ fix_new_hppa (frag_now, where - frag_now->fr_literal, 0,
+ NULL, (offsetT) 0, begin ? NULL : &exp,
+ 0, begin ? R_HPPA_BEGIN_TRY : R_HPPA_END_TRY,
+ e_fsel, 0, 0, NULL);
+#endif
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .CALL pseudo-op. This involves storing away information
+ about where arguments are to be found so the linker can detect
+ (and correct) argument location mismatches between caller and callee. */
+
+static void
+pa_call (unused)
+ int unused;
+{
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+
+ pa_call_args (&last_call_desc);
+ demand_empty_rest_of_line ();
+}
+
+/* Do the dirty work of building a call descriptor which describes
+ where the caller placed arguments to a function call. */
+
+static void
+pa_call_args (call_desc)
+ struct call_desc *call_desc;
+{
+ char *name, c, *p;
+ unsigned int temp, arg_reloc;
+
+ while (!is_end_of_statement ())
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ /* Process a source argument. */
+ if ((strncasecmp (name, "argw", 4) == 0))
+ {
+ temp = atoi (name + 4);
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ arg_reloc = pa_build_arg_reloc (name);
+ call_desc->arg_reloc |= pa_align_arg_reloc (temp, arg_reloc);
+ }
+ /* Process a return value. */
+ else if ((strncasecmp (name, "rtnval", 6) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ arg_reloc = pa_build_arg_reloc (name);
+ call_desc->arg_reloc |= (arg_reloc & 0x3);
+ }
+ else
+ {
+ as_bad (_("Invalid .CALL argument: %s"), name);
+ }
+ p = input_line_pointer;
+ *p = c;
+ if (!is_end_of_statement ())
+ input_line_pointer++;
+ }
+}
+
+/* Return TRUE if FRAG1 and FRAG2 are the same. */
+
+static int
+is_same_frag (frag1, frag2)
+ fragS *frag1;
+ fragS *frag2;
+{
+
+ if (frag1 == NULL)
+ return (FALSE);
+ else if (frag2 == NULL)
+ return (FALSE);
+ else if (frag1 == frag2)
+ return (TRUE);
+ else if (frag2->fr_type == rs_fill && frag2->fr_fix == 0)
+ return (is_same_frag (frag1, frag2->fr_next));
+ else
+ return (FALSE);
+}
+
+#ifdef OBJ_ELF
+/* Build an entry in the UNWIND subspace from the given function
+ attributes in CALL_INFO. This is not needed for SOM as using
+ R_ENTRY and R_EXIT relocations allow the linker to handle building
+ of the unwind spaces. */
+
+static void
+pa_build_unwind_subspace (call_info)
+ struct call_info *call_info;
+{
+ char *unwind;
+ asection *seg, *save_seg;
+ subsegT subseg, save_subseg;
+ int i;
+ char c, *p;
+
+ /* Get into the right seg/subseg. This may involve creating
+ the seg the first time through. Make sure to have the
+ old seg/subseg so that we can reset things when we are done. */
+ subseg = SUBSEG_UNWIND;
+ seg = bfd_get_section_by_name (stdoutput, UNWIND_SECTION_NAME);
+ if (seg == ASEC_NULL)
+ {
+ seg = bfd_make_section_old_way (stdoutput, UNWIND_SECTION_NAME);
+ bfd_set_section_flags (stdoutput, seg,
+ SEC_READONLY | SEC_HAS_CONTENTS
+ | SEC_LOAD | SEC_RELOC);
+ }
+
+ save_seg = now_seg;
+ save_subseg = now_subseg;
+ subseg_set (seg, subseg);
+
+
+ /* Get some space to hold relocation information for the unwind
+ descriptor. */
+ p = frag_more (4);
+ md_number_to_chars (p, 0, 4);
+
+ /* Relocation info. for start offset of the function. */
+ fix_new_hppa (frag_now, p - frag_now->fr_literal, 4,
+ call_info->start_symbol, (offsetT) 0,
+ (expressionS *) NULL, 0, R_PARISC_DIR32, e_fsel, 32, 0, NULL);
+
+ p = frag_more (4);
+ md_number_to_chars (p, 0, 4);
+
+ /* Relocation info. for end offset of the function.
+
+ Because we allow reductions of 32bit relocations for ELF, this will be
+ reduced to section_sym + offset which avoids putting the temporary
+ symbol into the symbol table. It (should) end up giving the same
+ value as call_info->start_symbol + function size once the linker is
+ finished with its work. */
+
+ fix_new_hppa (frag_now, p - frag_now->fr_literal, 4,
+ call_info->end_symbol, (offsetT) 0,
+ (expressionS *) NULL, 0, R_PARISC_DIR32, e_fsel, 32, 0, NULL);
+
+ /* Dump it. */
+ unwind = (char *) &call_info->ci_unwind;
+ for (i = 8; i < sizeof (struct unwind_table); i++)
+ {
+ c = *(unwind + i);
+ {
+ FRAG_APPEND_1_CHAR (c);
+ }
+ }
+
+ /* Return back to the original segment/subsegment. */
+ subseg_set (save_seg, save_subseg);
+}
+#endif
+
+/* Process a .CALLINFO pseudo-op. This information is used later
+ to build unwind descriptors and maybe one day to support
+ .ENTER and .LEAVE. */
+
+static void
+pa_callinfo (unused)
+ int unused;
+{
+ char *name, c, *p;
+ int temp;
+
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+
+ /* .CALLINFO must appear within a procedure definition. */
+ if (!within_procedure)
+ as_bad (_(".callinfo is not within a procedure definition"));
+
+ /* Mark the fact that we found the .CALLINFO for the
+ current procedure. */
+ callinfo_found = TRUE;
+
+ /* Iterate over the .CALLINFO arguments. */
+ while (!is_end_of_statement ())
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ /* Frame size specification. */
+ if ((strncasecmp (name, "frame", 5) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ temp = get_absolute_expression ();
+ if ((temp & 0x3) != 0)
+ {
+ as_bad (_("FRAME parameter must be a multiple of 8: %d\n"), temp);
+ temp = 0;
+ }
+
+ /* callinfo is in bytes and unwind_desc is in 8 byte units. */
+ last_call_info->ci_unwind.descriptor.frame_size = temp / 8;
+
+ }
+ /* Entry register (GR, GR and SR) specifications. */
+ else if ((strncasecmp (name, "entry_gr", 8) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ temp = get_absolute_expression ();
+ /* The HP assembler accepts 19 as the high bound for ENTRY_GR
+ even though %r19 is caller saved. I think this is a bug in
+ the HP assembler, and we are not going to emulate it. */
+ if (temp < 3 || temp > 18)
+ as_bad (_("Value for ENTRY_GR must be in the range 3..18\n"));
+ last_call_info->ci_unwind.descriptor.entry_gr = temp - 2;
+ }
+ else if ((strncasecmp (name, "entry_fr", 8) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ temp = get_absolute_expression ();
+ /* Similarly the HP assembler takes 31 as the high bound even
+ though %fr21 is the last callee saved floating point register. */
+ if (temp < 12 || temp > 21)
+ as_bad (_("Value for ENTRY_FR must be in the range 12..21\n"));
+ last_call_info->ci_unwind.descriptor.entry_fr = temp - 11;
+ }
+ else if ((strncasecmp (name, "entry_sr", 8) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ temp = get_absolute_expression ();
+ if (temp != 3)
+ as_bad (_("Value for ENTRY_SR must be 3\n"));
+ }
+ /* Note whether or not this function performs any calls. */
+ else if ((strncasecmp (name, "calls", 5) == 0) ||
+ (strncasecmp (name, "caller", 6) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ }
+ else if ((strncasecmp (name, "no_calls", 8) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ }
+ /* Should RP be saved into the stack. */
+ else if ((strncasecmp (name, "save_rp", 7) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ last_call_info->ci_unwind.descriptor.save_rp = 1;
+ }
+ /* Likewise for SP. */
+ else if ((strncasecmp (name, "save_sp", 7) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ last_call_info->ci_unwind.descriptor.save_sp = 1;
+ }
+ /* Is this an unwindable procedure. If so mark it so
+ in the unwind descriptor. */
+ else if ((strncasecmp (name, "no_unwind", 9) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ last_call_info->ci_unwind.descriptor.cannot_unwind = 1;
+ }
+ /* Is this an interrupt routine. If so mark it in the
+ unwind descriptor. */
+ else if ((strncasecmp (name, "hpux_int", 7) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ last_call_info->ci_unwind.descriptor.hpux_interrupt_marker = 1;
+ }
+ /* Is this a millicode routine. "millicode" isn't in my
+ assembler manual, but my copy is old. The HP assembler
+ accepts it, and there's a place in the unwind descriptor
+ to drop the information, so we'll accept it too. */
+ else if ((strncasecmp (name, "millicode", 9) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ last_call_info->ci_unwind.descriptor.millicode = 1;
+ }
+ else
+ {
+ as_bad (_("Invalid .CALLINFO argument: %s"), name);
+ *input_line_pointer = c;
+ }
+ if (!is_end_of_statement ())
+ input_line_pointer++;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Switch into the code subspace. */
+
+static void
+pa_code (unused)
+ int unused;
+{
+ current_space = is_defined_space ("$TEXT$");
+ current_subspace
+ = pa_subsegment_to_subspace (current_space->sd_seg, 0);
+ s_text (0);
+ pa_undefine_label ();
+}
+
+/* This is different than the standard GAS s_comm(). On HP9000/800 machines,
+ the .comm pseudo-op has the following symtax:
+
+ <label> .comm <length>
+
+ where <label> is optional and is a symbol whose address will be the start of
+ a block of memory <length> bytes long. <length> must be an absolute
+ expression. <length> bytes will be allocated in the current space
+ and subspace.
+
+ Also note the label may not even be on the same line as the .comm.
+
+ This difference in syntax means the colon function will be called
+ on the symbol before we arrive in pa_comm. colon will set a number
+ of attributes of the symbol that need to be fixed here. In particular
+ the value, section pointer, fragment pointer, flags, etc. What
+ a pain.
+
+ This also makes error detection all but impossible. */
+
+static void
+pa_comm (unused)
+ int unused;
+{
+ unsigned int size;
+ symbolS *symbol;
+ label_symbol_struct *label_symbol = pa_get_label ();
+
+ if (label_symbol)
+ symbol = label_symbol->lss_label;
+ else
+ symbol = NULL;
+
+ SKIP_WHITESPACE ();
+ size = get_absolute_expression ();
+
+ if (symbol)
+ {
+ S_SET_VALUE (symbol, size);
+ S_SET_SEGMENT (symbol, bfd_und_section_ptr);
+ S_SET_EXTERNAL (symbol);
+
+ /* colon() has already set the frag to the current location in the
+ current subspace; we need to reset the fragment to the zero address
+ fragment. We also need to reset the segment pointer. */
+ symbol->sy_frag = &zero_address_frag;
+ }
+ demand_empty_rest_of_line ();
+}
+
+/* Process a .END pseudo-op. */
+
+static void
+pa_end (unused)
+ int unused;
+{
+ demand_empty_rest_of_line ();
+}
+
+/* Process a .ENTER pseudo-op. This is not supported. */
+static void
+pa_enter (unused)
+ int unused;
+{
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+
+ as_bad (_("The .ENTER pseudo-op is not supported"));
+ demand_empty_rest_of_line ();
+}
+
+/* Process a .ENTRY pseudo-op. .ENTRY marks the beginning of the
+ procesure. */
+static void
+pa_entry (unused)
+ int unused;
+{
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+
+ if (!within_procedure)
+ as_bad (_("Misplaced .entry. Ignored."));
+ else
+ {
+ if (!callinfo_found)
+ as_bad (_("Missing .callinfo."));
+ }
+ demand_empty_rest_of_line ();
+ within_entry_exit = TRUE;
+
+#ifdef OBJ_SOM
+ /* SOM defers building of unwind descriptors until the link phase.
+ The assembler is responsible for creating an R_ENTRY relocation
+ to mark the beginning of a region and hold the unwind bits, and
+ for creating an R_EXIT relocation to mark the end of the region.
+
+ FIXME. ELF should be using the same conventions! The problem
+ is an unwind requires too much relocation space. Hmmm. Maybe
+ if we split the unwind bits up between the relocations which
+ denote the entry and exit points. */
+ if (last_call_info->start_symbol != NULL)
+ {
+ char *where = frag_more (0);
+
+ fix_new_hppa (frag_now, where - frag_now->fr_literal, 0,
+ NULL, (offsetT) 0, NULL,
+ 0, R_HPPA_ENTRY, e_fsel, 0, 0,
+ (int *) &last_call_info->ci_unwind.descriptor);
+ }
+#endif
+}
+
+/* Handle a .EQU pseudo-op. */
+
+static void
+pa_equ (reg)
+ int reg;
+{
+ label_symbol_struct *label_symbol = pa_get_label ();
+ symbolS *symbol;
+
+ if (label_symbol)
+ {
+ symbol = label_symbol->lss_label;
+ if (reg)
+ S_SET_VALUE (symbol, pa_parse_number (&input_line_pointer, 0));
+ else
+ S_SET_VALUE (symbol, (unsigned int) get_absolute_expression ());
+ S_SET_SEGMENT (symbol, bfd_abs_section_ptr);
+ }
+ else
+ {
+ if (reg)
+ as_bad (_(".REG must use a label"));
+ else
+ as_bad (_(".EQU must use a label"));
+ }
+
+ pa_undefine_label ();
+ demand_empty_rest_of_line ();
+}
+
+/* Helper function. Does processing for the end of a function. This
+ usually involves creating some relocations or building special
+ symbols to mark the end of the function. */
+
+static void
+process_exit ()
+{
+ char *where;
+
+ where = frag_more (0);
+
+#ifdef OBJ_ELF
+ /* Mark the end of the function, stuff away the location of the frag
+ for the end of the function, and finally call pa_build_unwind_subspace
+ to add an entry in the unwind table. */
+ hppa_elf_mark_end_of_function ();
+ pa_build_unwind_subspace (last_call_info);
+#else
+ /* SOM defers building of unwind descriptors until the link phase.
+ The assembler is responsible for creating an R_ENTRY relocation
+ to mark the beginning of a region and hold the unwind bits, and
+ for creating an R_EXIT relocation to mark the end of the region.
+
+ FIXME. ELF should be using the same conventions! The problem
+ is an unwind requires too much relocation space. Hmmm. Maybe
+ if we split the unwind bits up between the relocations which
+ denote the entry and exit points. */
+ fix_new_hppa (frag_now, where - frag_now->fr_literal, 0,
+ NULL, (offsetT) 0,
+ NULL, 0, R_HPPA_EXIT, e_fsel, 0, 0,
+ (int *) &last_call_info->ci_unwind.descriptor + 1);
+#endif
+}
+
+/* Process a .EXIT pseudo-op. */
+
+static void
+pa_exit (unused)
+ int unused;
+{
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+
+ if (!within_procedure)
+ as_bad (_(".EXIT must appear within a procedure"));
+ else
+ {
+ if (!callinfo_found)
+ as_bad (_("Missing .callinfo"));
+ else
+ {
+ if (!within_entry_exit)
+ as_bad (_("No .ENTRY for this .EXIT"));
+ else
+ {
+ within_entry_exit = FALSE;
+ process_exit ();
+ }
+ }
+ }
+ demand_empty_rest_of_line ();
+}
+
+/* Process a .EXPORT directive. This makes functions external
+ and provides information such as argument relocation entries
+ to callers. */
+
+static void
+pa_export (unused)
+ int unused;
+{
+ char *name, c, *p;
+ symbolS *symbol;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ /* Make sure the given symbol exists. */
+ if ((symbol = symbol_find_or_make (name)) == NULL)
+ {
+ as_bad (_("Cannot define export symbol: %s\n"), name);
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ }
+ else
+ {
+ /* OK. Set the external bits and process argument relocations. */
+ S_SET_EXTERNAL (symbol);
+ p = input_line_pointer;
+ *p = c;
+ if (!is_end_of_statement ())
+ {
+ input_line_pointer++;
+ pa_type_args (symbol, 1);
+ }
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Helper function to process arguments to a .EXPORT pseudo-op. */
+
+static void
+pa_type_args (symbolP, is_export)
+ symbolS *symbolP;
+ int is_export;
+{
+ char *name, c, *p;
+ unsigned int temp, arg_reloc;
+ pa_symbol_type type = SYMBOL_TYPE_UNKNOWN;
+ obj_symbol_type *symbol = (obj_symbol_type *) symbolP->bsym;
+
+ if (strncasecmp (input_line_pointer, "absolute", 8) == 0)
+
+ {
+ input_line_pointer += 8;
+ symbolP->bsym->flags &= ~BSF_FUNCTION;
+ S_SET_SEGMENT (symbolP, bfd_abs_section_ptr);
+ type = SYMBOL_TYPE_ABSOLUTE;
+ }
+ else if (strncasecmp (input_line_pointer, "code", 4) == 0)
+ {
+ input_line_pointer += 4;
+ /* IMPORTing/EXPORTing CODE types for functions is meaningless for SOM,
+ instead one should be IMPORTing/EXPORTing ENTRY types.
+
+ Complain if one tries to EXPORT a CODE type since that's never
+ done. Both GCC and HP C still try to IMPORT CODE types, so
+ silently fix them to be ENTRY types. */
+ if (symbolP->bsym->flags & BSF_FUNCTION)
+ {
+ if (is_export)
+ as_tsktsk (_("Using ENTRY rather than CODE in export directive for %s"), symbolP->bsym->name);
+
+ symbolP->bsym->flags |= BSF_FUNCTION;
+ type = SYMBOL_TYPE_ENTRY;
+ }
+ else
+ {
+ symbolP->bsym->flags &= ~BSF_FUNCTION;
+ type = SYMBOL_TYPE_CODE;
+ }
+ }
+ else if (strncasecmp (input_line_pointer, "data", 4) == 0)
+ {
+ input_line_pointer += 4;
+ symbolP->bsym->flags &= ~BSF_FUNCTION;
+ type = SYMBOL_TYPE_DATA;
+ }
+ else if ((strncasecmp (input_line_pointer, "entry", 5) == 0))
+ {
+ input_line_pointer += 5;
+ symbolP->bsym->flags |= BSF_FUNCTION;
+ type = SYMBOL_TYPE_ENTRY;
+ }
+ else if (strncasecmp (input_line_pointer, "millicode", 9) == 0)
+ {
+ input_line_pointer += 9;
+ symbolP->bsym->flags |= BSF_FUNCTION;
+ type = SYMBOL_TYPE_MILLICODE;
+ }
+ else if (strncasecmp (input_line_pointer, "plabel", 6) == 0)
+ {
+ input_line_pointer += 6;
+ symbolP->bsym->flags &= ~BSF_FUNCTION;
+ type = SYMBOL_TYPE_PLABEL;
+ }
+ else if (strncasecmp (input_line_pointer, "pri_prog", 8) == 0)
+ {
+ input_line_pointer += 8;
+ symbolP->bsym->flags |= BSF_FUNCTION;
+ type = SYMBOL_TYPE_PRI_PROG;
+ }
+ else if (strncasecmp (input_line_pointer, "sec_prog", 8) == 0)
+ {
+ input_line_pointer += 8;
+ symbolP->bsym->flags |= BSF_FUNCTION;
+ type = SYMBOL_TYPE_SEC_PROG;
+ }
+
+ /* SOM requires much more information about symbol types
+ than BFD understands. This is how we get this information
+ to the SOM BFD backend. */
+#ifdef obj_set_symbol_type
+ obj_set_symbol_type (symbolP->bsym, (int) type);
+#endif
+
+ /* Now that the type of the exported symbol has been handled,
+ handle any argument relocation information. */
+ while (!is_end_of_statement ())
+ {
+ if (*input_line_pointer == ',')
+ input_line_pointer++;
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ /* Argument sources. */
+ if ((strncasecmp (name, "argw", 4) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ temp = atoi (name + 4);
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ arg_reloc = pa_align_arg_reloc (temp, pa_build_arg_reloc (name));
+ symbol->tc_data.ap.hppa_arg_reloc |= arg_reloc;
+ *input_line_pointer = c;
+ }
+ /* The return value. */
+ else if ((strncasecmp (name, "rtnval", 6)) == 0)
+ {
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ arg_reloc = pa_build_arg_reloc (name);
+ symbol->tc_data.ap.hppa_arg_reloc |= arg_reloc;
+ *input_line_pointer = c;
+ }
+ /* Privelege level. */
+ else if ((strncasecmp (name, "priv_lev", 8)) == 0)
+ {
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ temp = atoi (input_line_pointer);
+ symbol->tc_data.ap.hppa_priv_level = temp;
+ c = get_symbol_end ();
+ *input_line_pointer = c;
+ }
+ else
+ {
+ as_bad (_("Undefined .EXPORT/.IMPORT argument (ignored): %s"), name);
+ p = input_line_pointer;
+ *p = c;
+ }
+ if (!is_end_of_statement ())
+ input_line_pointer++;
+ }
+}
+
+/* Handle an .IMPORT pseudo-op. Any symbol referenced in a given
+ assembly file must either be defined in the assembly file, or
+ explicitly IMPORTED from another. */
+
+static void
+pa_import (unused)
+ int unused;
+{
+ char *name, c, *p;
+ symbolS *symbol;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ symbol = symbol_find (name);
+ /* Ugh. We might be importing a symbol defined earlier in the file,
+ in which case all the code below will really screw things up
+ (set the wrong segment, symbol flags & type, etc). */
+ if (symbol == NULL || !S_IS_DEFINED (symbol))
+ {
+ symbol = symbol_find_or_make (name);
+ p = input_line_pointer;
+ *p = c;
+
+ if (!is_end_of_statement ())
+ {
+ input_line_pointer++;
+ pa_type_args (symbol, 0);
+ }
+ else
+ {
+ /* Sigh. To be compatable with the HP assembler and to help
+ poorly written assembly code, we assign a type based on
+ the the current segment. Note only BSF_FUNCTION really
+ matters, we do not need to set the full SYMBOL_TYPE_* info. */
+ if (now_seg == text_section)
+ symbol->bsym->flags |= BSF_FUNCTION;
+
+ /* If the section is undefined, then the symbol is undefined
+ Since this is an import, leave the section undefined. */
+ S_SET_SEGMENT (symbol, bfd_und_section_ptr);
+ }
+ }
+ else
+ {
+ /* The symbol was already defined. Just eat everything up to
+ the end of the current statement. */
+ while (!is_end_of_statement ())
+ input_line_pointer++;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .LABEL pseudo-op. */
+
+static void
+pa_label (unused)
+ int unused;
+{
+ char *name, c, *p;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ if (strlen (name) > 0)
+ {
+ colon (name);
+ p = input_line_pointer;
+ *p = c;
+ }
+ else
+ {
+ as_warn (_("Missing label name on .LABEL"));
+ }
+
+ if (!is_end_of_statement ())
+ {
+ as_warn (_("extra .LABEL arguments ignored."));
+ ignore_rest_of_line ();
+ }
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .LEAVE pseudo-op. This is not supported yet. */
+
+static void
+pa_leave (unused)
+ int unused;
+{
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+
+ as_bad (_("The .LEAVE pseudo-op is not supported"));
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .LEVEL pseudo-op. */
+
+static void
+pa_level (unused)
+ int unused;
+{
+ char *level;
+
+ level = input_line_pointer;
+ if (strncmp (level, "1.0", 3) == 0)
+ {
+ input_line_pointer += 3;
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_hppa, 10))
+ as_warn (_("could not set architecture and machine"));
+ }
+ else if (strncmp (level, "1.1", 3) == 0)
+ {
+ input_line_pointer += 3;
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_hppa, 11))
+ as_warn (_("could not set architecture and machine"));
+ }
+ else if (strncmp (level, "2.0", 3) == 0)
+ {
+ input_line_pointer += 3;
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_hppa, 20))
+ as_warn (_("could not set architecture and machine"));
+ }
+ else
+ {
+ as_bad (_("Unrecognized .LEVEL argument\n"));
+ ignore_rest_of_line ();
+ }
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .ORIGIN pseudo-op. */
+
+static void
+pa_origin (unused)
+ int unused;
+{
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+
+ s_org (0);
+ pa_undefine_label ();
+}
+
+/* Handle a .PARAM pseudo-op. This is much like a .EXPORT, except it
+ is for static functions. FIXME. Should share more code with .EXPORT. */
+
+static void
+pa_param (unused)
+ int unused;
+{
+ char *name, c, *p;
+ symbolS *symbol;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ if ((symbol = symbol_find_or_make (name)) == NULL)
+ {
+ as_bad (_("Cannot define static symbol: %s\n"), name);
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ }
+ else
+ {
+ S_CLEAR_EXTERNAL (symbol);
+ p = input_line_pointer;
+ *p = c;
+ if (!is_end_of_statement ())
+ {
+ input_line_pointer++;
+ pa_type_args (symbol, 0);
+ }
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .PROC pseudo-op. It is used to mark the beginning
+ of a procedure from a syntatical point of view. */
+
+static void
+pa_proc (unused)
+ int unused;
+{
+ struct call_info *call_info;
+
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+
+ if (within_procedure)
+ as_fatal (_("Nested procedures"));
+
+ /* Reset global variables for new procedure. */
+ callinfo_found = FALSE;
+ within_procedure = TRUE;
+
+ /* Create another call_info structure. */
+ call_info = (struct call_info *) xmalloc (sizeof (struct call_info));
+
+ if (!call_info)
+ as_fatal (_("Cannot allocate unwind descriptor\n"));
+
+ memset (call_info, 0, sizeof (struct call_info));
+
+ call_info->ci_next = NULL;
+
+ if (call_info_root == NULL)
+ {
+ call_info_root = call_info;
+ last_call_info = call_info;
+ }
+ else
+ {
+ last_call_info->ci_next = call_info;
+ last_call_info = call_info;
+ }
+
+ /* set up defaults on call_info structure */
+
+ call_info->ci_unwind.descriptor.cannot_unwind = 0;
+ call_info->ci_unwind.descriptor.region_desc = 1;
+ call_info->ci_unwind.descriptor.hpux_interrupt_marker = 0;
+
+ /* If we got a .PROC pseudo-op, we know that the function is defined
+ locally. Make sure it gets into the symbol table. */
+ {
+ label_symbol_struct *label_symbol = pa_get_label ();
+
+ if (label_symbol)
+ {
+ if (label_symbol->lss_label)
+ {
+ last_call_info->start_symbol = label_symbol->lss_label;
+ label_symbol->lss_label->bsym->flags |= BSF_FUNCTION;
+ }
+ else
+ as_bad (_("Missing function name for .PROC (corrupted label chain)"));
+ }
+ else
+ last_call_info->start_symbol = NULL;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Process the syntatical end of a procedure. Make sure all the
+ appropriate pseudo-ops were found within the procedure. */
+
+static void
+pa_procend (unused)
+ int unused;
+{
+
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+
+ /* If we are within a procedure definition, make sure we've
+ defined a label for the procedure; handle case where the
+ label was defined after the .PROC directive.
+
+ Note there's not need to diddle with the segment or fragment
+ for the label symbol in this case. We have already switched
+ into the new $CODE$ subspace at this point. */
+ if (within_procedure && last_call_info->start_symbol == NULL)
+ {
+ label_symbol_struct *label_symbol = pa_get_label ();
+
+ if (label_symbol)
+ {
+ if (label_symbol->lss_label)
+ {
+ last_call_info->start_symbol = label_symbol->lss_label;
+ label_symbol->lss_label->bsym->flags |= BSF_FUNCTION;
+#ifdef OBJ_SOM
+ /* Also handle allocation of a fixup to hold the unwind
+ information when the label appears after the proc/procend. */
+ if (within_entry_exit)
+ {
+ char *where = frag_more (0);
+
+ fix_new_hppa (frag_now, where - frag_now->fr_literal, 0,
+ NULL, (offsetT) 0, NULL,
+ 0, R_HPPA_ENTRY, e_fsel, 0, 0,
+ (int *) &last_call_info->ci_unwind.descriptor);
+ }
+#endif
+ }
+ else
+ as_bad (_("Missing function name for .PROC (corrupted label chain)"));
+ }
+ else
+ as_bad (_("Missing function name for .PROC"));
+ }
+
+ if (!within_procedure)
+ as_bad (_("misplaced .procend"));
+
+ if (!callinfo_found)
+ as_bad (_("Missing .callinfo for this procedure"));
+
+ if (within_entry_exit)
+ as_bad (_("Missing .EXIT for a .ENTRY"));
+
+#ifdef OBJ_ELF
+ /* ELF needs to mark the end of each function so that it can compute
+ the size of the function (apparently its needed in the symbol table). */
+ hppa_elf_mark_end_of_function ();
+#endif
+
+ within_procedure = FALSE;
+ demand_empty_rest_of_line ();
+ pa_undefine_label ();
+}
+
+/* Parse the parameters to a .SPACE directive; if CREATE_FLAG is nonzero,
+ then create a new space entry to hold the information specified
+ by the parameters to the .SPACE directive. */
+
+static sd_chain_struct *
+pa_parse_space_stmt (space_name, create_flag)
+ char *space_name;
+ int create_flag;
+{
+ char *name, *ptemp, c;
+ char loadable, defined, private, sort;
+ int spnum, temp;
+ asection *seg = NULL;
+ sd_chain_struct *space;
+
+ /* load default values */
+ spnum = 0;
+ sort = 0;
+ loadable = TRUE;
+ defined = TRUE;
+ private = FALSE;
+ if (strcmp (space_name, "$TEXT$") == 0)
+ {
+ seg = pa_def_spaces[0].segment;
+ defined = pa_def_spaces[0].defined;
+ private = pa_def_spaces[0].private;
+ sort = pa_def_spaces[0].sort;
+ spnum = pa_def_spaces[0].spnum;
+ }
+ else if (strcmp (space_name, "$PRIVATE$") == 0)
+ {
+ seg = pa_def_spaces[1].segment;
+ defined = pa_def_spaces[1].defined;
+ private = pa_def_spaces[1].private;
+ sort = pa_def_spaces[1].sort;
+ spnum = pa_def_spaces[1].spnum;
+ }
+
+ if (!is_end_of_statement ())
+ {
+ print_errors = FALSE;
+ ptemp = input_line_pointer + 1;
+ /* First see if the space was specified as a number rather than
+ as a name. According to the PA assembly manual the rest of
+ the line should be ignored. */
+ temp = pa_parse_number (&ptemp, 0);
+ if (temp >= 0)
+ {
+ spnum = temp;
+ input_line_pointer = ptemp;
+ }
+ else
+ {
+ while (!is_end_of_statement ())
+ {
+ input_line_pointer++;
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ if ((strncasecmp (name, "spnum", 5) == 0))
+ {
+ *input_line_pointer = c;
+ input_line_pointer++;
+ spnum = get_absolute_expression ();
+ }
+ else if ((strncasecmp (name, "sort", 4) == 0))
+ {
+ *input_line_pointer = c;
+ input_line_pointer++;
+ sort = get_absolute_expression ();
+ }
+ else if ((strncasecmp (name, "unloadable", 10) == 0))
+ {
+ *input_line_pointer = c;
+ loadable = FALSE;
+ }
+ else if ((strncasecmp (name, "notdefined", 10) == 0))
+ {
+ *input_line_pointer = c;
+ defined = FALSE;
+ }
+ else if ((strncasecmp (name, "private", 7) == 0))
+ {
+ *input_line_pointer = c;
+ private = TRUE;
+ }
+ else
+ {
+ as_bad (_("Invalid .SPACE argument"));
+ *input_line_pointer = c;
+ if (!is_end_of_statement ())
+ input_line_pointer++;
+ }
+ }
+ }
+ print_errors = TRUE;
+ }
+
+ if (create_flag && seg == NULL)
+ seg = subseg_new (space_name, 0);
+
+ /* If create_flag is nonzero, then create the new space with
+ the attributes computed above. Else set the values in
+ an already existing space -- this can only happen for
+ the first occurence of a built-in space. */
+ if (create_flag)
+ space = create_new_space (space_name, spnum, loadable, defined,
+ private, sort, seg, 1);
+ else
+ {
+ space = is_defined_space (space_name);
+ SPACE_SPNUM (space) = spnum;
+ SPACE_DEFINED (space) = defined & 1;
+ SPACE_USER_DEFINED (space) = 1;
+ }
+
+#ifdef obj_set_section_attributes
+ obj_set_section_attributes (seg, defined, private, sort, spnum);
+#endif
+
+ return space;
+}
+
+/* Handle a .SPACE pseudo-op; this switches the current space to the
+ given space, creating the new space if necessary. */
+
+static void
+pa_space (unused)
+ int unused;
+{
+ char *name, c, *space_name, *save_s;
+ int temp;
+ sd_chain_struct *sd_chain;
+
+ if (within_procedure)
+ {
+ as_bad (_("Can\'t change spaces within a procedure definition. Ignored"));
+ ignore_rest_of_line ();
+ }
+ else
+ {
+ /* Check for some of the predefined spaces. FIXME: most of the code
+ below is repeated several times, can we extract the common parts
+ and place them into a subroutine or something similar? */
+ /* FIXME Is this (and the next IF stmt) really right?
+ What if INPUT_LINE_POINTER points to "$TEXT$FOO"? */
+ if (strncmp (input_line_pointer, "$TEXT$", 6) == 0)
+ {
+ input_line_pointer += 6;
+ sd_chain = is_defined_space ("$TEXT$");
+ if (sd_chain == NULL)
+ sd_chain = pa_parse_space_stmt ("$TEXT$", 1);
+ else if (SPACE_USER_DEFINED (sd_chain) == 0)
+ sd_chain = pa_parse_space_stmt ("$TEXT$", 0);
+
+ current_space = sd_chain;
+ subseg_set (text_section, sd_chain->sd_last_subseg);
+ current_subspace
+ = pa_subsegment_to_subspace (text_section,
+ sd_chain->sd_last_subseg);
+ demand_empty_rest_of_line ();
+ return;
+ }
+ if (strncmp (input_line_pointer, "$PRIVATE$", 9) == 0)
+ {
+ input_line_pointer += 9;
+ sd_chain = is_defined_space ("$PRIVATE$");
+ if (sd_chain == NULL)
+ sd_chain = pa_parse_space_stmt ("$PRIVATE$", 1);
+ else if (SPACE_USER_DEFINED (sd_chain) == 0)
+ sd_chain = pa_parse_space_stmt ("$PRIVATE$", 0);
+
+ current_space = sd_chain;
+ subseg_set (data_section, sd_chain->sd_last_subseg);
+ current_subspace
+ = pa_subsegment_to_subspace (data_section,
+ sd_chain->sd_last_subseg);
+ demand_empty_rest_of_line ();
+ return;
+ }
+ if (!strncasecmp (input_line_pointer,
+ GDB_DEBUG_SPACE_NAME,
+ strlen (GDB_DEBUG_SPACE_NAME)))
+ {
+ input_line_pointer += strlen (GDB_DEBUG_SPACE_NAME);
+ sd_chain = is_defined_space (GDB_DEBUG_SPACE_NAME);
+ if (sd_chain == NULL)
+ sd_chain = pa_parse_space_stmt (GDB_DEBUG_SPACE_NAME, 1);
+ else if (SPACE_USER_DEFINED (sd_chain) == 0)
+ sd_chain = pa_parse_space_stmt (GDB_DEBUG_SPACE_NAME, 0);
+
+ current_space = sd_chain;
+
+ {
+ asection *gdb_section
+ = bfd_make_section_old_way (stdoutput, GDB_DEBUG_SPACE_NAME);
+
+ subseg_set (gdb_section, sd_chain->sd_last_subseg);
+ current_subspace
+ = pa_subsegment_to_subspace (gdb_section,
+ sd_chain->sd_last_subseg);
+ }
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ /* It could be a space specified by number. */
+ print_errors = 0;
+ save_s = input_line_pointer;
+ if ((temp = pa_parse_number (&input_line_pointer, 0)) >= 0)
+ {
+ if ((sd_chain = pa_find_space_by_number (temp)))
+ {
+ current_space = sd_chain;
+
+ subseg_set (sd_chain->sd_seg, sd_chain->sd_last_subseg);
+ current_subspace
+ = pa_subsegment_to_subspace (sd_chain->sd_seg,
+ sd_chain->sd_last_subseg);
+ demand_empty_rest_of_line ();
+ return;
+ }
+ }
+
+ /* Not a number, attempt to create a new space. */
+ print_errors = 1;
+ input_line_pointer = save_s;
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ space_name = xmalloc (strlen (name) + 1);
+ strcpy (space_name, name);
+ *input_line_pointer = c;
+
+ sd_chain = pa_parse_space_stmt (space_name, 1);
+ current_space = sd_chain;
+
+ subseg_set (sd_chain->sd_seg, sd_chain->sd_last_subseg);
+ current_subspace = pa_subsegment_to_subspace (sd_chain->sd_seg,
+ sd_chain->sd_last_subseg);
+ demand_empty_rest_of_line ();
+ }
+}
+
+/* Switch to a new space. (I think). FIXME. */
+
+static void
+pa_spnum (unused)
+ int unused;
+{
+ char *name;
+ char c;
+ char *p;
+ sd_chain_struct *space;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ space = is_defined_space (name);
+ if (space)
+ {
+ p = frag_more (4);
+ md_number_to_chars (p, SPACE_SPNUM (space), 4);
+ }
+ else
+ as_warn (_("Undefined space: '%s' Assuming space number = 0."), name);
+
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+}
+
+/* If VALUE is an exact power of two between zero and 2^31, then
+ return log2 (VALUE). Else return -1. */
+
+static int
+log2 (value)
+ int value;
+{
+ int shift = 0;
+
+ while ((1 << shift) != value && shift < 32)
+ shift++;
+
+ if (shift >= 32)
+ return -1;
+ else
+ return shift;
+}
+
+/* Handle a .SUBSPACE pseudo-op; this switches the current subspace to the
+ given subspace, creating the new subspace if necessary.
+
+ FIXME. Should mirror pa_space more closely, in particular how
+ they're broken up into subroutines. */
+
+static void
+pa_subspace (create_new)
+ int create_new;
+{
+ char *name, *ss_name, *alias, c;
+ char loadable, code_only, common, dup_common, zero, sort;
+ int i, access, space_index, alignment, quadrant, applicable, flags;
+ sd_chain_struct *space;
+ ssd_chain_struct *ssd;
+ asection *section;
+
+ if (current_space == NULL)
+ as_fatal (_("Must be in a space before changing or declaring subspaces.\n"));
+
+ if (within_procedure)
+ {
+ as_bad (_("Can\'t change subspaces within a procedure definition. Ignored"));
+ ignore_rest_of_line ();
+ }
+ else
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ ss_name = xmalloc (strlen (name) + 1);
+ strcpy (ss_name, name);
+ *input_line_pointer = c;
+
+ /* Load default values. */
+ sort = 0;
+ access = 0x7f;
+ loadable = 1;
+ common = 0;
+ dup_common = 0;
+ code_only = 0;
+ zero = 0;
+ space_index = ~0;
+ alignment = 1;
+ quadrant = 0;
+ alias = NULL;
+
+ space = current_space;
+ if (create_new)
+ ssd = NULL;
+ else
+ ssd = is_defined_subspace (ss_name);
+ /* Allow user to override the builtin attributes of subspaces. But
+ only allow the attributes to be changed once! */
+ if (ssd && SUBSPACE_DEFINED (ssd))
+ {
+ subseg_set (ssd->ssd_seg, ssd->ssd_subseg);
+ current_subspace = ssd;
+ if (!is_end_of_statement ())
+ as_warn (_("Parameters of an existing subspace can\'t be modified"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ else
+ {
+ /* A new subspace. Load default values if it matches one of
+ the builtin subspaces. */
+ i = 0;
+ while (pa_def_subspaces[i].name)
+ {
+ if (strcasecmp (pa_def_subspaces[i].name, ss_name) == 0)
+ {
+ loadable = pa_def_subspaces[i].loadable;
+ common = pa_def_subspaces[i].common;
+ dup_common = pa_def_subspaces[i].dup_common;
+ code_only = pa_def_subspaces[i].code_only;
+ zero = pa_def_subspaces[i].zero;
+ space_index = pa_def_subspaces[i].space_index;
+ alignment = pa_def_subspaces[i].alignment;
+ quadrant = pa_def_subspaces[i].quadrant;
+ access = pa_def_subspaces[i].access;
+ sort = pa_def_subspaces[i].sort;
+ if (USE_ALIASES && pa_def_subspaces[i].alias)
+ alias = pa_def_subspaces[i].alias;
+ break;
+ }
+ i++;
+ }
+ }
+
+ /* We should be working with a new subspace now. Fill in
+ any information as specified by the user. */
+ if (!is_end_of_statement ())
+ {
+ input_line_pointer++;
+ while (!is_end_of_statement ())
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ if ((strncasecmp (name, "quad", 4) == 0))
+ {
+ *input_line_pointer = c;
+ input_line_pointer++;
+ quadrant = get_absolute_expression ();
+ }
+ else if ((strncasecmp (name, "align", 5) == 0))
+ {
+ *input_line_pointer = c;
+ input_line_pointer++;
+ alignment = get_absolute_expression ();
+ if (log2 (alignment) == -1)
+ {
+ as_bad (_("Alignment must be a power of 2"));
+ alignment = 1;
+ }
+ }
+ else if ((strncasecmp (name, "access", 6) == 0))
+ {
+ *input_line_pointer = c;
+ input_line_pointer++;
+ access = get_absolute_expression ();
+ }
+ else if ((strncasecmp (name, "sort", 4) == 0))
+ {
+ *input_line_pointer = c;
+ input_line_pointer++;
+ sort = get_absolute_expression ();
+ }
+ else if ((strncasecmp (name, "code_only", 9) == 0))
+ {
+ *input_line_pointer = c;
+ code_only = 1;
+ }
+ else if ((strncasecmp (name, "unloadable", 10) == 0))
+ {
+ *input_line_pointer = c;
+ loadable = 0;
+ }
+ else if ((strncasecmp (name, "common", 6) == 0))
+ {
+ *input_line_pointer = c;
+ common = 1;
+ }
+ else if ((strncasecmp (name, "dup_comm", 8) == 0))
+ {
+ *input_line_pointer = c;
+ dup_common = 1;
+ }
+ else if ((strncasecmp (name, "zero", 4) == 0))
+ {
+ *input_line_pointer = c;
+ zero = 1;
+ }
+ else if ((strncasecmp (name, "first", 5) == 0))
+ as_bad (_("FIRST not supported as a .SUBSPACE argument"));
+ else
+ as_bad (_("Invalid .SUBSPACE argument"));
+ if (!is_end_of_statement ())
+ input_line_pointer++;
+ }
+ }
+
+ /* Compute a reasonable set of BFD flags based on the information
+ in the .subspace directive. */
+ applicable = bfd_applicable_section_flags (stdoutput);
+ flags = 0;
+ if (loadable)
+ flags |= (SEC_ALLOC | SEC_LOAD);
+ if (code_only)
+ flags |= SEC_CODE;
+ if (common || dup_common)
+ flags |= SEC_IS_COMMON;
+
+ flags |= SEC_RELOC | SEC_HAS_CONTENTS;
+
+ /* This is a zero-filled subspace (eg BSS). */
+ if (zero)
+ flags &= ~(SEC_LOAD | SEC_HAS_CONTENTS);
+
+ applicable &= flags;
+
+ /* If this is an existing subspace, then we want to use the
+ segment already associated with the subspace.
+
+ FIXME NOW! ELF BFD doesn't appear to be ready to deal with
+ lots of sections. It might be a problem in the PA ELF
+ code, I do not know yet. For now avoid creating anything
+ but the "standard" sections for ELF. */
+ if (create_new)
+ section = subseg_force_new (ss_name, 0);
+ else if (ssd)
+ section = ssd->ssd_seg;
+ else if (alias)
+ section = subseg_new (alias, 0);
+ else if (!alias && USE_ALIASES)
+ {
+ as_warn (_("Ignoring subspace decl due to ELF BFD bugs."));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ else
+ section = subseg_new (ss_name, 0);
+
+ if (zero)
+ seg_info (section)->bss = 1;
+
+ /* Now set the flags. */
+ bfd_set_section_flags (stdoutput, section, applicable);
+
+ /* Record any alignment request for this section. */
+ record_alignment (section, log2 (alignment));
+
+ /* Set the starting offset for this section. */
+ bfd_set_section_vma (stdoutput, section,
+ pa_subspace_start (space, quadrant));
+
+ /* Now that all the flags are set, update an existing subspace,
+ or create a new one. */
+ if (ssd)
+
+ current_subspace = update_subspace (space, ss_name, loadable,
+ code_only, common, dup_common,
+ sort, zero, access, space_index,
+ alignment, quadrant,
+ section);
+ else
+ current_subspace = create_new_subspace (space, ss_name, loadable,
+ code_only, common,
+ dup_common, zero, sort,
+ access, space_index,
+ alignment, quadrant, section);
+
+ demand_empty_rest_of_line ();
+ current_subspace->ssd_seg = section;
+ subseg_set (current_subspace->ssd_seg, current_subspace->ssd_subseg);
+ }
+ SUBSPACE_DEFINED (current_subspace) = 1;
+}
+
+
+/* Create default space and subspace dictionaries. */
+
+static void
+pa_spaces_begin ()
+{
+ int i;
+
+ space_dict_root = NULL;
+ space_dict_last = NULL;
+
+ i = 0;
+ while (pa_def_spaces[i].name)
+ {
+ char *name;
+
+ /* Pick the right name to use for the new section. */
+ if (pa_def_spaces[i].alias && USE_ALIASES)
+ name = pa_def_spaces[i].alias;
+ else
+ name = pa_def_spaces[i].name;
+
+ pa_def_spaces[i].segment = subseg_new (name, 0);
+ create_new_space (pa_def_spaces[i].name, pa_def_spaces[i].spnum,
+ pa_def_spaces[i].loadable, pa_def_spaces[i].defined,
+ pa_def_spaces[i].private, pa_def_spaces[i].sort,
+ pa_def_spaces[i].segment, 0);
+ i++;
+ }
+
+ i = 0;
+ while (pa_def_subspaces[i].name)
+ {
+ char *name;
+ int applicable, subsegment;
+ asection *segment = NULL;
+ sd_chain_struct *space;
+
+ /* Pick the right name for the new section and pick the right
+ subsegment number. */
+ if (pa_def_subspaces[i].alias && USE_ALIASES)
+ {
+ name = pa_def_subspaces[i].alias;
+ subsegment = pa_def_subspaces[i].subsegment;
+ }
+ else
+ {
+ name = pa_def_subspaces[i].name;
+ subsegment = 0;
+ }
+
+ /* Create the new section. */
+ segment = subseg_new (name, subsegment);
+
+
+ /* For SOM we want to replace the standard .text, .data, and .bss
+ sections with our own. We also want to set BFD flags for
+ all the built-in subspaces. */
+ if (!strcmp (pa_def_subspaces[i].name, "$CODE$") && !USE_ALIASES)
+ {
+ text_section = segment;
+ applicable = bfd_applicable_section_flags (stdoutput);
+ bfd_set_section_flags (stdoutput, segment,
+ applicable & (SEC_ALLOC | SEC_LOAD
+ | SEC_RELOC | SEC_CODE
+ | SEC_READONLY
+ | SEC_HAS_CONTENTS));
+ }
+ else if (!strcmp (pa_def_subspaces[i].name, "$DATA$") && !USE_ALIASES)
+ {
+ data_section = segment;
+ applicable = bfd_applicable_section_flags (stdoutput);
+ bfd_set_section_flags (stdoutput, segment,
+ applicable & (SEC_ALLOC | SEC_LOAD
+ | SEC_RELOC
+ | SEC_HAS_CONTENTS));
+
+
+ }
+ else if (!strcmp (pa_def_subspaces[i].name, "$BSS$") && !USE_ALIASES)
+ {
+ bss_section = segment;
+ applicable = bfd_applicable_section_flags (stdoutput);
+ bfd_set_section_flags (stdoutput, segment,
+ applicable & SEC_ALLOC);
+ }
+ else if (!strcmp (pa_def_subspaces[i].name, "$LIT$") && !USE_ALIASES)
+ {
+ applicable = bfd_applicable_section_flags (stdoutput);
+ bfd_set_section_flags (stdoutput, segment,
+ applicable & (SEC_ALLOC | SEC_LOAD
+ | SEC_RELOC
+ | SEC_READONLY
+ | SEC_HAS_CONTENTS));
+ }
+ else if (!strcmp (pa_def_subspaces[i].name, "$MILLICODE$")
+ && !USE_ALIASES)
+ {
+ applicable = bfd_applicable_section_flags (stdoutput);
+ bfd_set_section_flags (stdoutput, segment,
+ applicable & (SEC_ALLOC | SEC_LOAD
+ | SEC_RELOC
+ | SEC_READONLY
+ | SEC_HAS_CONTENTS));
+ }
+ else if (!strcmp (pa_def_subspaces[i].name, "$UNWIND$") && !USE_ALIASES)
+ {
+ applicable = bfd_applicable_section_flags (stdoutput);
+ bfd_set_section_flags (stdoutput, segment,
+ applicable & (SEC_ALLOC | SEC_LOAD
+ | SEC_RELOC
+ | SEC_READONLY
+ | SEC_HAS_CONTENTS));
+ }
+
+ /* Find the space associated with this subspace. */
+ space = pa_segment_to_space (pa_def_spaces[pa_def_subspaces[i].
+ def_space_index].segment);
+ if (space == NULL)
+ {
+ as_fatal (_("Internal error: Unable to find containing space for %s."),
+ pa_def_subspaces[i].name);
+ }
+
+ create_new_subspace (space, name,
+ pa_def_subspaces[i].loadable,
+ pa_def_subspaces[i].code_only,
+ pa_def_subspaces[i].common,
+ pa_def_subspaces[i].dup_common,
+ pa_def_subspaces[i].zero,
+ pa_def_subspaces[i].sort,
+ pa_def_subspaces[i].access,
+ pa_def_subspaces[i].space_index,
+ pa_def_subspaces[i].alignment,
+ pa_def_subspaces[i].quadrant,
+ segment);
+ i++;
+ }
+}
+
+
+
+/* Create a new space NAME, with the appropriate flags as defined
+ by the given parameters. */
+
+static sd_chain_struct *
+create_new_space (name, spnum, loadable, defined, private,
+ sort, seg, user_defined)
+ char *name;
+ int spnum;
+ int loadable;
+ int defined;
+ int private;
+ int sort;
+ asection *seg;
+ int user_defined;
+{
+ sd_chain_struct *chain_entry;
+
+ chain_entry = (sd_chain_struct *) xmalloc (sizeof (sd_chain_struct));
+ if (!chain_entry)
+ as_fatal (_("Out of memory: could not allocate new space chain entry: %s\n"),
+ name);
+
+ SPACE_NAME (chain_entry) = (char *) xmalloc (strlen (name) + 1);
+ strcpy (SPACE_NAME (chain_entry), name);
+ SPACE_DEFINED (chain_entry) = defined;
+ SPACE_USER_DEFINED (chain_entry) = user_defined;
+ SPACE_SPNUM (chain_entry) = spnum;
+
+ chain_entry->sd_seg = seg;
+ chain_entry->sd_last_subseg = -1;
+ chain_entry->sd_subspaces = NULL;
+ chain_entry->sd_next = NULL;
+
+ /* Find spot for the new space based on its sort key. */
+ if (!space_dict_last)
+ space_dict_last = chain_entry;
+
+ if (space_dict_root == NULL)
+ space_dict_root = chain_entry;
+ else
+ {
+ sd_chain_struct *chain_pointer;
+ sd_chain_struct *prev_chain_pointer;
+
+ chain_pointer = space_dict_root;
+ prev_chain_pointer = NULL;
+
+ while (chain_pointer)
+ {
+ prev_chain_pointer = chain_pointer;
+ chain_pointer = chain_pointer->sd_next;
+ }
+
+ /* At this point we've found the correct place to add the new
+ entry. So add it and update the linked lists as appropriate. */
+ if (prev_chain_pointer)
+ {
+ chain_entry->sd_next = chain_pointer;
+ prev_chain_pointer->sd_next = chain_entry;
+ }
+ else
+ {
+ space_dict_root = chain_entry;
+ chain_entry->sd_next = chain_pointer;
+ }
+
+ if (chain_entry->sd_next == NULL)
+ space_dict_last = chain_entry;
+ }
+
+ /* This is here to catch predefined spaces which do not get
+ modified by the user's input. Another call is found at
+ the bottom of pa_parse_space_stmt to handle cases where
+ the user modifies a predefined space. */
+#ifdef obj_set_section_attributes
+ obj_set_section_attributes (seg, defined, private, sort, spnum);
+#endif
+
+ return chain_entry;
+}
+
+/* Create a new subspace NAME, with the appropriate flags as defined
+ by the given parameters.
+
+ Add the new subspace to the subspace dictionary chain in numerical
+ order as defined by the SORT entries. */
+
+static ssd_chain_struct *
+create_new_subspace (space, name, loadable, code_only, common,
+ dup_common, is_zero, sort, access, space_index,
+ alignment, quadrant, seg)
+ sd_chain_struct *space;
+ char *name;
+ int loadable, code_only, common, dup_common, is_zero;
+ int sort;
+ int access;
+ int space_index;
+ int alignment;
+ int quadrant;
+ asection *seg;
+{
+ ssd_chain_struct *chain_entry;
+
+ chain_entry = (ssd_chain_struct *) xmalloc (sizeof (ssd_chain_struct));
+ if (!chain_entry)
+ as_fatal (_("Out of memory: could not allocate new subspace chain entry: %s\n"), name);
+
+ SUBSPACE_NAME (chain_entry) = (char *) xmalloc (strlen (name) + 1);
+ strcpy (SUBSPACE_NAME (chain_entry), name);
+
+ /* Initialize subspace_defined. When we hit a .subspace directive
+ we'll set it to 1 which "locks-in" the subspace attributes. */
+ SUBSPACE_DEFINED (chain_entry) = 0;
+
+ chain_entry->ssd_subseg = USE_ALIASES ? pa_next_subseg (space) : 0;
+ chain_entry->ssd_seg = seg;
+ chain_entry->ssd_next = NULL;
+
+ /* Find spot for the new subspace based on its sort key. */
+ if (space->sd_subspaces == NULL)
+ space->sd_subspaces = chain_entry;
+ else
+ {
+ ssd_chain_struct *chain_pointer;
+ ssd_chain_struct *prev_chain_pointer;
+
+ chain_pointer = space->sd_subspaces;
+ prev_chain_pointer = NULL;
+
+ while (chain_pointer)
+ {
+ prev_chain_pointer = chain_pointer;
+ chain_pointer = chain_pointer->ssd_next;
+ }
+
+ /* Now we have somewhere to put the new entry. Insert it and update
+ the links. */
+ if (prev_chain_pointer)
+ {
+ chain_entry->ssd_next = chain_pointer;
+ prev_chain_pointer->ssd_next = chain_entry;
+ }
+ else
+ {
+ space->sd_subspaces = chain_entry;
+ chain_entry->ssd_next = chain_pointer;
+ }
+ }
+
+#ifdef obj_set_subsection_attributes
+ obj_set_subsection_attributes (seg, space->sd_seg, access,
+ sort, quadrant);
+#endif
+
+ return chain_entry;
+}
+
+/* Update the information for the given subspace based upon the
+ various arguments. Return the modified subspace chain entry. */
+
+static ssd_chain_struct *
+update_subspace (space, name, loadable, code_only, common, dup_common, sort,
+ zero, access, space_index, alignment, quadrant, section)
+ sd_chain_struct *space;
+ char *name;
+ int loadable;
+ int code_only;
+ int common;
+ int dup_common;
+ int zero;
+ int sort;
+ int access;
+ int space_index;
+ int alignment;
+ int quadrant;
+ asection *section;
+{
+ ssd_chain_struct *chain_entry;
+
+ chain_entry = is_defined_subspace (name);
+
+#ifdef obj_set_subsection_attributes
+ obj_set_subsection_attributes (section, space->sd_seg, access,
+ sort, quadrant);
+#endif
+
+ return chain_entry;
+}
+
+/* Return the space chain entry for the space with the name NAME or
+ NULL if no such space exists. */
+
+static sd_chain_struct *
+is_defined_space (name)
+ char *name;
+{
+ sd_chain_struct *chain_pointer;
+
+ for (chain_pointer = space_dict_root;
+ chain_pointer;
+ chain_pointer = chain_pointer->sd_next)
+ {
+ if (strcmp (SPACE_NAME (chain_pointer), name) == 0)
+ return chain_pointer;
+ }
+
+ /* No mapping from segment to space was found. Return NULL. */
+ return NULL;
+}
+
+/* Find and return the space associated with the given seg. If no mapping
+ from the given seg to a space is found, then return NULL.
+
+ Unlike subspaces, the number of spaces is not expected to grow much,
+ so a linear exhaustive search is OK here. */
+
+static sd_chain_struct *
+pa_segment_to_space (seg)
+ asection *seg;
+{
+ sd_chain_struct *space_chain;
+
+ /* Walk through each space looking for the correct mapping. */
+ for (space_chain = space_dict_root;
+ space_chain;
+ space_chain = space_chain->sd_next)
+ {
+ if (space_chain->sd_seg == seg)
+ return space_chain;
+ }
+
+ /* Mapping was not found. Return NULL. */
+ return NULL;
+}
+
+/* Return the space chain entry for the subspace with the name NAME or
+ NULL if no such subspace exists.
+
+ Uses a linear search through all the spaces and subspaces, this may
+ not be appropriate if we ever being placing each function in its
+ own subspace. */
+
+static ssd_chain_struct *
+is_defined_subspace (name)
+ char *name;
+{
+ sd_chain_struct *space_chain;
+ ssd_chain_struct *subspace_chain;
+
+ /* Walk through each space. */
+ for (space_chain = space_dict_root;
+ space_chain;
+ space_chain = space_chain->sd_next)
+ {
+ /* Walk through each subspace looking for a name which matches. */
+ for (subspace_chain = space_chain->sd_subspaces;
+ subspace_chain;
+ subspace_chain = subspace_chain->ssd_next)
+ if (strcmp (SUBSPACE_NAME (subspace_chain), name) == 0)
+ return subspace_chain;
+ }
+
+ /* Subspace wasn't found. Return NULL. */
+ return NULL;
+}
+
+/* Find and return the subspace associated with the given seg. If no
+ mapping from the given seg to a subspace is found, then return NULL.
+
+ If we ever put each procedure/function within its own subspace
+ (to make life easier on the compiler and linker), then this will have
+ to become more efficient. */
+
+static ssd_chain_struct *
+pa_subsegment_to_subspace (seg, subseg)
+ asection *seg;
+ subsegT subseg;
+{
+ sd_chain_struct *space_chain;
+ ssd_chain_struct *subspace_chain;
+
+ /* Walk through each space. */
+ for (space_chain = space_dict_root;
+ space_chain;
+ space_chain = space_chain->sd_next)
+ {
+ if (space_chain->sd_seg == seg)
+ {
+ /* Walk through each subspace within each space looking for
+ the correct mapping. */
+ for (subspace_chain = space_chain->sd_subspaces;
+ subspace_chain;
+ subspace_chain = subspace_chain->ssd_next)
+ if (subspace_chain->ssd_subseg == (int) subseg)
+ return subspace_chain;
+ }
+ }
+
+ /* No mapping from subsegment to subspace found. Return NULL. */
+ return NULL;
+}
+
+/* Given a number, try and find a space with the name number.
+
+ Return a pointer to a space dictionary chain entry for the space
+ that was found or NULL on failure. */
+
+static sd_chain_struct *
+pa_find_space_by_number (number)
+ int number;
+{
+ sd_chain_struct *space_chain;
+
+ for (space_chain = space_dict_root;
+ space_chain;
+ space_chain = space_chain->sd_next)
+ {
+ if (SPACE_SPNUM (space_chain) == (unsigned int) number)
+ return space_chain;
+ }
+
+ /* No appropriate space found. Return NULL. */
+ return NULL;
+}
+
+/* Return the starting address for the given subspace. If the starting
+ address is unknown then return zero. */
+
+static unsigned int
+pa_subspace_start (space, quadrant)
+ sd_chain_struct *space;
+ int quadrant;
+{
+#ifdef OBJ_SOM
+ /* FIXME. Assumes everyone puts read/write data at 0x4000000, this
+ is not correct for the PA OSF1 port. */
+ if ((strcmp (SPACE_NAME (space), "$PRIVATE$") == 0) && quadrant == 1)
+ return 0x40000000;
+ else if (space->sd_seg == data_section && quadrant == 1)
+ return 0x40000000;
+ else
+ return 0;
+#endif
+ return 0;
+}
+
+/* FIXME. Needs documentation. */
+static int
+pa_next_subseg (space)
+ sd_chain_struct *space;
+{
+
+ space->sd_last_subseg++;
+ return space->sd_last_subseg;
+}
+
+/* Helper function for pa_stringer. Used to find the end of
+ a string. */
+
+static unsigned int
+pa_stringer_aux (s)
+ char *s;
+{
+ unsigned int c = *s & CHAR_MASK;
+
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+
+ switch (c)
+ {
+ case '\"':
+ c = NOT_A_CHAR;
+ break;
+ default:
+ break;
+ }
+ return c;
+}
+
+/* Handle a .STRING type pseudo-op. */
+
+static void
+pa_stringer (append_zero)
+ int append_zero;
+{
+ char *s, num_buf[4];
+ unsigned int c;
+ int i;
+
+ /* Preprocess the string to handle PA-specific escape sequences.
+ For example, \xDD where DD is a hexidecimal number should be
+ changed to \OOO where OOO is an octal number. */
+
+ /* Skip the opening quote. */
+ s = input_line_pointer + 1;
+
+ while (is_a_char (c = pa_stringer_aux (s++)))
+ {
+ if (c == '\\')
+ {
+ c = *s;
+ switch (c)
+ {
+ /* Handle \x<num>. */
+ case 'x':
+ {
+ unsigned int number;
+ int num_digit;
+ char dg;
+ char *s_start = s;
+
+ /* Get pas the 'x'. */
+ s++;
+ for (num_digit = 0, number = 0, dg = *s;
+ num_digit < 2
+ && (isdigit (dg) || (dg >= 'a' && dg <= 'f')
+ || (dg >= 'A' && dg <= 'F'));
+ num_digit++)
+ {
+ if (isdigit (dg))
+ number = number * 16 + dg - '0';
+ else if (dg >= 'a' && dg <= 'f')
+ number = number * 16 + dg - 'a' + 10;
+ else
+ number = number * 16 + dg - 'A' + 10;
+
+ s++;
+ dg = *s;
+ }
+ if (num_digit > 0)
+ {
+ switch (num_digit)
+ {
+ case 1:
+ sprintf (num_buf, "%02o", number);
+ break;
+ case 2:
+ sprintf (num_buf, "%03o", number);
+ break;
+ }
+ for (i = 0; i <= num_digit; i++)
+ s_start[i] = num_buf[i];
+ }
+ break;
+ }
+ /* This might be a "\"", skip over the escaped char. */
+ default:
+ s++;
+ break;
+ }
+ }
+ }
+ stringer (append_zero);
+ pa_undefine_label ();
+}
+
+/* Handle a .VERSION pseudo-op. */
+
+static void
+pa_version (unused)
+ int unused;
+{
+ obj_version (0);
+ pa_undefine_label ();
+}
+
+#ifdef OBJ_SOM
+
+/* Handle a .COMPILER pseudo-op. */
+
+static void
+pa_compiler (unused)
+ int unused;
+{
+ obj_som_compiler (0);
+ pa_undefine_label ();
+}
+
+#endif
+
+/* Handle a .COPYRIGHT pseudo-op. */
+
+static void
+pa_copyright (unused)
+ int unused;
+{
+ obj_copyright (0);
+ pa_undefine_label ();
+}
+
+/* Just like a normal cons, but when finished we have to undefine
+ the latest space label. */
+
+static void
+pa_cons (nbytes)
+ int nbytes;
+{
+ cons (nbytes);
+ pa_undefine_label ();
+}
+
+/* Switch to the data space. As usual delete our label. */
+
+static void
+pa_data (unused)
+ int unused;
+{
+ current_space = is_defined_space ("$PRIVATE$");
+ current_subspace
+ = pa_subsegment_to_subspace (current_space->sd_seg, 0);
+ s_data (0);
+ pa_undefine_label ();
+}
+
+/* Like float_cons, but we need to undefine our label. */
+
+static void
+pa_float_cons (float_type)
+ int float_type;
+{
+ float_cons (float_type);
+ pa_undefine_label ();
+}
+
+/* Like s_fill, but delete our label when finished. */
+
+static void
+pa_fill (unused)
+ int unused;
+{
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+
+ s_fill (0);
+ pa_undefine_label ();
+}
+
+/* Like lcomm, but delete our label when finished. */
+
+static void
+pa_lcomm (needs_align)
+ int needs_align;
+{
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+
+ s_lcomm (needs_align);
+ pa_undefine_label ();
+}
+
+/* Like lsym, but delete our label when finished. */
+
+static void
+pa_lsym (unused)
+ int unused;
+{
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+
+ s_lsym (0);
+ pa_undefine_label ();
+}
+
+/* Switch to the text space. Like s_text, but delete our
+ label when finished. */
+static void
+pa_text (unused)
+ int unused;
+{
+ current_space = is_defined_space ("$TEXT$");
+ current_subspace
+ = pa_subsegment_to_subspace (current_space->sd_seg, 0);
+
+ s_text (0);
+ pa_undefine_label ();
+}
+
+/* On the PA relocations which involve function symbols must not be
+ adjusted. This so that the linker can know when/how to create argument
+ relocation stubs for indirect calls and calls to static functions.
+
+ "T" field selectors create DLT relative fixups for accessing
+ globals and statics in PIC code; each DLT relative fixup creates
+ an entry in the DLT table. The entries contain the address of
+ the final target (eg accessing "foo" would create a DLT entry
+ with the address of "foo").
+
+ Unfortunately, the HP linker doesn't take into account any addend
+ when generating the DLT; so accessing $LIT$+8 puts the address of
+ $LIT$ into the DLT rather than the address of $LIT$+8.
+
+ The end result is we can't perform relocation symbol reductions for
+ any fixup which creates entries in the DLT (eg they use "T" field
+ selectors).
+
+ Reject reductions involving symbols with external scope; such
+ reductions make life a living hell for object file editors.
+
+ FIXME. Also reject R_HPPA relocations which are 32bits wide in
+ the code space. The SOM BFD backend doesn't know how to pull the
+ right bits out of an instruction. */
+
+int
+hppa_fix_adjustable (fixp)
+ fixS *fixp;
+{
+ struct hppa_fix_struct *hppa_fix;
+
+ hppa_fix = (struct hppa_fix_struct *) fixp->tc_fix_data;
+
+#ifdef OBJ_SOM
+ /* Reject reductions of symbols in 32bit relocs. */
+ if (fixp->fx_r_type == R_HPPA && hppa_fix->fx_r_format == 32)
+ return 0;
+
+ /* Reject reductions of symbols in sym1-sym2 expressions when
+ the fixup will occur in a CODE subspace.
+
+ XXX FIXME: Long term we probably want to reject all of these;
+ for example reducing in the debug section would lose if we ever
+ supported using the optimizing hp linker. */
+ if (fixp->fx_addsy
+ && fixp->fx_subsy
+ && (hppa_fix->segment->flags & SEC_CODE))
+ {
+ /* Apparently sy_used_in_reloc never gets set for sub symbols. */
+ fixp->fx_subsy->sy_used_in_reloc = 1;
+ return 0;
+ }
+
+ /* We can't adjust any relocs that use LR% and RR% field selectors.
+ That confuses the HP linker. */
+ if (hppa_fix->fx_r_field == e_lrsel
+ || hppa_fix->fx_r_field == e_rrsel
+ || hppa_fix->fx_r_field == e_nlrsel)
+ return 0;
+#endif
+
+ /* Reject reductions of symbols in DLT relative relocs,
+ relocations with plabels. */
+ if (hppa_fix->fx_r_field == e_tsel
+ || hppa_fix->fx_r_field == e_ltsel
+ || hppa_fix->fx_r_field == e_rtsel
+ || hppa_fix->fx_r_field == e_psel
+ || hppa_fix->fx_r_field == e_rpsel
+ || hppa_fix->fx_r_field == e_lpsel)
+ return 0;
+
+ if (fixp->fx_addsy && fixp->fx_addsy->bsym->flags & BSF_GLOBAL)
+ return 0;
+
+ /* Reject absolute calls (jumps). */
+ if (hppa_fix->fx_r_type == R_HPPA_ABS_CALL)
+ return 0;
+
+ /* Reject reductions of function symbols. */
+ if (fixp->fx_addsy == 0
+ || (fixp->fx_addsy->bsym->flags & BSF_FUNCTION) == 0)
+ return 1;
+
+ return 0;
+}
+
+/* Return nonzero if the fixup in FIXP will require a relocation,
+ even it if appears that the fixup could be completely handled
+ within GAS. */
+
+int
+hppa_force_relocation (fixp)
+ fixS *fixp;
+{
+ struct hppa_fix_struct *hppa_fixp;
+ int distance;
+
+ hppa_fixp = (struct hppa_fix_struct *) fixp->tc_fix_data;
+#ifdef OBJ_SOM
+ if (fixp->fx_r_type == R_HPPA_ENTRY || fixp->fx_r_type == R_HPPA_EXIT
+ || fixp->fx_r_type == R_HPPA_BEGIN_BRTAB
+ || fixp->fx_r_type == R_HPPA_END_BRTAB
+ || fixp->fx_r_type == R_HPPA_BEGIN_TRY
+ || fixp->fx_r_type == R_HPPA_END_TRY
+ || (fixp->fx_addsy != NULL && fixp->fx_subsy != NULL
+ && (hppa_fixp->segment->flags & SEC_CODE) != 0))
+ return 1;
+#endif
+
+#define arg_reloc_stub_needed(CALLER, CALLEE) \
+ ((CALLEE) && (CALLER) && ((CALLEE) != (CALLER)))
+
+ /* It is necessary to force PC-relative calls/jumps to have a relocation
+ entry if they're going to need either a argument relocation or long
+ call stub. FIXME. Can't we need the same for absolute calls? */
+ if (fixp->fx_pcrel && fixp->fx_addsy
+ && (arg_reloc_stub_needed ((long) ((obj_symbol_type *)
+ fixp->fx_addsy->bsym)->tc_data.ap.hppa_arg_reloc,
+
+ hppa_fixp->fx_arg_reloc)))
+ return 1;
+ distance = (fixp->fx_offset + S_GET_VALUE (fixp->fx_addsy)
+ - md_pcrel_from (fixp));
+ /* Now check and see if we're going to need a long-branch stub. */
+ if (fixp->fx_r_type == R_HPPA_PCREL_CALL
+ && (distance > 262143 || distance < -262144))
+ return 1;
+
+ if (fixp->fx_r_type == R_HPPA_ABS_CALL)
+ return 1;
+#undef arg_reloc_stub_needed
+
+ /* No need (yet) to force another relocations to be emitted. */
+ return 0;
+}
+
+/* Now for some ELF specific code. FIXME. */
+#ifdef OBJ_ELF
+/* Mark the end of a function so that it's possible to compute
+ the size of the function in hppa_elf_final_processing. */
+
+static void
+hppa_elf_mark_end_of_function ()
+{
+ /* ELF does not have EXIT relocations. All we do is create a
+ temporary symbol marking the end of the function. */
+ char *name = (char *)
+ xmalloc (strlen ("L$\001end_") +
+ strlen (S_GET_NAME (last_call_info->start_symbol)) + 1);
+
+ if (name)
+ {
+ symbolS *symbolP;
+
+ strcpy (name, "L$\001end_");
+ strcat (name, S_GET_NAME (last_call_info->start_symbol));
+
+ /* If we have a .exit followed by a .procend, then the
+ symbol will have already been defined. */
+ symbolP = symbol_find (name);
+ if (symbolP)
+ {
+ /* The symbol has already been defined! This can
+ happen if we have a .exit followed by a .procend.
+
+ This is *not* an error. All we want to do is free
+ the memory we just allocated for the name and continue. */
+ xfree (name);
+ }
+ else
+ {
+ /* symbol value should be the offset of the
+ last instruction of the function */
+ symbolP = symbol_new (name, now_seg, (valueT) (frag_now_fix () - 4),
+ frag_now);
+
+ assert (symbolP);
+ symbolP->bsym->flags = BSF_LOCAL;
+ symbol_table_insert (symbolP);
+ }
+
+ if (symbolP)
+ last_call_info->end_symbol = symbolP;
+ else
+ as_bad (_("Symbol '%s' could not be created."), name);
+
+ }
+ else
+ as_bad (_("No memory for symbol name."));
+
+}
+
+/* For ELF, this function serves one purpose: to setup the st_size
+ field of STT_FUNC symbols. To do this, we need to scan the
+ call_info structure list, determining st_size in by taking the
+ difference in the address of the beginning/end marker symbols. */
+
+void
+elf_hppa_final_processing ()
+{
+ struct call_info *call_info_pointer;
+
+ for (call_info_pointer = call_info_root;
+ call_info_pointer;
+ call_info_pointer = call_info_pointer->ci_next)
+ {
+ elf_symbol_type *esym
+ = (elf_symbol_type *) call_info_pointer->start_symbol->bsym;
+ esym->internal_elf_sym.st_size =
+ S_GET_VALUE (call_info_pointer->end_symbol)
+ - S_GET_VALUE (call_info_pointer->start_symbol) + 4;
+ }
+}
+#endif
diff --git a/gas/config/tc-hppa.h b/gas/config/tc-hppa.h
new file mode 100644
index 0000000..c1541d9
--- /dev/null
+++ b/gas/config/tc-hppa.h
@@ -0,0 +1,162 @@
+/* tc-hppa.h -- Header file for the PA
+ Copyright (C) 1989, 93, 94, 95, 96, 97, 1998 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+
+/* HP PA-RISC support was contributed by the Center for Software Science
+ at the University of Utah. */
+
+/* Please refrain from exposing the world to the internals of tc-hppa.c
+ when this file is included. This means only declaring exported functions,
+ (please PARAMize them!) not exporting structures and data items which
+ are used solely within tc-hppa.c, etc.
+
+ Also refrain from adding any more object file dependent code, there is
+ already far too much object file format dependent code in this file.
+ In theory this file should contain only exported functions, structures
+ and data declarations common to all PA assemblers. */
+
+#ifndef _TC_HPPA_H
+#define _TC_HPPA_H
+
+#ifndef TC_HPPA
+#define TC_HPPA 1
+#endif
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+#define TARGET_ARCH bfd_arch_hppa
+
+#define WORKING_DOT_WORD
+
+/* FIXME. The lack of a place to put things which are both target cpu
+ and target format dependent makes hacks like this necessary. */
+#ifdef OBJ_ELF
+#include "bfd/elf32-hppa.h"
+#define TARGET_FORMAT "elf32-hppa"
+#endif
+
+#ifdef OBJ_SOM
+#include "bfd/som.h"
+#define TARGET_FORMAT "som"
+#endif
+
+/* FIXME. Why oh why aren't these defined somewhere globally? */
+#ifndef FALSE
+#define FALSE (0)
+#define TRUE (!FALSE)
+#endif
+
+#define ASEC_NULL (asection *)0
+
+/* Labels are not required to have a colon for a suffix. */
+#define LABELS_WITHOUT_COLONS
+
+/* FIXME. This should be static and declared in tc-hppa.c, but
+ pa_define_label gets used outside of tc-hppa.c via tc_frob_label.
+ Should also be PARAMized, but symbolS isn't available here. */
+extern void pa_define_label ();
+
+/* FIXME. Types not available here, so they can't be PARAMized. */
+extern void parse_cons_expression_hppa ();
+extern void cons_fix_new_hppa ();
+extern int hppa_force_relocation ();
+
+/* This gets called before writing the object file to make sure
+ things like entry/exit and proc/procend pairs match. */
+extern void pa_check_eof PARAMS ((void));
+#define tc_frob_file pa_check_eof
+
+#define tc_frob_label(sym) pa_define_label (sym)
+
+/* The PA does not need support for either of these. */
+#define tc_crawl_symbol_chain(headers) {;}
+#define tc_headers_hook(headers) {;}
+
+#define RELOC_EXPANSION_POSSIBLE
+#define MAX_RELOC_EXPANSION 6
+
+/* FIXME. More things which are both HPPA and ELF specific. There is
+ nowhere to put such things. */
+#ifdef OBJ_ELF
+#define elf_tc_final_processing elf_hppa_final_processing
+void elf_hppa_final_processing PARAMS ((void));
+#endif
+
+/* The PA needs to parse field selectors in .byte, etc. */
+
+#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) \
+ parse_cons_expression_hppa (EXP)
+#define TC_CONS_FIX_NEW cons_fix_new_hppa
+
+/* On the PA, an equal sign often appears as a condition or nullification
+ completer in an instruction. This can be detected by checking the
+ previous character, if the character is a comma, then the equal is
+ being used as part of an instruction. */
+#define TC_EQUAL_IN_INSN(C, PTR) ((C) == ',')
+
+/* Similarly for an exclamation point. It is used in FP comparison
+ instructions and as an end of line marker. When used in an instruction
+ it will always follow a comma. */
+#define TC_EOL_IN_INSN(PTR) (*(PTR) == '!' && (PTR)[-1] == ',')
+
+#define tc_fix_adjustable hppa_fix_adjustable
+
+/* Because of the strange PA calling conventions, it is sometimes
+ necessary to emit a relocation for a call even though it would
+ normally appear safe to handle it completely within GAS. */
+#define TC_FORCE_RELOCATION(FIXP) hppa_force_relocation (FIXP)
+
+#ifdef OBJ_SOM
+/* If a symbol is imported, but never used, then the symbol should
+ *not* end up in the symbol table. Likewise for absolute symbols
+ with local scope. */
+#define tc_frob_symbol(sym,punt) \
+ if ((S_GET_SEGMENT (sym) == &bfd_und_section && sym->sy_used == 0) \
+ || (S_GET_SEGMENT (sym) == &bfd_abs_section \
+ && (sym->bsym->flags & BSF_EXPORT) == 0)) \
+ punt = 1
+
+/* We need to be able to make relocations involving the difference of
+ two symbols. This includes the difference of two symbols when
+ one of them is undefined (this comes up in PIC code generation).
+
+ We don't define DIFF_EXPR_OK because it does the wrong thing if
+ the add symbol is undefined and the sub symbol is a symbol in
+ the same section as the relocation. We also need some way to
+ specialize some code in adjust_reloc_syms. */
+#define UNDEFINED_DIFFERENCE_OK
+#endif
+
+#ifdef OBJ_ELF
+#define tc_frob_symbol(sym,punt) \
+ { \
+ if ((S_GET_SEGMENT (sym) == &bfd_und_section && sym->sy_used == 0) \
+ || (S_GET_SEGMENT (sym) == &bfd_abs_section \
+ && (sym->bsym->flags & BSF_EXPORT) == 0)) \
+ punt = 1; \
+ }
+#endif
+
+#define md_operand(x)
+
+#define TC_FIX_TYPE PTR
+#define TC_INIT_FIX_DATA(FIXP) ((FIXP)->tc_fix_data = NULL)
+
+#endif /* _TC_HPPA_H */
diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
new file mode 100644
index 0000000..7149d71
--- /dev/null
+++ b/gas/config/tc-i386.c
@@ -0,0 +1,4475 @@
+/* i386.c -- Assemble code for the Intel 80386
+ Copyright (C) 1989, 91, 92, 93, 94, 95, 96, 97, 98, 1999
+ Free Software Foundation.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+/*
+ Intel 80386 machine specific gas.
+ Written by Eliot Dresselhaus (eliot@mgm.mit.edu).
+ Bugs & suggestions are completely welcome. This is free software.
+ Please help us make it better.
+ */
+
+#include <ctype.h>
+
+#include "as.h"
+#include "subsegs.h"
+#include "opcode/i386.h"
+
+#ifndef TC_RELOC
+#define TC_RELOC(X,Y) (Y)
+#endif
+
+#ifndef REGISTER_WARNINGS
+#define REGISTER_WARNINGS 1
+#endif
+
+#ifndef SCALE1_WHEN_NO_INDEX
+/* Specifying a scale factor besides 1 when there is no index is
+ futile. eg. `mov (%ebx,2),%al' does exactly the same as
+ `mov (%ebx),%al'. To slavishly follow what the programmer
+ specified, set SCALE1_WHEN_NO_INDEX to 0. */
+#define SCALE1_WHEN_NO_INDEX 1
+#endif
+
+#define true 1
+#define false 0
+
+static unsigned int mode_from_disp_size PARAMS ((unsigned int));
+static int fits_in_signed_byte PARAMS ((long));
+static int fits_in_unsigned_byte PARAMS ((long));
+static int fits_in_unsigned_word PARAMS ((long));
+static int fits_in_signed_word PARAMS ((long));
+static int smallest_imm_type PARAMS ((long));
+static int add_prefix PARAMS ((unsigned int));
+static void set_16bit_code_flag PARAMS ((int));
+static void set_intel_syntax PARAMS ((int));
+
+#ifdef BFD_ASSEMBLER
+static bfd_reloc_code_real_type reloc
+ PARAMS ((int, int, bfd_reloc_code_real_type));
+#endif
+
+/* 'md_assemble ()' gathers together information and puts it into a
+ i386_insn. */
+
+struct _i386_insn
+ {
+ /* TM holds the template for the insn were currently assembling. */
+ template tm;
+
+ /* SUFFIX holds the instruction mnemonic suffix if given.
+ (e.g. 'l' for 'movl') */
+ char suffix;
+
+ /* Operands are coded with OPERANDS, TYPES, DISPS, IMMS, and REGS. */
+
+ /* OPERANDS gives the number of given operands. */
+ unsigned int operands;
+
+ /* REG_OPERANDS, DISP_OPERANDS, MEM_OPERANDS, IMM_OPERANDS give the number
+ of given register, displacement, memory operands and immediate
+ operands. */
+ unsigned int reg_operands, disp_operands, mem_operands, imm_operands;
+
+ /* TYPES [i] is the type (see above #defines) which tells us how to
+ search through DISPS [i] & IMMS [i] & REGS [i] for the required
+ operand. */
+ unsigned int types[MAX_OPERANDS];
+
+ /* Displacements (if given) for each operand. */
+ expressionS *disps[MAX_OPERANDS];
+
+ /* Relocation type for operand */
+#ifdef BFD_ASSEMBLER
+ enum bfd_reloc_code_real disp_reloc[MAX_OPERANDS];
+#else
+ int disp_reloc[MAX_OPERANDS];
+#endif
+
+ /* Immediate operands (if given) for each operand. */
+ expressionS *imms[MAX_OPERANDS];
+
+ /* Register operands (if given) for each operand. */
+ const reg_entry *regs[MAX_OPERANDS];
+
+ /* BASE_REG, INDEX_REG, and LOG2_SCALE_FACTOR are used to encode
+ the base index byte below. */
+ const reg_entry *base_reg;
+ const reg_entry *index_reg;
+ unsigned int log2_scale_factor;
+
+ /* SEG gives the seg_entries of this insn. They are zero unless
+ explicit segment overrides are given. */
+ const seg_entry *seg[2]; /* segments for memory operands (if given) */
+
+ /* PREFIX holds all the given prefix opcodes (usually null).
+ PREFIXES is the number of prefix opcodes. */
+ unsigned int prefixes;
+ unsigned char prefix[MAX_PREFIXES];
+
+ /* RM and SIB are the modrm byte and the sib byte where the
+ addressing modes of this insn are encoded. */
+
+ modrm_byte rm;
+ sib_byte sib;
+ };
+
+typedef struct _i386_insn i386_insn;
+
+/* List of chars besides those in app.c:symbol_chars that can start an
+ operand. Used to prevent the scrubber eating vital white-space. */
+#ifdef LEX_AT
+const char extra_symbol_chars[] = "*%-(@";
+#else
+const char extra_symbol_chars[] = "*%-(";
+#endif
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful */
+#if defined (TE_I386AIX) || ((defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)) && ! defined (TE_LINUX))
+/* Putting '/' here makes it impossible to use the divide operator.
+ However, we need it for compatibility with SVR4 systems. */
+const char comment_chars[] = "#/";
+#define PREFIX_SEPARATOR '\\'
+#else
+const char comment_chars[] = "#";
+#define PREFIX_SEPARATOR '/'
+#endif
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that comments started like this one will always work if
+ '/' isn't otherwise defined. */
+#if defined (TE_I386AIX) || ((defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)) && ! defined (TE_LINUX))
+const char line_comment_chars[] = "";
+#else
+const char line_comment_chars[] = "/";
+#endif
+
+const char line_separator_chars[] = "";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+const char FLT_CHARS[] = "fFdDxX";
+
+/* tables for lexical analysis */
+static char mnemonic_chars[256];
+static char register_chars[256];
+static char operand_chars[256];
+static char identifier_chars[256];
+static char digit_chars[256];
+
+/* lexical macros */
+#define is_mnemonic_char(x) (mnemonic_chars[(unsigned char) x])
+#define is_operand_char(x) (operand_chars[(unsigned char) x])
+#define is_register_char(x) (register_chars[(unsigned char) x])
+#define is_space_char(x) ((x) == ' ')
+#define is_identifier_char(x) (identifier_chars[(unsigned char) x])
+#define is_digit_char(x) (digit_chars[(unsigned char) x])
+
+/* put here all non-digit non-letter charcters that may occur in an operand */
+static char operand_special_chars[] = "%$-+(,)*._~/<>|&^!:[@]";
+
+/* md_assemble() always leaves the strings it's passed unaltered. To
+ effect this we maintain a stack of saved characters that we've smashed
+ with '\0's (indicating end of strings for various sub-fields of the
+ assembler instruction). */
+static char save_stack[32];
+static char *save_stack_p; /* stack pointer */
+#define END_STRING_AND_SAVE(s) \
+ do { *save_stack_p++ = *(s); *(s) = '\0'; } while (0)
+#define RESTORE_END_STRING(s) \
+ do { *(s) = *--save_stack_p; } while (0)
+
+/* The instruction we're assembling. */
+static i386_insn i;
+
+/* Possible templates for current insn. */
+static const templates *current_templates;
+
+/* Per instruction expressionS buffers: 2 displacements & 2 immediate max. */
+static expressionS disp_expressions[2], im_expressions[2];
+
+static int this_operand; /* current operand we are working on */
+
+static int flag_do_long_jump; /* FIXME what does this do? */
+
+static int flag_16bit_code; /* 1 if we're writing 16-bit code, 0 if 32-bit */
+
+static int intel_syntax = 0; /* 1 for intel syntax, 0 if att syntax */
+
+static int allow_naked_reg = 0; /* 1 if register prefix % not required */
+
+/* Interface to relax_segment.
+ There are 2 relax states for 386 jump insns: one for conditional &
+ one for unconditional jumps. This is because the these two types
+ of jumps add different sizes to frags when we're figuring out what
+ sort of jump to choose to reach a given label. */
+
+/* types */
+#define COND_JUMP 1 /* conditional jump */
+#define UNCOND_JUMP 2 /* unconditional jump */
+/* sizes */
+#define CODE16 1
+#define SMALL 0
+#define SMALL16 (SMALL|CODE16)
+#define BIG 2
+#define BIG16 (BIG|CODE16)
+
+#ifndef INLINE
+#ifdef __GNUC__
+#define INLINE __inline__
+#else
+#define INLINE
+#endif
+#endif
+
+#define ENCODE_RELAX_STATE(type,size) \
+ ((relax_substateT)((type<<2) | (size)))
+#define SIZE_FROM_RELAX_STATE(s) \
+ ( (((s) & 0x3) == BIG ? 4 : (((s) & 0x3) == BIG16 ? 2 : 1)) )
+
+/* This table is used by relax_frag to promote short jumps to long
+ ones where necessary. SMALL (short) jumps may be promoted to BIG
+ (32 bit long) ones, and SMALL16 jumps to BIG16 (16 bit long). We
+ don't allow a short jump in a 32 bit code segment to be promoted to
+ a 16 bit offset jump because it's slower (requires data size
+ prefix), and doesn't work, unless the destination is in the bottom
+ 64k of the code segment (The top 16 bits of eip are zeroed). */
+
+const relax_typeS md_relax_table[] =
+{
+/* The fields are:
+ 1) most positive reach of this state,
+ 2) most negative reach of this state,
+ 3) how many bytes this mode will add to the size of the current frag
+ 4) which index into the table to try if we can't fit into this one.
+ */
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+
+ {127 + 1, -128 + 1, 0, ENCODE_RELAX_STATE (COND_JUMP, BIG)},
+ {127 + 1, -128 + 1, 0, ENCODE_RELAX_STATE (COND_JUMP, BIG16)},
+ /* dword conditionals adds 4 bytes to frag:
+ 1 extra opcode byte, 3 extra displacement bytes. */
+ {0, 0, 4, 0},
+ /* word conditionals add 2 bytes to frag:
+ 1 extra opcode byte, 1 extra displacement byte. */
+ {0, 0, 2, 0},
+
+ {127 + 1, -128 + 1, 0, ENCODE_RELAX_STATE (UNCOND_JUMP, BIG)},
+ {127 + 1, -128 + 1, 0, ENCODE_RELAX_STATE (UNCOND_JUMP, BIG16)},
+ /* dword jmp adds 3 bytes to frag:
+ 0 extra opcode bytes, 3 extra displacement bytes. */
+ {0, 0, 3, 0},
+ /* word jmp adds 1 byte to frag:
+ 0 extra opcode bytes, 1 extra displacement byte. */
+ {0, 0, 1, 0}
+
+};
+
+
+void
+i386_align_code (fragP, count)
+ fragS *fragP;
+ int count;
+{
+ /* Various efficient no-op patterns for aligning code labels. */
+ /* Note: Don't try to assemble the instructions in the comments. */
+ /* 0L and 0w are not legal */
+ static const char f32_1[] =
+ {0x90}; /* nop */
+ static const char f32_2[] =
+ {0x89,0xf6}; /* movl %esi,%esi */
+ static const char f32_3[] =
+ {0x8d,0x76,0x00}; /* leal 0(%esi),%esi */
+ static const char f32_4[] =
+ {0x8d,0x74,0x26,0x00}; /* leal 0(%esi,1),%esi */
+ static const char f32_5[] =
+ {0x90, /* nop */
+ 0x8d,0x74,0x26,0x00}; /* leal 0(%esi,1),%esi */
+ static const char f32_6[] =
+ {0x8d,0xb6,0x00,0x00,0x00,0x00}; /* leal 0L(%esi),%esi */
+ static const char f32_7[] =
+ {0x8d,0xb4,0x26,0x00,0x00,0x00,0x00}; /* leal 0L(%esi,1),%esi */
+ static const char f32_8[] =
+ {0x90, /* nop */
+ 0x8d,0xb4,0x26,0x00,0x00,0x00,0x00}; /* leal 0L(%esi,1),%esi */
+ static const char f32_9[] =
+ {0x89,0xf6, /* movl %esi,%esi */
+ 0x8d,0xbc,0x27,0x00,0x00,0x00,0x00}; /* leal 0L(%edi,1),%edi */
+ static const char f32_10[] =
+ {0x8d,0x76,0x00, /* leal 0(%esi),%esi */
+ 0x8d,0xbc,0x27,0x00,0x00,0x00,0x00}; /* leal 0L(%edi,1),%edi */
+ static const char f32_11[] =
+ {0x8d,0x74,0x26,0x00, /* leal 0(%esi,1),%esi */
+ 0x8d,0xbc,0x27,0x00,0x00,0x00,0x00}; /* leal 0L(%edi,1),%edi */
+ static const char f32_12[] =
+ {0x8d,0xb6,0x00,0x00,0x00,0x00, /* leal 0L(%esi),%esi */
+ 0x8d,0xbf,0x00,0x00,0x00,0x00}; /* leal 0L(%edi),%edi */
+ static const char f32_13[] =
+ {0x8d,0xb6,0x00,0x00,0x00,0x00, /* leal 0L(%esi),%esi */
+ 0x8d,0xbc,0x27,0x00,0x00,0x00,0x00}; /* leal 0L(%edi,1),%edi */
+ static const char f32_14[] =
+ {0x8d,0xb4,0x26,0x00,0x00,0x00,0x00, /* leal 0L(%esi,1),%esi */
+ 0x8d,0xbc,0x27,0x00,0x00,0x00,0x00}; /* leal 0L(%edi,1),%edi */
+ static const char f32_15[] =
+ {0xeb,0x0d,0x90,0x90,0x90,0x90,0x90, /* jmp .+15; lotsa nops */
+ 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90};
+ static const char f16_4[] =
+ {0x8d,0xb4,0x00,0x00}; /* lea 0w(%si),%si */
+ static const char f16_5[] =
+ {0x90, /* nop */
+ 0x8d,0xb4,0x00,0x00}; /* lea 0w(%si),%si */
+ static const char f16_6[] =
+ {0x89,0xf6, /* mov %si,%si */
+ 0x8d,0xbd,0x00,0x00}; /* lea 0w(%di),%di */
+ static const char f16_7[] =
+ {0x8d,0x74,0x00, /* lea 0(%si),%si */
+ 0x8d,0xbd,0x00,0x00}; /* lea 0w(%di),%di */
+ static const char f16_8[] =
+ {0x8d,0xb4,0x00,0x00, /* lea 0w(%si),%si */
+ 0x8d,0xbd,0x00,0x00}; /* lea 0w(%di),%di */
+ static const char *const f32_patt[] = {
+ f32_1, f32_2, f32_3, f32_4, f32_5, f32_6, f32_7, f32_8,
+ f32_9, f32_10, f32_11, f32_12, f32_13, f32_14, f32_15
+ };
+ static const char *const f16_patt[] = {
+ f32_1, f32_2, f32_3, f16_4, f16_5, f16_6, f16_7, f16_8,
+ f32_15, f32_15, f32_15, f32_15, f32_15, f32_15, f32_15
+ };
+
+ if (count > 0 && count <= 15)
+ {
+ if (flag_16bit_code)
+ {
+ memcpy(fragP->fr_literal + fragP->fr_fix,
+ f16_patt[count - 1], count);
+ if (count > 8) /* adjust jump offset */
+ fragP->fr_literal[fragP->fr_fix + 1] = count - 2;
+ }
+ else
+ memcpy(fragP->fr_literal + fragP->fr_fix,
+ f32_patt[count - 1], count);
+ fragP->fr_var = count;
+ }
+}
+
+static char *output_invalid PARAMS ((int c));
+static int i386_operand PARAMS ((char *operand_string));
+static int i386_intel_operand PARAMS ((char *operand_string, int got_a_float));
+static const reg_entry *parse_register PARAMS ((char *reg_string,
+ char **end_op));
+
+#ifndef I386COFF
+static void s_bss PARAMS ((int));
+#endif
+
+symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
+
+static INLINE unsigned int
+mode_from_disp_size (t)
+ unsigned int t;
+{
+ return (t & Disp8) ? 1 : (t & (Disp16|Disp32)) ? 2 : 0;
+}
+
+static INLINE int
+fits_in_signed_byte (num)
+ long num;
+{
+ return (num >= -128) && (num <= 127);
+} /* fits_in_signed_byte() */
+
+static INLINE int
+fits_in_unsigned_byte (num)
+ long num;
+{
+ return (num & 0xff) == num;
+} /* fits_in_unsigned_byte() */
+
+static INLINE int
+fits_in_unsigned_word (num)
+ long num;
+{
+ return (num & 0xffff) == num;
+} /* fits_in_unsigned_word() */
+
+static INLINE int
+fits_in_signed_word (num)
+ long num;
+{
+ return (-32768 <= num) && (num <= 32767);
+} /* fits_in_signed_word() */
+
+static int
+smallest_imm_type (num)
+ long num;
+{
+#if 0
+ /* This code is disabled because all the Imm1 forms in the opcode table
+ are slower on the i486, and they're the versions with the implicitly
+ specified single-position displacement, which has another syntax if
+ you really want to use that form. If you really prefer to have the
+ one-byte-shorter Imm1 form despite these problems, re-enable this
+ code. */
+ if (num == 1)
+ return Imm1 | Imm8 | Imm8S | Imm16 | Imm32;
+#endif
+ return (fits_in_signed_byte (num)
+ ? (Imm8S | Imm8 | Imm16 | Imm32)
+ : fits_in_unsigned_byte (num)
+ ? (Imm8 | Imm16 | Imm32)
+ : (fits_in_signed_word (num) || fits_in_unsigned_word (num))
+ ? (Imm16 | Imm32)
+ : (Imm32));
+} /* smallest_imm_type() */
+
+/* Returns 0 if attempting to add a prefix where one from the same
+ class already exists, 1 if non rep/repne added, 2 if rep/repne
+ added. */
+static int
+add_prefix (prefix)
+ unsigned int prefix;
+{
+ int ret = 1;
+ int q;
+
+ switch (prefix)
+ {
+ default:
+ abort ();
+
+ case CS_PREFIX_OPCODE:
+ case DS_PREFIX_OPCODE:
+ case ES_PREFIX_OPCODE:
+ case FS_PREFIX_OPCODE:
+ case GS_PREFIX_OPCODE:
+ case SS_PREFIX_OPCODE:
+ q = SEG_PREFIX;
+ break;
+
+ case REPNE_PREFIX_OPCODE:
+ case REPE_PREFIX_OPCODE:
+ ret = 2;
+ /* fall thru */
+ case LOCK_PREFIX_OPCODE:
+ q = LOCKREP_PREFIX;
+ break;
+
+ case FWAIT_OPCODE:
+ q = WAIT_PREFIX;
+ break;
+
+ case ADDR_PREFIX_OPCODE:
+ q = ADDR_PREFIX;
+ break;
+
+ case DATA_PREFIX_OPCODE:
+ q = DATA_PREFIX;
+ break;
+ }
+
+ if (i.prefix[q])
+ {
+ as_bad (_("same type of prefix used twice"));
+ return 0;
+ }
+
+ i.prefixes += 1;
+ i.prefix[q] = prefix;
+ return ret;
+}
+
+static void
+set_16bit_code_flag (new_16bit_code_flag)
+ int new_16bit_code_flag;
+{
+ flag_16bit_code = new_16bit_code_flag;
+}
+
+static void
+set_intel_syntax (syntax_flag)
+ int syntax_flag;
+{
+ /* Find out if register prefixing is specified. */
+ int ask_naked_reg = 0;
+
+ SKIP_WHITESPACE ();
+ if (! is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ char *string = input_line_pointer;
+ int e = get_symbol_end ();
+
+ if (strcmp(string, "prefix") == 0)
+ ask_naked_reg = 1;
+ else if (strcmp(string, "noprefix") == 0)
+ ask_naked_reg = -1;
+ else
+ as_bad (_("Bad argument to syntax directive."));
+ *input_line_pointer = e;
+ }
+ demand_empty_rest_of_line ();
+
+ intel_syntax = syntax_flag;
+
+ if (ask_naked_reg == 0)
+ {
+#ifdef BFD_ASSEMBLER
+ allow_naked_reg = (intel_syntax
+ && (bfd_get_symbol_leading_char (stdoutput) != '\0'));
+#else
+ allow_naked_reg = 0; /* conservative default */
+#endif
+ }
+ else
+ allow_naked_reg = (ask_naked_reg < 0);
+}
+
+const pseudo_typeS md_pseudo_table[] =
+{
+#ifndef I386COFF
+ {"bss", s_bss, 0},
+#endif
+#if !defined(OBJ_AOUT) && !defined(USE_ALIGN_PTWO)
+ {"align", s_align_bytes, 0},
+#else
+ {"align", s_align_ptwo, 0},
+#endif
+ {"ffloat", float_cons, 'f'},
+ {"dfloat", float_cons, 'd'},
+ {"tfloat", float_cons, 'x'},
+ {"value", cons, 2},
+ {"noopt", s_ignore, 0},
+ {"optim", s_ignore, 0},
+ {"code16", set_16bit_code_flag, 1},
+ {"code32", set_16bit_code_flag, 0},
+ {"intel_syntax", set_intel_syntax, 1},
+ {"att_syntax", set_intel_syntax, 0},
+ {0, 0, 0}
+};
+
+/* for interface with expression () */
+extern char *input_line_pointer;
+
+/* hash table for instruction mnemonic lookup */
+static struct hash_control *op_hash;
+/* hash table for register lookup */
+static struct hash_control *reg_hash;
+
+
+void
+md_begin ()
+{
+ const char *hash_err;
+
+ /* initialize op_hash hash table */
+ op_hash = hash_new ();
+
+ {
+ register const template *optab;
+ register templates *core_optab;
+
+ optab = i386_optab; /* setup for loop */
+ core_optab = (templates *) xmalloc (sizeof (templates));
+ core_optab->start = optab;
+
+ while (1)
+ {
+ ++optab;
+ if (optab->name == NULL
+ || strcmp (optab->name, (optab - 1)->name) != 0)
+ {
+ /* different name --> ship out current template list;
+ add to hash table; & begin anew */
+ core_optab->end = optab;
+ hash_err = hash_insert (op_hash,
+ (optab - 1)->name,
+ (PTR) core_optab);
+ if (hash_err)
+ {
+ hash_error:
+ as_fatal (_("Internal Error: Can't hash %s: %s"),
+ (optab - 1)->name,
+ hash_err);
+ }
+ if (optab->name == NULL)
+ break;
+ core_optab = (templates *) xmalloc (sizeof (templates));
+ core_optab->start = optab;
+ }
+ }
+ }
+
+ /* initialize reg_hash hash table */
+ reg_hash = hash_new ();
+ {
+ register const reg_entry *regtab;
+
+ for (regtab = i386_regtab;
+ regtab < i386_regtab + sizeof (i386_regtab) / sizeof (i386_regtab[0]);
+ regtab++)
+ {
+ hash_err = hash_insert (reg_hash, regtab->reg_name, (PTR) regtab);
+ if (hash_err)
+ goto hash_error;
+ }
+ }
+
+ /* fill in lexical tables: mnemonic_chars, operand_chars. */
+ {
+ register int c;
+ register char *p;
+
+ for (c = 0; c < 256; c++)
+ {
+ if (isdigit (c))
+ {
+ digit_chars[c] = c;
+ mnemonic_chars[c] = c;
+ register_chars[c] = c;
+ operand_chars[c] = c;
+ }
+ else if (islower (c))
+ {
+ mnemonic_chars[c] = c;
+ register_chars[c] = c;
+ operand_chars[c] = c;
+ }
+ else if (isupper (c))
+ {
+ mnemonic_chars[c] = tolower (c);
+ register_chars[c] = mnemonic_chars[c];
+ operand_chars[c] = c;
+ }
+
+ if (isalpha (c) || isdigit (c))
+ identifier_chars[c] = c;
+ else if (c >= 128)
+ {
+ identifier_chars[c] = c;
+ operand_chars[c] = c;
+ }
+ }
+
+#ifdef LEX_AT
+ identifier_chars['@'] = '@';
+#endif
+ register_chars[')'] = ')';
+ register_chars['('] = '(';
+ digit_chars['-'] = '-';
+ identifier_chars['_'] = '_';
+ identifier_chars['.'] = '.';
+
+ for (p = operand_special_chars; *p != '\0'; p++)
+ operand_chars[(unsigned char) *p] = *p;
+ }
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ {
+ record_alignment (text_section, 2);
+ record_alignment (data_section, 2);
+ record_alignment (bss_section, 2);
+ }
+#endif
+}
+
+void
+i386_print_statistics (file)
+ FILE *file;
+{
+ hash_print_statistics (file, "i386 opcode", op_hash);
+ hash_print_statistics (file, "i386 register", reg_hash);
+}
+
+
+#ifdef DEBUG386
+
+/* debugging routines for md_assemble */
+static void pi PARAMS ((char *, i386_insn *));
+static void pte PARAMS ((template *));
+static void pt PARAMS ((unsigned int));
+static void pe PARAMS ((expressionS *));
+static void ps PARAMS ((symbolS *));
+
+static void
+pi (line, x)
+ char *line;
+ i386_insn *x;
+{
+ register template *p;
+ int i;
+
+ fprintf (stdout, "%s: template ", line);
+ pte (&x->tm);
+ fprintf (stdout, " modrm: mode %x reg %x reg/mem %x",
+ x->rm.mode, x->rm.reg, x->rm.regmem);
+ fprintf (stdout, " base %x index %x scale %x\n",
+ x->bi.base, x->bi.index, x->bi.scale);
+ for (i = 0; i < x->operands; i++)
+ {
+ fprintf (stdout, " #%d: ", i + 1);
+ pt (x->types[i]);
+ fprintf (stdout, "\n");
+ if (x->types[i]
+ & (Reg | SReg2 | SReg3 | Control | Debug | Test | RegMMX))
+ fprintf (stdout, "%s\n", x->regs[i]->reg_name);
+ if (x->types[i] & Imm)
+ pe (x->imms[i]);
+ if (x->types[i] & Disp)
+ pe (x->disps[i]);
+ }
+}
+
+static void
+pte (t)
+ template *t;
+{
+ int i;
+ fprintf (stdout, " %d operands ", t->operands);
+ fprintf (stdout, "opcode %x ",
+ t->base_opcode);
+ if (t->extension_opcode != None)
+ fprintf (stdout, "ext %x ", t->extension_opcode);
+ if (t->opcode_modifier & D)
+ fprintf (stdout, "D");
+ if (t->opcode_modifier & W)
+ fprintf (stdout, "W");
+ fprintf (stdout, "\n");
+ for (i = 0; i < t->operands; i++)
+ {
+ fprintf (stdout, " #%d type ", i + 1);
+ pt (t->operand_types[i]);
+ fprintf (stdout, "\n");
+ }
+}
+
+static void
+pe (e)
+ expressionS *e;
+{
+ fprintf (stdout, " operation %d\n", e->X_op);
+ fprintf (stdout, " add_number %d (%x)\n",
+ e->X_add_number, e->X_add_number);
+ if (e->X_add_symbol)
+ {
+ fprintf (stdout, " add_symbol ");
+ ps (e->X_add_symbol);
+ fprintf (stdout, "\n");
+ }
+ if (e->X_op_symbol)
+ {
+ fprintf (stdout, " op_symbol ");
+ ps (e->X_op_symbol);
+ fprintf (stdout, "\n");
+ }
+}
+
+static void
+ps (s)
+ symbolS *s;
+{
+ fprintf (stdout, "%s type %s%s",
+ S_GET_NAME (s),
+ S_IS_EXTERNAL (s) ? "EXTERNAL " : "",
+ segment_name (S_GET_SEGMENT (s)));
+}
+
+struct type_name
+ {
+ unsigned int mask;
+ char *tname;
+ }
+
+type_names[] =
+{
+ { Reg8, "r8" },
+ { Reg16, "r16" },
+ { Reg32, "r32" },
+ { Imm8, "i8" },
+ { Imm8S, "i8s" },
+ { Imm16, "i16" },
+ { Imm32, "i32" },
+ { Imm1, "i1" },
+ { BaseIndex, "BaseIndex" },
+ { Disp8, "d8" },
+ { Disp16, "d16" },
+ { Disp32, "d32" },
+ { InOutPortReg, "InOutPortReg" },
+ { ShiftCount, "ShiftCount" },
+ { Control, "control reg" },
+ { Test, "test reg" },
+ { Debug, "debug reg" },
+ { FloatReg, "FReg" },
+ { FloatAcc, "FAcc" },
+ { SReg2, "SReg2" },
+ { SReg3, "SReg3" },
+ { Acc, "Acc" },
+ { JumpAbsolute, "Jump Absolute" },
+ { RegMMX, "rMMX" },
+ { EsSeg, "es" },
+ { 0, "" }
+};
+
+static void
+pt (t)
+ unsigned int t;
+{
+ register struct type_name *ty;
+
+ if (t == Unknown)
+ {
+ fprintf (stdout, _("Unknown"));
+ }
+ else
+ {
+ for (ty = type_names; ty->mask; ty++)
+ if (t & ty->mask)
+ fprintf (stdout, "%s, ", ty->tname);
+ }
+ fflush (stdout);
+}
+
+#endif /* DEBUG386 */
+
+int
+tc_i386_force_relocation (fixp)
+ struct fix *fixp;
+{
+#ifdef BFD_ASSEMBLER
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 1;
+ return 0;
+#else
+ /* For COFF */
+ return fixp->fx_r_type==7;
+#endif
+}
+
+#ifdef BFD_ASSEMBLER
+static bfd_reloc_code_real_type reloc
+ PARAMS ((int, int, bfd_reloc_code_real_type));
+
+static bfd_reloc_code_real_type
+reloc (size, pcrel, other)
+ int size;
+ int pcrel;
+ bfd_reloc_code_real_type other;
+{
+ if (other != NO_RELOC) return other;
+
+ if (pcrel)
+ {
+ switch (size)
+ {
+ case 1: return BFD_RELOC_8_PCREL;
+ case 2: return BFD_RELOC_16_PCREL;
+ case 4: return BFD_RELOC_32_PCREL;
+ }
+ as_bad (_("Can not do %d byte pc-relative relocation"), size);
+ }
+ else
+ {
+ switch (size)
+ {
+ case 1: return BFD_RELOC_8;
+ case 2: return BFD_RELOC_16;
+ case 4: return BFD_RELOC_32;
+ }
+ as_bad (_("Can not do %d byte relocation"), size);
+ }
+
+ return BFD_RELOC_NONE;
+}
+
+/*
+ * Here we decide which fixups can be adjusted to make them relative to
+ * the beginning of the section instead of the symbol. Basically we need
+ * to make sure that the dynamic relocations are done correctly, so in
+ * some cases we force the original symbol to be used.
+ */
+int
+tc_i386_fix_adjustable(fixP)
+ fixS * fixP;
+{
+#ifdef OBJ_ELF
+ /* Prevent all adjustments to global symbols. */
+ if (S_IS_EXTERN (fixP->fx_addsy))
+ return 0;
+ if (S_IS_WEAK (fixP->fx_addsy))
+ return 0;
+#endif
+ /* adjust_reloc_syms doesn't know about the GOT */
+ if (fixP->fx_r_type == BFD_RELOC_386_GOTOFF
+ || fixP->fx_r_type == BFD_RELOC_386_PLT32
+ || fixP->fx_r_type == BFD_RELOC_386_GOT32
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+ return 1;
+}
+#else
+#define reloc(SIZE,PCREL,OTHER) 0
+#define BFD_RELOC_16 0
+#define BFD_RELOC_32 0
+#define BFD_RELOC_16_PCREL 0
+#define BFD_RELOC_32_PCREL 0
+#define BFD_RELOC_386_PLT32 0
+#define BFD_RELOC_386_GOT32 0
+#define BFD_RELOC_386_GOTOFF 0
+#endif
+
+int
+intel_float_operand (mnemonic)
+ char *mnemonic;
+{
+ if (mnemonic[0] == 'f' && mnemonic[1] =='i')
+ return 0;
+
+ if (mnemonic[0] == 'f')
+ return 1;
+
+ return 0;
+}
+
+/* This is the guts of the machine-dependent assembler. LINE points to a
+ machine dependent instruction. This function is supposed to emit
+ the frags/bytes it assembles to. */
+
+void
+md_assemble (line)
+ char *line;
+{
+ /* Points to template once we've found it. */
+ const template *t;
+
+ /* Count the size of the instruction generated. */
+ int insn_size = 0;
+
+ int j;
+
+ char mnemonic[MAX_MNEM_SIZE];
+
+ /* Initialize globals. */
+ memset (&i, '\0', sizeof (i));
+ for (j = 0; j < MAX_OPERANDS; j++)
+ i.disp_reloc[j] = NO_RELOC;
+ memset (disp_expressions, '\0', sizeof (disp_expressions));
+ memset (im_expressions, '\0', sizeof (im_expressions));
+ save_stack_p = save_stack; /* reset stack pointer */
+
+ /* First parse an instruction mnemonic & call i386_operand for the operands.
+ We assume that the scrubber has arranged it so that line[0] is the valid
+ start of a (possibly prefixed) mnemonic. */
+ {
+ char *l = line;
+ char *token_start = l;
+ char *mnem_p;
+
+ /* Non-zero if we found a prefix only acceptable with string insns. */
+ const char *expecting_string_instruction = NULL;
+
+ while (1)
+ {
+ mnem_p = mnemonic;
+ while ((*mnem_p = mnemonic_chars[(unsigned char) *l]) != 0)
+ {
+ mnem_p++;
+ if (mnem_p >= mnemonic + sizeof (mnemonic))
+ {
+ as_bad (_("no such 386 instruction: `%s'"), token_start);
+ return;
+ }
+ l++;
+ }
+ if (!is_space_char (*l)
+ && *l != END_OF_INSN
+ && *l != PREFIX_SEPARATOR)
+ {
+ as_bad (_("invalid character %s in mnemonic"),
+ output_invalid (*l));
+ return;
+ }
+ if (token_start == l)
+ {
+ if (*l == PREFIX_SEPARATOR)
+ as_bad (_("expecting prefix; got nothing"));
+ else
+ as_bad (_("expecting mnemonic; got nothing"));
+ return;
+ }
+
+ /* Look up instruction (or prefix) via hash table. */
+ current_templates = hash_find (op_hash, mnemonic);
+
+ if (*l != END_OF_INSN
+ && (! is_space_char (*l) || l[1] != END_OF_INSN)
+ && current_templates
+ && (current_templates->start->opcode_modifier & IsPrefix))
+ {
+ /* If we are in 16-bit mode, do not allow addr16 or data16.
+ Similarly, in 32-bit mode, do not allow addr32 or data32. */
+ if ((current_templates->start->opcode_modifier & (Size16 | Size32))
+ && (((current_templates->start->opcode_modifier & Size32) != 0)
+ ^ flag_16bit_code))
+ {
+ as_bad (_("redundant %s prefix"),
+ current_templates->start->name);
+ return;
+ }
+ /* Add prefix, checking for repeated prefixes. */
+ switch (add_prefix (current_templates->start->base_opcode))
+ {
+ case 0:
+ return;
+ case 2:
+ expecting_string_instruction =
+ current_templates->start->name;
+ break;
+ }
+ /* Skip past PREFIX_SEPARATOR and reset token_start. */
+ token_start = ++l;
+ }
+ else
+ break;
+ }
+
+ if (!current_templates)
+ {
+ /* See if we can get a match by trimming off a suffix. */
+ switch (mnem_p[-1])
+ {
+ case DWORD_MNEM_SUFFIX:
+ case WORD_MNEM_SUFFIX:
+ case BYTE_MNEM_SUFFIX:
+ case SHORT_MNEM_SUFFIX:
+#if LONG_MNEM_SUFFIX != DWORD_MNEM_SUFFIX
+ case LONG_MNEM_SUFFIX:
+#endif
+ i.suffix = mnem_p[-1];
+ mnem_p[-1] = '\0';
+ current_templates = hash_find (op_hash, mnemonic);
+ break;
+
+ /* Intel Syntax */
+ case INTEL_DWORD_MNEM_SUFFIX:
+ if (intel_syntax)
+ {
+ i.suffix = mnem_p[-1];
+ mnem_p[-1] = '\0';
+ current_templates = hash_find (op_hash, mnemonic);
+ break;
+ }
+ }
+ if (!current_templates)
+ {
+ as_bad (_("no such 386 instruction: `%s'"), token_start);
+ return;
+ }
+ }
+
+ /* check for rep/repne without a string instruction */
+ if (expecting_string_instruction
+ && !(current_templates->start->opcode_modifier & IsString))
+ {
+ as_bad (_("expecting string instruction after `%s'"),
+ expecting_string_instruction);
+ return;
+ }
+
+ /* There may be operands to parse. */
+ if (*l != END_OF_INSN)
+ {
+ /* parse operands */
+
+ /* 1 if operand is pending after ','. */
+ unsigned int expecting_operand = 0;
+
+ /* Non-zero if operand parens not balanced. */
+ unsigned int paren_not_balanced;
+
+ do
+ {
+ /* skip optional white space before operand */
+ if (is_space_char (*l))
+ ++l;
+ if (!is_operand_char (*l) && *l != END_OF_INSN)
+ {
+ as_bad (_("invalid character %s before operand %d"),
+ output_invalid (*l),
+ i.operands + 1);
+ return;
+ }
+ token_start = l; /* after white space */
+ paren_not_balanced = 0;
+ while (paren_not_balanced || *l != ',')
+ {
+ if (*l == END_OF_INSN)
+ {
+ if (paren_not_balanced)
+ {
+ if (!intel_syntax)
+ as_bad (_("unbalanced parenthesis in operand %d."),
+ i.operands + 1);
+ else
+ as_bad (_("unbalanced brackets in operand %d."),
+ i.operands + 1);
+ return;
+ }
+ else
+ break; /* we are done */
+ }
+ else if (!is_operand_char (*l) && !is_space_char (*l))
+ {
+ as_bad (_("invalid character %s in operand %d"),
+ output_invalid (*l),
+ i.operands + 1);
+ return;
+ }
+ if (!intel_syntax)
+ {
+ if (*l == '(')
+ ++paren_not_balanced;
+ if (*l == ')')
+ --paren_not_balanced;
+ }
+ else
+ {
+ if (*l == '[')
+ ++paren_not_balanced;
+ if (*l == ']')
+ --paren_not_balanced;
+ }
+ l++;
+ }
+ if (l != token_start)
+ { /* yes, we've read in another operand */
+ unsigned int operand_ok;
+ this_operand = i.operands++;
+ if (i.operands > MAX_OPERANDS)
+ {
+ as_bad (_("spurious operands; (%d operands/instruction max)"),
+ MAX_OPERANDS);
+ return;
+ }
+ /* now parse operand adding info to 'i' as we go along */
+ END_STRING_AND_SAVE (l);
+
+ if (intel_syntax)
+ operand_ok = i386_intel_operand (token_start, intel_float_operand (mnemonic));
+ else
+ operand_ok = i386_operand (token_start);
+
+ RESTORE_END_STRING (l); /* restore old contents */
+ if (!operand_ok)
+ return;
+ }
+ else
+ {
+ if (expecting_operand)
+ {
+ expecting_operand_after_comma:
+ as_bad (_("expecting operand after ','; got nothing"));
+ return;
+ }
+ if (*l == ',')
+ {
+ as_bad (_("expecting operand before ','; got nothing"));
+ return;
+ }
+ }
+
+ /* now *l must be either ',' or END_OF_INSN */
+ if (*l == ',')
+ {
+ if (*++l == END_OF_INSN)
+ { /* just skip it, if it's \n complain */
+ goto expecting_operand_after_comma;
+ }
+ expecting_operand = 1;
+ }
+ }
+ while (*l != END_OF_INSN); /* until we get end of insn */
+ }
+ }
+
+ /* Now we've parsed the mnemonic into a set of templates, and have the
+ operands at hand.
+
+ Next, we find a template that matches the given insn,
+ making sure the overlap of the given operands types is consistent
+ with the template operand types. */
+
+#define MATCH(overlap, given, template) \
+ ((overlap) \
+ && ((given) & BaseIndex) == ((overlap) & BaseIndex) \
+ && ((given) & JumpAbsolute) == ((template) & JumpAbsolute))
+
+ /* If given types r0 and r1 are registers they must be of the same type
+ unless the expected operand type register overlap is null.
+ Note that Acc in a template matches every size of reg. */
+#define CONSISTENT_REGISTER_MATCH(m0, g0, t0, m1, g1, t1) \
+ ( ((g0) & Reg) == 0 || ((g1) & Reg) == 0 || \
+ ((g0) & Reg) == ((g1) & Reg) || \
+ ((((m0) & Acc) ? Reg : (t0)) & (((m1) & Acc) ? Reg : (t1)) & Reg) == 0 )
+
+ {
+ register unsigned int overlap0, overlap1;
+ expressionS *exp;
+ unsigned int overlap2;
+ unsigned int found_reverse_match;
+ int suffix_check;
+
+ /* All intel opcodes have reversed operands except for BOUND and ENTER */
+ if (intel_syntax
+ && (strcmp (mnemonic, "enter") != 0)
+ && (strcmp (mnemonic, "bound") != 0)
+ && (strncmp (mnemonic, "fsub", 4) !=0)
+ && (strncmp (mnemonic, "fdiv", 4) !=0))
+ {
+ const reg_entry *temp_reg;
+ expressionS *temp_disp;
+ expressionS *temp_imm;
+ unsigned int temp_type;
+ int xchg1, xchg2;
+
+ if (i.operands == 2)
+ {
+ xchg1 = 0;
+ xchg2 = 1;
+ }
+ else if (i.operands == 3)
+ {
+ xchg1 = 0;
+ xchg2 = 2;
+ }
+
+ if (i.operands > 1)
+ {
+ temp_type = i.types[xchg2];
+ if (temp_type & (Reg | FloatReg))
+ temp_reg = i.regs[xchg2];
+ else if (temp_type & Imm)
+ temp_imm = i.imms[xchg2];
+ else if (temp_type & Disp)
+ temp_disp = i.disps[xchg2];
+
+ i.types[xchg2] = i.types[xchg1];
+
+ if (i.types[xchg1] & (Reg | FloatReg))
+ {
+ i.regs[xchg2] = i.regs[xchg1];
+ i.regs[xchg1] = NULL;
+ }
+ else if (i.types[xchg2] & Imm)
+ {
+ i.imms[xchg2] = i.imms[xchg1];
+ i.imms[xchg1] = NULL;
+ }
+ else if (i.types[xchg2] & Disp)
+ {
+ i.disps[xchg2] = i.disps[xchg1];
+ i.disps[xchg1] = NULL;
+ }
+
+ if (temp_type & (Reg | FloatReg))
+ {
+ i.regs[xchg1] = temp_reg;
+ if (! (i.types[xchg1] & (Reg | FloatReg)))
+ i.regs[xchg2] = NULL;
+ }
+ else if (temp_type & Imm)
+ {
+ i.imms[xchg1] = temp_imm;
+ if (! (i.types[xchg1] & Imm))
+ i.imms[xchg2] = NULL;
+ }
+ else if (temp_type & Disp)
+ {
+ i.disps[xchg1] = temp_disp;
+ if (! (i.types[xchg1] & Disp))
+ i.disps[xchg2] = NULL;
+ }
+
+ i.types[xchg1] = temp_type;
+ }
+ if (!strcmp(mnemonic,"jmp")
+ || !strcmp (mnemonic, "call"))
+ if ((i.types[0] & Reg) || i.types[0] & BaseIndex)
+ i.types[0] |= JumpAbsolute;
+
+ }
+ overlap0 = 0;
+ overlap1 = 0;
+ overlap2 = 0;
+ found_reverse_match = 0;
+ suffix_check = (i.suffix == BYTE_MNEM_SUFFIX
+ ? No_bSuf
+ : (i.suffix == WORD_MNEM_SUFFIX
+ ? No_wSuf
+ : (i.suffix == SHORT_MNEM_SUFFIX
+ ? No_sSuf
+ : (i.suffix == LONG_MNEM_SUFFIX
+ ? No_lSuf
+ : (i.suffix == INTEL_DWORD_MNEM_SUFFIX
+ ? No_dSuf
+ : (i.suffix == LONG_DOUBLE_MNEM_SUFFIX ? No_xSuf : 0))))));
+
+ for (t = current_templates->start;
+ t < current_templates->end;
+ t++)
+ {
+ /* Must have right number of operands. */
+ if (i.operands != t->operands)
+ continue;
+
+ /* For some opcodes, don't check the suffix */
+ if (intel_syntax)
+ {
+ if (strcmp (t->name, "fnstcw")
+ && strcmp (t->name, "fldcw")
+ && (t->opcode_modifier & suffix_check))
+ continue;
+ }
+ /* Must not have disallowed suffix. */
+ else if ((t->opcode_modifier & suffix_check))
+ continue;
+
+ else if (!t->operands)
+ break; /* 0 operands always matches */
+
+ overlap0 = i.types[0] & t->operand_types[0];
+ switch (t->operands)
+ {
+ case 1:
+ if (!MATCH (overlap0, i.types[0], t->operand_types[0]))
+ continue;
+ break;
+ case 2:
+ case 3:
+ overlap1 = i.types[1] & t->operand_types[1];
+ if (!MATCH (overlap0, i.types[0], t->operand_types[0])
+ || !MATCH (overlap1, i.types[1], t->operand_types[1])
+ || !CONSISTENT_REGISTER_MATCH (overlap0, i.types[0],
+ t->operand_types[0],
+ overlap1, i.types[1],
+ t->operand_types[1]))
+ {
+
+ /* check if other direction is valid ... */
+ if ((t->opcode_modifier & (D|FloatD)) == 0)
+ continue;
+
+ /* try reversing direction of operands */
+ overlap0 = i.types[0] & t->operand_types[1];
+ overlap1 = i.types[1] & t->operand_types[0];
+ if (!MATCH (overlap0, i.types[0], t->operand_types[1])
+ || !MATCH (overlap1, i.types[1], t->operand_types[0])
+ || !CONSISTENT_REGISTER_MATCH (overlap0, i.types[0],
+ t->operand_types[1],
+ overlap1, i.types[1],
+ t->operand_types[0]))
+ {
+ /* does not match either direction */
+ continue;
+ }
+ /* found_reverse_match holds which of D or FloatDR
+ we've found. */
+ found_reverse_match = t->opcode_modifier & (D|FloatDR);
+ break;
+ }
+ /* found a forward 2 operand match here */
+ if (t->operands == 3)
+ {
+ /* Here we make use of the fact that there are no
+ reverse match 3 operand instructions, and all 3
+ operand instructions only need to be checked for
+ register consistency between operands 2 and 3. */
+ overlap2 = i.types[2] & t->operand_types[2];
+ if (!MATCH (overlap2, i.types[2], t->operand_types[2])
+ || !CONSISTENT_REGISTER_MATCH (overlap1, i.types[1],
+ t->operand_types[1],
+ overlap2, i.types[2],
+ t->operand_types[2]))
+
+ continue;
+ }
+ /* found either forward/reverse 2 or 3 operand match here:
+ slip through to break */
+ }
+ break; /* we've found a match; break out of loop */
+ } /* for (t = ... */
+ if (t == current_templates->end)
+ { /* we found no match */
+ as_bad (_("suffix or operands invalid for `%s'"),
+ current_templates->start->name);
+ return;
+ }
+
+ if ((t->opcode_modifier & (IsPrefix|IgnoreSize)) == (IsPrefix|IgnoreSize))
+ {
+ /* Warn them that a data or address size prefix doesn't affect
+ assembly of the next line of code. */
+ as_warn (_("stand-alone `%s' prefix"), t->name);
+ }
+
+ /* Copy the template we found. */
+ i.tm = *t;
+ if (found_reverse_match)
+ {
+ i.tm.operand_types[0] = t->operand_types[1];
+ i.tm.operand_types[1] = t->operand_types[0];
+ }
+
+
+ if (i.tm.opcode_modifier & FWait)
+ if (! add_prefix (FWAIT_OPCODE))
+ return;
+
+ /* Check string instruction segment overrides */
+ if ((i.tm.opcode_modifier & IsString) != 0 && i.mem_operands != 0)
+ {
+ int mem_op = (i.types[0] & AnyMem) ? 0 : 1;
+ if ((i.tm.operand_types[mem_op] & EsSeg) != 0)
+ {
+ if (i.seg[0] != NULL && i.seg[0] != &es)
+ {
+ as_bad (_("`%s' operand %d must use `%%es' segment"),
+ i.tm.name,
+ mem_op + 1);
+ return;
+ }
+ /* There's only ever one segment override allowed per instruction.
+ This instruction possibly has a legal segment override on the
+ second operand, so copy the segment to where non-string
+ instructions store it, allowing common code. */
+ i.seg[0] = i.seg[1];
+ }
+ else if ((i.tm.operand_types[mem_op + 1] & EsSeg) != 0)
+ {
+ if (i.seg[1] != NULL && i.seg[1] != &es)
+ {
+ as_bad (_("`%s' operand %d must use `%%es' segment"),
+ i.tm.name,
+ mem_op + 2);
+ return;
+ }
+ }
+ }
+
+ /* If matched instruction specifies an explicit instruction mnemonic
+ suffix, use it. */
+ if (i.tm.opcode_modifier & (Size16 | Size32))
+ {
+ if (i.tm.opcode_modifier & Size16)
+ i.suffix = WORD_MNEM_SUFFIX;
+ else
+ i.suffix = DWORD_MNEM_SUFFIX;
+ }
+ else if (i.reg_operands)
+ {
+ /* If there's no instruction mnemonic suffix we try to invent one
+ based on register operands. */
+ if (!i.suffix)
+ {
+ /* We take i.suffix from the last register operand specified,
+ Destination register type is more significant than source
+ register type. */
+ int op;
+ for (op = i.operands; --op >= 0; )
+ if (i.types[op] & Reg)
+ {
+ i.suffix = ((i.types[op] & Reg8) ? BYTE_MNEM_SUFFIX :
+ (i.types[op] & Reg16) ? WORD_MNEM_SUFFIX :
+ DWORD_MNEM_SUFFIX);
+ break;
+ }
+ }
+ else if (i.suffix == BYTE_MNEM_SUFFIX)
+ {
+ int op;
+ for (op = i.operands; --op >= 0; )
+ {
+ /* If this is an eight bit register, it's OK. If it's
+ the 16 or 32 bit version of an eight bit register,
+ we will just use the low portion, and that's OK too. */
+ if (i.types[op] & Reg8)
+ continue;
+
+ /* movzx and movsx should not generate this warning. */
+ if (intel_syntax
+ && (i.tm.base_opcode == 0xfb7
+ || i.tm.base_opcode == 0xfb6
+ || i.tm.base_opcode == 0xfbe
+ || i.tm.base_opcode == 0xfbf))
+ continue;
+
+ if ((i.types[op] & WordReg) && i.regs[op]->reg_num < 4
+#if 0
+ /* Check that the template allows eight bit regs
+ This kills insns such as `orb $1,%edx', which
+ maybe should be allowed. */
+ && (i.tm.operand_types[op] & (Reg8|InOutPortReg))
+#endif
+ )
+ {
+#if REGISTER_WARNINGS
+ if ((i.tm.operand_types[op] & InOutPortReg) == 0)
+ as_warn (_("using `%%%s' instead of `%%%s' due to `%c' suffix"),
+ (i.regs[op] - (i.types[op] & Reg16 ? 8 : 16))->reg_name,
+ i.regs[op]->reg_name,
+ i.suffix);
+#endif
+ continue;
+ }
+ /* Any other register is bad */
+ if (i.types[op] & (Reg | RegMMX | Control | Debug | Test
+ | FloatReg | FloatAcc | SReg2 | SReg3))
+ {
+ as_bad (_("`%%%s' not allowed with `%s%c'"),
+ i.regs[op]->reg_name,
+ i.tm.name,
+ i.suffix);
+ return;
+ }
+ }
+ }
+ else if (i.suffix == DWORD_MNEM_SUFFIX)
+ {
+ int op;
+ for (op = i.operands; --op >= 0; )
+ /* Reject eight bit registers, except where the template
+ requires them. (eg. movzb) */
+ if ((i.types[op] & Reg8) != 0
+ && (i.tm.operand_types[op] & (Reg16|Reg32|Acc)) != 0)
+ {
+ as_bad (_("`%%%s' not allowed with `%s%c'"),
+ i.regs[op]->reg_name,
+ i.tm.name,
+ i.suffix);
+ return;
+ }
+#if REGISTER_WARNINGS
+ /* Warn if the e prefix on a general reg is missing. */
+ else if ((i.types[op] & Reg16) != 0
+ && (i.tm.operand_types[op] & (Reg32|Acc)) != 0)
+ {
+ as_warn (_("using `%%%s' instead of `%%%s' due to `%c' suffix"),
+ (i.regs[op] + 8)->reg_name,
+ i.regs[op]->reg_name,
+ i.suffix);
+ }
+#endif
+ }
+ else if (i.suffix == WORD_MNEM_SUFFIX)
+ {
+ int op;
+ for (op = i.operands; --op >= 0; )
+ /* Reject eight bit registers, except where the template
+ requires them. (eg. movzb) */
+ if ((i.types[op] & Reg8) != 0
+ && (i.tm.operand_types[op] & (Reg16|Reg32|Acc)) != 0)
+ {
+ as_bad (_("`%%%s' not allowed with `%s%c'"),
+ i.regs[op]->reg_name,
+ i.tm.name,
+ i.suffix);
+ return;
+ }
+#if REGISTER_WARNINGS
+ /* Warn if the e prefix on a general reg is present. */
+ else if ((i.types[op] & Reg32) != 0
+ && (i.tm.operand_types[op] & (Reg16|Acc)) != 0)
+ {
+ as_warn (_("using `%%%s' instead of `%%%s' due to `%c' suffix"),
+ (i.regs[op] - 8)->reg_name,
+ i.regs[op]->reg_name,
+ i.suffix);
+ }
+#endif
+ }
+ else
+ abort();
+ }
+
+ /* Make still unresolved immediate matches conform to size of immediate
+ given in i.suffix. Note: overlap2 cannot be an immediate! */
+ if ((overlap0 & (Imm8 | Imm8S | Imm16 | Imm32))
+ && overlap0 != Imm8 && overlap0 != Imm8S
+ && overlap0 != Imm16 && overlap0 != Imm32)
+ {
+ if (i.suffix)
+ {
+ overlap0 &= (i.suffix == BYTE_MNEM_SUFFIX ? (Imm8 | Imm8S) :
+ (i.suffix == WORD_MNEM_SUFFIX ? Imm16 : Imm32));
+ }
+ else if (overlap0 == (Imm16 | Imm32))
+ {
+ overlap0 =
+ (flag_16bit_code ^ (i.prefix[DATA_PREFIX] != 0)) ? Imm16 : Imm32;
+ }
+ else
+ {
+ as_bad (_("no instruction mnemonic suffix given; can't determine immediate size"));
+ return;
+ }
+ }
+ if ((overlap1 & (Imm8 | Imm8S | Imm16 | Imm32))
+ && overlap1 != Imm8 && overlap1 != Imm8S
+ && overlap1 != Imm16 && overlap1 != Imm32)
+ {
+ if (i.suffix)
+ {
+ overlap1 &= (i.suffix == BYTE_MNEM_SUFFIX ? (Imm8 | Imm8S) :
+ (i.suffix == WORD_MNEM_SUFFIX ? Imm16 : Imm32));
+ }
+ else if (overlap1 == (Imm16 | Imm32))
+ {
+ overlap1 =
+ (flag_16bit_code ^ (i.prefix[DATA_PREFIX] != 0)) ? Imm16 : Imm32;
+ }
+ else
+ {
+ as_bad (_("no instruction mnemonic suffix given; can't determine immediate size"));
+ return;
+ }
+ }
+ assert ((overlap2 & Imm) == 0);
+
+ i.types[0] = overlap0;
+ if (overlap0 & ImplicitRegister)
+ i.reg_operands--;
+ if (overlap0 & Imm1)
+ i.imm_operands = 0; /* kludge for shift insns */
+
+ i.types[1] = overlap1;
+ if (overlap1 & ImplicitRegister)
+ i.reg_operands--;
+
+ i.types[2] = overlap2;
+ if (overlap2 & ImplicitRegister)
+ i.reg_operands--;
+
+ /* Finalize opcode. First, we change the opcode based on the operand
+ size given by i.suffix: We need not change things for byte insns. */
+
+ if (!i.suffix && (i.tm.opcode_modifier & W))
+ {
+ as_bad (_("no instruction mnemonic suffix given and no register operands; can't size instruction"));
+ return;
+ }
+
+ /* For movzx and movsx, need to check the register type */
+ if (intel_syntax
+ && (i.tm.base_opcode == 0xfb6 || i.tm.base_opcode == 0xfbe))
+ if (i.suffix && i.suffix == BYTE_MNEM_SUFFIX)
+ {
+ unsigned int prefix = DATA_PREFIX_OPCODE;
+
+ if ((i.regs[1]->reg_type & Reg16) != 0)
+ if (!add_prefix (prefix))
+ return;
+ }
+
+ if (i.suffix && i.suffix != BYTE_MNEM_SUFFIX)
+ {
+ /* It's not a byte, select word/dword operation. */
+ if (i.tm.opcode_modifier & W)
+ {
+ if (i.tm.opcode_modifier & ShortForm)
+ i.tm.base_opcode |= 8;
+ else
+ i.tm.base_opcode |= 1;
+ }
+ /* Now select between word & dword operations via the operand
+ size prefix, except for instructions that will ignore this
+ prefix anyway. */
+ if (((intel_syntax && (i.suffix == INTEL_DWORD_MNEM_SUFFIX))
+ || i.suffix == DWORD_MNEM_SUFFIX
+ || i.suffix == LONG_MNEM_SUFFIX) == flag_16bit_code
+ && !(i.tm.opcode_modifier & IgnoreSize))
+ {
+ unsigned int prefix = DATA_PREFIX_OPCODE;
+ if (i.tm.opcode_modifier & JumpByte) /* jcxz, loop */
+ prefix = ADDR_PREFIX_OPCODE;
+
+ if (! add_prefix (prefix))
+ return;
+ }
+ /* Size floating point instruction. */
+ if (i.suffix == LONG_MNEM_SUFFIX
+ || (intel_syntax && i.suffix == INTEL_DWORD_MNEM_SUFFIX))
+ {
+ if (i.tm.opcode_modifier & FloatMF)
+ i.tm.base_opcode ^= 4;
+ }
+
+ if (intel_syntax && i.suffix == LONG_DOUBLE_MNEM_SUFFIX)
+ {
+ if (i.tm.opcode_modifier & FloatMF)
+ i.tm.base_opcode ^= 2;
+ }
+ }
+
+ if (i.tm.base_opcode == AMD_3DNOW_OPCODE)
+ {
+ /* These AMD specific instructions have an opcode suffix which
+ is coded in the same place as an 8-bit immediate field
+ would be. Here we fake an 8-bit immediate operand from the
+ opcode suffix stored in tm.extension_opcode. */
+
+ expressionS *exp;
+
+ assert(i.imm_operands == 0 && i.operands <= 2);
+
+ exp = &im_expressions[i.imm_operands++];
+ i.imms[i.operands] = exp;
+ i.types[i.operands++] = Imm8;
+ exp->X_op = O_constant;
+ exp->X_add_number = i.tm.extension_opcode;
+ i.tm.extension_opcode = None;
+ }
+
+ /* For insns with operands there are more diddles to do to the opcode. */
+ if (i.operands)
+ {
+ /* Default segment register this instruction will use
+ for memory accesses. 0 means unknown.
+ This is only for optimizing out unnecessary segment overrides. */
+ const seg_entry *default_seg = 0;
+
+ /* If we found a reverse match we must alter the opcode
+ direction bit. found_reverse_match holds bits to change
+ (different for int & float insns). */
+
+ i.tm.base_opcode ^= found_reverse_match;
+
+ /* The imul $imm, %reg instruction is converted into
+ imul $imm, %reg, %reg, and the clr %reg instruction
+ is converted into xor %reg, %reg. */
+ if (i.tm.opcode_modifier & regKludge)
+ {
+ unsigned int first_reg_op = (i.types[0] & Reg) ? 0 : 1;
+ /* Pretend we saw the extra register operand. */
+ i.regs[first_reg_op+1] = i.regs[first_reg_op];
+ i.reg_operands = 2;
+ }
+
+ if (i.tm.opcode_modifier & ShortForm)
+ {
+ /* The register or float register operand is in operand 0 or 1. */
+ unsigned int op = (i.types[0] & (Reg | FloatReg)) ? 0 : 1;
+ /* Register goes in low 3 bits of opcode. */
+ i.tm.base_opcode |= i.regs[op]->reg_num;
+ if ((i.tm.opcode_modifier & Ugh) != 0)
+ {
+ /* Warn about some common errors, but press on regardless.
+ The first case can be generated by gcc (<= 2.8.1). */
+ if (i.operands == 2)
+ {
+ /* reversed arguments on faddp, fsubp, etc. */
+ as_warn (_("translating to `%s %%%s,%%%s'"), i.tm.name,
+ i.regs[1]->reg_name,
+ i.regs[0]->reg_name);
+ }
+ else
+ {
+ /* extraneous `l' suffix on fp insn */
+ as_warn (_("translating to `%s %%%s'"), i.tm.name,
+ i.regs[0]->reg_name);
+ }
+ }
+ }
+ else if (i.tm.opcode_modifier & Modrm)
+ {
+ /* The opcode is completed (modulo i.tm.extension_opcode which
+ must be put into the modrm byte).
+ Now, we make the modrm & index base bytes based on all the
+ info we've collected. */
+
+ /* i.reg_operands MUST be the number of real register operands;
+ implicit registers do not count. */
+ if (i.reg_operands == 2)
+ {
+ unsigned int source, dest;
+ source = ((i.types[0]
+ & (Reg
+ | SReg2
+ | SReg3
+ | Control
+ | Debug
+ | Test
+ | RegMMX))
+ ? 0 : 1);
+ dest = source + 1;
+
+ /* Certain instructions expect the destination to be
+ in the i.rm.reg field. This is by far the
+ exceptional case. For these instructions, if the
+ source operand is a register, we must reverse the
+ i.rm.reg and i.rm.regmem fields. We accomplish
+ this by pretending that the two register operands
+ were given in the reverse order. */
+ if (i.tm.opcode_modifier & ReverseRegRegmem)
+ {
+ const reg_entry *tmp = i.regs[source];
+ i.regs[source] = i.regs[dest];
+ i.regs[dest] = tmp;
+ }
+
+ i.rm.mode = 3;
+ /* We must be careful to make sure that all
+ segment/control/test/debug/MMX registers go into
+ the i.rm.reg field (despite whether they are
+ source or destination operands). */
+ if (i.regs[dest]->reg_type
+ & (SReg2 | SReg3 | Control | Debug | Test | RegMMX))
+ {
+ i.rm.reg = i.regs[dest]->reg_num;
+ i.rm.regmem = i.regs[source]->reg_num;
+ }
+ else
+ {
+ i.rm.reg = i.regs[source]->reg_num;
+ i.rm.regmem = i.regs[dest]->reg_num;
+ }
+ }
+ else
+ { /* if it's not 2 reg operands... */
+ if (i.mem_operands)
+ {
+ unsigned int fake_zero_displacement = 0;
+ unsigned int op = ((i.types[0] & AnyMem)
+ ? 0
+ : (i.types[1] & AnyMem) ? 1 : 2);
+
+ default_seg = &ds;
+
+ if (! i.base_reg)
+ {
+ i.rm.mode = 0;
+ if (! i.disp_operands)
+ fake_zero_displacement = 1;
+ if (! i.index_reg)
+ {
+ /* Operand is just <disp> */
+ if (flag_16bit_code ^ (i.prefix[ADDR_PREFIX] != 0))
+ {
+ i.rm.regmem = NO_BASE_REGISTER_16;
+ i.types[op] &= ~Disp;
+ i.types[op] |= Disp16;
+ }
+ else
+ {
+ i.rm.regmem = NO_BASE_REGISTER;
+ i.types[op] &= ~Disp;
+ i.types[op] |= Disp32;
+ }
+ }
+ else /* ! i.base_reg && i.index_reg */
+ {
+ i.sib.index = i.index_reg->reg_num;
+ i.sib.base = NO_BASE_REGISTER;
+ i.sib.scale = i.log2_scale_factor;
+ i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
+ i.types[op] &= ~Disp;
+ i.types[op] |= Disp32; /* Must be 32 bit */
+ }
+ }
+ else if (i.base_reg->reg_type & Reg16)
+ {
+ switch (i.base_reg->reg_num)
+ {
+ case 3: /* (%bx) */
+ if (! i.index_reg)
+ i.rm.regmem = 7;
+ else /* (%bx,%si) -> 0, or (%bx,%di) -> 1 */
+ i.rm.regmem = i.index_reg->reg_num - 6;
+ break;
+ case 5: /* (%bp) */
+ default_seg = &ss;
+ if (! i.index_reg)
+ {
+ i.rm.regmem = 6;
+ if ((i.types[op] & Disp) == 0)
+ {
+ /* fake (%bp) into 0(%bp) */
+ i.types[op] |= Disp8;
+ fake_zero_displacement = 1;
+ }
+ }
+ else /* (%bp,%si) -> 2, or (%bp,%di) -> 3 */
+ i.rm.regmem = i.index_reg->reg_num - 6 + 2;
+ break;
+ default: /* (%si) -> 4 or (%di) -> 5 */
+ i.rm.regmem = i.base_reg->reg_num - 6 + 4;
+ }
+ i.rm.mode = mode_from_disp_size (i.types[op]);
+ }
+ else /* i.base_reg and 32 bit mode */
+ {
+ i.rm.regmem = i.base_reg->reg_num;
+ i.sib.base = i.base_reg->reg_num;
+ if (i.base_reg->reg_num == EBP_REG_NUM)
+ {
+ default_seg = &ss;
+ if (i.disp_operands == 0)
+ {
+ fake_zero_displacement = 1;
+ i.types[op] |= Disp8;
+ }
+ }
+ else if (i.base_reg->reg_num == ESP_REG_NUM)
+ {
+ default_seg = &ss;
+ }
+ i.sib.scale = i.log2_scale_factor;
+ if (! i.index_reg)
+ {
+ /* <disp>(%esp) becomes two byte modrm
+ with no index register. We've already
+ stored the code for esp in i.rm.regmem
+ ie. ESCAPE_TO_TWO_BYTE_ADDRESSING. Any
+ base register besides %esp will not use
+ the extra modrm byte. */
+ i.sib.index = NO_INDEX_REGISTER;
+#if ! SCALE1_WHEN_NO_INDEX
+ /* Another case where we force the second
+ modrm byte. */
+ if (i.log2_scale_factor)
+ i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
+#endif
+ }
+ else
+ {
+ i.sib.index = i.index_reg->reg_num;
+ i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
+ }
+ i.rm.mode = mode_from_disp_size (i.types[op]);
+ }
+
+ if (fake_zero_displacement)
+ {
+ /* Fakes a zero displacement assuming that i.types[op]
+ holds the correct displacement size. */
+ exp = &disp_expressions[i.disp_operands++];
+ i.disps[op] = exp;
+ exp->X_op = O_constant;
+ exp->X_add_number = 0;
+ exp->X_add_symbol = (symbolS *) 0;
+ exp->X_op_symbol = (symbolS *) 0;
+ }
+ }
+
+ /* Fill in i.rm.reg or i.rm.regmem field with register
+ operand (if any) based on i.tm.extension_opcode.
+ Again, we must be careful to make sure that
+ segment/control/debug/test/MMX registers are coded
+ into the i.rm.reg field. */
+ if (i.reg_operands)
+ {
+ unsigned int op =
+ ((i.types[0]
+ & (Reg | SReg2 | SReg3 | Control | Debug
+ | Test | RegMMX))
+ ? 0
+ : ((i.types[1]
+ & (Reg | SReg2 | SReg3 | Control | Debug
+ | Test | RegMMX))
+ ? 1
+ : 2));
+ /* If there is an extension opcode to put here, the
+ register number must be put into the regmem field. */
+ if (i.tm.extension_opcode != None)
+ i.rm.regmem = i.regs[op]->reg_num;
+ else
+ i.rm.reg = i.regs[op]->reg_num;
+
+ /* Now, if no memory operand has set i.rm.mode = 0, 1, 2
+ we must set it to 3 to indicate this is a register
+ operand in the regmem field. */
+ if (!i.mem_operands)
+ i.rm.mode = 3;
+ }
+
+ /* Fill in i.rm.reg field with extension opcode (if any). */
+ if (i.tm.extension_opcode != None)
+ i.rm.reg = i.tm.extension_opcode;
+ }
+ }
+ else if (i.tm.opcode_modifier & (Seg2ShortForm | Seg3ShortForm))
+ {
+ if (i.tm.base_opcode == POP_SEG_SHORT && i.regs[0]->reg_num == 1)
+ {
+ as_bad (_("you can't `pop %%cs'"));
+ return;
+ }
+ i.tm.base_opcode |= (i.regs[0]->reg_num << 3);
+ }
+ else if ((i.tm.base_opcode & ~(D|W)) == MOV_AX_DISP32)
+ {
+ default_seg = &ds;
+ }
+ else if ((i.tm.opcode_modifier & IsString) != 0)
+ {
+ /* For the string instructions that allow a segment override
+ on one of their operands, the default segment is ds. */
+ default_seg = &ds;
+ }
+
+ /* If a segment was explicitly specified,
+ and the specified segment is not the default,
+ use an opcode prefix to select it.
+ If we never figured out what the default segment is,
+ then default_seg will be zero at this point,
+ and the specified segment prefix will always be used. */
+ if ((i.seg[0]) && (i.seg[0] != default_seg))
+ {
+ if (! add_prefix (i.seg[0]->seg_prefix))
+ return;
+ }
+ }
+ else if ((i.tm.opcode_modifier & Ugh) != 0)
+ {
+ /* UnixWare fsub no args is alias for fsubp, fadd -> faddp, etc */
+ as_warn (_("translating to `%sp'"), i.tm.name);
+ }
+ }
+
+ /* Handle conversion of 'int $3' --> special int3 insn. */
+ if (i.tm.base_opcode == INT_OPCODE && i.imms[0]->X_add_number == 3)
+ {
+ i.tm.base_opcode = INT3_OPCODE;
+ i.imm_operands = 0;
+ }
+
+ /* We are ready to output the insn. */
+ {
+ register char *p;
+
+ /* Output jumps. */
+ if (i.tm.opcode_modifier & Jump)
+ {
+ long n = (long) i.disps[0]->X_add_number;
+ int prefix = (i.prefix[DATA_PREFIX] != 0);
+ int code16 = 0;
+
+ if (prefix)
+ {
+ i.prefixes -= 1;
+ code16 = CODE16;
+ }
+ if (flag_16bit_code)
+ code16 ^= CODE16;
+
+ if (!intel_syntax && (i.prefixes != 0))
+ as_warn (_("skipping prefixes on this instruction"));
+
+ if (i.disps[0]->X_op == O_constant)
+ {
+ if (fits_in_signed_byte (n))
+ {
+ insn_size += 2;
+ p = frag_more (2);
+ p[0] = i.tm.base_opcode;
+ p[1] = n;
+ }
+ else
+ {
+ /* Use 16-bit jumps only for 16-bit code,
+ because text segments are limited to 64K anyway;
+ Use 32-bit jumps for 32-bit code, because they're faster,
+ and a 16-bit jump will clear the top 16 bits of %eip. */
+ int jmp_size = code16 ? 2 : 4;
+ if (code16 && !fits_in_signed_word (n))
+ {
+ as_bad (_("16-bit jump out of range"));
+ return;
+ }
+
+ if (i.tm.base_opcode == JUMP_PC_RELATIVE)
+ { /* pace */
+ /* unconditional jump */
+ insn_size += prefix + 1 + jmp_size;
+ p = frag_more (prefix + 1 + jmp_size);
+ if (prefix)
+ *p++ = DATA_PREFIX_OPCODE;
+ *p++ = (char) 0xe9;
+ md_number_to_chars (p, (valueT) n, jmp_size);
+ }
+ else
+ {
+ /* conditional jump */
+ insn_size += prefix + 2 + jmp_size;
+ p = frag_more (prefix + 2 + jmp_size);
+ if (prefix)
+ *p++ = DATA_PREFIX_OPCODE;
+ *p++ = TWO_BYTE_OPCODE_ESCAPE;
+ *p++ = i.tm.base_opcode + 0x10;
+ md_number_to_chars (p, (valueT) n, jmp_size);
+ }
+ }
+ }
+ else
+ {
+ int size = code16 ? 2 : 4;
+
+ /* It's a symbol; end frag & setup for relax.
+ Make sure there are more than 6 chars left in the current frag;
+ if not we'll have to start a new one. */
+ frag_grow (prefix + 1 + 2 + size);
+ insn_size += 1 + prefix;
+ p = frag_more (1 + prefix);
+ if (prefix)
+ *p++ = DATA_PREFIX_OPCODE;
+ *p = i.tm.base_opcode;
+ frag_var (rs_machine_dependent,
+ prefix + 2 + size, /* 2 opcode/prefix + displacement */
+ 1,
+ ((unsigned char) *p == JUMP_PC_RELATIVE
+ ? ENCODE_RELAX_STATE (UNCOND_JUMP, SMALL) | code16
+ : ENCODE_RELAX_STATE (COND_JUMP, SMALL) | code16),
+ i.disps[0]->X_add_symbol,
+ (offsetT) n, p);
+ }
+ }
+ else if (i.tm.opcode_modifier & (JumpByte | JumpDword))
+ {
+ int size = (i.tm.opcode_modifier & JumpByte) ? 1 : 4;
+ long n = (long) i.disps[0]->X_add_number;
+
+ if (size == 1) /* then this is a loop or jecxz type instruction */
+ {
+ if (i.prefix[ADDR_PREFIX])
+ {
+ insn_size += 1;
+ FRAG_APPEND_1_CHAR (ADDR_PREFIX_OPCODE);
+ i.prefixes -= 1;
+ }
+ }
+ else
+ {
+ int code16 = 0;
+
+ if (i.prefix[DATA_PREFIX])
+ {
+ insn_size += 1;
+ FRAG_APPEND_1_CHAR (DATA_PREFIX_OPCODE);
+ i.prefixes -= 1;
+ code16 = CODE16;
+ }
+ if (flag_16bit_code)
+ code16 ^= CODE16;
+
+ if (code16)
+ size = 2;
+ }
+
+ if (!intel_syntax && (i.prefixes != 0))
+ as_warn (_("skipping prefixes on this instruction"));
+
+ if (fits_in_unsigned_byte (i.tm.base_opcode))
+ {
+ insn_size += 1 + size;
+ p = frag_more (1 + size);
+ }
+ else
+ {
+ insn_size += 2 + size; /* opcode can be at most two bytes */
+ p = frag_more (2 + size);
+ *p++ = (i.tm.base_opcode >> 8) & 0xff;
+ }
+ *p++ = i.tm.base_opcode & 0xff;
+
+ if (i.disps[0]->X_op == O_constant)
+ {
+ if (size == 1 && !fits_in_signed_byte (n))
+ {
+ as_bad (_("`%s' only takes byte displacement; %ld shortened to %d"),
+ i.tm.name, n, *p);
+ }
+ else if (size == 2 && !fits_in_signed_word (n))
+ {
+ as_bad (_("16-bit jump out of range"));
+ return;
+ }
+ md_number_to_chars (p, (valueT) n, size);
+ }
+ else
+ {
+ fix_new_exp (frag_now, p - frag_now->fr_literal, size,
+ i.disps[0], 1, reloc (size, 1, i.disp_reloc[0]));
+
+ }
+ }
+ else if (i.tm.opcode_modifier & JumpInterSegment)
+ {
+ int size;
+ int reloc_type;
+ int prefix = i.prefix[DATA_PREFIX] != 0;
+ int code16 = 0;
+
+ if (prefix)
+ {
+ code16 = CODE16;
+ i.prefixes -= 1;
+ }
+ if (flag_16bit_code)
+ code16 ^= CODE16;
+
+ size = 4;
+ reloc_type = BFD_RELOC_32;
+ if (code16)
+ {
+ size = 2;
+ reloc_type = BFD_RELOC_16;
+ }
+
+ if (!intel_syntax && (i.prefixes != 0))
+ as_warn (_("skipping prefixes on this instruction"));
+
+ insn_size += prefix + 1 + 2 + size; /* 1 opcode; 2 segment; offset */
+ p = frag_more (prefix + 1 + 2 + size);
+ if (prefix)
+ *p++ = DATA_PREFIX_OPCODE;
+ *p++ = i.tm.base_opcode;
+ if (i.imms[1]->X_op == O_constant)
+ {
+ long n = (long) i.imms[1]->X_add_number;
+
+ if (size == 2 && !fits_in_unsigned_word (n))
+ {
+ as_bad (_("16-bit jump out of range"));
+ return;
+ }
+ md_number_to_chars (p, (valueT) n, size);
+ }
+ else
+ fix_new_exp (frag_now, p - frag_now->fr_literal, size,
+ i.imms[1], 0, reloc_type);
+ if (i.imms[0]->X_op != O_constant)
+ as_bad (_("can't handle non absolute segment in `%s'"),
+ i.tm.name);
+ md_number_to_chars (p + size, (valueT) i.imms[0]->X_add_number, 2);
+ }
+ else
+ {
+ /* Output normal instructions here. */
+ unsigned char *q;
+
+ /* The prefix bytes. */
+ for (q = i.prefix;
+ q < i.prefix + sizeof (i.prefix) / sizeof (i.prefix[0]);
+ q++)
+ {
+ if (*q)
+ {
+ insn_size += 1;
+ p = frag_more (1);
+ md_number_to_chars (p, (valueT) *q, 1);
+ }
+ }
+
+ /* Now the opcode; be careful about word order here! */
+ if (fits_in_unsigned_byte (i.tm.base_opcode))
+ {
+ insn_size += 1;
+ FRAG_APPEND_1_CHAR (i.tm.base_opcode);
+ }
+ else if (fits_in_unsigned_word (i.tm.base_opcode))
+ {
+ insn_size += 2;
+ p = frag_more (2);
+ /* put out high byte first: can't use md_number_to_chars! */
+ *p++ = (i.tm.base_opcode >> 8) & 0xff;
+ *p = i.tm.base_opcode & 0xff;
+ }
+ else
+ { /* opcode is either 3 or 4 bytes */
+ if (i.tm.base_opcode & 0xff000000)
+ {
+ insn_size += 4;
+ p = frag_more (4);
+ *p++ = (i.tm.base_opcode >> 24) & 0xff;
+ }
+ else
+ {
+ insn_size += 3;
+ p = frag_more (3);
+ }
+ *p++ = (i.tm.base_opcode >> 16) & 0xff;
+ *p++ = (i.tm.base_opcode >> 8) & 0xff;
+ *p = (i.tm.base_opcode) & 0xff;
+ }
+
+ /* Now the modrm byte and sib byte (if present). */
+ if (i.tm.opcode_modifier & Modrm)
+ {
+ insn_size += 1;
+ p = frag_more (1);
+ md_number_to_chars (p,
+ (valueT) (i.rm.regmem << 0
+ | i.rm.reg << 3
+ | i.rm.mode << 6),
+ 1);
+ /* If i.rm.regmem == ESP (4)
+ && i.rm.mode != (Register mode)
+ && not 16 bit
+ ==> need second modrm byte. */
+ if (i.rm.regmem == ESCAPE_TO_TWO_BYTE_ADDRESSING
+ && i.rm.mode != 3
+ && !(i.base_reg && (i.base_reg->reg_type & Reg16) != 0))
+ {
+ insn_size += 1;
+ p = frag_more (1);
+ md_number_to_chars (p,
+ (valueT) (i.sib.base << 0
+ | i.sib.index << 3
+ | i.sib.scale << 6),
+ 1);
+ }
+ }
+
+ if (i.disp_operands)
+ {
+ register unsigned int n;
+
+ for (n = 0; n < i.operands; n++)
+ {
+ if (i.disps[n])
+ {
+ if (i.disps[n]->X_op == O_constant)
+ {
+ if (i.types[n] & Disp8)
+ {
+ insn_size += 1;
+ p = frag_more (1);
+ md_number_to_chars (p,
+ (valueT) i.disps[n]->X_add_number,
+ 1);
+ }
+ else if (i.types[n] & Disp16)
+ {
+ insn_size += 2;
+ p = frag_more (2);
+ md_number_to_chars (p,
+ (valueT) i.disps[n]->X_add_number,
+ 2);
+ }
+ else
+ { /* Disp32 */
+ insn_size += 4;
+ p = frag_more (4);
+ md_number_to_chars (p,
+ (valueT) i.disps[n]->X_add_number,
+ 4);
+ }
+ }
+ else if (i.types[n] & Disp32)
+ {
+ insn_size += 4;
+ p = frag_more (4);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 4,
+ i.disps[n], 0,
+ TC_RELOC (i.disp_reloc[n], BFD_RELOC_32));
+ }
+ else
+ { /* must be Disp16 */
+ insn_size += 2;
+ p = frag_more (2);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 2,
+ i.disps[n], 0,
+ TC_RELOC (i.disp_reloc[n], BFD_RELOC_16));
+ }
+ }
+ }
+ } /* end displacement output */
+
+ /* output immediate */
+ if (i.imm_operands)
+ {
+ register unsigned int n;
+
+ for (n = 0; n < i.operands; n++)
+ {
+ if (i.imms[n])
+ {
+ if (i.imms[n]->X_op == O_constant)
+ {
+ if (i.types[n] & (Imm8 | Imm8S))
+ {
+ insn_size += 1;
+ p = frag_more (1);
+ md_number_to_chars (p,
+ (valueT) i.imms[n]->X_add_number,
+ 1);
+ }
+ else if (i.types[n] & Imm16)
+ {
+ insn_size += 2;
+ p = frag_more (2);
+ md_number_to_chars (p,
+ (valueT) i.imms[n]->X_add_number,
+ 2);
+ }
+ else
+ {
+ insn_size += 4;
+ p = frag_more (4);
+ md_number_to_chars (p,
+ (valueT) i.imms[n]->X_add_number,
+ 4);
+ }
+ }
+ else
+ { /* not absolute_section */
+ /* Need a 32-bit fixup (don't support 8bit
+ non-absolute ims). Try to support other
+ sizes ... */
+ int r_type;
+ int size;
+ int pcrel = 0;
+
+ if (i.types[n] & (Imm8 | Imm8S))
+ size = 1;
+ else if (i.types[n] & Imm16)
+ size = 2;
+ else
+ size = 4;
+ insn_size += size;
+ p = frag_more (size);
+ r_type = reloc (size, 0, i.disp_reloc[0]);
+#ifdef BFD_ASSEMBLER
+ if (r_type == BFD_RELOC_32
+ && GOT_symbol
+ && GOT_symbol == i.imms[n]->X_add_symbol
+ && (i.imms[n]->X_op == O_symbol
+ || (i.imms[n]->X_op == O_add
+ && (i.imms[n]->X_op_symbol->sy_value.X_op
+ == O_subtract))))
+ {
+ r_type = BFD_RELOC_386_GOTPC;
+ i.imms[n]->X_add_number += 3;
+ }
+#endif
+ fix_new_exp (frag_now, p - frag_now->fr_literal, size,
+ i.imms[n], pcrel, r_type);
+ }
+ }
+ }
+ } /* end immediate output */
+ }
+
+#ifdef DEBUG386
+ if (flag_debug)
+ {
+ pi (line, &i);
+ }
+#endif /* DEBUG386 */
+ }
+}
+
+static int i386_is_reg PARAMS ((char *));
+
+static int
+i386_is_reg (reg_string)
+ char *reg_string;
+{
+ register char *s = reg_string;
+ register char *p;
+ char reg_name_given[MAX_REG_NAME_SIZE + 1];
+
+ if (is_space_char (*s))
+ ++s;
+
+ p = reg_name_given;
+ while ((*p++ = register_chars[(unsigned char) *s++]) != '\0')
+ if (p >= reg_name_given + MAX_REG_NAME_SIZE)
+ return 0;
+
+ if (!hash_find (reg_hash, reg_name_given))
+ return 0;
+ else
+ return 1;
+}
+
+static int i386_immediate PARAMS ((char *));
+
+static int
+i386_immediate (imm_start)
+ char *imm_start;
+{
+ char *save_input_line_pointer;
+ segT exp_seg = 0;
+ expressionS * exp;
+
+ if (i.imm_operands == MAX_IMMEDIATE_OPERANDS)
+ {
+ as_bad (_("Only 1 or 2 immediate operands are allowed"));
+ return 0;
+ }
+
+ exp = &im_expressions[i.imm_operands++];
+ i.imms[this_operand] = exp;
+
+ if (is_space_char (*imm_start))
+ ++imm_start;
+
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = imm_start;
+
+#ifndef LEX_AT
+ {
+ /*
+ * We can have operands of the form
+ * <symbol>@GOTOFF+<nnn>
+ * Take the easy way out here and copy everything
+ * into a temporary buffer...
+ */
+ register char *cp;
+
+ cp = strchr (input_line_pointer, '@');
+ if (cp != NULL)
+ {
+ char *tmpbuf;
+ int len, first;
+
+ /* GOT relocations are not supported in 16 bit mode */
+ if (flag_16bit_code)
+ as_bad (_("GOT relocations not supported in 16 bit mode"));
+
+ if (GOT_symbol == NULL)
+ GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
+
+ if (strncmp (cp + 1, "PLT", 3) == 0)
+ {
+ i.disp_reloc[this_operand] = BFD_RELOC_386_PLT32;
+ len = 3;
+ }
+ else if (strncmp (cp + 1, "GOTOFF", 6) == 0)
+ {
+ i.disp_reloc[this_operand] = BFD_RELOC_386_GOTOFF;
+ len = 6;
+ }
+ else if (strncmp (cp + 1, "GOT", 3) == 0)
+ {
+ i.disp_reloc[this_operand] = BFD_RELOC_386_GOT32;
+ len = 3;
+ }
+ else
+ as_bad (_("Bad reloc specifier in expression"));
+
+ /* Replace the relocation token with ' ', so that errors like
+ foo@GOTOFF1 will be detected. */
+ first = cp - input_line_pointer;
+ tmpbuf = (char *) alloca (strlen(input_line_pointer));
+ memcpy (tmpbuf, input_line_pointer, first);
+ tmpbuf[first] = ' ';
+ strcpy (tmpbuf + first + 1, cp + 1 + len);
+ input_line_pointer = tmpbuf;
+ }
+ }
+#endif
+
+ exp_seg = expression (exp);
+
+ if (*input_line_pointer)
+ as_bad (_("Ignoring junk `%s' after expression"), input_line_pointer);
+
+ input_line_pointer = save_input_line_pointer;
+
+ if (exp->X_op == O_absent)
+ {
+ /* missing or bad expr becomes absolute 0 */
+ as_bad (_("Missing or invalid immediate expression `%s' taken as 0"),
+ imm_start);
+ exp->X_op = O_constant;
+ exp->X_add_number = 0;
+ exp->X_add_symbol = (symbolS *) 0;
+ exp->X_op_symbol = (symbolS *) 0;
+ i.types[this_operand] |= Imm;
+ }
+ else if (exp->X_op == O_constant)
+ {
+ i.types[this_operand] |=
+ smallest_imm_type ((long) exp->X_add_number);
+
+ /* If a suffix is given, this operand may be shortended. */
+ switch (i.suffix)
+ {
+ case WORD_MNEM_SUFFIX:
+ i.types[this_operand] |= Imm16;
+ break;
+ case BYTE_MNEM_SUFFIX:
+ i.types[this_operand] |= Imm16 | Imm8 | Imm8S;
+ break;
+ }
+ }
+#ifdef OBJ_AOUT
+ else if (exp_seg != text_section
+ && exp_seg != data_section
+ && exp_seg != bss_section
+ && exp_seg != undefined_section
+#ifdef BFD_ASSEMBLER
+ && !bfd_is_com_section (exp_seg)
+#endif
+ )
+ {
+ seg_unimplemented:
+ as_bad (_("Unimplemented segment type %d in operand"), exp_seg);
+ return 0;
+ }
+#endif
+ else
+ {
+ /* This is an address. The size of the address will be
+ determined later, depending on destination register,
+ suffix, or the default for the section. We exclude
+ Imm8S here so that `push $foo' and other instructions
+ with an Imm8S form will use Imm16 or Imm32. */
+ i.types[this_operand] |= (Imm8 | Imm16 | Imm32);
+ }
+
+ return 1;
+}
+
+static int i386_scale PARAMS ((char *));
+
+static int
+i386_scale (scale)
+ char *scale;
+{
+ if (!isdigit (*scale))
+ goto bad_scale;
+
+ switch (*scale)
+ {
+ case '0':
+ case '1':
+ i.log2_scale_factor = 0;
+ break;
+ case '2':
+ i.log2_scale_factor = 1;
+ break;
+ case '4':
+ i.log2_scale_factor = 2;
+ break;
+ case '8':
+ i.log2_scale_factor = 3;
+ break;
+ default:
+ bad_scale:
+ as_bad (_("expecting scale factor of 1, 2, 4, or 8: got `%s'"),
+ scale);
+ return 0;
+ }
+ if (i.log2_scale_factor != 0 && ! i.index_reg)
+ {
+ as_warn (_("scale factor of %d without an index register"),
+ 1 << i.log2_scale_factor);
+#if SCALE1_WHEN_NO_INDEX
+ i.log2_scale_factor = 0;
+#endif
+ }
+ return 1;
+}
+
+static int i386_displacement PARAMS ((char *, char *));
+
+static int
+i386_displacement (disp_start, disp_end)
+ char *disp_start;
+ char *disp_end;
+{
+ register expressionS *exp;
+ segT exp_seg = 0;
+ char *save_input_line_pointer;
+ int bigdisp = Disp32;
+
+ /* All of the pieces of the displacement expression are handled together. */
+ if (intel_syntax && i.disp_operands != 0)
+ return 1;
+
+ if (flag_16bit_code ^ (i.prefix[ADDR_PREFIX] != 0))
+ bigdisp = Disp16;
+ i.types[this_operand] |= bigdisp;
+
+ exp = &disp_expressions[i.disp_operands];
+ i.disps[this_operand] = exp;
+ i.disp_reloc[this_operand] = NO_RELOC;
+ i.disp_operands++;
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = disp_start;
+ END_STRING_AND_SAVE (disp_end);
+
+#ifndef GCC_ASM_O_HACK
+#define GCC_ASM_O_HACK 0
+#endif
+#if GCC_ASM_O_HACK
+ END_STRING_AND_SAVE (disp_end + 1);
+ if ((i.types[this_operand] & BaseIndex) != 0
+ && displacement_string_end[-1] == '+')
+ {
+ /* This hack is to avoid a warning when using the "o"
+ constraint within gcc asm statements.
+ For instance:
+
+ #define _set_tssldt_desc(n,addr,limit,type) \
+ __asm__ __volatile__ ( \
+ "movw %w2,%0\n\t" \
+ "movw %w1,2+%0\n\t" \
+ "rorl $16,%1\n\t" \
+ "movb %b1,4+%0\n\t" \
+ "movb %4,5+%0\n\t" \
+ "movb $0,6+%0\n\t" \
+ "movb %h1,7+%0\n\t" \
+ "rorl $16,%1" \
+ : "=o"(*(n)) : "q" (addr), "ri"(limit), "i"(type))
+
+ This works great except that the output assembler ends
+ up looking a bit weird if it turns out that there is
+ no offset. You end up producing code that looks like:
+
+ #APP
+ movw $235,(%eax)
+ movw %dx,2+(%eax)
+ rorl $16,%edx
+ movb %dl,4+(%eax)
+ movb $137,5+(%eax)
+ movb $0,6+(%eax)
+ movb %dh,7+(%eax)
+ rorl $16,%edx
+ #NO_APP
+
+ So here we provide the missing zero.
+ */
+
+ *displacement_string_end = '0';
+ }
+#endif
+#ifndef LEX_AT
+ {
+ /*
+ * We can have operands of the form
+ * <symbol>@GOTOFF+<nnn>
+ * Take the easy way out here and copy everything
+ * into a temporary buffer...
+ */
+ register char *cp;
+
+ cp = strchr (input_line_pointer, '@');
+ if (cp != NULL)
+ {
+ char *tmpbuf;
+ int len, first;
+
+ /* GOT relocations are not supported in 16 bit mode */
+ if (flag_16bit_code)
+ as_bad (_("GOT relocations not supported in 16 bit mode"));
+
+ if (GOT_symbol == NULL)
+ GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
+
+ if (strncmp (cp + 1, "PLT", 3) == 0)
+ {
+ i.disp_reloc[this_operand] = BFD_RELOC_386_PLT32;
+ len = 3;
+ }
+ else if (strncmp (cp + 1, "GOTOFF", 6) == 0)
+ {
+ i.disp_reloc[this_operand] = BFD_RELOC_386_GOTOFF;
+ len = 6;
+ }
+ else if (strncmp (cp + 1, "GOT", 3) == 0)
+ {
+ i.disp_reloc[this_operand] = BFD_RELOC_386_GOT32;
+ len = 3;
+ }
+ else
+ as_bad (_("Bad reloc specifier in expression"));
+
+ /* Replace the relocation token with ' ', so that errors like
+ foo@GOTOFF1 will be detected. */
+ first = cp - input_line_pointer;
+ tmpbuf = (char *) alloca (strlen(input_line_pointer));
+ memcpy (tmpbuf, input_line_pointer, first);
+ tmpbuf[first] = ' ';
+ strcpy (tmpbuf + first + 1, cp + 1 + len);
+ input_line_pointer = tmpbuf;
+ }
+ }
+#endif
+
+ exp_seg = expression (exp);
+
+#ifdef BFD_ASSEMBLER
+ /* We do this to make sure that the section symbol is in
+ the symbol table. We will ultimately change the relocation
+ to be relative to the beginning of the section */
+ if (i.disp_reloc[this_operand] == BFD_RELOC_386_GOTOFF)
+ {
+ if (S_IS_LOCAL(exp->X_add_symbol)
+ && S_GET_SEGMENT (exp->X_add_symbol) != undefined_section)
+ section_symbol(exp->X_add_symbol->bsym->section);
+ assert (exp->X_op == O_symbol);
+ exp->X_op = O_subtract;
+ exp->X_op_symbol = GOT_symbol;
+ i.disp_reloc[this_operand] = BFD_RELOC_32;
+ }
+#endif
+
+ if (*input_line_pointer)
+ as_bad (_("Ignoring junk `%s' after expression"),
+ input_line_pointer);
+#if GCC_ASM_O_HACK
+ RESTORE_END_STRING (disp_end + 1);
+#endif
+ RESTORE_END_STRING (disp_end);
+ input_line_pointer = save_input_line_pointer;
+
+ if (exp->X_op == O_constant)
+ {
+ if (fits_in_signed_byte (exp->X_add_number))
+ i.types[this_operand] |= Disp8;
+ }
+#ifdef OBJ_AOUT
+ else if (exp_seg != text_section
+ && exp_seg != data_section
+ && exp_seg != bss_section
+ && exp_seg != undefined_section)
+ {
+ as_bad (_ ("Unimplemented segment type %d in operand"), exp_seg);
+ return 0;
+ }
+#endif
+ return 1;
+}
+
+static int i386_operand_modifier PARAMS ((char **, int));
+
+static int
+i386_operand_modifier (op_string, got_a_float)
+ char **op_string;
+ int got_a_float;
+{
+ if (!strncasecmp (*op_string, "BYTE PTR", 8))
+ {
+ i.suffix = BYTE_MNEM_SUFFIX;
+ *op_string += 8;
+ return BYTE_PTR;
+
+ }
+ else if (!strncasecmp (*op_string, "WORD PTR", 8))
+ {
+ i.suffix = WORD_MNEM_SUFFIX;
+ *op_string += 8;
+ return WORD_PTR;
+ }
+
+ else if (!strncasecmp (*op_string, "DWORD PTR", 9))
+ {
+ if (got_a_float)
+ i.suffix = SHORT_MNEM_SUFFIX;
+ else
+ i.suffix = DWORD_MNEM_SUFFIX;
+ *op_string += 9;
+ return DWORD_PTR;
+ }
+
+ else if (!strncasecmp (*op_string, "QWORD PTR", 9))
+ {
+ i.suffix = INTEL_DWORD_MNEM_SUFFIX;
+ *op_string += 9;
+ return QWORD_PTR;
+ }
+
+ else if (!strncasecmp (*op_string, "XWORD PTR", 9))
+ {
+ i.suffix = LONG_DOUBLE_MNEM_SUFFIX;
+ *op_string += 9;
+ return XWORD_PTR;
+ }
+
+ else if (!strncasecmp (*op_string, "SHORT", 5))
+ {
+ *op_string += 5;
+ return SHORT;
+ }
+
+ else if (!strncasecmp (*op_string, "OFFSET FLAT:", 12))
+ {
+ *op_string += 12;
+ return OFFSET_FLAT;
+ }
+
+ else if (!strncasecmp (*op_string, "FLAT", 4))
+ {
+ *op_string += 4;
+ return FLAT;
+ }
+
+ else return NONE_FOUND;
+}
+
+static char * build_displacement_string PARAMS ((int, char *));
+
+static char *
+build_displacement_string (initial_disp, op_string)
+ int initial_disp;
+ char *op_string;
+{
+ char *temp_string = (char *) malloc (strlen (op_string) + 1);
+ char *end_of_operand_string;
+ char *tc;
+ char *temp_disp;
+
+ temp_string[0] = '\0';
+ tc = end_of_operand_string = strchr (op_string, '[');
+ if ( initial_disp && !end_of_operand_string)
+ {
+ strcpy (temp_string, op_string);
+ return (temp_string);
+ }
+
+ /* Build the whole displacement string */
+ if (initial_disp)
+ {
+ strncpy (temp_string, op_string, end_of_operand_string - op_string);
+ temp_string[end_of_operand_string - op_string] = '\0';
+ temp_disp = tc;
+ }
+ else
+ temp_disp = op_string;
+
+ while (*temp_disp != '\0')
+ {
+ int add_minus = (*temp_disp == '-');
+
+ if (*temp_disp == '+' || *temp_disp == '-' || *temp_disp == '[')
+ temp_disp++;
+
+ if (is_space_char (*temp_disp))
+ temp_disp++;
+
+ /* Don't consider registers */
+ if (*temp_disp != REGISTER_PREFIX
+ && !(allow_naked_reg && i386_is_reg (temp_disp)))
+ {
+ char *string_start = temp_disp;
+
+ while (*temp_disp != ']'
+ && *temp_disp != '+'
+ && *temp_disp != '-'
+ && *temp_disp != '*')
+ ++temp_disp;
+
+ if (add_minus)
+ strcat (temp_string, "-");
+ else
+ strcat (temp_string, "+");
+
+ strncat (temp_string, string_start, temp_disp - string_start);
+ if (*temp_disp == '+' || *temp_disp == '-')
+ --temp_disp;
+ }
+
+ while (*temp_disp != '\0'
+ && *temp_disp != '+'
+ && *temp_disp != '-')
+ ++temp_disp;
+ }
+
+ return temp_string;
+}
+
+static int i386_parse_seg PARAMS ((char *));
+
+static int
+i386_parse_seg (op_string)
+ char *op_string;
+{
+ if (is_space_char (*op_string))
+ ++op_string;
+
+ /* Should be one of es, cs, ss, ds fs or gs */
+ switch (*op_string++)
+ {
+ case 'e':
+ i.seg[i.mem_operands] = &es;
+ break;
+ case 'c':
+ i.seg[i.mem_operands] = &cs;
+ break;
+ case 's':
+ i.seg[i.mem_operands] = &ss;
+ break;
+ case 'd':
+ i.seg[i.mem_operands] = &ds;
+ break;
+ case 'f':
+ i.seg[i.mem_operands] = &fs;
+ break;
+ case 'g':
+ i.seg[i.mem_operands] = &gs;
+ break;
+ default:
+ as_bad (_("bad segment name `%s'"), op_string);
+ return 0;
+ }
+
+ if (*op_string++ != 's')
+ {
+ as_bad (_("bad segment name `%s'"), op_string);
+ return 0;
+ }
+
+ if (is_space_char (*op_string))
+ ++op_string;
+
+ if (*op_string != ':')
+ {
+ as_bad (_("bad segment name `%s'"), op_string);
+ return 0;
+ }
+
+ return 1;
+
+}
+
+static int i386_intel_memory_operand PARAMS ((char *));
+
+static int
+i386_intel_memory_operand (op_string)
+ char *op_string;
+{
+
+ char *end_of_operand_string;
+
+ if (is_digit_char (*op_string)
+ && strchr (op_string, '[') == 0)
+ {
+ if (!i386_immediate (op_string))
+ return 0;
+ else
+ return 1;
+ }
+
+ /* Look for displacement preceding open bracket */
+ if (*op_string != '[')
+ {
+ char *end_seg;
+ char *temp_string;
+
+ end_seg = strchr (op_string, ':');
+ if (end_seg)
+ {
+ if (!i386_parse_seg (op_string))
+ return 0;
+ op_string = end_seg + 1;
+ }
+
+ temp_string = build_displacement_string (true, op_string);
+ if (!i386_displacement (temp_string, temp_string + strlen (temp_string)))
+ return 0;
+
+ end_of_operand_string = strchr (op_string, '[');
+ if (!end_of_operand_string)
+ end_of_operand_string = op_string + strlen (op_string);
+
+ if (is_space_char (*end_of_operand_string))
+ --end_of_operand_string;
+
+ op_string = end_of_operand_string;
+ }
+
+ if (*op_string == '[')
+ {
+ ++op_string;
+
+ /* Pick off each component and figure out where it belongs */
+
+ end_of_operand_string = op_string;
+
+ while (*op_string != ']')
+ {
+
+ while (*end_of_operand_string != '+'
+ && *end_of_operand_string != '-'
+ && *end_of_operand_string != '*'
+ && *end_of_operand_string != ']')
+ end_of_operand_string++;
+
+ if (*op_string == '+')
+ {
+ char *temp_string = op_string + 1;
+ if (is_space_char (*temp_string))
+ ++temp_string;
+ if (*temp_string == REGISTER_PREFIX
+ || (allow_naked_reg && i386_is_reg (temp_string)))
+ ++op_string;
+ }
+
+ if (*op_string == REGISTER_PREFIX
+ || (allow_naked_reg && i386_is_reg (op_string)))
+ {
+ const reg_entry *temp_reg;
+ char *end_op;
+
+ END_STRING_AND_SAVE (end_of_operand_string);
+ temp_reg = parse_register (op_string, &end_op);
+ RESTORE_END_STRING (end_of_operand_string);
+
+ if (temp_reg == NULL)
+ return 0;
+
+ if (i.base_reg == NULL)
+ i.base_reg = temp_reg;
+ else
+ i.index_reg = temp_reg;
+
+ i.types[this_operand] |= BaseIndex;
+
+ }
+ else if (is_digit_char (*op_string) || *op_string == '+' || *op_string == '-')
+ {
+
+ char *temp_string = build_displacement_string (false, op_string);
+
+ if (*temp_string == '+')
+ ++temp_string;
+
+ if (!i386_displacement (temp_string, temp_string + strlen (temp_string)))
+ return 0;
+
+ ++op_string;
+ end_of_operand_string = op_string;
+ while (*end_of_operand_string != ']'
+ && *end_of_operand_string != '+'
+ && *end_of_operand_string != '-'
+ && *end_of_operand_string != '*')
+ ++end_of_operand_string;
+ }
+ else if (*op_string == '*')
+ {
+ ++op_string;
+
+ if (i.base_reg && !i.index_reg)
+ {
+ i.index_reg = i.base_reg;
+ i.base_reg = 0;
+ }
+
+ if (!i386_scale (op_string))
+ return 0;
+ }
+ op_string = end_of_operand_string;
+ ++end_of_operand_string;
+ }
+ }
+
+ return 1;
+}
+
+static int i386_intel_operand PARAMS ((char *, int));
+
+static int
+i386_intel_operand (operand_string, got_a_float)
+ char *operand_string;
+ int got_a_float;
+{
+ char *op_string = operand_string;
+
+ int operand_modifier = i386_operand_modifier (&op_string, got_a_float);
+ if (is_space_char (*op_string))
+ ++op_string;
+
+ switch (operand_modifier)
+ {
+ case BYTE_PTR:
+ case WORD_PTR:
+ case DWORD_PTR:
+ case QWORD_PTR:
+ case XWORD_PTR:
+ if ((i.mem_operands == 1
+ && (current_templates->start->opcode_modifier & IsString) == 0)
+ || i.mem_operands == 2)
+ {
+ as_bad (_("too many memory references for `%s'"),
+ current_templates->start->name);
+ return 0;
+ }
+
+ if (!i386_intel_memory_operand (op_string))
+ return 0;
+
+ i.mem_operands++;
+ break;
+
+ case FLAT:
+
+ case OFFSET_FLAT:
+ if (!i386_immediate (op_string))
+ return 0;
+ break;
+
+ case SHORT:
+
+ case NONE_FOUND:
+ /* Should be register or immediate */
+ if (is_digit_char (*op_string)
+ && strchr (op_string, '[') == 0)
+ {
+ if (!i386_immediate (op_string))
+ return 0;
+ }
+ else if (*op_string == REGISTER_PREFIX
+ || (allow_naked_reg
+ && i386_is_reg (op_string)))
+ {
+
+ register const reg_entry * r;
+ char *end_op;
+
+ r = parse_register (op_string, &end_op);
+ if (r == NULL)
+ return 0;
+
+ /* Check for a segment override by searching for ':' after a
+ segment register. */
+ op_string = end_op;
+ if (is_space_char (*op_string))
+ ++op_string;
+ if (*op_string == ':' && (r->reg_type & (SReg2 | SReg3)))
+ {
+ switch (r->reg_num)
+ {
+ case 0:
+ i.seg[i.mem_operands] = &es;
+ break;
+ case 1:
+ i.seg[i.mem_operands] = &cs;
+ break;
+ case 2:
+ i.seg[i.mem_operands] = &ss;
+ break;
+ case 3:
+ i.seg[i.mem_operands] = &ds;
+ break;
+ case 4:
+ i.seg[i.mem_operands] = &fs;
+ break;
+ case 5:
+ i.seg[i.mem_operands] = &gs;
+ break;
+ }
+
+ }
+ i.types[this_operand] |= r->reg_type & ~BaseIndex;
+ i.regs[this_operand] = r;
+ i.reg_operands++;
+ }
+
+ else
+ {
+
+ if (!i386_intel_memory_operand (op_string))
+ return 0;
+
+ i.mem_operands++;
+ }
+ break;
+
+ } /* end switch */
+ /* Special case for (%dx) while doing input/output op. */
+ if (i.base_reg
+ && i.base_reg->reg_type == (Reg16 | InOutPortReg)
+ && i.index_reg == 0
+ && i.log2_scale_factor == 0
+ && i.seg[i.mem_operands] == 0
+ && (i.types[this_operand] & Disp) == 0)
+ {
+ i.types[this_operand] = InOutPortReg;
+ return 1;
+ }
+ /* Make sure the memory operand we've been dealt is valid. */
+ if (flag_16bit_code ^ (i.prefix[ADDR_PREFIX] != 0))
+ {
+ if ((i.base_reg
+ && ((i.base_reg->reg_type & (Reg16|BaseIndex))
+ != (Reg16|BaseIndex)))
+ || (i.index_reg
+ && (((i.index_reg->reg_type & (Reg16|BaseIndex))
+ != (Reg16|BaseIndex))
+ || ! (i.base_reg
+ && i.base_reg->reg_num < 6
+ && i.index_reg->reg_num >= 6
+ && i.log2_scale_factor == 0))))
+ {
+ as_bad (_("`%s' is not a valid %s bit base/index expression"),
+ operand_string, "16");
+ return 0;
+ }
+ }
+ else
+ {
+ if ((i.base_reg
+ && (i.base_reg->reg_type & Reg32) == 0)
+ || (i.index_reg
+ && ((i.index_reg->reg_type & (Reg32|BaseIndex))
+ != (Reg32|BaseIndex))))
+ {
+ as_bad (_("`%s' is not a valid %s bit base/index expression"),
+ operand_string, "32");
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* Parse OPERAND_STRING into the i386_insn structure I. Returns non-zero
+ on error. */
+
+static int i386_operand PARAMS ((char *));
+
+static int
+i386_operand (operand_string)
+ char *operand_string;
+{
+ char *op_string = operand_string;
+
+ if (is_space_char (*op_string))
+ ++op_string;
+
+ /* We check for an absolute prefix (differentiating,
+ for example, 'jmp pc_relative_label' from 'jmp *absolute_label'. */
+ if (*op_string == ABSOLUTE_PREFIX)
+ {
+ ++op_string;
+ if (is_space_char (*op_string))
+ ++op_string;
+ i.types[this_operand] |= JumpAbsolute;
+ }
+
+ /* Check if operand is a register. */
+ if (*op_string == REGISTER_PREFIX
+ || (allow_naked_reg && i386_is_reg (op_string)))
+ {
+ register const reg_entry *r;
+ char *end_op;
+
+ r = parse_register (op_string, &end_op);
+ if (r == NULL)
+ return 0;
+
+ /* Check for a segment override by searching for ':' after a
+ segment register. */
+ op_string = end_op;
+ if (is_space_char (*op_string))
+ ++op_string;
+ if (*op_string == ':' && (r->reg_type & (SReg2 | SReg3)))
+ {
+ switch (r->reg_num)
+ {
+ case 0:
+ i.seg[i.mem_operands] = &es;
+ break;
+ case 1:
+ i.seg[i.mem_operands] = &cs;
+ break;
+ case 2:
+ i.seg[i.mem_operands] = &ss;
+ break;
+ case 3:
+ i.seg[i.mem_operands] = &ds;
+ break;
+ case 4:
+ i.seg[i.mem_operands] = &fs;
+ break;
+ case 5:
+ i.seg[i.mem_operands] = &gs;
+ break;
+ }
+
+ /* Skip the ':' and whitespace. */
+ ++op_string;
+ if (is_space_char (*op_string))
+ ++op_string;
+
+ /* Pretend given string starts here. */
+ operand_string = op_string;
+ if (!is_digit_char (*op_string)
+ && !is_identifier_char (*op_string)
+ && *op_string != '('
+ && *op_string != ABSOLUTE_PREFIX)
+ {
+ as_bad (_("bad memory operand `%s'"), op_string);
+ return 0;
+ }
+ /* Handle case of %es:*foo. */
+ if (*op_string == ABSOLUTE_PREFIX)
+ {
+ ++op_string;
+ if (is_space_char (*op_string))
+ ++op_string;
+ i.types[this_operand] |= JumpAbsolute;
+ }
+ goto do_memory_reference;
+ }
+ if (*op_string)
+ {
+ as_bad (_("Junk `%s' after register"), op_string);
+ return 0;
+ }
+ i.types[this_operand] |= r->reg_type & ~BaseIndex;
+ i.regs[this_operand] = r;
+ i.reg_operands++;
+ }
+ else if (*op_string == IMMEDIATE_PREFIX)
+ { /* ... or an immediate */
+ ++op_string;
+ if (!i386_immediate (op_string))
+ return 0;
+ }
+ else if (is_digit_char (*op_string)
+ || is_identifier_char (*op_string)
+ || *op_string == '(' )
+ {
+ /* This is a memory reference of some sort. */
+ char *end_of_operand_string;
+ register char *base_string;
+ int found_base_index_form;
+
+ /* Start and end of displacement string expression (if found). */
+ char *displacement_string_start;
+ char *displacement_string_end;
+
+ do_memory_reference:
+
+ if ((i.mem_operands == 1
+ && (current_templates->start->opcode_modifier & IsString) == 0)
+ || i.mem_operands == 2)
+ {
+ as_bad (_("too many memory references for `%s'"),
+ current_templates->start->name);
+ return 0;
+ }
+
+ /* Check for base index form. We detect the base index form by
+ looking for an ')' at the end of the operand, searching
+ for the '(' matching it, and finding a REGISTER_PREFIX or ','
+ after the '('. */
+ found_base_index_form = 0;
+ end_of_operand_string = op_string + strlen (op_string);
+
+ --end_of_operand_string;
+ if (is_space_char (*end_of_operand_string))
+ --end_of_operand_string;
+
+ base_string = end_of_operand_string;
+
+ if (*base_string == ')')
+ {
+ unsigned int parens_balanced = 1;
+ /* We've already checked that the number of left & right ()'s are
+ equal, so this loop will not be infinite. */
+ do
+ {
+ base_string--;
+ if (*base_string == ')')
+ parens_balanced++;
+ if (*base_string == '(')
+ parens_balanced--;
+ }
+ while (parens_balanced);
+
+ /* If there is a displacement set-up for it to be parsed later. */
+ displacement_string_start = op_string;
+ displacement_string_end = base_string;
+
+ /* Skip past '(' and whitespace. */
+ ++base_string;
+ if (is_space_char (*base_string))
+ ++base_string;
+
+ if (*base_string == REGISTER_PREFIX
+ || (allow_naked_reg && i386_is_reg (base_string))
+ || *base_string == ',')
+ found_base_index_form = 1;
+ }
+
+ /* If we can't parse a base index register expression, we've found
+ a pure displacement expression. We set up displacement_string_start
+ and displacement_string_end for the code below. */
+ if (!found_base_index_form)
+ {
+ displacement_string_start = op_string;
+ displacement_string_end = end_of_operand_string + 1;
+ }
+ else
+ {
+ i.types[this_operand] |= BaseIndex;
+
+ /* Find base register (if any). */
+ if (*base_string != ',')
+ {
+ char *end_op;
+
+ /* Trim off the closing ')' so that parse_register won't
+ see it. */
+ END_STRING_AND_SAVE (end_of_operand_string);
+ i.base_reg = parse_register (base_string, &end_op);
+ RESTORE_END_STRING (end_of_operand_string);
+
+ if (i.base_reg == NULL)
+ return 0;
+
+ base_string = end_op;
+ if (is_space_char (*base_string))
+ ++base_string;
+ }
+
+ /* There may be an index reg or scale factor here. */
+ if (*base_string == ',')
+ {
+ ++base_string;
+ if (is_space_char (*base_string))
+ ++base_string;
+
+ if (*base_string == REGISTER_PREFIX
+ || (allow_naked_reg && i386_is_reg (base_string)))
+ {
+ char *end_op;
+
+ END_STRING_AND_SAVE (end_of_operand_string);
+ i.index_reg = parse_register (base_string, &end_op);
+ RESTORE_END_STRING (end_of_operand_string);
+
+ if (i.index_reg == NULL)
+ return 0;
+
+ base_string = end_op;
+ if (is_space_char (*base_string))
+ ++base_string;
+ if (*base_string == ',')
+ {
+ ++base_string;
+ if (is_space_char (*base_string))
+ ++base_string;
+ }
+ else if (*base_string != ')' )
+ {
+ as_bad (_("expecting `,' or `)' after index register in `%s'"),
+ operand_string);
+ return 0;
+ }
+ }
+
+ /* Check for scale factor. */
+ if (isdigit ((unsigned char) *base_string))
+ {
+ if (!i386_scale (base_string))
+ return 0;
+
+ ++base_string;
+ if (is_space_char (*base_string))
+ ++base_string;
+ if (*base_string != ')')
+ {
+ as_bad (_("expecting `)' after scale factor in `%s'"),
+ operand_string);
+ return 0;
+ }
+ }
+ else if (!i.index_reg)
+ {
+ as_bad (_("expecting index register or scale factor after `,'; got '%c'"),
+ *base_string);
+ return 0;
+ }
+ }
+ else if (*base_string != ')')
+ {
+ as_bad (_("expecting `,' or `)' after base register in `%s'"),
+ operand_string);
+ return 0;
+ }
+ }
+
+ /* If there's an expression beginning the operand, parse it,
+ assuming displacement_string_start and
+ displacement_string_end are meaningful. */
+ if (displacement_string_start != displacement_string_end)
+ {
+ if (!i386_displacement (displacement_string_start,
+ displacement_string_end))
+ return 0;
+ }
+
+ /* Special case for (%dx) while doing input/output op. */
+ if (i.base_reg
+ && i.base_reg->reg_type == (Reg16 | InOutPortReg)
+ && i.index_reg == 0
+ && i.log2_scale_factor == 0
+ && i.seg[i.mem_operands] == 0
+ && (i.types[this_operand] & Disp) == 0)
+ {
+ i.types[this_operand] = InOutPortReg;
+ return 1;
+ }
+ /* Make sure the memory operand we've been dealt is valid. */
+ if (flag_16bit_code ^ (i.prefix[ADDR_PREFIX] != 0))
+ {
+ if ((i.base_reg
+ && ((i.base_reg->reg_type & (Reg16|BaseIndex))
+ != (Reg16|BaseIndex)))
+ || (i.index_reg
+ && (((i.index_reg->reg_type & (Reg16|BaseIndex))
+ != (Reg16|BaseIndex))
+ || ! (i.base_reg
+ && i.base_reg->reg_num < 6
+ && i.index_reg->reg_num >= 6
+ && i.log2_scale_factor == 0))))
+ {
+ as_bad (_("`%s' is not a valid %s bit base/index expression"),
+ operand_string, "16");
+ return 0;
+ }
+ }
+ else
+ {
+ if ((i.base_reg
+ && (i.base_reg->reg_type & Reg32) == 0)
+ || (i.index_reg
+ && ((i.index_reg->reg_type & (Reg32|BaseIndex))
+ != (Reg32|BaseIndex))))
+ {
+ as_bad (_("`%s' is not a valid %s bit base/index expression"),
+ operand_string, "32");
+ return 0;
+ }
+ }
+ i.mem_operands++;
+ }
+ else
+ { /* it's not a memory operand; argh! */
+ as_bad (_("invalid char %s beginning operand %d `%s'"),
+ output_invalid (*op_string),
+ this_operand + 1,
+ op_string);
+ return 0;
+ }
+ return 1; /* normal return */
+}
+
+/*
+ * md_estimate_size_before_relax()
+ *
+ * Called just before relax().
+ * Any symbol that is now undefined will not become defined.
+ * Return the correct fr_subtype in the frag.
+ * Return the initial "guess for fr_var" to caller.
+ * The guess for fr_var is ACTUALLY the growth beyond fr_fix.
+ * Whatever we do to grow fr_fix or fr_var contributes to our returned value.
+ * Although it may not be explicit in the frag, pretend fr_var starts with a
+ * 0 value.
+ */
+int
+md_estimate_size_before_relax (fragP, segment)
+ register fragS *fragP;
+ register segT segment;
+{
+ register unsigned char *opcode;
+ register int old_fr_fix;
+
+ old_fr_fix = fragP->fr_fix;
+ opcode = (unsigned char *) fragP->fr_opcode;
+ /* We've already got fragP->fr_subtype right; all we have to do is
+ check for un-relaxable symbols. */
+ if (S_GET_SEGMENT (fragP->fr_symbol) != segment)
+ {
+ /* symbol is undefined in this segment */
+ int code16 = fragP->fr_subtype & CODE16;
+ int size = code16 ? 2 : 4;
+ int pcrel_reloc = code16 ? BFD_RELOC_16_PCREL : BFD_RELOC_32_PCREL;
+
+ switch (opcode[0])
+ {
+ case JUMP_PC_RELATIVE: /* make jmp (0xeb) a dword displacement jump */
+ opcode[0] = 0xe9; /* dword disp jmp */
+ fragP->fr_fix += size;
+ fix_new (fragP, old_fr_fix, size,
+ fragP->fr_symbol,
+ fragP->fr_offset, 1,
+ (GOT_symbol && /* Not quite right - we should switch on
+ presence of @PLT, but I cannot see how
+ to get to that from here. We should have
+ done this in md_assemble to really
+ get it right all of the time, but I
+ think it does not matter that much, as
+ this will be right most of the time. ERY*/
+ S_GET_SEGMENT(fragP->fr_symbol) == undefined_section)
+ ? BFD_RELOC_386_PLT32 : pcrel_reloc);
+ break;
+
+ default:
+ /* This changes the byte-displacement jump 0x7N -->
+ the dword-displacement jump 0x0f8N */
+ opcode[1] = opcode[0] + 0x10;
+ opcode[0] = TWO_BYTE_OPCODE_ESCAPE; /* two-byte escape */
+ fragP->fr_fix += 1 + size; /* we've added an opcode byte */
+ fix_new (fragP, old_fr_fix + 1, size,
+ fragP->fr_symbol,
+ fragP->fr_offset, 1,
+ (GOT_symbol && /* Not quite right - we should switch on
+ presence of @PLT, but I cannot see how
+ to get to that from here. ERY */
+ S_GET_SEGMENT(fragP->fr_symbol) == undefined_section)
+ ? BFD_RELOC_386_PLT32 : pcrel_reloc);
+ break;
+ }
+ frag_wane (fragP);
+ }
+ return (fragP->fr_var + fragP->fr_fix - old_fr_fix);
+} /* md_estimate_size_before_relax() */
+
+/*
+ * md_convert_frag();
+ *
+ * Called after relax() is finished.
+ * In: Address of frag.
+ * fr_type == rs_machine_dependent.
+ * fr_subtype is what the address relaxed to.
+ *
+ * Out: Any fixSs and constants are set up.
+ * Caller will turn frag into a ".space 0".
+ */
+#ifndef BFD_ASSEMBLER
+void
+md_convert_frag (headers, sec, fragP)
+ object_headers *headers;
+ segT sec;
+ register fragS *fragP;
+#else
+void
+md_convert_frag (abfd, sec, fragP)
+ bfd *abfd;
+ segT sec;
+ register fragS *fragP;
+#endif
+{
+ register unsigned char *opcode;
+ unsigned char *where_to_put_displacement = NULL;
+ unsigned int target_address;
+ unsigned int opcode_address;
+ unsigned int extension = 0;
+ int displacement_from_opcode_start;
+
+ opcode = (unsigned char *) fragP->fr_opcode;
+
+ /* Address we want to reach in file space. */
+ target_address = S_GET_VALUE (fragP->fr_symbol) + fragP->fr_offset;
+#ifdef BFD_ASSEMBLER /* not needed otherwise? */
+ target_address += fragP->fr_symbol->sy_frag->fr_address;
+#endif
+
+ /* Address opcode resides at in file space. */
+ opcode_address = fragP->fr_address + fragP->fr_fix;
+
+ /* Displacement from opcode start to fill into instruction. */
+ displacement_from_opcode_start = target_address - opcode_address;
+
+ switch (fragP->fr_subtype)
+ {
+ case ENCODE_RELAX_STATE (COND_JUMP, SMALL):
+ case ENCODE_RELAX_STATE (COND_JUMP, SMALL16):
+ case ENCODE_RELAX_STATE (UNCOND_JUMP, SMALL):
+ case ENCODE_RELAX_STATE (UNCOND_JUMP, SMALL16):
+ /* don't have to change opcode */
+ extension = 1; /* 1 opcode + 1 displacement */
+ where_to_put_displacement = &opcode[1];
+ break;
+
+ case ENCODE_RELAX_STATE (COND_JUMP, BIG):
+ extension = 5; /* 2 opcode + 4 displacement */
+ opcode[1] = opcode[0] + 0x10;
+ opcode[0] = TWO_BYTE_OPCODE_ESCAPE;
+ where_to_put_displacement = &opcode[2];
+ break;
+
+ case ENCODE_RELAX_STATE (UNCOND_JUMP, BIG):
+ extension = 4; /* 1 opcode + 4 displacement */
+ opcode[0] = 0xe9;
+ where_to_put_displacement = &opcode[1];
+ break;
+
+ case ENCODE_RELAX_STATE (COND_JUMP, BIG16):
+ extension = 3; /* 2 opcode + 2 displacement */
+ opcode[1] = opcode[0] + 0x10;
+ opcode[0] = TWO_BYTE_OPCODE_ESCAPE;
+ where_to_put_displacement = &opcode[2];
+ break;
+
+ case ENCODE_RELAX_STATE (UNCOND_JUMP, BIG16):
+ extension = 2; /* 1 opcode + 2 displacement */
+ opcode[0] = 0xe9;
+ where_to_put_displacement = &opcode[1];
+ break;
+
+ default:
+ BAD_CASE (fragP->fr_subtype);
+ break;
+ }
+ /* now put displacement after opcode */
+ md_number_to_chars ((char *) where_to_put_displacement,
+ (valueT) (displacement_from_opcode_start - extension),
+ SIZE_FROM_RELAX_STATE (fragP->fr_subtype));
+ fragP->fr_fix += extension;
+}
+
+
+int md_short_jump_size = 2; /* size of byte displacement jmp */
+int md_long_jump_size = 5; /* size of dword displacement jmp */
+const int md_reloc_size = 8; /* Size of relocation record */
+
+void
+md_create_short_jump (ptr, from_addr, to_addr, frag, to_symbol)
+ char *ptr;
+ addressT from_addr, to_addr;
+ fragS *frag;
+ symbolS *to_symbol;
+{
+ long offset;
+
+ offset = to_addr - (from_addr + 2);
+ md_number_to_chars (ptr, (valueT) 0xeb, 1); /* opcode for byte-disp jump */
+ md_number_to_chars (ptr + 1, (valueT) offset, 1);
+}
+
+void
+md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol)
+ char *ptr;
+ addressT from_addr, to_addr;
+ fragS *frag;
+ symbolS *to_symbol;
+{
+ long offset;
+
+ if (flag_do_long_jump)
+ {
+ offset = to_addr - S_GET_VALUE (to_symbol);
+ md_number_to_chars (ptr, (valueT) 0xe9, 1);/* opcode for long jmp */
+ md_number_to_chars (ptr + 1, (valueT) offset, 4);
+ fix_new (frag, (ptr + 1) - frag->fr_literal, 4,
+ to_symbol, (offsetT) 0, 0, BFD_RELOC_32);
+ }
+ else
+ {
+ offset = to_addr - (from_addr + 5);
+ md_number_to_chars (ptr, (valueT) 0xe9, 1);
+ md_number_to_chars (ptr + 1, (valueT) offset, 4);
+ }
+}
+
+/* Apply a fixup (fixS) to segment data, once it has been determined
+ by our caller that we have all the info we need to fix it up.
+
+ On the 386, immediates, displacements, and data pointers are all in
+ the same (little-endian) format, so we don't need to care about which
+ we are handling. */
+
+int
+md_apply_fix3 (fixP, valp, seg)
+ fixS *fixP; /* The fix we're to put in. */
+ valueT *valp; /* Pointer to the value of the bits. */
+ segT seg; /* Segment fix is from. */
+{
+ register char *p = fixP->fx_where + fixP->fx_frag->fr_literal;
+ valueT value = *valp;
+
+ if (fixP->fx_r_type == BFD_RELOC_32 && fixP->fx_pcrel)
+ fixP->fx_r_type = BFD_RELOC_32_PCREL;
+
+#if defined (BFD_ASSEMBLER) && !defined (TE_Mach)
+ /*
+ * This is a hack. There should be a better way to
+ * handle this.
+ */
+ if (fixP->fx_r_type == BFD_RELOC_32_PCREL && fixP->fx_addsy)
+ {
+#ifndef OBJ_AOUT
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+#ifdef TE_PE
+ || OUTPUT_FLAVOR == bfd_target_coff_flavour
+#endif
+ )
+ value += fixP->fx_where + fixP->fx_frag->fr_address;
+#endif
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+ && (S_GET_SEGMENT (fixP->fx_addsy) == seg
+ || (fixP->fx_addsy->bsym->flags & BSF_SECTION_SYM) != 0)
+ && ! S_IS_EXTERNAL (fixP->fx_addsy)
+ && ! S_IS_WEAK (fixP->fx_addsy)
+ && S_IS_DEFINED (fixP->fx_addsy)
+ && ! S_IS_COMMON (fixP->fx_addsy))
+ {
+ /* Yes, we add the values in twice. This is because
+ bfd_perform_relocation subtracts them out again. I think
+ bfd_perform_relocation is broken, but I don't dare change
+ it. FIXME. */
+ value += fixP->fx_where + fixP->fx_frag->fr_address;
+ }
+#endif
+#if defined (OBJ_COFF) && defined (TE_PE)
+ /* For some reason, the PE format does not store a section
+ address offset for a PC relative symbol. */
+ if (S_GET_SEGMENT (fixP->fx_addsy) != seg)
+ value += md_pcrel_from (fixP);
+#endif
+ }
+
+ /* Fix a few things - the dynamic linker expects certain values here,
+ and we must not dissappoint it. */
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+ && fixP->fx_addsy)
+ switch (fixP->fx_r_type) {
+ case BFD_RELOC_386_PLT32:
+ /* Make the jump instruction point to the address of the operand. At
+ runtime we merely add the offset to the actual PLT entry. */
+ value = 0xfffffffc;
+ break;
+ case BFD_RELOC_386_GOTPC:
+/*
+ * This is tough to explain. We end up with this one if we have
+ * operands that look like "_GLOBAL_OFFSET_TABLE_+[.-.L284]". The goal
+ * here is to obtain the absolute address of the GOT, and it is strongly
+ * preferable from a performance point of view to avoid using a runtime
+ * relocation for this. The actual sequence of instructions often look
+ * something like:
+ *
+ * call .L66
+ * .L66:
+ * popl %ebx
+ * addl $_GLOBAL_OFFSET_TABLE_+[.-.L66],%ebx
+ *
+ * The call and pop essentially return the absolute address of
+ * the label .L66 and store it in %ebx. The linker itself will
+ * ultimately change the first operand of the addl so that %ebx points to
+ * the GOT, but to keep things simple, the .o file must have this operand
+ * set so that it generates not the absolute address of .L66, but the
+ * absolute address of itself. This allows the linker itself simply
+ * treat a GOTPC relocation as asking for a pcrel offset to the GOT to be
+ * added in, and the addend of the relocation is stored in the operand
+ * field for the instruction itself.
+ *
+ * Our job here is to fix the operand so that it would add the correct
+ * offset so that %ebx would point to itself. The thing that is tricky is
+ * that .-.L66 will point to the beginning of the instruction, so we need
+ * to further modify the operand so that it will point to itself.
+ * There are other cases where you have something like:
+ *
+ * .long $_GLOBAL_OFFSET_TABLE_+[.-.L66]
+ *
+ * and here no correction would be required. Internally in the assembler
+ * we treat operands of this form as not being pcrel since the '.' is
+ * explicitly mentioned, and I wonder whether it would simplify matters
+ * to do it this way. Who knows. In earlier versions of the PIC patches,
+ * the pcrel_adjust field was used to store the correction, but since the
+ * expression is not pcrel, I felt it would be confusing to do it this way.
+ */
+ value -= 1;
+ break;
+ case BFD_RELOC_386_GOT32:
+ value = 0; /* Fully resolved at runtime. No addend. */
+ break;
+ case BFD_RELOC_386_GOTOFF:
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ return 1;
+
+ default:
+ break;
+ }
+#endif
+
+#endif
+ md_number_to_chars (p, value, fixP->fx_size);
+
+ return 1;
+}
+
+#if 0
+/* This is never used. */
+long /* Knows about the byte order in a word. */
+md_chars_to_number (con, nbytes)
+ unsigned char con[]; /* Low order byte 1st. */
+ int nbytes; /* Number of bytes in the input. */
+{
+ long retval;
+ for (retval = 0, con += nbytes - 1; nbytes--; con--)
+ {
+ retval <<= BITS_PER_CHAR;
+ retval |= *con;
+ }
+ return retval;
+}
+#endif /* 0 */
+
+
+#define MAX_LITTLENUMS 6
+
+/* Turn the string pointed to by litP into a floating point constant of type
+ type, and emit the appropriate bytes. The number of LITTLENUMS emitted
+ is stored in *sizeP . An error message is returned, or NULL on OK. */
+char *
+md_atof (type, litP, sizeP)
+ char type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 5;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("Bad call to md_atof ()");
+ }
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+
+ *sizeP = prec * sizeof (LITTLENUM_TYPE);
+ /* This loops outputs the LITTLENUMs in REVERSE order; in accord with
+ the bigendian 386. */
+ for (wordP = words + prec - 1; prec--;)
+ {
+ md_number_to_chars (litP, (valueT) (*wordP--), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ return 0;
+}
+
+char output_invalid_buf[8];
+
+static char * output_invalid PARAMS ((int));
+
+static char *
+output_invalid (c)
+ int c;
+{
+ if (isprint (c))
+ sprintf (output_invalid_buf, "'%c'", c);
+ else
+ sprintf (output_invalid_buf, "(0x%x)", (unsigned) c);
+ return output_invalid_buf;
+}
+
+/* REG_STRING starts *before* REGISTER_PREFIX. */
+
+static const reg_entry * parse_register PARAMS ((char *, char **));
+
+static const reg_entry *
+parse_register (reg_string, end_op)
+ char *reg_string;
+ char **end_op;
+{
+ register char *s = reg_string;
+ register char *p;
+ char reg_name_given[MAX_REG_NAME_SIZE + 1];
+ const reg_entry *r;
+
+ /* Skip possible REGISTER_PREFIX and possible whitespace. */
+ if (*s == REGISTER_PREFIX)
+ ++s;
+
+ if (is_space_char (*s))
+ ++s;
+
+ p = reg_name_given;
+ while ((*p++ = register_chars[(unsigned char) *s++]) != '\0')
+ {
+ if (p >= reg_name_given + MAX_REG_NAME_SIZE)
+ {
+ if (!allow_naked_reg)
+ {
+ *p = '\0';
+ as_bad (_("bad register name `%s'"), reg_name_given);
+ }
+ return (const reg_entry *) NULL;
+ }
+ }
+
+ *end_op = s - 1;
+
+ r = (const reg_entry *) hash_find (reg_hash, reg_name_given);
+
+ if (r == NULL)
+ {
+ if (!allow_naked_reg)
+ as_bad (_("bad register name `%s'"), reg_name_given);
+ return (const reg_entry *) NULL;
+ }
+
+ return r;
+}
+
+#ifdef OBJ_ELF
+CONST char *md_shortopts = "kmVQ:";
+#else
+CONST char *md_shortopts = "m";
+#endif
+struct option md_longopts[] = {
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ switch (c)
+ {
+ case 'm':
+ flag_do_long_jump = 1;
+ break;
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ /* -k: Ignore for FreeBSD compatibility. */
+ case 'k':
+ break;
+
+ /* -V: SVR4 argument to print version ID. */
+ case 'V':
+ print_version_id ();
+ break;
+
+ /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section
+ should be emitted or not. FIXME: Not implemented. */
+ case 'Q':
+ break;
+#endif
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ fprintf (stream, _("\
+-m do long jump\n"));
+}
+
+#ifdef BFD_ASSEMBLER
+#ifdef OBJ_MAYBE_ELF
+#ifdef OBJ_MAYBE_COFF
+
+/* Pick the target format to use. */
+
+const char *
+i386_target_format ()
+{
+ switch (OUTPUT_FLAVOR)
+ {
+ case bfd_target_coff_flavour:
+ return "coff-i386";
+ case bfd_target_elf_flavour:
+ return "elf32-i386";
+ default:
+ abort ();
+ return NULL;
+ }
+}
+
+#endif /* OBJ_MAYBE_COFF */
+#endif /* OBJ_MAYBE_ELF */
+#endif /* BFD_ASSEMBLER */
+
+/* ARGSUSED */
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ if (*name == '_' && *(name+1) == 'G'
+ && strcmp(name, GLOBAL_OFFSET_TABLE_NAME) == 0)
+ {
+ if (!GOT_symbol)
+ {
+ if (symbol_find (name))
+ as_bad (_("GOT already in symbol table"));
+ GOT_symbol = symbol_new (name, undefined_section,
+ (valueT) 0, &zero_address_frag);
+ };
+ return GOT_symbol;
+ }
+ return 0;
+}
+
+/* Round up a section size to the appropriate boundary. */
+valueT
+md_section_align (segment, size)
+ segT segment;
+ valueT size;
+{
+#ifdef OBJ_AOUT
+#ifdef BFD_ASSEMBLER
+ /* For a.out, force the section size to be aligned. If we don't do
+ this, BFD will align it for us, but it will not write out the
+ final bytes of the section. This may be a bug in BFD, but it is
+ easier to fix it here since that is how the other a.out targets
+ work. */
+ int align;
+
+ align = bfd_get_section_alignment (stdoutput, segment);
+ size = ((size + (1 << align) - 1) & ((valueT) -1 << align));
+#endif
+#endif
+
+ return size;
+}
+
+/* On the i386, PC-relative offsets are relative to the start of the
+ next instruction. That is, the address of the offset, plus its
+ size, since the offset is always the last part of the insn. */
+
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+#ifndef I386COFF
+
+static void
+s_bss (ignore)
+ int ignore;
+{
+ register int temp;
+
+ temp = get_absolute_expression ();
+ subseg_set (bss_section, (subsegT) temp);
+ demand_empty_rest_of_line ();
+}
+
+#endif
+
+
+#ifdef BFD_ASSEMBLER
+
+void
+i386_validate_fix (fixp)
+ fixS *fixp;
+{
+ if (fixp->fx_subsy && fixp->fx_subsy == GOT_symbol)
+ {
+ fixp->fx_r_type = BFD_RELOC_386_GOTOFF;
+ fixp->fx_subsy = 0;
+ }
+}
+
+#define F(SZ,PCREL) (((SZ) << 1) + (PCREL))
+#define MAP(SZ,PCREL,TYPE) case F(SZ,PCREL): code = (TYPE); break
+
+arelent *
+tc_gen_reloc (section, fixp)
+ asection *section;
+ fixS *fixp;
+{
+ arelent *rel;
+ bfd_reloc_code_real_type code;
+
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_386_PLT32:
+ case BFD_RELOC_386_GOT32:
+ case BFD_RELOC_386_GOTOFF:
+ case BFD_RELOC_386_GOTPC:
+ case BFD_RELOC_RVA:
+ case BFD_RELOC_VTABLE_ENTRY:
+ case BFD_RELOC_VTABLE_INHERIT:
+ code = fixp->fx_r_type;
+ break;
+ default:
+ switch (F (fixp->fx_size, fixp->fx_pcrel))
+ {
+ MAP (1, 0, BFD_RELOC_8);
+ MAP (2, 0, BFD_RELOC_16);
+ MAP (4, 0, BFD_RELOC_32);
+ MAP (1, 1, BFD_RELOC_8_PCREL);
+ MAP (2, 1, BFD_RELOC_16_PCREL);
+ MAP (4, 1, BFD_RELOC_32_PCREL);
+ default:
+ if (fixp->fx_pcrel)
+ as_bad (_("Can not do %d byte pc-relative relocation"),
+ fixp->fx_size);
+ else
+ as_bad (_("Can not do %d byte relocation"), fixp->fx_size);
+ code = BFD_RELOC_32;
+ break;
+ }
+ break;
+ }
+#undef MAP
+#undef F
+
+ if (code == BFD_RELOC_32
+ && GOT_symbol
+ && fixp->fx_addsy == GOT_symbol)
+ code = BFD_RELOC_386_GOTPC;
+
+ rel = (arelent *) xmalloc (sizeof (arelent));
+ rel->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+
+ rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ /* HACK: Since i386 ELF uses Rel instead of Rela, encode the
+ vtable entry to be used in the relocation's section offset. */
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ rel->address = fixp->fx_offset;
+
+ if (fixp->fx_pcrel)
+ rel->addend = fixp->fx_addnumber;
+ else
+ rel->addend = 0;
+
+ rel->howto = bfd_reloc_type_lookup (stdoutput, code);
+ if (rel->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Cannot represent relocation type %s"),
+ bfd_get_reloc_code_name (code));
+ /* Set howto to a garbage value so that we can keep going. */
+ rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
+ assert (rel->howto != NULL);
+ }
+
+ return rel;
+}
+
+#else /* ! BFD_ASSEMBLER */
+
+#if (defined(OBJ_AOUT) | defined(OBJ_BOUT))
+void
+tc_aout_fix_to_chars (where, fixP, segment_address_in_file)
+ char *where;
+ fixS *fixP;
+ relax_addressT segment_address_in_file;
+{
+ /*
+ * In: length of relocation (or of address) in chars: 1, 2 or 4.
+ * Out: GNU LD relocation length code: 0, 1, or 2.
+ */
+
+ static const unsigned char nbytes_r_length[] = {42, 0, 1, 42, 2};
+ long r_symbolnum;
+
+ know (fixP->fx_addsy != NULL);
+
+ md_number_to_chars (where,
+ (valueT) (fixP->fx_frag->fr_address
+ + fixP->fx_where - segment_address_in_file),
+ 4);
+
+ r_symbolnum = (S_IS_DEFINED (fixP->fx_addsy)
+ ? S_GET_TYPE (fixP->fx_addsy)
+ : fixP->fx_addsy->sy_number);
+
+ where[6] = (r_symbolnum >> 16) & 0x0ff;
+ where[5] = (r_symbolnum >> 8) & 0x0ff;
+ where[4] = r_symbolnum & 0x0ff;
+ where[7] = ((((!S_IS_DEFINED (fixP->fx_addsy)) << 3) & 0x08)
+ | ((nbytes_r_length[fixP->fx_size] << 1) & 0x06)
+ | (((fixP->fx_pcrel << 0) & 0x01) & 0x0f));
+}
+
+#endif /* OBJ_AOUT or OBJ_BOUT */
+
+#if defined (I386COFF)
+
+short
+tc_coff_fix2rtype (fixP)
+ fixS *fixP;
+{
+ if (fixP->fx_r_type == R_IMAGEBASE)
+ return R_IMAGEBASE;
+
+ return (fixP->fx_pcrel ?
+ (fixP->fx_size == 1 ? R_PCRBYTE :
+ fixP->fx_size == 2 ? R_PCRWORD :
+ R_PCRLONG) :
+ (fixP->fx_size == 1 ? R_RELBYTE :
+ fixP->fx_size == 2 ? R_RELWORD :
+ R_DIR32));
+}
+
+int
+tc_coff_sizemachdep (frag)
+ fragS *frag;
+{
+ if (frag->fr_next)
+ return (frag->fr_next->fr_address - frag->fr_address);
+ else
+ return 0;
+}
+
+#endif /* I386COFF */
+
+#endif /* BFD_ASSEMBLER? */
+
+/* end of tc-i386.c */
diff --git a/gas/config/tc-i386.h b/gas/config/tc-i386.h
new file mode 100644
index 0000000..d876d61
--- /dev/null
+++ b/gas/config/tc-i386.h
@@ -0,0 +1,471 @@
+/* tc-i386.h -- Header file for tc-i386.c
+ Copyright (C) 1989, 92, 93, 94, 95, 96, 97, 1998 Free Software Foundation.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifndef TC_I386
+#define TC_I386 1
+
+#ifdef ANSI_PROTOTYPES
+struct fix;
+#endif
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#ifdef TE_LYNX
+#define TARGET_FORMAT "coff-i386-lynx"
+#endif
+
+#ifdef BFD_ASSEMBLER
+/* This is used to determine relocation types in tc-i386.c. The first
+ parameter is the current relocation type, the second one is the desired
+ type. The idea is that if the original type is already some kind of PIC
+ relocation, we leave it alone, otherwise we give it the desired type */
+
+#define TC_RELOC(X,Y) (((X) != BFD_RELOC_386_PLT32 && \
+ (X) != BFD_RELOC_386_GOTOFF && \
+ (X) != BFD_RELOC_386_GOT32 && \
+ (X) != BFD_RELOC_386_GOTPC) ? Y : X)
+
+#define tc_fix_adjustable(X) tc_i386_fix_adjustable(X)
+extern int tc_i386_fix_adjustable PARAMS ((struct fix *));
+
+/* This is the relocation type for direct references to GLOBAL_OFFSET_TABLE.
+ * It comes up in complicated expressions such as
+ * _GLOBAL_OFFSET_TABLE_+[.-.L284], which cannot be expressed normally with
+ * the regular expressions. The fixup specified here when used at runtime
+ * implies that we should add the address of the GOT to the specified location,
+ * and as a result we have simplified the expression into something we can use.
+ */
+#define TC_RELOC_GLOBAL_OFFSET_TABLE BFD_RELOC_386_GOTPC
+
+/* This expression evaluates to false if the relocation is for a local object
+ for which we still want to do the relocation at runtime. True if we
+ are willing to perform this relocation while building the .o file.
+ This is only used for pcrel relocations, so GOTOFF does not need to be
+ checked here. I am not sure if some of the others are ever used with
+ pcrel, but it is easier to be safe than sorry. */
+
+#define TC_RELOC_RTSYM_LOC_FIXUP(FIX) \
+ ((FIX)->fx_r_type != BFD_RELOC_386_PLT32 \
+ && (FIX)->fx_r_type != BFD_RELOC_386_GOT32 \
+ && (FIX)->fx_r_type != BFD_RELOC_386_GOTPC \
+ && ((FIX)->fx_addsy == NULL \
+ || (! S_IS_EXTERNAL ((FIX)->fx_addsy) \
+ && ! S_IS_WEAK ((FIX)->fx_addsy) \
+ && S_IS_DEFINED ((FIX)->fx_addsy) \
+ && ! S_IS_COMMON ((FIX)->fx_addsy))))
+
+#define TARGET_ARCH bfd_arch_i386
+
+#ifdef OBJ_AOUT
+#ifdef TE_NetBSD
+#define TARGET_FORMAT "a.out-i386-netbsd"
+#endif
+#ifdef TE_386BSD
+#define TARGET_FORMAT "a.out-i386-bsd"
+#endif
+#ifdef TE_LINUX
+#define TARGET_FORMAT "a.out-i386-linux"
+#endif
+#ifdef TE_Mach
+#define TARGET_FORMAT "a.out-mach3"
+#endif
+#ifdef TE_DYNIX
+#define TARGET_FORMAT "a.out-i386-dynix"
+#endif
+#ifndef TARGET_FORMAT
+#define TARGET_FORMAT "a.out-i386"
+#endif
+#endif /* OBJ_AOUT */
+
+#ifdef OBJ_ELF
+#define TARGET_FORMAT "elf32-i386"
+#endif
+
+#ifdef OBJ_MAYBE_ELF
+#ifdef OBJ_MAYBE_COFF
+extern const char *i386_target_format PARAMS ((void));
+#define TARGET_FORMAT i386_target_format ()
+#endif
+#endif
+
+#else /* ! BFD_ASSEMBLER */
+
+/* COFF STUFF */
+
+#define COFF_MAGIC I386MAGIC
+#define BFD_ARCH bfd_arch_i386
+#define COFF_FLAGS F_AR32WR
+#define TC_COUNT_RELOC(x) ((x)->fx_addsy || (x)->fx_r_type==7)
+#define TC_COFF_FIX2RTYPE(fixP) tc_coff_fix2rtype(fixP)
+extern short tc_coff_fix2rtype PARAMS ((struct fix *));
+#define TC_COFF_SIZEMACHDEP(frag) tc_coff_sizemachdep(frag)
+extern int tc_coff_sizemachdep PARAMS ((fragS *frag));
+#define SUB_SEGMENT_ALIGN(SEG) 2
+#define TC_RVA_RELOC 7
+/* Need this for PIC relocations */
+#define NEED_FX_R_TYPE
+
+
+#ifdef TE_386BSD
+/* The BSDI linker apparently rejects objects with a machine type of
+ M_386 (100). */
+#define AOUT_MACHTYPE 0
+#else
+#define AOUT_MACHTYPE 100
+#endif
+
+#undef REVERSE_SORT_RELOCS
+
+#endif /* ! BFD_ASSEMBLER */
+
+#define TC_FORCE_RELOCATION(fixp) tc_i386_force_relocation(fixp)
+extern int tc_i386_force_relocation PARAMS ((struct fix *));
+
+#ifdef BFD_ASSEMBLER
+#define NO_RELOC BFD_RELOC_NONE
+#else
+#define NO_RELOC 0
+#endif
+#define tc_coff_symbol_emit_hook(a) ; /* not used */
+
+#ifndef BFD_ASSEMBLER
+#ifndef OBJ_AOUT
+#ifndef TE_PE
+#ifndef TE_GO32
+/* Local labels starts with .L */
+#define LOCAL_LABEL(name) (name[0] == '.' \
+ && (name[1] == 'L' || name[1] == 'X' || name[1] == '.'))
+#endif
+#endif
+#endif
+#endif
+
+#define LOCAL_LABELS_FB 1
+
+#define tc_aout_pre_write_hook(x) {;} /* not used */
+#define tc_crawl_symbol_chain(a) {;} /* not used */
+#define tc_headers_hook(a) {;} /* not used */
+
+extern const char extra_symbol_chars[];
+#define tc_symbol_chars extra_symbol_chars
+
+#define MAX_OPERANDS 3 /* max operands per insn */
+#define MAX_IMMEDIATE_OPERANDS 2/* max immediates per insn (lcall, ljmp) */
+#define MAX_MEMORY_OPERANDS 2 /* max memory refs per insn (string ops) */
+
+/* Prefixes will be emitted in the order defined below.
+ WAIT_PREFIX must be the first prefix since FWAIT is really is an
+ instruction, and so must come before any prefixes. */
+#define WAIT_PREFIX 0
+#define LOCKREP_PREFIX 1
+#define ADDR_PREFIX 2
+#define DATA_PREFIX 3
+#define SEG_PREFIX 4
+#define MAX_PREFIXES 5 /* max prefixes per opcode */
+
+/* we define the syntax here (modulo base,index,scale syntax) */
+#define REGISTER_PREFIX '%'
+#define IMMEDIATE_PREFIX '$'
+#define ABSOLUTE_PREFIX '*'
+
+#define TWO_BYTE_OPCODE_ESCAPE 0x0f
+#define NOP_OPCODE (char) 0x90
+
+/* register numbers */
+#define EBP_REG_NUM 5
+#define ESP_REG_NUM 4
+
+/* modrm_byte.regmem for twobyte escape */
+#define ESCAPE_TO_TWO_BYTE_ADDRESSING ESP_REG_NUM
+/* index_base_byte.index for no index register addressing */
+#define NO_INDEX_REGISTER ESP_REG_NUM
+/* index_base_byte.base for no base register addressing */
+#define NO_BASE_REGISTER EBP_REG_NUM
+#define NO_BASE_REGISTER_16 6
+
+/* these are the instruction mnemonic suffixes. */
+#define DWORD_MNEM_SUFFIX 'l'
+#define WORD_MNEM_SUFFIX 'w'
+#define BYTE_MNEM_SUFFIX 'b'
+#define SHORT_MNEM_SUFFIX 's'
+#define LONG_MNEM_SUFFIX 'l'
+/* Intel Syntax */
+#define LONG_DOUBLE_MNEM_SUFFIX 'x'
+/* Intel Syntax */
+#define INTEL_DWORD_MNEM_SUFFIX 'd'
+
+/* modrm.mode = REGMEM_FIELD_HAS_REG when a register is in there */
+#define REGMEM_FIELD_HAS_REG 0x3/* always = 0x3 */
+#define REGMEM_FIELD_HAS_MEM (~REGMEM_FIELD_HAS_REG)
+
+#define END_OF_INSN '\0'
+
+/* Intel Syntax */
+/* Values 0-4 map onto scale factor */
+#define BYTE_PTR 0
+#define WORD_PTR 1
+#define DWORD_PTR 2
+#define QWORD_PTR 3
+#define XWORD_PTR 4
+#define SHORT 5
+#define OFFSET_FLAT 6
+#define FLAT 7
+#define NONE_FOUND 8
+/*
+ When an operand is read in it is classified by its type. This type includes
+ all the possible ways an operand can be used. Thus, '%eax' is both 'register
+ # 0' and 'The Accumulator'. In our language this is expressed by OR'ing
+ 'Reg32' (any 32 bit register) and 'Acc' (the accumulator).
+ Operands are classified so that we can match given operand types with
+ the opcode table in opcode/i386.h.
+ */
+/* register */
+#define Reg8 0x1 /* 8 bit reg */
+#define Reg16 0x2 /* 16 bit reg */
+#define Reg32 0x4 /* 32 bit reg */
+/* immediate */
+#define Imm8 0x8 /* 8 bit immediate */
+#define Imm8S 0x10 /* 8 bit immediate sign extended */
+#define Imm16 0x20 /* 16 bit immediate */
+#define Imm32 0x40 /* 32 bit immediate */
+#define Imm1 0x80 /* 1 bit immediate */
+/* memory */
+#define BaseIndex 0x100
+/* Disp8,16,32 are used in different ways, depending on the
+ instruction. For jumps, they specify the size of the PC relative
+ displacement, for baseindex type instructions, they specify the
+ size of the offset relative to the base register, and for memory
+ offset instructions such as `mov 1234,%al' they specify the size of
+ the offset relative to the segment base. */
+#define Disp8 0x200 /* 8 bit displacement */
+#define Disp16 0x400 /* 16 bit displacement */
+#define Disp32 0x800 /* 32 bit displacement */
+/* specials */
+#define InOutPortReg 0x1000 /* register to hold in/out port addr = dx */
+#define ShiftCount 0x2000 /* register to hold shift cound = cl */
+#define Control 0x4000 /* Control register */
+#define Debug 0x8000 /* Debug register */
+#define Test 0x10000 /* Test register */
+#define FloatReg 0x20000 /* Float register */
+#define FloatAcc 0x40000 /* Float stack top %st(0) */
+#define SReg2 0x80000 /* 2 bit segment register */
+#define SReg3 0x100000 /* 3 bit segment register */
+#define Acc 0x200000 /* Accumulator %al or %ax or %eax */
+#define JumpAbsolute 0x400000
+#define RegMMX 0x800000 /* MMX register */
+#define EsSeg 0x1000000 /* String insn operand with fixed es segment */
+
+#define Reg (Reg8|Reg16|Reg32) /* gen'l register */
+#define WordReg (Reg16|Reg32)
+#define ImplicitRegister (InOutPortReg|ShiftCount|Acc|FloatAcc)
+#define Imm (Imm8|Imm8S|Imm16|Imm32) /* gen'l immediate */
+#define Disp (Disp8|Disp16|Disp32) /* General displacement */
+#define AnyMem (Disp|BaseIndex) /* General memory */
+/* The following aliases are defined because the opcode table
+ carefully specifies the allowed memory types for each instruction.
+ At the moment we can only tell a memory reference size by the
+ instruction suffix, so there's not much point in defining Mem8,
+ Mem16, Mem32 and Mem64 opcode modifiers - We might as well just use
+ the suffix directly to check memory operands. */
+#define LLongMem AnyMem /* 64 bits (or more) */
+#define LongMem AnyMem /* 32 bit memory ref */
+#define ShortMem AnyMem /* 16 bit memory ref */
+#define WordMem AnyMem /* 16 or 32 bit memory ref */
+#define ByteMem AnyMem /* 8 bit memory ref */
+
+#define SMALLEST_DISP_TYPE(num) \
+ (fits_in_signed_byte(num) ? (Disp8|Disp32) : Disp32)
+
+typedef struct
+{
+ /* instruction name sans width suffix ("mov" for movl insns) */
+ char *name;
+
+ /* how many operands */
+ unsigned int operands;
+
+ /* base_opcode is the fundamental opcode byte without optional
+ prefix(es). */
+ unsigned int base_opcode;
+
+ /* extension_opcode is the 3 bit extension for group <n> insns.
+ This field is also used to store the 8-bit opcode suffix for the
+ AMD 3DNow! instructions.
+ If this template has no extension opcode (the usual case) use None */
+ unsigned int extension_opcode;
+#define None 0xffff /* If no extension_opcode is possible. */
+
+ /* the bits in opcode_modifier are used to generate the final opcode from
+ the base_opcode. These bits also are used to detect alternate forms of
+ the same instruction */
+ unsigned int opcode_modifier;
+
+ /* opcode_modifier bits: */
+#define W 0x1 /* set if operands can be words or dwords
+ encoded the canonical way */
+#define D 0x2 /* D = 0 if Reg --> Regmem;
+ D = 1 if Regmem --> Reg: MUST BE 0x2 */
+#define Modrm 0x4
+#define ReverseRegRegmem 0x8 /* swap reg,regmem fields for 2 reg case */
+#define FloatR 0x8 /* src/dest swap for floats: MUST BE 0x8 */
+#define ShortForm 0x10 /* register is in low 3 bits of opcode */
+#define FloatMF 0x20 /* FP insn memory format bit, sized by 0x4 */
+#define Jump 0x40 /* special case for jump insns. */
+#define JumpDword 0x80 /* call and jump */
+#define JumpByte 0x100 /* loop and jecxz */
+#define JumpInterSegment 0x200 /* special case for intersegment leaps/calls */
+#define FloatD 0x400 /* direction for float insns: MUST BE 0x400 */
+#define Seg2ShortForm 0x800 /* encoding of load segment reg insns */
+#define Seg3ShortForm 0x1000 /* fs/gs segment register insns. */
+#define Size16 0x2000 /* needs size prefix if in 32-bit mode */
+#define Size32 0x4000 /* needs size prefix if in 16-bit mode */
+#define IgnoreSize 0x8000 /* instruction ignores operand size prefix */
+#define No_bSuf 0x10000 /* b suffix on instruction illegal */
+#define No_wSuf 0x20000 /* w suffix on instruction illegal */
+#define No_lSuf 0x40000 /* l suffix on instruction illegal */
+#define No_sSuf 0x80000 /* s suffix on instruction illegal */
+#define FWait 0x100000 /* instruction needs FWAIT */
+#define IsString 0x200000 /* quick test for string instructions */
+#define regKludge 0x400000 /* fake an extra reg operand for clr, imul */
+#define IsPrefix 0x800000 /* opcode is a prefix */
+#define No_dSuf 0x1000000 /* d suffix on instruction illegal */
+#define No_xSuf 0x2000000 /* x suffix on instruction illegal */
+#define Ugh 0x80000000 /* deprecated fp insn, gets a warning */
+
+ /* operand_types[i] describes the type of operand i. This is made
+ by OR'ing together all of the possible type masks. (e.g.
+ 'operand_types[i] = Reg|Imm' specifies that operand i can be
+ either a register or an immediate operand */
+ unsigned int operand_types[3];
+}
+template;
+
+/*
+ 'templates' is for grouping together 'template' structures for opcodes
+ of the same name. This is only used for storing the insns in the grand
+ ole hash table of insns.
+ The templates themselves start at START and range up to (but not including)
+ END.
+ */
+typedef struct
+ {
+ const template *start;
+ const template *end;
+ } templates;
+
+/* these are for register name --> number & type hash lookup */
+typedef struct
+ {
+ char *reg_name;
+ unsigned int reg_type;
+ unsigned int reg_num;
+ }
+reg_entry;
+
+typedef struct
+ {
+ char *seg_name;
+ unsigned int seg_prefix;
+ }
+seg_entry;
+
+/* 386 operand encoding bytes: see 386 book for details of this. */
+typedef struct
+ {
+ unsigned int regmem; /* codes register or memory operand */
+ unsigned int reg; /* codes register operand (or extended opcode) */
+ unsigned int mode; /* how to interpret regmem & reg */
+ }
+modrm_byte;
+
+/* 386 opcode byte to code indirect addressing. */
+typedef struct
+ {
+ unsigned base;
+ unsigned index;
+ unsigned scale;
+ }
+sib_byte;
+
+/* The name of the global offset table generated by the compiler. Allow
+ this to be overridden if need be. */
+#ifndef GLOBAL_OFFSET_TABLE_NAME
+#define GLOBAL_OFFSET_TABLE_NAME "_GLOBAL_OFFSET_TABLE_"
+#endif
+
+#ifdef BFD_ASSEMBLER
+void i386_validate_fix PARAMS ((struct fix *));
+#define TC_VALIDATE_FIX(FIXP,SEGTYPE,SKIP) i386_validate_fix(FIXP)
+#endif
+
+#endif /* TC_I386 */
+
+#define md_operand(x)
+
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+
+extern int flag_16bit_code;
+
+#ifdef BFD_ASSEMBLER
+#define md_maybe_text() \
+ ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
+#else
+#define md_maybe_text() \
+ (now_seg != data_section && now_seg != bss_section)
+#endif
+
+#define md_do_align(n, fill, len, max, around) \
+if ((n) && !need_pass_2 \
+ && (!(fill) || ((char)*(fill) == (char)0x90 && (len) == 1)) \
+ && md_maybe_text ()) \
+ { \
+ char *p; \
+ p = frag_var (rs_align_code, 15, 1, (relax_substateT) max, \
+ (symbolS *) 0, (offsetT) (n), (char *) 0); \
+ *p = 0x90; \
+ goto around; \
+ }
+
+extern void i386_align_code PARAMS ((fragS *, int));
+
+#define HANDLE_ALIGN(fragP) \
+if (fragP->fr_type == rs_align_code) \
+ i386_align_code (fragP, (fragP->fr_next->fr_address \
+ - fragP->fr_address \
+ - fragP->fr_fix));
+
+/* call md_apply_fix3 with segment instead of md_apply_fix */
+#define MD_APPLY_FIX3
+
+void i386_print_statistics PARAMS ((FILE *));
+#define tc_print_statistics i386_print_statistics
+
+#define md_number_to_chars number_to_chars_littleendian
+
+#ifdef SCO_ELF
+#define tc_init_after_args() sco_id ()
+extern void sco_id PARAMS ((void));
+#endif
+
+#define DIFF_EXPR_OK /* foo-. gets turned into PC relative relocs */
+
+/* end of tc-i386.h */
diff --git a/gas/config/tc-i860.c b/gas/config/tc-i860.c
new file mode 100644
index 0000000..f2e2b19
--- /dev/null
+++ b/gas/config/tc-i860.c
@@ -0,0 +1,1263 @@
+/* tc-i860.c -- Assemble for the I860
+ Copyright (C) 1989, 92, 93, 94, 95, 1998 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with GAS; see the file COPYING. If not, write to the Free Software
+ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "as.h"
+
+#include "opcode/i860.h"
+
+void md_begin ();
+void md_number_to_chars ();
+void md_assemble ();
+char *md_atof ();
+void md_convert_frag ();
+int md_estimate_size_before_relax ();
+void md_number_to_imm ();
+void md_number_to_disp ();
+void md_number_to_field ();
+void md_ri_to_chars ();
+static void i860_ip ();
+/* void emit_machine_reloc(); */
+
+const int md_reloc_size = sizeof (struct relocation_info);
+
+/* void (*md_emit_relocations)() = emit_machine_reloc; */
+
+/* handle of the OPCODE hash table */
+static struct hash_control *op_hash = NULL;
+
+static void s_dual (), s_enddual ();
+static void s_atmp ();
+
+const pseudo_typeS
+ md_pseudo_table[] =
+{
+ {"dual", s_dual, 4},
+ {"enddual", s_enddual, 4},
+ {"atmp", s_atmp, 4},
+ {NULL, 0, 0},
+};
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful */
+const char comment_chars[] = "!/"; /* JF removed '|' from comment_chars */
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that comments like this one will always work. */
+const char line_comment_chars[] = "#/";
+
+const char line_separator_chars[] = "";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c . Ideally it shouldn't have to know about it at all,
+ but nothing is ideal around here.
+ */
+int size_reloc_info = sizeof (struct relocation_info);
+
+static unsigned char octal[256];
+#define isoctal(c) octal[c]
+static unsigned char toHex[256];
+
+struct i860_it
+ {
+ char *error;
+ unsigned long opcode;
+ struct nlist *nlistp;
+ expressionS exp;
+ int pcrel;
+ enum expand_type expand;
+ enum highlow_type highlow;
+ enum reloc_type reloc;
+ } the_insn;
+
+#if __STDC__ == 1
+
+static void print_insn (struct i860_it *insn);
+static int getExpression (char *str);
+
+#else /* not __STDC__ */
+
+static void print_insn ();
+static int getExpression ();
+
+#endif /* not __STDC__ */
+
+static char *expr_end;
+static char last_expand; /* error if expansion after branch */
+
+enum dual
+{
+ DUAL_OFF = 0, DUAL_ON, DUAL_DDOT, DUAL_ONDDOT,
+};
+static enum dual dual_mode = DUAL_OFF; /* dual-instruction mode */
+
+static void
+s_dual () /* floating point instructions have dual set */
+{
+ dual_mode = DUAL_ON;
+}
+
+static void
+s_enddual () /* floating point instructions have dual set */
+{
+ dual_mode = DUAL_OFF;
+}
+
+static int atmp = 31; /* temporary register for pseudo's */
+
+static void
+s_atmp ()
+{
+ register int temp;
+ if (strncmp (input_line_pointer, "sp", 2) == 0)
+ {
+ input_line_pointer += 2;
+ atmp = 2;
+ }
+ else if (strncmp (input_line_pointer, "fp", 2) == 0)
+ {
+ input_line_pointer += 2;
+ atmp = 3;
+ }
+ else if (strncmp (input_line_pointer, "r", 1) == 0)
+ {
+ input_line_pointer += 1;
+ temp = get_absolute_expression ();
+ if (temp >= 0 && temp <= 31)
+ atmp = temp;
+ else
+ as_bad (_("Unknown temporary pseudo register"));
+ }
+ else
+ {
+ as_bad (_("Unknown temporary pseudo register"));
+ }
+ demand_empty_rest_of_line ();
+}
+
+/* This function is called once, at assembler startup time. It should
+ set up all the tables, etc. that the MD part of the assembler will need. */
+void
+md_begin ()
+{
+ register char *retval = NULL;
+ int lose = 0;
+ register unsigned int i = 0;
+
+ op_hash = hash_new ();
+
+ while (i < NUMOPCODES)
+ {
+ const char *name = i860_opcodes[i].name;
+ retval = hash_insert (op_hash, name, &i860_opcodes[i]);
+ if (retval != NULL)
+ {
+ fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
+ i860_opcodes[i].name, retval);
+ lose = 1;
+ }
+ do
+ {
+ if (i860_opcodes[i].match & i860_opcodes[i].lose)
+ {
+ fprintf (stderr, _("internal error: losing opcode: `%s' \"%s\"\n"),
+ i860_opcodes[i].name, i860_opcodes[i].args);
+ lose = 1;
+ }
+ ++i;
+ }
+ while (i < NUMOPCODES
+ && !strcmp (i860_opcodes[i].name, name));
+ }
+
+ if (lose)
+ as_fatal (_("Broken assembler. No assembly attempted."));
+
+ for (i = '0'; i < '8'; ++i)
+ octal[i] = 1;
+ for (i = '0'; i <= '9'; ++i)
+ toHex[i] = i - '0';
+ for (i = 'a'; i <= 'f'; ++i)
+ toHex[i] = i + 10 - 'a';
+ for (i = 'A'; i <= 'F'; ++i)
+ toHex[i] = i + 10 - 'A';
+}
+
+void
+md_assemble (str)
+ char *str;
+{
+ char *toP;
+ int rsd;
+ int no_opcodes = 1;
+ int i;
+ struct i860_it pseudo[3];
+
+ assert (str);
+ i860_ip (str);
+
+ /* check for expandable flag to produce pseudo-instructions */
+ if (the_insn.expand != 0 && the_insn.highlow == NO_SPEC)
+ {
+ for (i = 0; i < 3; i++)
+ pseudo[i] = the_insn;
+
+ switch (the_insn.expand)
+ {
+
+ case E_DELAY:
+ no_opcodes = 1;
+ break;
+
+ case E_MOV:
+ if (the_insn.exp.X_add_symbol == NULL &&
+ the_insn.exp.X_op_symbol == NULL &&
+ (the_insn.exp.X_add_number < (1 << 15) &&
+ the_insn.exp.X_add_number >= -(1 << 15)))
+ break;
+ /* or l%const,r0,ireg_dest */
+ pseudo[0].opcode = (the_insn.opcode & 0x001f0000) | 0xe4000000;
+ pseudo[0].highlow = PAIR;
+ /* orh h%const,ireg_dest,ireg_dest */
+ pseudo[1].opcode = (the_insn.opcode & 0x03ffffff) | 0xec000000 |
+ ((the_insn.opcode & 0x001f0000) << 5);
+ pseudo[1].highlow = HIGH;
+ no_opcodes = 2;
+ break;
+
+ case E_ADDR:
+ if (the_insn.exp.X_add_symbol == NULL &&
+ the_insn.exp.X_op_symbol == NULL)
+ break;
+ /* orh ha%addr_expr,r0,r31 */
+ pseudo[0].opcode = 0xec000000 | (atmp << 16);
+ pseudo[0].highlow = HIGHADJ;
+ pseudo[0].reloc = LOW0; /* must overwrite */
+ /* l%addr_expr(r31),ireg_dest */
+ pseudo[1].opcode = (the_insn.opcode & ~0x003e0000) | (atmp << 21);
+ pseudo[1].highlow = PAIR;
+ no_opcodes = 2;
+ break;
+
+ case E_U32: /* 2nd version emulates Intel as, not doc. */
+ if (the_insn.exp.X_add_symbol == NULL &&
+ the_insn.exp.X_op_symbol == NULL &&
+ (the_insn.exp.X_add_number < (1 << 16) &&
+ the_insn.exp.X_add_number >= 0))
+ break;
+ /* $(opcode)h h%const,ireg_src2,ireg_dest
+ pseudo[0].opcode = (the_insn.opcode & 0xf3ffffff) | 0x0c000000; */
+ /* $(opcode)h h%const,ireg_src2,r31 */
+ pseudo[0].opcode = (the_insn.opcode & 0xf3e0ffff) | 0x0c000000 |
+ (atmp << 16);
+ pseudo[0].highlow = HIGH;
+ /* $(opcode) l%const,ireg_dest,ireg_dest
+ pseudo[1].opcode = (the_insn.opcode & 0xf01f0000) | 0x04000000 |
+ ((the_insn.opcode & 0x001f0000) << 5); */
+ /* $(opcode) l%const,r31,ireg_dest */
+ pseudo[1].opcode = (the_insn.opcode & 0xf01f0000) | 0x04000000 |
+ (atmp << 21);
+ pseudo[1].highlow = PAIR;
+ no_opcodes = 2;
+ break;
+
+ case E_AND: /* 2nd version emulates Intel as, not doc. */
+ if (the_insn.exp.X_add_symbol == NULL &&
+ the_insn.exp.X_op_symbol == NULL &&
+ (the_insn.exp.X_add_number < (1 << 16) &&
+ the_insn.exp.X_add_number >= 0))
+ break;
+ /* andnot h%const,ireg_src2,ireg_dest
+ pseudo[0].opcode = (the_insn.opcode & 0x03ffffff) | 0xd4000000; */
+ /* andnot h%const,ireg_src2,r31 */
+ pseudo[0].opcode = (the_insn.opcode & 0x03e0ffff) | 0xd4000000 |
+ (atmp << 16);
+ pseudo[0].highlow = HIGH;
+ pseudo[0].exp.X_add_number = -1 - the_insn.exp.X_add_number;
+ /* andnot l%const,ireg_dest,ireg_dest
+ pseudo[1].opcode = (the_insn.opcode & 0x001f0000) | 0xd4000000 |
+ ((the_insn.opcode & 0x001f0000) << 5); */
+ /* andnot l%const,r31,ireg_dest */
+ pseudo[1].opcode = (the_insn.opcode & 0x001f0000) | 0xd4000000 |
+ (atmp << 21);
+ pseudo[1].highlow = PAIR;
+ pseudo[1].exp.X_add_number = -1 - the_insn.exp.X_add_number;
+ no_opcodes = 2;
+ break;
+
+ case E_S32:
+ if (the_insn.exp.X_add_symbol == NULL &&
+ the_insn.exp.X_op_symbol == NULL &&
+ (the_insn.exp.X_add_number < (1 << 15) &&
+ the_insn.exp.X_add_number >= -(1 << 15)))
+ break;
+ /* orh h%const,r0,r31 */
+ pseudo[0].opcode = 0xec000000 | (atmp << 16);
+ pseudo[0].highlow = HIGH;
+ /* or l%const,r31,r31 */
+ pseudo[1].opcode = 0xe4000000 | (atmp << 21) | (atmp << 16);
+ pseudo[1].highlow = PAIR;
+ /* r31,ireg_src2,ireg_dest */
+ pseudo[2].opcode = (the_insn.opcode & ~0x0400ffff) | (atmp << 11);
+ pseudo[2].reloc = NO_RELOC;
+ no_opcodes = 3;
+ break;
+
+ default:
+ as_fatal (_("failed sanity check."));
+ }
+
+ the_insn = pseudo[0];
+ /* check for expanded opcode after branch or in dual */
+ if (no_opcodes > 1 && last_expand == 1)
+ as_warn (_("Expanded opcode after delayed branch: `%s'"), str);
+ if (no_opcodes > 1 && dual_mode != DUAL_OFF)
+ as_warn (_("Expanded opcode in dual mode: `%s'"), str);
+ }
+
+ i = 0;
+ do
+ { /* always produce at least one opcode */
+ toP = frag_more (4);
+ /* put out the opcode */
+ md_number_to_chars (toP, the_insn.opcode, 4);
+
+ /* check for expanded opcode after branch or in dual */
+ last_expand = the_insn.pcrel;
+
+ /* put out the symbol-dependent stuff */
+ if (the_insn.reloc != NO_RELOC)
+ {
+ fix_new (frag_now, /* which frag */
+ (toP - frag_now->fr_literal), /* where */
+ 4, /* size */
+ &the_insn.exp,
+ the_insn.pcrel,
+ /* merge bit fields into one argument */
+ (int) (((the_insn.highlow & 0x3) << 4) | (the_insn.reloc & 0xf)));
+ }
+ the_insn = pseudo[++i];
+ }
+ while (--no_opcodes > 0);
+
+}
+
+static void
+i860_ip (str)
+ char *str;
+{
+ char *s;
+ const char *args;
+ char c;
+ unsigned long i;
+ struct i860_opcode *insn;
+ char *argsStart;
+ unsigned long opcode;
+ unsigned int mask;
+ int match = 0;
+ int comma = 0;
+
+
+ for (s = str; islower (*s) || *s == '.' || *s == '3'; ++s)
+ ;
+ switch (*s)
+ {
+
+ case '\0':
+ break;
+
+ case ',':
+ comma = 1;
+
+ /*FALLTHROUGH*/
+
+ case ' ':
+ *s++ = '\0';
+ break;
+
+ default:
+ as_fatal (_("Unknown opcode: `%s'"), str);
+ }
+
+ if (strncmp (str, "d.", 2) == 0)
+ { /* check for d. opcode prefix */
+ if (dual_mode == DUAL_ON)
+ dual_mode = DUAL_ONDDOT;
+ else
+ dual_mode = DUAL_DDOT;
+ str += 2;
+ }
+
+ if ((insn = (struct i860_opcode *) hash_find (op_hash, str)) == NULL)
+ {
+ if (dual_mode == DUAL_DDOT || dual_mode == DUAL_ONDDOT)
+ str -= 2;
+ as_bad (_("Unknown opcode: `%s'"), str);
+ return;
+ }
+ if (comma)
+ {
+ *--s = ',';
+ }
+ argsStart = s;
+ for (;;)
+ {
+ opcode = insn->match;
+ memset (&the_insn, '\0', sizeof (the_insn));
+ the_insn.reloc = NO_RELOC;
+
+ /*
+ * Build the opcode, checking as we go to make
+ * sure that the operands match
+ */
+ for (args = insn->args;; ++args)
+ {
+ switch (*args)
+ {
+
+ case '\0': /* end of args */
+ if (*s == '\0')
+ {
+ match = 1;
+ }
+ break;
+
+ case '+':
+ case '(': /* these must match exactly */
+ case ')':
+ case ',':
+ case ' ':
+ if (*s++ == *args)
+ continue;
+ break;
+
+ case '#': /* must be at least one digit */
+ if (isdigit (*s++))
+ {
+ while (isdigit (*s))
+ {
+ ++s;
+ }
+ continue;
+ }
+ break;
+
+ case '1': /* next operand must be a register */
+ case '2':
+ case 'd':
+ switch (*s)
+ {
+
+ case 'f': /* frame pointer */
+ s++;
+ if (*s++ == 'p')
+ {
+ mask = 0x3;
+ break;
+ }
+ goto error;
+
+ case 's': /* stack pointer */
+ s++;
+ if (*s++ == 'p')
+ {
+ mask = 0x2;
+ break;
+ }
+ goto error;
+
+ case 'r': /* any register */
+ s++;
+ if (!isdigit (c = *s++))
+ {
+ goto error;
+ }
+ if (isdigit (*s))
+ {
+ if ((c = 10 * (c - '0') + (*s++ - '0')) >= 32)
+ {
+ goto error;
+ }
+ }
+ else
+ {
+ c -= '0';
+ }
+ mask = c;
+ break;
+
+ default: /* not this opcode */
+ goto error;
+ }
+ /*
+ * Got the register, now figure out where
+ * it goes in the opcode.
+ */
+ switch (*args)
+ {
+
+ case '1':
+ opcode |= mask << 11;
+ continue;
+
+ case '2':
+ opcode |= mask << 21;
+ continue;
+
+ case 'd':
+ opcode |= mask << 16;
+ continue;
+
+ }
+ break;
+
+ case 'e': /* next operand is a floating point register */
+ case 'f':
+ case 'g':
+ if (*s++ == 'f' && isdigit (*s))
+ {
+ mask = *s++;
+ if (isdigit (*s))
+ {
+ mask = 10 * (mask - '0') + (*s++ - '0');
+ if (mask >= 32)
+ {
+ break;
+ }
+ }
+ else
+ {
+ mask -= '0';
+ }
+ switch (*args)
+ {
+
+ case 'e':
+ opcode |= mask << 11;
+ continue;
+
+ case 'f':
+ opcode |= mask << 21;
+ continue;
+
+ case 'g':
+ opcode |= mask << 16;
+ if (dual_mode != DUAL_OFF)
+ opcode |= (1 << 9); /* dual mode instruction */
+ if (dual_mode == DUAL_DDOT)
+ dual_mode = DUAL_OFF;
+ if (dual_mode == DUAL_ONDDOT)
+ dual_mode = DUAL_ON;
+ if ((opcode & (1 << 10)) && (mask == ((opcode >> 11) & 0x1f)))
+ as_warn (_("Fsr1 equals fdest with Pipelining"));
+ continue;
+ }
+ }
+ break;
+
+ case 'c': /* next operand must be a control register */
+ if (strncmp (s, "fir", 3) == 0)
+ {
+ opcode |= 0x0 << 21;
+ s += 3;
+ continue;
+ }
+ if (strncmp (s, "psr", 3) == 0)
+ {
+ opcode |= 0x1 << 21;
+ s += 3;
+ continue;
+ }
+ if (strncmp (s, "dirbase", 7) == 0)
+ {
+ opcode |= 0x2 << 21;
+ s += 7;
+ continue;
+ }
+ if (strncmp (s, "db", 2) == 0)
+ {
+ opcode |= 0x3 << 21;
+ s += 2;
+ continue;
+ }
+ if (strncmp (s, "fsr", 3) == 0)
+ {
+ opcode |= 0x4 << 21;
+ s += 3;
+ continue;
+ }
+ if (strncmp (s, "epsr", 4) == 0)
+ {
+ opcode |= 0x5 << 21;
+ s += 4;
+ continue;
+ }
+ break;
+
+ case '5': /* 5 bit immediate in src1 */
+ memset (&the_insn, '\0', sizeof (the_insn));
+ if (!getExpression (s))
+ {
+ s = expr_end;
+ if (the_insn.exp.X_add_number & ~0x1f)
+ as_bad (_("5-bit immediate too large"));
+ opcode |= (the_insn.exp.X_add_number & 0x1f) << 11;
+ memset (&the_insn, '\0', sizeof (the_insn));
+ the_insn.reloc = NO_RELOC;
+ continue;
+ }
+ break;
+
+ case 'l': /* 26 bit immediate, relative branch */
+ the_insn.reloc = BRADDR;
+ the_insn.pcrel = 1;
+ goto immediate;
+
+ case 's': /* 16 bit immediate, split relative branch */
+ /* upper 5 bits of offset in dest field */
+ the_insn.pcrel = 1;
+ the_insn.reloc = SPLIT0;
+ goto immediate;
+
+ case 'S': /* 16 bit immediate, split (st), aligned */
+ if (opcode & (1 << 28))
+ if (opcode & 0x1)
+ the_insn.reloc = SPLIT2;
+ else
+ the_insn.reloc = SPLIT1;
+ else
+ the_insn.reloc = SPLIT0;
+ goto immediate;
+
+ case 'I': /* 16 bit immediate, aligned */
+ if (opcode & (1 << 28))
+ if (opcode & 0x1)
+ the_insn.reloc = LOW2;
+ else
+ the_insn.reloc = LOW1;
+ else
+ the_insn.reloc = LOW0;
+ goto immediate;
+
+ case 'i': /* 16 bit immediate */
+ the_insn.reloc = LOW0;
+
+ /*FALLTHROUGH*/
+
+ immediate:
+ if (*s == ' ')
+ s++;
+ if (strncmp (s, "ha%", 3) == 0)
+ {
+ the_insn.highlow = HIGHADJ;
+ s += 3;
+ }
+ else if (strncmp (s, "h%", 2) == 0)
+ {
+ the_insn.highlow = HIGH;
+ s += 2;
+ }
+ else if (strncmp (s, "l%", 2) == 0)
+ {
+ the_insn.highlow = PAIR;
+ s += 2;
+ }
+ the_insn.expand = insn->expand;
+
+ /* Note that if the getExpression() fails, we will still have
+ created U entries in the symbol table for the 'symbols'
+ in the input string. Try not to create U symbols for
+ registers, etc. */
+
+ if (!getExpression (s))
+ {
+ s = expr_end;
+ continue;
+ }
+ break;
+
+ default:
+ as_fatal (_("failed sanity check."));
+ }
+ break;
+ }
+ error:
+ if (match == 0)
+ {
+ /* Args don't match. */
+ if (&insn[1] - i860_opcodes < NUMOPCODES
+ && !strcmp (insn->name, insn[1].name))
+ {
+ ++insn;
+ s = argsStart;
+ continue;
+ }
+ else
+ {
+ as_bad (_("Illegal operands"));
+ return;
+ }
+ }
+ break;
+ }
+
+ the_insn.opcode = opcode;
+}
+
+static int
+getExpression (str)
+ char *str;
+{
+ char *save_in;
+ segT seg;
+
+ save_in = input_line_pointer;
+ input_line_pointer = str;
+ seg = expression (&the_insn.exp);
+ if (seg != absolute_section
+ && seg != undefined_section
+ && ! SEG_NORMAL (seg))
+ {
+ the_insn.error = _("bad segment");
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+ return 1;
+ }
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+ return 0;
+}
+
+
+/*
+ This is identical to the md_atof in m68k.c. I think this is right,
+ but I'm not sure.
+
+ Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP . An error message is returned, or NULL on OK.
+ */
+
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+char *
+md_atof (type, litP, sizeP)
+ char type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+ char *atof_ieee ();
+
+ switch (type)
+ {
+
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("Bad call to MD_ATOF()");
+ }
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+ *sizeP = prec * sizeof (LITTLENUM_TYPE);
+ for (wordP = words; prec--;)
+ {
+ md_number_to_chars (litP, (long) (*wordP++), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ return 0;
+}
+
+/*
+ * Write out big-endian.
+ */
+void
+md_number_to_chars (buf, val, n)
+ char *buf;
+ valueT val;
+ int n;
+{
+ number_to_chars_bigendian (buf, val, n);
+}
+
+void
+md_number_to_imm (buf, val, n, fixP)
+ char *buf;
+ long val;
+ int n;
+ fixS *fixP;
+{
+ enum reloc_type reloc = fixP->fx_r_type & 0xf;
+ enum highlow_type highlow = (fixP->fx_r_type >> 4) & 0x3;
+
+ assert (buf);
+ assert (n == 4); /* always on i860 */
+
+ switch (highlow)
+ {
+
+ case HIGHADJ: /* adjusts the high-order 16-bits */
+ if (val & (1 << 15))
+ val += (1 << 16);
+
+ /*FALLTHROUGH*/
+
+ case HIGH: /* selects the high-order 16-bits */
+ val >>= 16;
+ break;
+
+ case PAIR: /* selects the low-order 16-bits */
+ val = val & 0xffff;
+ break;
+
+ default:
+ break;
+ }
+
+ switch (reloc)
+ {
+
+ case BRADDR: /* br,call,bc,bc.t,bnc,bnc.t w/26-bit immediate */
+ if (fixP->fx_pcrel != 1)
+ as_bad (_("26-bit branch w/o pc relative set: 0x%08x"), val);
+ val >>= 2; /* align pcrel offset, see manual */
+
+ if (val >= (1 << 25) || val < -(1 << 25)) /* check for overflow */
+ as_bad (_("26-bit branch offset overflow: 0x%08x"), val);
+ buf[0] = (buf[0] & 0xfc) | ((val >> 24) & 0x3);
+ buf[1] = val >> 16;
+ buf[2] = val >> 8;
+ buf[3] = val;
+ break;
+
+ case SPLIT2: /* 16 bit immediate, 4-byte aligned */
+ if (val & 0x3)
+ as_bad (_("16-bit immediate 4-byte alignment error: 0x%08x"), val);
+ val &= ~0x3; /* 4-byte align value */
+ /*FALLTHROUGH*/
+ case SPLIT1: /* 16 bit immediate, 2-byte aligned */
+ if (val & 0x1)
+ as_bad (_("16-bit immediate 2-byte alignment error: 0x%08x"), val);
+ val &= ~0x1; /* 2-byte align value */
+ /*FALLTHROUGH*/
+ case SPLIT0: /* st,bla,bte,btne w/16-bit immediate */
+ if (fixP->fx_pcrel == 1)
+ val >>= 2; /* align pcrel offset, see manual */
+ /* check for bounds */
+ if (highlow != PAIR && (val >= (1 << 16) || val < -(1 << 15)))
+ as_bad (_("16-bit branch offset overflow: 0x%08x"), val);
+ buf[1] = (buf[1] & ~0x1f) | ((val >> 11) & 0x1f);
+ buf[2] = (buf[2] & ~0x7) | ((val >> 8) & 0x7);
+ buf[3] |= val; /* perserve bottom opcode bits */
+ break;
+
+ case LOW4: /* fld,pfld,pst,flush 16-byte aligned */
+ if (val & 0xf)
+ as_bad (_("16-bit immediate 16-byte alignment error: 0x%08x"), val);
+ val &= ~0xf; /* 16-byte align value */
+ /*FALLTHROUGH*/
+ case LOW3: /* fld,pfld,pst,flush 8-byte aligned */
+ if (val & 0x7)
+ as_bad (_("16-bit immediate 8-byte alignment error: 0x%08x"), val);
+ val &= ~0x7; /* 8-byte align value */
+ /*FALLTHROUGH*/
+ case LOW2: /* 16 bit immediate, 4-byte aligned */
+ if (val & 0x3)
+ as_bad (_("16-bit immediate 4-byte alignment error: 0x%08x"), val);
+ val &= ~0x3; /* 4-byte align value */
+ /*FALLTHROUGH*/
+ case LOW1: /* 16 bit immediate, 2-byte aligned */
+ if (val & 0x1)
+ as_bad (_("16-bit immediate 2-byte alignment error: 0x%08x"), val);
+ val &= ~0x1; /* 2-byte align value */
+ /*FALLTHROUGH*/
+ case LOW0: /* 16 bit immediate, byte aligned */
+ /* check for bounds */
+ if (highlow != PAIR && (val >= (1 << 16) || val < -(1 << 15)))
+ as_bad (_("16-bit immediate overflow: 0x%08x"), val);
+ buf[2] = val >> 8;
+ buf[3] |= val; /* perserve bottom opcode bits */
+ break;
+
+ case NO_RELOC:
+ default:
+ as_bad (_("bad relocation type: 0x%02x"), reloc);
+ break;
+ }
+}
+
+/* should never be called for i860 */
+void
+md_number_to_disp (buf, val, n)
+ char *buf;
+ long val;
+{
+ as_fatal (_("md_number_to_disp\n"));
+}
+
+/* should never be called for i860 */
+void
+md_number_to_field (buf, val, fix)
+ char *buf;
+ long val;
+ void *fix;
+{
+ as_fatal (_("i860_number_to_field\n"));
+}
+
+/* the bit-field entries in the relocation_info struct plays hell
+ with the byte-order problems of cross-assembly. So as a hack,
+ I added this mach. dependent ri twiddler. Ugly, but it gets
+ you there. -KWK */
+/* on i860: first 4 bytes are normal unsigned long address, next three
+ bytes are index, most sig. byte first. Byte 7 is broken up with
+ bit 7 as pcrel, bit 6 as extern, and the lower six bits as
+ relocation type (highlow 5-4). Next 4 bytes are long addend. */
+/* Thanx and a tip of the hat to Michael Bloom, mb@ttidca.tti.com */
+void
+md_ri_to_chars (ri_p, ri)
+ struct relocation_info *ri_p, ri;
+{
+#if 0
+ unsigned char the_bytes[sizeof (*ri_p)];
+
+ /* this is easy */
+ md_number_to_chars (the_bytes, ri.r_address, sizeof (ri.r_address));
+ /* now the fun stuff */
+ the_bytes[4] = (ri.r_index >> 16) & 0x0ff;
+ the_bytes[5] = (ri.r_index >> 8) & 0x0ff;
+ the_bytes[6] = ri.r_index & 0x0ff;
+ the_bytes[7] = ((ri.r_extern << 7) & 0x80) | (0 & 0x60) | (ri.r_type & 0x1F);
+ /* Also easy */
+ md_number_to_chars (&the_bytes[8], ri.r_addend, sizeof (ri.r_addend));
+ /* now put it back where you found it, Junior... */
+ memcpy ((char *) ri_p, the_bytes, sizeof (*ri_p));
+#endif
+}
+
+/* should never be called for i860 */
+void
+md_convert_frag (headers, seg, fragP)
+ object_headers *headers;
+ segT seg;
+ register fragS *fragP;
+{
+ as_fatal (_("i860_convert_frag\n"));
+}
+
+/* should never be called for i860 */
+int
+md_estimate_size_before_relax (fragP, segtype)
+ register fragS *fragP;
+ segT segtype;
+{
+ as_fatal (_("i860_estimate_size_before_relax\n"));
+}
+
+/* for debugging only, must match enum reloc_type */
+static char *Reloc[] =
+{
+ "NO_RELOC",
+ "BRADDR",
+ "LOW0",
+ "LOW1",
+ "LOW2",
+ "LOW3",
+ "LOW4",
+ "SPLIT0",
+ "SPLIT1",
+ "SPLIT2",
+ "RELOC_32",
+};
+static char *Highlow[] =
+{
+ "NO_SPEC",
+ "PAIR",
+ "HIGH",
+ "HIGHADJ",
+};
+static void
+print_insn (insn)
+ struct i860_it *insn;
+{
+ if (insn->error)
+ {
+ fprintf (stderr, "ERROR: %s\n");
+ }
+ fprintf (stderr, "opcode=0x%08x\t", insn->opcode);
+ fprintf (stderr, "expand=0x%08x\t", insn->expand);
+ fprintf (stderr, "reloc = %s\t", Reloc[insn->reloc]);
+ fprintf (stderr, "highlow = %s\n", Highlow[insn->highlow]);
+ fprintf (stderr, "exp = {\n");
+ fprintf (stderr, "\t\tX_add_symbol = %s\n",
+ insn->exp.X_add_symbol ?
+ (S_GET_NAME (insn->exp.X_add_symbol) ?
+ S_GET_NAME (insn->exp.X_add_symbol) : "???") : "0");
+ fprintf (stderr, "\t\tX_op_symbol = %s\n",
+ insn->exp.X_op_symbol ?
+ (S_GET_NAME (insn->exp.X_op_symbol) ?
+ S_GET_NAME (insn->exp.X_op_symbol) : "???") : "0");
+ fprintf (stderr, "\t\tX_add_number = %d\n",
+ insn->exp.X_add_number);
+ fprintf (stderr, "}\n");
+}
+
+CONST char *md_shortopts = "";
+struct option md_longopts[] = {
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ return 0;
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+}
+
+#ifdef comment
+/*
+ * I860 relocations are completely different, so it needs
+ * this machine dependent routine to emit them.
+ */
+void
+emit_machine_reloc (fixP, segment_address_in_file)
+ register fixS *fixP;
+ relax_addressT segment_address_in_file;
+{
+ struct reloc_info_i860 ri;
+ register symbolS *symbolP;
+ extern char *next_object_file_charP;
+ long add_number;
+
+ memset ((char *) &ri, '\0', sizeof (ri));
+ for (; fixP; fixP = fixP->fx_next)
+ {
+
+ if (fixP->fx_r_type & ~0x3f)
+ {
+ as_fatal ("fixP->fx_r_type = %d\n", fixP->fx_r_type);
+ }
+ ri.r_pcrel = fixP->fx_pcrel;
+ ri.r_type = fixP->fx_r_type;
+
+ if ((symbolP = fixP->fx_addsy) != NULL)
+ {
+ ri.r_address = fixP->fx_frag->fr_address +
+ fixP->fx_where - segment_address_in_file;
+ if (!S_IS_DEFINED (symbolP))
+ {
+ ri.r_extern = 1;
+ ri.r_symbolnum = symbolP->sy_number;
+ }
+ else
+ {
+ ri.r_extern = 0;
+ ri.r_symbolnum = S_GET_TYPE (symbolP);
+ }
+ if (symbolP && symbolP->sy_frag)
+ {
+ ri.r_addend = symbolP->sy_frag->fr_address;
+ }
+ ri.r_type = fixP->fx_r_type;
+ if (fixP->fx_pcrel)
+ {
+ /* preserve actual offset vs. pc + 4 */
+ ri.r_addend -= (ri.r_address + 4);
+ }
+ else
+ {
+ ri.r_addend = fixP->fx_addnumber;
+ }
+
+ md_ri_to_chars ((char *) &ri, ri);
+ append (&next_object_file_charP, (char *) &ri, sizeof (ri));
+ }
+ }
+}
+
+#endif /* comment */
+
+#ifdef OBJ_AOUT
+
+/* on i860: first 4 bytes are normal unsigned long address, next three
+ bytes are index, most sig. byte first. Byte 7 is broken up with
+ bit 7 as pcrel, bit 6 as extern, and the lower six bits as
+ relocation type (highlow 5-4). Next 4 bytes are long addend. */
+
+void
+tc_aout_fix_to_chars (where, fixP, segment_address_in_file)
+ char *where;
+ fixS *fixP;
+ relax_addressT segment_address_in_file;
+{
+ long r_index;
+ long r_extern;
+ long r_addend = 0;
+ long r_address;
+
+ know (fixP->fx_addsy);
+ know (!(fixP->fx_r_type & ~0x3f));
+
+ if (!S_IS_DEFINED (fixP->fx_addsy))
+ {
+ r_extern = 1;
+ r_index = fixP->fx_addsy->sy_number;
+ }
+ else
+ {
+ r_extern = 0;
+ r_index = S_GET_TYPE (fixP->fx_addsy);
+ }
+
+ md_number_to_chars (where,
+ r_address = fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file,
+ 4);
+
+ where[4] = (r_index >> 16) & 0x0ff;
+ where[5] = (r_index >> 8) & 0x0ff;
+ where[6] = r_index & 0x0ff;
+ where[7] = (((fixP->fx_pcrel << 7) & 0x80)
+ | ((r_extern << 6) & 0x40)
+ | (fixP->fx_r_type & 0x3F));
+
+ if (fixP->fx_addsy->sy_frag)
+ {
+ r_addend = fixP->fx_addsy->sy_frag->fr_address;
+ }
+
+ if (fixP->fx_pcrel)
+ {
+ /* preserve actual offset vs. pc + 4 */
+ r_addend -= (r_address + 4);
+ }
+ else
+ {
+ r_addend = fixP->fx_addnumber;
+ }
+
+ md_number_to_chars (&where[8], r_addend, 4);
+}
+
+#endif /* OBJ_AOUT */
+
+/* We have no need to default values of symbols. */
+
+/* ARGSUSED */
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ return 0;
+}
+
+/* Round up a section size to the appropriate boundary. */
+valueT
+md_section_align (segment, size)
+ segT segment;
+ valueT size;
+{
+ return size; /* Byte alignment is fine */
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the i860, they're relative to the address of the offset, plus
+ its size. (??? Is this right? FIXME-SOON!) */
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+void
+md_apply_fix (fixP, val)
+ fixS *fixP;
+ long val;
+{
+ char *place = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ /* looks to me like i860 never has bit fixes. Let's see. xoxorich. */
+ know (fixP->fx_bit_fixP == NULL);
+ if (!fixP->fx_bit_fixP)
+ {
+ fixP->fx_addnumber = val;
+ md_number_to_imm (place, val, fixP->fx_size, fixP);
+ }
+ else
+ {
+ md_number_to_field (place, val, fixP->fx_bit_fixP);
+ }
+}
+
+/*
+ * Local Variables:
+ * fill-column: 131
+ * comment-column: 0
+ * End:
+ */
+
+/* end of tc-i860.c */
diff --git a/gas/config/tc-i860.h b/gas/config/tc-i860.h
new file mode 100644
index 0000000..afb21c1
--- /dev/null
+++ b/gas/config/tc-i860.h
@@ -0,0 +1,39 @@
+/* tc-i860.h -- Header file for the I860
+ Copyright (C) 1991, 92, 95, 1998 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with GAS; see the file COPYING. If not, write to the Free Software
+ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#define TC_I860 1
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+#define WORKING_DOT_WORD
+
+#define tc_headers_hook(a) {;} /* not used */
+#define tc_crawl_symbol_chain(a) {;} /* not used */
+#define tc_aout_pre_write_hook(x) {;} /* not used */
+
+#define md_operand(x)
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of tc-i860.h */
diff --git a/gas/config/tc-i960.c b/gas/config/tc-i960.c
new file mode 100644
index 0000000..936b662
--- /dev/null
+++ b/gas/config/tc-i960.c
@@ -0,0 +1,3231 @@
+/* tc-i960.c - All the i80960-specific stuff
+ Copyright (C) 1989, 90, 91, 92, 93, 94, 95, 96, 97, 1998
+ Free Software Foundation, Inc.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+/* See comment on md_parse_option for 80960-specific invocation options. */
+
+/* There are 4 different lengths of (potentially) symbol-based displacements
+ in the 80960 instruction set, each of which could require address fix-ups
+ and (in the case of external symbols) emission of relocation directives:
+
+ 32-bit (MEMB)
+ This is a standard length for the base assembler and requires no
+ special action.
+
+ 13-bit (COBR)
+ This is a non-standard length, but the base assembler has a
+ hook for bit field address fixups: the fixS structure can
+ point to a descriptor of the field, in which case our
+ md_number_to_field() routine gets called to process it.
+
+ I made the hook a little cleaner by having fix_new() (in the base
+ assembler) return a pointer to the fixS in question. And I made it a
+ little simpler by storing the field size (in this case 13) instead of
+ of a pointer to another structure: 80960 displacements are ALWAYS
+ stored in the low-order bits of a 4-byte word.
+
+ Since the target of a COBR cannot be external, no relocation
+ directives for this size displacement have to be generated.
+ But the base assembler had to be modified to issue error
+ messages if the symbol did turn out to be external.
+
+ 24-bit (CTRL)
+ Fixups are handled as for the 13-bit case (except that 24 is stored
+ in the fixS).
+
+ The relocation directive generated is the same as that for the 32-bit
+ displacement, except that it's PC-relative (the 32-bit displacement
+ never is). The i80960 version of the linker needs a mod to
+ distinguish and handle the 24-bit case.
+
+ 12-bit (MEMA)
+ MEMA formats are always promoted to MEMB (32-bit) if the displacement
+ is based on a symbol, because it could be relocated at link time.
+ The only time we use the 12-bit format is if an absolute value of
+ less than 4096 is specified, in which case we need neither a fixup nor
+ a relocation directive. */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "as.h"
+
+#include "obstack.h"
+
+#include "opcode/i960.h"
+
+#if defined (OBJ_AOUT) || defined (OBJ_BOUT)
+
+#define TC_S_IS_SYSPROC(s) ((1<=S_GET_OTHER(s)) && (S_GET_OTHER(s)<=32))
+#define TC_S_IS_BALNAME(s) (S_GET_OTHER(s) == N_BALNAME)
+#define TC_S_IS_CALLNAME(s) (S_GET_OTHER(s) == N_CALLNAME)
+#define TC_S_IS_BADPROC(s) ((S_GET_OTHER(s) != 0) && !TC_S_IS_CALLNAME(s) && !TC_S_IS_BALNAME(s) && !TC_S_IS_SYSPROC(s))
+
+#define TC_S_SET_SYSPROC(s, p) (S_SET_OTHER((s), (p)+1))
+#define TC_S_GET_SYSPROC(s) (S_GET_OTHER(s)-1)
+
+#define TC_S_FORCE_TO_BALNAME(s) (S_SET_OTHER((s), N_BALNAME))
+#define TC_S_FORCE_TO_CALLNAME(s) (S_SET_OTHER((s), N_CALLNAME))
+#define TC_S_FORCE_TO_SYSPROC(s) {;}
+
+#else /* ! OBJ_A/BOUT */
+#ifdef OBJ_COFF
+
+#define TC_S_IS_SYSPROC(s) (S_GET_STORAGE_CLASS(s) == C_SCALL)
+#define TC_S_IS_BALNAME(s) (SF_GET_BALNAME(s))
+#define TC_S_IS_CALLNAME(s) (SF_GET_CALLNAME(s))
+#define TC_S_IS_BADPROC(s) (TC_S_IS_SYSPROC(s) && TC_S_GET_SYSPROC(s) < 0 && 31 < TC_S_GET_SYSPROC(s))
+
+#define TC_S_SET_SYSPROC(s, p) ((s)->sy_symbol.ost_auxent[1].x_sc.x_stindx = (p))
+#define TC_S_GET_SYSPROC(s) ((s)->sy_symbol.ost_auxent[1].x_sc.x_stindx)
+
+#define TC_S_FORCE_TO_BALNAME(s) (SF_SET_BALNAME(s))
+#define TC_S_FORCE_TO_CALLNAME(s) (SF_SET_CALLNAME(s))
+#define TC_S_FORCE_TO_SYSPROC(s) (S_SET_STORAGE_CLASS((s), C_SCALL))
+
+#else /* ! OBJ_COFF */
+you lose;
+#endif /* ! OBJ_COFF */
+#endif /* ! OBJ_A/BOUT */
+
+extern char *input_line_pointer;
+
+#if !defined (BFD_ASSEMBLER) && !defined (BFD)
+#ifdef OBJ_COFF
+const int md_reloc_size = sizeof (struct reloc);
+#else /* OBJ_COFF */
+const int md_reloc_size = sizeof (struct relocation_info);
+#endif /* OBJ_COFF */
+#endif
+
+/* Local i80960 routines. */
+
+static void brcnt_emit (); /* Emit branch-prediction instrumentation code */
+static char *brlab_next (); /* Return next branch local label */
+void brtab_emit (); /* Emit br-predict instrumentation table */
+static void cobr_fmt (); /* Generate COBR instruction */
+static void ctrl_fmt (); /* Generate CTRL instruction */
+static char *emit (); /* Emit (internally) binary */
+static int get_args (); /* Break arguments out of comma-separated list */
+static void get_cdisp (); /* Handle COBR or CTRL displacement */
+static char *get_ispec (); /* Find index specification string */
+static int get_regnum (); /* Translate text to register number */
+static int i_scan (); /* Lexical scan of instruction source */
+static void mem_fmt (); /* Generate MEMA or MEMB instruction */
+static void mema_to_memb (); /* Convert MEMA instruction to MEMB format */
+static void parse_expr (); /* Parse an expression */
+static int parse_ldconst (); /* Parse and replace a 'ldconst' pseudo-op */
+static void parse_memop (); /* Parse a memory operand */
+static void parse_po (); /* Parse machine-dependent pseudo-op */
+static void parse_regop (); /* Parse a register operand */
+static void reg_fmt (); /* Generate a REG format instruction */
+void reloc_callj (); /* Relocate a 'callj' instruction */
+static void relax_cobr (); /* "De-optimize" cobr into compare/branch */
+static void s_leafproc (); /* Process '.leafproc' pseudo-op */
+static void s_sysproc (); /* Process '.sysproc' pseudo-op */
+static int shift_ok (); /* Will a 'shlo' substiture for a 'ldconst'? */
+static void syntax (); /* Give syntax error */
+static int targ_has_sfr (); /* Target chip supports spec-func register? */
+static int targ_has_iclass (); /* Target chip supports instruction set? */
+
+/* See md_parse_option() for meanings of these options */
+static char norelax; /* True if -norelax switch seen */
+static char instrument_branches; /* True if -b switch seen */
+
+/* Characters that always start a comment.
+ If the pre-processor is disabled, these aren't very useful.
+ */
+const char comment_chars[] = "#";
+
+/* Characters that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output.
+
+ Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output.
+ */
+
+/* Also note that comments started like this one will always work. */
+
+const char line_comment_chars[1];
+
+const char line_separator_chars[1];
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant,
+ as in 0f12.456 or 0d1.2345e12
+ */
+const char FLT_CHARS[] = "fFdDtT";
+
+
+/* Table used by base assembler to relax addresses based on varying length
+ instructions. The fields are:
+ 1) most positive reach of this state,
+ 2) most negative reach of this state,
+ 3) how many bytes this mode will add to the size of the current frag
+ 4) which index into the table to try if we can't fit into this one.
+
+ For i80960, the only application is the (de-)optimization of cobr
+ instructions into separate compare and branch instructions when a 13-bit
+ displacement won't hack it.
+ */
+const relax_typeS md_relax_table[] =
+{
+ {0, 0, 0, 0}, /* State 0 => no more relaxation possible */
+ {4088, -4096, 0, 2}, /* State 1: conditional branch (cobr) */
+ {0x800000 - 8, -0x800000, 4, 0}, /* State 2: compare (reg) & branch (ctrl) */
+};
+
+static void s_endian PARAMS ((int));
+
+/* These are the machine dependent pseudo-ops.
+
+ This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ integer arg to pass to the function
+ */
+#define S_LEAFPROC 1
+#define S_SYSPROC 2
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"bss", s_lcomm, 1},
+ {"endian", s_endian, 0},
+ {"extended", float_cons, 't'},
+ {"leafproc", parse_po, S_LEAFPROC},
+ {"sysproc", parse_po, S_SYSPROC},
+
+ {"word", cons, 4},
+ {"quad", cons, 16},
+
+ {0, 0, 0}
+};
+
+/* Macros to extract info from an 'expressionS' structure 'e' */
+#define adds(e) e.X_add_symbol
+#define offs(e) e.X_add_number
+
+
+/* Branch-prediction bits for CTRL/COBR format opcodes */
+#define BP_MASK 0x00000002 /* Mask for branch-prediction bit */
+#define BP_TAKEN 0x00000000 /* Value to OR in to predict branch */
+#define BP_NOT_TAKEN 0x00000002 /* Value to OR in to predict no branch */
+
+
+/* Some instruction opcodes that we need explicitly */
+#define BE 0x12000000
+#define BG 0x11000000
+#define BGE 0x13000000
+#define BL 0x14000000
+#define BLE 0x16000000
+#define BNE 0x15000000
+#define BNO 0x10000000
+#define BO 0x17000000
+#define CHKBIT 0x5a002700
+#define CMPI 0x5a002080
+#define CMPO 0x5a002000
+
+#define B 0x08000000
+#define BAL 0x0b000000
+#define CALL 0x09000000
+#define CALLS 0x66003800
+#define RET 0x0a000000
+
+
+/* These masks are used to build up a set of MEMB mode bits. */
+#define A_BIT 0x0400
+#define I_BIT 0x0800
+#define MEMB_BIT 0x1000
+#define D_BIT 0x2000
+
+
+/* Mask for the only mode bit in a MEMA instruction (if set, abase reg is
+ used). */
+#define MEMA_ABASE 0x2000
+
+/* Info from which a MEMA or MEMB format instruction can be generated */
+typedef struct
+ {
+ /* (First) 32 bits of instruction */
+ long opcode;
+ /* 0-(none), 12- or, 32-bit displacement needed */
+ int disp;
+ /* The expression in the source instruction from which the
+ displacement should be determined. */
+ char *e;
+ }
+
+memS;
+
+
+/* The two pieces of info we need to generate a register operand */
+struct regop
+ {
+ int mode; /* 0 =>local/global/spec reg; 1=> literal or fp reg */
+ int special; /* 0 =>not a sfr; 1=> is a sfr (not valid w/mode=0) */
+ int n; /* Register number or literal value */
+ };
+
+
+/* Number and assembler mnemonic for all registers that can appear in
+ operands. */
+static const struct
+ {
+ char *reg_name;
+ int reg_num;
+ }
+regnames[] =
+{
+ { "pfp", 0 },
+ { "sp", 1 },
+ { "rip", 2 },
+ { "r3", 3 },
+ { "r4", 4 },
+ { "r5", 5 },
+ { "r6", 6 },
+ { "r7", 7 },
+ { "r8", 8 },
+ { "r9", 9 },
+ { "r10", 10 },
+ { "r11", 11 },
+ { "r12", 12 },
+ { "r13", 13 },
+ { "r14", 14 },
+ { "r15", 15 },
+ { "g0", 16 },
+ { "g1", 17 },
+ { "g2", 18 },
+ { "g3", 19 },
+ { "g4", 20 },
+ { "g5", 21 },
+ { "g6", 22 },
+ { "g7", 23 },
+ { "g8", 24 },
+ { "g9", 25 },
+ { "g10", 26 },
+ { "g11", 27 },
+ { "g12", 28 },
+ { "g13", 29 },
+ { "g14", 30 },
+ { "fp", 31 },
+
+ /* Numbers for special-function registers are for assembler internal
+ use only: they are scaled back to range [0-31] for binary output. */
+#define SF0 32
+
+ { "sf0", 32 },
+ { "sf1", 33 },
+ { "sf2", 34 },
+ { "sf3", 35 },
+ { "sf4", 36 },
+ { "sf5", 37 },
+ { "sf6", 38 },
+ { "sf7", 39 },
+ { "sf8", 40 },
+ { "sf9", 41 },
+ { "sf10", 42 },
+ { "sf11", 43 },
+ { "sf12", 44 },
+ { "sf13", 45 },
+ { "sf14", 46 },
+ { "sf15", 47 },
+ { "sf16", 48 },
+ { "sf17", 49 },
+ { "sf18", 50 },
+ { "sf19", 51 },
+ { "sf20", 52 },
+ { "sf21", 53 },
+ { "sf22", 54 },
+ { "sf23", 55 },
+ { "sf24", 56 },
+ { "sf25", 57 },
+ { "sf26", 58 },
+ { "sf27", 59 },
+ { "sf28", 60 },
+ { "sf29", 61 },
+ { "sf30", 62 },
+ { "sf31", 63 },
+
+ /* Numbers for floating point registers are for assembler internal
+ use only: they are scaled back to [0-3] for binary output. */
+#define FP0 64
+
+ { "fp0", 64 },
+ { "fp1", 65 },
+ { "fp2", 66 },
+ { "fp3", 67 },
+
+ { NULL, 0 }, /* END OF LIST */
+};
+
+#define IS_RG_REG(n) ((0 <= (n)) && ((n) < SF0))
+#define IS_SF_REG(n) ((SF0 <= (n)) && ((n) < FP0))
+#define IS_FP_REG(n) ((n) >= FP0)
+
+/* Number and assembler mnemonic for all registers that can appear as
+ 'abase' (indirect addressing) registers. */
+static const struct
+ {
+ char *areg_name;
+ int areg_num;
+ }
+aregs[] =
+{
+ { "(pfp)", 0 },
+ { "(sp)", 1 },
+ { "(rip)", 2 },
+ { "(r3)", 3 },
+ { "(r4)", 4 },
+ { "(r5)", 5 },
+ { "(r6)", 6 },
+ { "(r7)", 7 },
+ { "(r8)", 8 },
+ { "(r9)", 9 },
+ { "(r10)", 10 },
+ { "(r11)", 11 },
+ { "(r12)", 12 },
+ { "(r13)", 13 },
+ { "(r14)", 14 },
+ { "(r15)", 15 },
+ { "(g0)", 16 },
+ { "(g1)", 17 },
+ { "(g2)", 18 },
+ { "(g3)", 19 },
+ { "(g4)", 20 },
+ { "(g5)", 21 },
+ { "(g6)", 22 },
+ { "(g7)", 23 },
+ { "(g8)", 24 },
+ { "(g9)", 25 },
+ { "(g10)", 26 },
+ { "(g11)", 27 },
+ { "(g12)", 28 },
+ { "(g13)", 29 },
+ { "(g14)", 30 },
+ { "(fp)", 31 },
+
+#define IPREL 32
+ /* For assembler internal use only: this number never appears in binary
+ output. */
+ { "(ip)", IPREL },
+
+ { NULL, 0 }, /* END OF LIST */
+};
+
+
+/* Hash tables */
+static struct hash_control *op_hash; /* Opcode mnemonics */
+static struct hash_control *reg_hash; /* Register name hash table */
+static struct hash_control *areg_hash; /* Abase register hash table */
+
+
+/* Architecture for which we are assembling */
+#define ARCH_ANY 0 /* Default: no architecture checking done */
+#define ARCH_KA 1
+#define ARCH_KB 2
+#define ARCH_MC 3
+#define ARCH_CA 4
+#define ARCH_JX 5
+#define ARCH_HX 6
+int architecture = ARCH_ANY; /* Architecture requested on invocation line */
+int iclasses_seen; /* OR of instruction classes (I_* constants)
+ * for which we've actually assembled
+ * instructions.
+ */
+
+
+/* BRANCH-PREDICTION INSTRUMENTATION
+
+ The following supports generation of branch-prediction instrumentation
+ (turned on by -b switch). The instrumentation collects counts
+ of branches taken/not-taken for later input to a utility that will
+ set the branch prediction bits of the instructions in accordance with
+ the behavior observed. (Note that the KX series does not have
+ brach-prediction.)
+
+ The instrumentation consists of:
+
+ (1) before and after each conditional branch, a call to an external
+ routine that increments and steps over an inline counter. The
+ counter itself, initialized to 0, immediately follows the call
+ instruction. For each branch, the counter following the branch
+ is the number of times the branch was not taken, and the difference
+ between the counters is the number of times it was taken. An
+ example of an instrumented conditional branch:
+
+ call BR_CNT_FUNC
+ .word 0
+ LBRANCH23: be label
+ call BR_CNT_FUNC
+ .word 0
+
+ (2) a table of pointers to the instrumented branches, so that an
+ external postprocessing routine can locate all of the counters.
+ the table begins with a 2-word header: a pointer to the next in
+ a linked list of such tables (initialized to 0); and a count
+ of the number of entries in the table (exclusive of the header.
+
+ Note that input source code is expected to already contain calls
+ an external routine that will link the branch local table into a
+ list of such tables.
+ */
+
+/* Number of branches instrumented so far. Also used to generate
+ unique local labels for each instrumented branch. */
+static int br_cnt;
+
+#define BR_LABEL_BASE "LBRANCH"
+/* Basename of local labels on instrumented branches, to avoid
+ conflict with compiler- generated local labels. */
+
+#define BR_CNT_FUNC "__inc_branch"
+/* Name of the external routine that will increment (and step over) an
+ inline counter. */
+
+#define BR_TAB_NAME "__BRANCH_TABLE__"
+/* Name of the table of pointers to branches. A local (i.e.,
+ non-external) symbol. */
+
+/*****************************************************************************
+ md_begin: One-time initialization.
+
+ Set up hash tables.
+
+ *************************************************************************** */
+void
+md_begin ()
+{
+ int i; /* Loop counter */
+ const struct i960_opcode *oP; /* Pointer into opcode table */
+ const char *retval; /* Value returned by hash functions */
+
+ op_hash = hash_new ();
+ reg_hash = hash_new ();
+ areg_hash = hash_new ();
+
+ /* For some reason, the base assembler uses an empty string for "no
+ error message", instead of a NULL pointer. */
+ retval = 0;
+
+ for (oP = i960_opcodes; oP->name && !retval; oP++)
+ retval = hash_insert (op_hash, oP->name, (PTR) oP);
+
+ for (i = 0; regnames[i].reg_name && !retval; i++)
+ retval = hash_insert (reg_hash, regnames[i].reg_name,
+ (char *) &regnames[i].reg_num);
+
+ for (i = 0; aregs[i].areg_name && !retval; i++)
+ retval = hash_insert (areg_hash, aregs[i].areg_name,
+ (char *) &aregs[i].areg_num);
+
+ if (retval)
+ as_fatal (_("Hashing returned \"%s\"."), retval);
+}
+
+/*****************************************************************************
+ md_assemble: Assemble an instruction
+
+ Assumptions about the passed-in text:
+ - all comments, labels removed
+ - text is an instruction
+ - all white space compressed to single blanks
+ - all character constants have been replaced with decimal
+
+ *************************************************************************** */
+void
+md_assemble (textP)
+ char *textP; /* Source text of instruction */
+{
+ /* Parsed instruction text, containing NO whitespace: arg[0]->opcode
+ mnemonic arg[1-3]->operands, with char constants replaced by
+ decimal numbers. */
+ char *args[4];
+
+ int n_ops; /* Number of instruction operands */
+ /* Pointer to instruction description */
+ struct i960_opcode *oP;
+ /* TRUE iff opcode mnemonic included branch-prediction suffix (".f"
+ or ".t"). */
+ int branch_predict;
+ /* Setting of branch-prediction bit(s) to be OR'd into instruction
+ opcode of CTRL/COBR format instructions. */
+ long bp_bits;
+
+ int n; /* Offset of last character in opcode mnemonic */
+
+ const char *bp_error_msg = _("branch prediction invalid on this opcode");
+
+
+ /* Parse instruction into opcode and operands */
+ memset (args, '\0', sizeof (args));
+ n_ops = i_scan (textP, args);
+ if (n_ops == -1)
+ {
+ return; /* Error message already issued */
+ }
+
+ /* Do "macro substitution" (sort of) on 'ldconst' pseudo-instruction */
+ if (!strcmp (args[0], "ldconst"))
+ {
+ n_ops = parse_ldconst (args);
+ if (n_ops == -1)
+ {
+ return;
+ }
+ }
+
+
+
+ /* Check for branch-prediction suffix on opcode mnemonic, strip it off */
+ n = strlen (args[0]) - 1;
+ branch_predict = 0;
+ bp_bits = 0;
+ if (args[0][n - 1] == '.' && (args[0][n] == 't' || args[0][n] == 'f'))
+ {
+ /* We could check here to see if the target architecture
+ supports branch prediction, but why bother? The bit will
+ just be ignored by processors that don't use it. */
+ branch_predict = 1;
+ bp_bits = (args[0][n] == 't') ? BP_TAKEN : BP_NOT_TAKEN;
+ args[0][n - 1] = '\0'; /* Strip suffix from opcode mnemonic */
+ }
+
+ /* Look up opcode mnemonic in table and check number of operands.
+ Check that opcode is legal for the target architecture. If all
+ looks good, assemble instruction. */
+ oP = (struct i960_opcode *) hash_find (op_hash, args[0]);
+ if (!oP || !targ_has_iclass (oP->iclass))
+ {
+ as_bad (_("invalid opcode, \"%s\"."), args[0]);
+
+ }
+ else if (n_ops != oP->num_ops)
+ {
+ as_bad (_("improper number of operands. expecting %d, got %d"),
+ oP->num_ops, n_ops);
+ }
+ else
+ {
+ switch (oP->format)
+ {
+ case FBRA:
+ case CTRL:
+ ctrl_fmt (args[1], oP->opcode | bp_bits, oP->num_ops);
+ if (oP->format == FBRA)
+ {
+ /* Now generate a 'bno' to same arg */
+ ctrl_fmt (args[1], BNO | bp_bits, 1);
+ }
+ break;
+ case COBR:
+ case COJ:
+ cobr_fmt (args, oP->opcode | bp_bits, oP);
+ break;
+ case REG:
+ if (branch_predict)
+ {
+ as_warn (bp_error_msg);
+ }
+ reg_fmt (args, oP);
+ break;
+ case MEM1:
+ if (args[0][0] == 'c' && args[0][1] == 'a')
+ {
+ if (branch_predict)
+ {
+ as_warn (bp_error_msg);
+ }
+ mem_fmt (args, oP, 1);
+ break;
+ }
+ case MEM2:
+ case MEM4:
+ case MEM8:
+ case MEM12:
+ case MEM16:
+ if (branch_predict)
+ {
+ as_warn (bp_error_msg);
+ }
+ mem_fmt (args, oP, 0);
+ break;
+ case CALLJ:
+ if (branch_predict)
+ {
+ as_warn (bp_error_msg);
+ }
+ /* Output opcode & set up "fixup" (relocation); flag
+ relocation as 'callj' type. */
+ know (oP->num_ops == 1);
+ get_cdisp (args[1], "CTRL", oP->opcode, 24, 0, 1);
+ break;
+ default:
+ BAD_CASE (oP->format);
+ break;
+ }
+ }
+} /* md_assemble() */
+
+/*****************************************************************************
+ md_number_to_chars: convert a number to target byte order
+
+ *************************************************************************** */
+void
+md_number_to_chars (buf, value, n)
+ char *buf;
+ valueT value;
+ int n;
+{
+ number_to_chars_littleendian (buf, value, n);
+}
+
+/*****************************************************************************
+ md_chars_to_number: convert from target byte order to host byte order.
+
+ *************************************************************************** */
+int
+md_chars_to_number (val, n)
+ unsigned char *val; /* Value in target byte order */
+ int n; /* Number of bytes in the input */
+{
+ int retval;
+
+ for (retval = 0; n--;)
+ {
+ retval <<= 8;
+ retval |= val[n];
+ }
+ return retval;
+}
+
+
+#define MAX_LITTLENUMS 6
+#define LNUM_SIZE sizeof(LITTLENUM_TYPE)
+
+/*****************************************************************************
+ md_atof: convert ascii to floating point
+
+ Turn a string at input_line_pointer into a floating point constant of type
+ 'type', and store the appropriate bytes at *litP. The number of LITTLENUMS
+ emitted is returned at 'sizeP'. An error message is returned, or a pointer
+ to an empty message if OK.
+
+ Note we call the i386 floating point routine, rather than complicating
+ things with more files or symbolic links.
+
+ *************************************************************************** */
+char *
+md_atof (type, litP, sizeP)
+ int type;
+ char *litP;
+ int *sizeP;
+{
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ int prec;
+ char *t;
+ char *atof_ieee ();
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ prec = 4;
+ break;
+
+ case 't':
+ case 'T':
+ prec = 5;
+ type = 'x'; /* That's what atof_ieee() understands */
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("Bad call to md_atof()");
+ }
+
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ {
+ input_line_pointer = t;
+ }
+
+ *sizeP = prec * LNUM_SIZE;
+
+ /* Output the LITTLENUMs in REVERSE order in accord with i80960
+ word-order. (Dunno why atof_ieee doesn't do it in the right
+ order in the first place -- probably because it's a hack of
+ atof_m68k.) */
+
+ for (wordP = words + prec - 1; prec--;)
+ {
+ md_number_to_chars (litP, (long) (*wordP--), LNUM_SIZE);
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+
+ return 0;
+}
+
+
+/*****************************************************************************
+ md_number_to_imm
+
+ *************************************************************************** */
+void
+md_number_to_imm (buf, val, n)
+ char *buf;
+ long val;
+ int n;
+{
+ md_number_to_chars (buf, val, n);
+}
+
+
+/*****************************************************************************
+ md_number_to_disp
+
+ *************************************************************************** */
+void
+md_number_to_disp (buf, val, n)
+ char *buf;
+ long val;
+ int n;
+{
+ md_number_to_chars (buf, val, n);
+}
+
+/*****************************************************************************
+ md_number_to_field:
+
+ Stick a value (an address fixup) into a bit field of
+ previously-generated instruction.
+
+ *************************************************************************** */
+void
+md_number_to_field (instrP, val, bfixP)
+ char *instrP; /* Pointer to instruction to be fixed */
+ long val; /* Address fixup value */
+ bit_fixS *bfixP; /* Description of bit field to be fixed up */
+{
+ int numbits; /* Length of bit field to be fixed */
+ long instr; /* 32-bit instruction to be fixed-up */
+ long sign; /* 0 or -1, according to sign bit of 'val' */
+
+ /* Convert instruction back to host byte order. */
+ instr = md_chars_to_number (instrP, 4);
+
+ /* Surprise! -- we stored the number of bits to be modified rather
+ than a pointer to a structure. */
+ numbits = (int) bfixP;
+ if (numbits == 1)
+ {
+ /* This is a no-op, stuck here by reloc_callj() */
+ return;
+ }
+
+ know ((numbits == 13) || (numbits == 24));
+
+ /* Propagate sign bit of 'val' for the given number of bits. Result
+ should be all 0 or all 1. */
+ sign = val >> ((int) numbits - 1);
+ if (((val < 0) && (sign != -1))
+ || ((val > 0) && (sign != 0)))
+ {
+ as_bad (_("Fixup of %ld too large for field width of %d"),
+ val, numbits);
+ }
+ else
+ {
+ /* Put bit field into instruction and write back in target
+ * byte order.
+ */
+ val &= ~(-1 << (int) numbits); /* Clear unused sign bits */
+ instr |= val;
+ md_number_to_chars (instrP, instr, 4);
+ }
+} /* md_number_to_field() */
+
+
+/*****************************************************************************
+ md_parse_option
+ Invocation line includes a switch not recognized by the base assembler.
+ See if it's a processor-specific option. For the 960, these are:
+
+ -norelax:
+ Conditional branch instructions that require displacements
+ greater than 13 bits (or that have external targets) should
+ generate errors. The default is to replace each such
+ instruction with the corresponding compare (or chkbit) and
+ branch instructions. Note that the Intel "j" cobr directives
+ are ALWAYS "de-optimized" in this way when necessary,
+ regardless of the setting of this option.
+
+ -b:
+ Add code to collect information about branches taken, for
+ later optimization of branch prediction bits by a separate
+ tool. COBR and CNTL format instructions have branch
+ prediction bits (in the CX architecture); if "BR" represents
+ an instruction in one of these classes, the following rep-
+ resents the code generated by the assembler:
+
+ call <increment routine>
+ .word 0 # pre-counter
+ Label: BR
+ call <increment routine>
+ .word 0 # post-counter
+
+ A table of all such "Labels" is also generated.
+
+
+ -AKA, -AKB, -AKC, -ASA, -ASB, -AMC, -ACA:
+ Select the 80960 architecture. Instructions or features not
+ supported by the selected architecture cause fatal errors.
+ The default is to generate code for any instruction or feature
+ that is supported by SOME version of the 960 (even if this
+ means mixing architectures!).
+
+ ****************************************************************************/
+
+CONST char *md_shortopts = "A:b";
+struct option md_longopts[] =
+{
+#define OPTION_LINKRELAX (OPTION_MD_BASE)
+ {"linkrelax", no_argument, NULL, OPTION_LINKRELAX},
+ {"link-relax", no_argument, NULL, OPTION_LINKRELAX},
+#define OPTION_NORELAX (OPTION_MD_BASE + 1)
+ {"norelax", no_argument, NULL, OPTION_NORELAX},
+ {"no-relax", no_argument, NULL, OPTION_NORELAX},
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+struct tabentry
+ {
+ char *flag;
+ int arch;
+ };
+static const struct tabentry arch_tab[] =
+{
+ {"KA", ARCH_KA},
+ {"KB", ARCH_KB},
+ {"SA", ARCH_KA}, /* Synonym for KA */
+ {"SB", ARCH_KB}, /* Synonym for KB */
+ {"KC", ARCH_MC}, /* Synonym for MC */
+ {"MC", ARCH_MC},
+ {"CA", ARCH_CA},
+ {"JX", ARCH_JX},
+ {"HX", ARCH_HX},
+ {NULL, 0}
+};
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ switch (c)
+ {
+ case OPTION_LINKRELAX:
+ linkrelax = 1;
+ flag_keep_locals = 1;
+ break;
+
+ case OPTION_NORELAX:
+ norelax = 1;
+ break;
+
+ case 'b':
+ instrument_branches = 1;
+ break;
+
+ case 'A':
+ {
+ const struct tabentry *tp;
+ char *p = arg;
+
+ for (tp = arch_tab; tp->flag != NULL; tp++)
+ if (!strcmp (p, tp->flag))
+ break;
+
+ if (tp->flag == NULL)
+ {
+ as_bad (_("invalid architecture %s"), p);
+ return 0;
+ }
+ else
+ architecture = tp->arch;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ int i;
+ fprintf (stream, _("I960 options:\n"));
+ for (i = 0; arch_tab[i].flag; i++)
+ fprintf (stream, "%s-A%s", i ? " | " : "", arch_tab[i].flag);
+ fprintf (stream, _("\n\
+ specify variant of 960 architecture\n\
+-b add code to collect statistics about branches taken\n\
+-link-relax preserve individual alignment directives so linker\n\
+ can do relaxing (b.out format only)\n\
+-no-relax don't alter compare-and-branch instructions for\n\
+ long displacements\n"));
+}
+
+
+#ifndef BFD_ASSEMBLER
+/*****************************************************************************
+ md_convert_frag:
+ Called by base assembler after address relaxation is finished: modify
+ variable fragments according to how much relaxation was done.
+
+ If the fragment substate is still 1, a 13-bit displacement was enough
+ to reach the symbol in question. Set up an address fixup, but otherwise
+ leave the cobr instruction alone.
+
+ If the fragment substate is 2, a 13-bit displacement was not enough.
+ Replace the cobr with a two instructions (a compare and a branch).
+
+ *************************************************************************** */
+void
+md_convert_frag (headers, seg, fragP)
+ object_headers *headers;
+ segT seg;
+ fragS *fragP;
+{
+ fixS *fixP; /* Structure describing needed address fix */
+
+ switch (fragP->fr_subtype)
+ {
+ case 1:
+ /* LEAVE SINGLE COBR INSTRUCTION */
+ fixP = fix_new (fragP,
+ fragP->fr_opcode - fragP->fr_literal,
+ 4,
+ fragP->fr_symbol,
+ fragP->fr_offset,
+ 1,
+ NO_RELOC);
+
+ fixP->fx_bit_fixP = (bit_fixS *) 13; /* size of bit field */
+ break;
+ case 2:
+ /* REPLACE COBR WITH COMPARE/BRANCH INSTRUCTIONS */
+ relax_cobr (fragP);
+ break;
+ default:
+ BAD_CASE (fragP->fr_subtype);
+ break;
+ }
+}
+
+/*****************************************************************************
+ md_estimate_size_before_relax: How much does it look like *fragP will grow?
+
+ Called by base assembler just before address relaxation.
+ Return the amount by which the fragment will grow.
+
+ Any symbol that is now undefined will not become defined; cobr's
+ based on undefined symbols will have to be replaced with a compare
+ instruction and a branch instruction, and the code fragment will grow
+ by 4 bytes.
+
+ *************************************************************************** */
+int
+md_estimate_size_before_relax (fragP, segment_type)
+ register fragS *fragP;
+ register segT segment_type;
+{
+ /* If symbol is undefined in this segment, go to "relaxed" state
+ (compare and branch instructions instead of cobr) right now. */
+ if (S_GET_SEGMENT (fragP->fr_symbol) != segment_type)
+ {
+ relax_cobr (fragP);
+ return 4;
+ }
+ return 0;
+} /* md_estimate_size_before_relax() */
+
+
+/*****************************************************************************
+ md_ri_to_chars:
+ This routine exists in order to overcome machine byte-order problems
+ when dealing with bit-field entries in the relocation_info struct.
+
+ But relocation info will be used on the host machine only (only
+ executable code is actually downloaded to the i80960). Therefore,
+ we leave it in host byte order.
+
+ The above comment is no longer true. This routine now really
+ does do the reordering (Ian Taylor 28 Aug 92).
+
+ *************************************************************************** */
+void
+md_ri_to_chars (where, ri)
+ char *where;
+ struct relocation_info *ri;
+{
+ md_number_to_chars (where, ri->r_address,
+ sizeof (ri->r_address));
+ where[4] = ri->r_index & 0x0ff;
+ where[5] = (ri->r_index >> 8) & 0x0ff;
+ where[6] = (ri->r_index >> 16) & 0x0ff;
+ where[7] = ((ri->r_pcrel << 0)
+ | (ri->r_length << 1)
+ | (ri->r_extern << 3)
+ | (ri->r_bsr << 4)
+ | (ri->r_disp << 5)
+ | (ri->r_callj << 6));
+}
+
+#endif /* BFD_ASSEMBLER */
+
+/* FOLLOWING ARE THE LOCAL ROUTINES, IN ALPHABETICAL ORDER */
+
+/*****************************************************************************
+ brcnt_emit: Emit code to increment inline branch counter.
+
+ See the comments above the declaration of 'br_cnt' for details on
+ branch-prediction instrumentation.
+ *************************************************************************** */
+static void
+brcnt_emit ()
+{
+ ctrl_fmt (BR_CNT_FUNC, CALL, 1); /* Emit call to "increment" routine */
+ emit (0); /* Emit inline counter to be incremented */
+}
+
+/*****************************************************************************
+ brlab_next: generate the next branch local label
+
+ See the comments above the declaration of 'br_cnt' for details on
+ branch-prediction instrumentation.
+ *************************************************************************** */
+static char *
+brlab_next ()
+{
+ static char buf[20];
+
+ sprintf (buf, "%s%d", BR_LABEL_BASE, br_cnt++);
+ return buf;
+}
+
+/*****************************************************************************
+ brtab_emit: generate the fetch-prediction branch table.
+
+ See the comments above the declaration of 'br_cnt' for details on
+ branch-prediction instrumentation.
+
+ The code emitted here would be functionally equivalent to the following
+ example assembler source.
+
+ .data
+ .align 2
+ BR_TAB_NAME:
+ .word 0 # link to next table
+ .word 3 # length of table
+ .word LBRANCH0 # 1st entry in table proper
+ .word LBRANCH1
+ .word LBRANCH2
+ **************************************************************************** */
+void
+brtab_emit ()
+{
+ int i;
+ char buf[20];
+ char *p; /* Where the binary was output to */
+ /* Pointer to description of deferred address fixup. */
+ fixS *fixP;
+
+ if (!instrument_branches)
+ {
+ return;
+ }
+
+ subseg_set (data_section, 0); /* .data */
+ frag_align (2, 0, 0); /* .align 2 */
+ record_alignment (now_seg, 2);
+ colon (BR_TAB_NAME); /* BR_TAB_NAME: */
+ emit (0); /* .word 0 #link to next table */
+ emit (br_cnt); /* .word n #length of table */
+
+ for (i = 0; i < br_cnt; i++)
+ {
+ sprintf (buf, "%s%d", BR_LABEL_BASE, i);
+ p = emit (0);
+ fixP = fix_new (frag_now,
+ p - frag_now->fr_literal,
+ 4,
+ symbol_find (buf),
+ 0,
+ 0,
+ NO_RELOC);
+ }
+}
+
+/*****************************************************************************
+ cobr_fmt: generate a COBR-format instruction
+
+ *************************************************************************** */
+static
+void
+cobr_fmt (arg, opcode, oP)
+ /* arg[0]->opcode mnemonic, arg[1-3]->operands (ascii) */
+ char *arg[];
+ /* Opcode, with branch-prediction bits already set if necessary. */
+ long opcode;
+ /* Pointer to description of instruction. */
+ struct i960_opcode *oP;
+{
+ long instr; /* 32-bit instruction */
+ struct regop regop; /* Description of register operand */
+ int n; /* Number of operands */
+ int var_frag; /* 1 if varying length code fragment should
+ * be emitted; 0 if an address fix
+ * should be emitted.
+ */
+
+ instr = opcode;
+ n = oP->num_ops;
+
+ if (n >= 1)
+ {
+ /* First operand (if any) of a COBR is always a register
+ operand. Parse it. */
+ parse_regop (&regop, arg[1], oP->operand[0]);
+ instr |= (regop.n << 19) | (regop.mode << 13);
+ }
+ if (n >= 2)
+ {
+ /* Second operand (if any) of a COBR is always a register
+ operand. Parse it. */
+ parse_regop (&regop, arg[2], oP->operand[1]);
+ instr |= (regop.n << 14) | regop.special;
+ }
+
+
+ if (n < 3)
+ {
+ emit (instr);
+
+ }
+ else
+ {
+ if (instrument_branches)
+ {
+ brcnt_emit ();
+ colon (brlab_next ());
+ }
+
+ /* A third operand to a COBR is always a displacement. Parse
+ it; if it's relaxable (a cobr "j" directive, or any cobr
+ other than bbs/bbc when the "-norelax" option is not in use)
+ set up a variable code fragment; otherwise set up an address
+ fix. */
+ var_frag = !norelax || (oP->format == COJ); /* TRUE or FALSE */
+ get_cdisp (arg[3], "COBR", instr, 13, var_frag, 0);
+
+ if (instrument_branches)
+ {
+ brcnt_emit ();
+ }
+ }
+} /* cobr_fmt() */
+
+
+/*****************************************************************************
+ ctrl_fmt: generate a CTRL-format instruction
+
+ *************************************************************************** */
+static
+void
+ctrl_fmt (targP, opcode, num_ops)
+ char *targP; /* Pointer to text of lone operand (if any) */
+ long opcode; /* Template of instruction */
+ int num_ops; /* Number of operands */
+{
+ int instrument; /* TRUE iff we should add instrumentation to track
+ * how often the branch is taken
+ */
+
+
+ if (num_ops == 0)
+ {
+ emit (opcode); /* Output opcode */
+ }
+ else
+ {
+
+ instrument = instrument_branches && (opcode != CALL)
+ && (opcode != B) && (opcode != RET) && (opcode != BAL);
+
+ if (instrument)
+ {
+ brcnt_emit ();
+ colon (brlab_next ());
+ }
+
+ /* The operand MUST be an ip-relative displacment. Parse it
+ * and set up address fix for the instruction we just output.
+ */
+ get_cdisp (targP, "CTRL", opcode, 24, 0, 0);
+
+ if (instrument)
+ {
+ brcnt_emit ();
+ }
+ }
+
+}
+
+
+/*****************************************************************************
+ emit: output instruction binary
+
+ Output instruction binary, in target byte order, 4 bytes at a time.
+ Return pointer to where it was placed.
+
+ *************************************************************************** */
+static
+char *
+emit (instr)
+ long instr; /* Word to be output, host byte order */
+{
+ char *toP; /* Where to output it */
+
+ toP = frag_more (4); /* Allocate storage */
+ md_number_to_chars (toP, instr, 4); /* Convert to target byte order */
+ return toP;
+}
+
+
+/*****************************************************************************
+ get_args: break individual arguments out of comma-separated list
+
+ Input assumptions:
+ - all comments and labels have been removed
+ - all strings of whitespace have been collapsed to a single blank.
+ - all character constants ('x') have been replaced with decimal
+
+ Output:
+ args[0] is untouched. args[1] points to first operand, etc. All args:
+ - are NULL-terminated
+ - contain no whitespace
+
+ Return value:
+ Number of operands (0,1,2, or 3) or -1 on error.
+
+ *************************************************************************** */
+static int
+get_args (p, args)
+ /* Pointer to comma-separated operands; MUCKED BY US */
+ register char *p;
+ /* Output arg: pointers to operands placed in args[1-3]. MUST
+ ACCOMMODATE 4 ENTRIES (args[0-3]). */
+ char *args[];
+{
+ register int n; /* Number of operands */
+ register char *to;
+
+ /* Skip lead white space */
+ while (*p == ' ')
+ {
+ p++;
+ }
+
+ if (*p == '\0')
+ {
+ return 0;
+ }
+
+ n = 1;
+ args[1] = p;
+
+ /* Squeze blanks out by moving non-blanks toward start of string.
+ * Isolate operands, whenever comma is found.
+ */
+ to = p;
+ while (*p != '\0')
+ {
+
+ if (*p == ' '
+ && (! isalnum ((unsigned char) p[1])
+ || ! isalnum ((unsigned char) p[-1])))
+ {
+ p++;
+
+ }
+ else if (*p == ',')
+ {
+
+ /* Start of operand */
+ if (n == 3)
+ {
+ as_bad (_("too many operands"));
+ return -1;
+ }
+ *to++ = '\0'; /* Terminate argument */
+ args[++n] = to; /* Start next argument */
+ p++;
+
+ }
+ else
+ {
+ *to++ = *p++;
+ }
+ }
+ *to = '\0';
+ return n;
+}
+
+
+/*****************************************************************************
+ get_cdisp: handle displacement for a COBR or CTRL instruction.
+
+ Parse displacement for a COBR or CTRL instruction.
+
+ If successful, output the instruction opcode and set up for it,
+ depending on the arg 'var_frag', either:
+ o an address fixup to be done when all symbol values are known, or
+ o a varying length code fragment, with address fixup info. This
+ will be done for cobr instructions that may have to be relaxed
+ in to compare/branch instructions (8 bytes) if the final
+ address displacement is greater than 13 bits.
+
+ ****************************************************************************/
+static
+void
+get_cdisp (dispP, ifmtP, instr, numbits, var_frag, callj)
+ /* displacement as specified in source instruction */
+ char *dispP;
+ /* "COBR" or "CTRL" (for use in error message) */
+ char *ifmtP;
+ /* Instruction needing the displacement */
+ long instr;
+ /* # bits of displacement (13 for COBR, 24 for CTRL) */
+ int numbits;
+ /* 1 if varying length code fragment should be emitted;
+ * 0 if an address fix should be emitted.
+ */
+ int var_frag;
+ /* 1 if callj relocation should be done; else 0 */
+ int callj;
+{
+ expressionS e; /* Parsed expression */
+ fixS *fixP; /* Structure describing needed address fix */
+ char *outP; /* Where instruction binary is output to */
+
+ fixP = NULL;
+
+ parse_expr (dispP, &e);
+ switch (e.X_op)
+ {
+ case O_illegal:
+ as_bad (_("expression syntax error"));
+
+ case O_symbol:
+ if (S_GET_SEGMENT (e.X_add_symbol) == now_seg
+ || S_GET_SEGMENT (e.X_add_symbol) == undefined_section)
+ {
+ if (var_frag)
+ {
+ outP = frag_more (8); /* Allocate worst-case storage */
+ md_number_to_chars (outP, instr, 4);
+ frag_variant (rs_machine_dependent, 4, 4, 1,
+ adds (e), offs (e), outP);
+ }
+ else
+ {
+ /* Set up a new fix structure, so address can be updated
+ * when all symbol values are known.
+ */
+ outP = emit (instr);
+ fixP = fix_new (frag_now,
+ outP - frag_now->fr_literal,
+ 4,
+ adds (e),
+ offs (e),
+ 1,
+ NO_RELOC);
+
+ fixP->fx_tcbit = callj;
+
+ /* We want to modify a bit field when the address is
+ * known. But we don't need all the garbage in the
+ * bit_fix structure. So we're going to lie and store
+ * the number of bits affected instead of a pointer.
+ */
+ fixP->fx_bit_fixP = (bit_fixS *) numbits;
+ }
+ }
+ else
+ as_bad (_("attempt to branch into different segment"));
+ break;
+
+ default:
+ as_bad (_("target of %s instruction must be a label"), ifmtP);
+ break;
+ }
+}
+
+
+/*****************************************************************************
+ get_ispec: parse a memory operand for an index specification
+
+ Here, an "index specification" is taken to be anything surrounded
+ by square brackets and NOT followed by anything else.
+
+ If it's found, detach it from the input string, remove the surrounding
+ square brackets, and return a pointer to it. Otherwise, return NULL.
+
+ *************************************************************************** */
+static
+char *
+get_ispec (textP)
+ /* Pointer to memory operand from source instruction, no white space. */
+ char *textP;
+{
+ /* Points to start of index specification. */
+ char *start;
+ /* Points to end of index specification. */
+ char *end;
+
+ /* Find opening square bracket, if any. */
+ start = strchr (textP, '[');
+
+ if (start != NULL)
+ {
+
+ /* Eliminate '[', detach from rest of operand */
+ *start++ = '\0';
+
+ end = strchr (start, ']');
+
+ if (end == NULL)
+ {
+ as_bad (_("unmatched '['"));
+
+ }
+ else
+ {
+ /* Eliminate ']' and make sure it was the last thing
+ * in the string.
+ */
+ *end = '\0';
+ if (*(end + 1) != '\0')
+ {
+ as_bad (_("garbage after index spec ignored"));
+ }
+ }
+ }
+ return start;
+}
+
+/*****************************************************************************
+ get_regnum:
+
+ Look up a (suspected) register name in the register table and return the
+ associated register number (or -1 if not found).
+
+ *************************************************************************** */
+static
+int
+get_regnum (regname)
+ char *regname; /* Suspected register name */
+{
+ int *rP;
+
+ rP = (int *) hash_find (reg_hash, regname);
+ return (rP == NULL) ? -1 : *rP;
+}
+
+
+/*****************************************************************************
+ i_scan: perform lexical scan of ascii assembler instruction.
+
+ Input assumptions:
+ - input string is an i80960 instruction (not a pseudo-op)
+ - all comments and labels have been removed
+ - all strings of whitespace have been collapsed to a single blank.
+
+ Output:
+ args[0] points to opcode, other entries point to operands. All strings:
+ - are NULL-terminated
+ - contain no whitespace
+ - have character constants ('x') replaced with a decimal number
+
+ Return value:
+ Number of operands (0,1,2, or 3) or -1 on error.
+
+ *************************************************************************** */
+static int
+i_scan (iP, args)
+ /* Pointer to ascii instruction; MUCKED BY US. */
+ register char *iP;
+ /* Output arg: pointers to opcode and operands placed here. MUST
+ ACCOMMODATE 4 ENTRIES. */
+ char *args[];
+{
+
+ /* Isolate opcode */
+ if (*(iP) == ' ')
+ {
+ iP++;
+ } /* Skip lead space, if any */
+ args[0] = iP;
+ for (; *iP != ' '; iP++)
+ {
+ if (*iP == '\0')
+ {
+ /* There are no operands */
+ if (args[0] == iP)
+ {
+ /* We never moved: there was no opcode either! */
+ as_bad (_("missing opcode"));
+ return -1;
+ }
+ return 0;
+ }
+ }
+ *iP++ = '\0'; /* Terminate opcode */
+ return (get_args (iP, args));
+} /* i_scan() */
+
+
+/*****************************************************************************
+ mem_fmt: generate a MEMA- or MEMB-format instruction
+
+ *************************************************************************** */
+static void
+mem_fmt (args, oP, callx)
+ char *args[]; /* args[0]->opcode mnemonic, args[1-3]->operands */
+ struct i960_opcode *oP; /* Pointer to description of instruction */
+ int callx; /* Is this a callx opcode */
+{
+ int i; /* Loop counter */
+ struct regop regop; /* Description of register operand */
+ char opdesc; /* Operand descriptor byte */
+ memS instr; /* Description of binary to be output */
+ char *outP; /* Where the binary was output to */
+ expressionS expr; /* Parsed expression */
+ /* ->description of deferred address fixup */
+ fixS *fixP;
+
+#ifdef OBJ_COFF
+ /* COFF support isn't in place yet for callx relaxing. */
+ callx = 0;
+#endif
+
+ memset (&instr, '\0', sizeof (memS));
+ instr.opcode = oP->opcode;
+
+ /* Process operands. */
+ for (i = 1; i <= oP->num_ops; i++)
+ {
+ opdesc = oP->operand[i - 1];
+
+ if (MEMOP (opdesc))
+ {
+ parse_memop (&instr, args[i], oP->format);
+ }
+ else
+ {
+ parse_regop (&regop, args[i], opdesc);
+ instr.opcode |= regop.n << 19;
+ }
+ }
+
+ /* Parse the displacement; this must be done before emitting the
+ opcode, in case it is an expression using `.'. */
+ parse_expr (instr.e, &expr);
+
+ /* Output opcode */
+ outP = emit (instr.opcode);
+
+ if (instr.disp == 0)
+ {
+ return;
+ }
+
+ /* Process the displacement */
+ switch (expr.X_op)
+ {
+ case O_illegal:
+ as_bad (_("expression syntax error"));
+ break;
+
+ case O_constant:
+ if (instr.disp == 32)
+ {
+ (void) emit (offs (expr)); /* Output displacement */
+ }
+ else
+ {
+ /* 12-bit displacement */
+ if (offs (expr) & ~0xfff)
+ {
+ /* Won't fit in 12 bits: convert already-output
+ * instruction to MEMB format, output
+ * displacement.
+ */
+ mema_to_memb (outP);
+ (void) emit (offs (expr));
+ }
+ else
+ {
+ /* WILL fit in 12 bits: OR into opcode and
+ * overwrite the binary we already put out
+ */
+ instr.opcode |= offs (expr);
+ md_number_to_chars (outP, instr.opcode, 4);
+ }
+ }
+ break;
+
+ default:
+ if (instr.disp == 12)
+ {
+ /* Displacement is dependent on a symbol, whose value
+ * may change at link time. We HAVE to reserve 32 bits.
+ * Convert already-output opcode to MEMB format.
+ */
+ mema_to_memb (outP);
+ }
+
+ /* Output 0 displacement and set up address fixup for when
+ * this symbol's value becomes known.
+ */
+ outP = emit ((long) 0);
+ fixP = fix_new_exp (frag_now,
+ outP - frag_now->fr_literal,
+ 4,
+ &expr,
+ 0,
+ NO_RELOC);
+ /* Steve's linker relaxing hack. Mark this 32-bit relocation as
+ being in the instruction stream, specifically as part of a callx
+ instruction. */
+ fixP->fx_bsr = callx;
+ break;
+ }
+} /* memfmt() */
+
+
+/*****************************************************************************
+ mema_to_memb: convert a MEMA-format opcode to a MEMB-format opcode.
+
+ There are 2 possible MEMA formats:
+ - displacement only
+ - displacement + abase
+
+ They are distinguished by the setting of the MEMA_ABASE bit.
+
+ *************************************************************************** */
+static void
+mema_to_memb (opcodeP)
+ char *opcodeP; /* Where to find the opcode, in target byte order */
+{
+ long opcode; /* Opcode in host byte order */
+ long mode; /* Mode bits for MEMB instruction */
+
+ opcode = md_chars_to_number (opcodeP, 4);
+ know (!(opcode & MEMB_BIT));
+
+ mode = MEMB_BIT | D_BIT;
+ if (opcode & MEMA_ABASE)
+ {
+ mode |= A_BIT;
+ }
+
+ opcode &= 0xffffc000; /* Clear MEMA offset and mode bits */
+ opcode |= mode; /* Set MEMB mode bits */
+
+ md_number_to_chars (opcodeP, opcode, 4);
+} /* mema_to_memb() */
+
+
+/*****************************************************************************
+ parse_expr: parse an expression
+
+ Use base assembler's expression parser to parse an expression.
+ It, unfortunately, runs off a global which we have to save/restore
+ in order to make it work for us.
+
+ An empty expression string is treated as an absolute 0.
+
+ Sets O_illegal regardless of expression evaluation if entire input
+ string is not consumed in the evaluation -- tolerate no dangling junk!
+
+ *************************************************************************** */
+static void
+parse_expr (textP, expP)
+ char *textP; /* Text of expression to be parsed */
+ expressionS *expP; /* Where to put the results of parsing */
+{
+ char *save_in; /* Save global here */
+ symbolS *symP;
+
+ know (textP);
+
+ if (*textP == '\0')
+ {
+ /* Treat empty string as absolute 0 */
+ expP->X_add_symbol = expP->X_op_symbol = NULL;
+ expP->X_add_number = 0;
+ expP->X_op = O_constant;
+ }
+ else
+ {
+ save_in = input_line_pointer; /* Save global */
+ input_line_pointer = textP; /* Make parser work for us */
+
+ (void) expression (expP);
+ if ((size_t) (input_line_pointer - textP) != strlen (textP))
+ {
+ /* Did not consume all of the input */
+ expP->X_op = O_illegal;
+ }
+ symP = expP->X_add_symbol;
+ if (symP && (hash_find (reg_hash, S_GET_NAME (symP))))
+ {
+ /* Register name in an expression */
+ /* FIXME: this isn't much of a check any more. */
+ expP->X_op = O_illegal;
+ }
+
+ input_line_pointer = save_in; /* Restore global */
+ }
+}
+
+
+/*****************************************************************************
+ parse_ldcont:
+ Parse and replace a 'ldconst' pseudo-instruction with an appropriate
+ i80960 instruction.
+
+ Assumes the input consists of:
+ arg[0] opcode mnemonic ('ldconst')
+ arg[1] first operand (constant)
+ arg[2] name of register to be loaded
+
+ Replaces opcode and/or operands as appropriate.
+
+ Returns the new number of arguments, or -1 on failure.
+
+ *************************************************************************** */
+static
+int
+parse_ldconst (arg)
+ char *arg[]; /* See above */
+{
+ int n; /* Constant to be loaded */
+ int shift; /* Shift count for "shlo" instruction */
+ static char buf[5]; /* Literal for first operand */
+ static char buf2[5]; /* Literal for second operand */
+ expressionS e; /* Parsed expression */
+
+
+ arg[3] = NULL; /* So we can tell at the end if it got used or not */
+
+ parse_expr (arg[1], &e);
+ switch (e.X_op)
+ {
+ default:
+ /* We're dependent on one or more symbols -- use "lda" */
+ arg[0] = "lda";
+ break;
+
+ case O_constant:
+ /* Try the following mappings:
+ * ldconst 0,<reg> ->mov 0,<reg>
+ * ldconst 31,<reg> ->mov 31,<reg>
+ * ldconst 32,<reg> ->addo 1,31,<reg>
+ * ldconst 62,<reg> ->addo 31,31,<reg>
+ * ldconst 64,<reg> ->shlo 8,3,<reg>
+ * ldconst -1,<reg> ->subo 1,0,<reg>
+ * ldconst -31,<reg>->subo 31,0,<reg>
+ *
+ * anthing else becomes:
+ * lda xxx,<reg>
+ */
+ n = offs (e);
+ if ((0 <= n) && (n <= 31))
+ {
+ arg[0] = "mov";
+
+ }
+ else if ((-31 <= n) && (n <= -1))
+ {
+ arg[0] = "subo";
+ arg[3] = arg[2];
+ sprintf (buf, "%d", -n);
+ arg[1] = buf;
+ arg[2] = "0";
+
+ }
+ else if ((32 <= n) && (n <= 62))
+ {
+ arg[0] = "addo";
+ arg[3] = arg[2];
+ arg[1] = "31";
+ sprintf (buf, "%d", n - 31);
+ arg[2] = buf;
+
+ }
+ else if ((shift = shift_ok (n)) != 0)
+ {
+ arg[0] = "shlo";
+ arg[3] = arg[2];
+ sprintf (buf, "%d", shift);
+ arg[1] = buf;
+ sprintf (buf2, "%d", n >> shift);
+ arg[2] = buf2;
+
+ }
+ else
+ {
+ arg[0] = "lda";
+ }
+ break;
+
+ case O_illegal:
+ as_bad (_("invalid constant"));
+ return -1;
+ break;
+ }
+ return (arg[3] == 0) ? 2 : 3;
+}
+
+/*****************************************************************************
+ parse_memop: parse a memory operand
+
+ This routine is based on the observation that the 4 mode bits of the
+ MEMB format, taken individually, have fairly consistent meaning:
+
+ M3 (bit 13): 1 if displacement is present (D_BIT)
+ M2 (bit 12): 1 for MEMB instructions (MEMB_BIT)
+ M1 (bit 11): 1 if index is present (I_BIT)
+ M0 (bit 10): 1 if abase is present (A_BIT)
+
+ So we parse the memory operand and set bits in the mode as we find
+ things. Then at the end, if we go to MEMB format, we need only set
+ the MEMB bit (M2) and our mode is built for us.
+
+ Unfortunately, I said "fairly consistent". The exceptions:
+
+ DBIA
+ 0100 Would seem illegal, but means "abase-only".
+
+ 0101 Would seem to mean "abase-only" -- it means IP-relative.
+ Must be converted to 0100.
+
+ 0110 Would seem to mean "index-only", but is reserved.
+ We turn on the D bit and provide a 0 displacement.
+
+ The other thing to observe is that we parse from the right, peeling
+ things * off as we go: first any index spec, then any abase, then
+ the displacement.
+
+ *************************************************************************** */
+static
+void
+parse_memop (memP, argP, optype)
+ memS *memP; /* Where to put the results */
+ char *argP; /* Text of the operand to be parsed */
+ int optype; /* MEM1, MEM2, MEM4, MEM8, MEM12, or MEM16 */
+{
+ char *indexP; /* Pointer to index specification with "[]" removed */
+ char *p; /* Temp char pointer */
+ char iprel_flag; /* True if this is an IP-relative operand */
+ int regnum; /* Register number */
+ /* Scale factor: 1,2,4,8, or 16. Later converted to internal format
+ (0,1,2,3,4 respectively). */
+ int scale;
+ int mode; /* MEMB mode bits */
+ int *intP; /* Pointer to register number */
+
+ /* The following table contains the default scale factors for each
+ type of memory instruction. It is accessed using (optype-MEM1)
+ as an index -- thus it assumes the 'optype' constants are
+ assigned consecutive values, in the order they appear in this
+ table. */
+ static const int def_scale[] =
+ {
+ 1, /* MEM1 */
+ 2, /* MEM2 */
+ 4, /* MEM4 */
+ 8, /* MEM8 */
+ -1, /* MEM12 -- no valid default */
+ 16 /* MEM16 */
+ };
+
+
+ iprel_flag = mode = 0;
+
+ /* Any index present? */
+ indexP = get_ispec (argP);
+ if (indexP)
+ {
+ p = strchr (indexP, '*');
+ if (p == NULL)
+ {
+ /* No explicit scale -- use default for this instruction
+ type and assembler mode. */
+ if (flag_mri)
+ scale = 1;
+ else
+ /* GNU960 compatibility */
+ scale = def_scale[optype - MEM1];
+ }
+ else
+ {
+ *p++ = '\0'; /* Eliminate '*' */
+
+ /* Now indexP->a '\0'-terminated register name,
+ * and p->a scale factor.
+ */
+
+ if (!strcmp (p, "16"))
+ {
+ scale = 16;
+ }
+ else if (strchr ("1248", *p) && (p[1] == '\0'))
+ {
+ scale = *p - '0';
+ }
+ else
+ {
+ scale = -1;
+ }
+ }
+
+ regnum = get_regnum (indexP); /* Get index reg. # */
+ if (!IS_RG_REG (regnum))
+ {
+ as_bad (_("invalid index register"));
+ return;
+ }
+
+ /* Convert scale to its binary encoding */
+ switch (scale)
+ {
+ case 1:
+ scale = 0 << 7;
+ break;
+ case 2:
+ scale = 1 << 7;
+ break;
+ case 4:
+ scale = 2 << 7;
+ break;
+ case 8:
+ scale = 3 << 7;
+ break;
+ case 16:
+ scale = 4 << 7;
+ break;
+ default:
+ as_bad (_("invalid scale factor"));
+ return;
+ };
+
+ memP->opcode |= scale | regnum; /* Set index bits in opcode */
+ mode |= I_BIT; /* Found a valid index spec */
+ }
+
+ /* Any abase (Register Indirect) specification present? */
+ if ((p = strrchr (argP, '(')) != NULL)
+ {
+ /* "(" is there -- does it start a legal abase spec? If not, it
+ could be part of a displacement expression. */
+ intP = (int *) hash_find (areg_hash, p);
+ if (intP != NULL)
+ {
+ /* Got an abase here */
+ regnum = *intP;
+ *p = '\0'; /* discard register spec */
+ if (regnum == IPREL)
+ {
+ /* We have to specialcase ip-rel mode */
+ iprel_flag = 1;
+ }
+ else
+ {
+ memP->opcode |= regnum << 14;
+ mode |= A_BIT;
+ }
+ }
+ }
+
+ /* Any expression present? */
+ memP->e = argP;
+ if (*argP != '\0')
+ {
+ mode |= D_BIT;
+ }
+
+ /* Special-case ip-relative addressing */
+ if (iprel_flag)
+ {
+ if (mode & I_BIT)
+ {
+ syntax ();
+ }
+ else
+ {
+ memP->opcode |= 5 << 10; /* IP-relative mode */
+ memP->disp = 32;
+ }
+ return;
+ }
+
+ /* Handle all other modes */
+ switch (mode)
+ {
+ case D_BIT | A_BIT:
+ /* Go with MEMA instruction format for now (grow to MEMB later
+ if 12 bits is not enough for the displacement). MEMA format
+ has a single mode bit: set it to indicate that abase is
+ present. */
+ memP->opcode |= MEMA_ABASE;
+ memP->disp = 12;
+ break;
+
+ case D_BIT:
+ /* Go with MEMA instruction format for now (grow to MEMB later
+ if 12 bits is not enough for the displacement). */
+ memP->disp = 12;
+ break;
+
+ case A_BIT:
+ /* For some reason, the bit string for this mode is not
+ consistent: it should be 0 (exclusive of the MEMB bit), so we
+ set it "by hand" here. */
+ memP->opcode |= MEMB_BIT;
+ break;
+
+ case A_BIT | I_BIT:
+ /* set MEMB bit in mode, and OR in mode bits */
+ memP->opcode |= mode | MEMB_BIT;
+ break;
+
+ case I_BIT:
+ /* Treat missing displacement as displacement of 0. */
+ mode |= D_BIT;
+ /* Fall into next case. */
+ case D_BIT | A_BIT | I_BIT:
+ case D_BIT | I_BIT:
+ /* set MEMB bit in mode, and OR in mode bits */
+ memP->opcode |= mode | MEMB_BIT;
+ memP->disp = 32;
+ break;
+
+ default:
+ syntax ();
+ break;
+ }
+}
+
+/*****************************************************************************
+ parse_po: parse machine-dependent pseudo-op
+
+ This is a top-level routine for machine-dependent pseudo-ops. It slurps
+ up the rest of the input line, breaks out the individual arguments,
+ and dispatches them to the correct handler.
+ *************************************************************************** */
+static
+void
+parse_po (po_num)
+ int po_num; /* Pseudo-op number: currently S_LEAFPROC or S_SYSPROC */
+{
+ /* Pointers operands, with no embedded whitespace.
+ arg[0] unused, arg[1-3]->operands */
+ char *args[4];
+ int n_ops; /* Number of operands */
+ char *p; /* Pointer to beginning of unparsed argument string */
+ char eol; /* Character that indicated end of line */
+
+ extern char is_end_of_line[];
+
+ /* Advance input pointer to end of line. */
+ p = input_line_pointer;
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ input_line_pointer++;
+ }
+ eol = *input_line_pointer; /* Save end-of-line char */
+ *input_line_pointer = '\0'; /* Terminate argument list */
+
+ /* Parse out operands */
+ n_ops = get_args (p, args);
+ if (n_ops == -1)
+ {
+ return;
+ }
+
+ /* Dispatch to correct handler */
+ switch (po_num)
+ {
+ case S_SYSPROC:
+ s_sysproc (n_ops, args);
+ break;
+ case S_LEAFPROC:
+ s_leafproc (n_ops, args);
+ break;
+ default:
+ BAD_CASE (po_num);
+ break;
+ }
+
+ /* Restore eol, so line numbers get updated correctly. Base
+ assembler assumes we leave input pointer pointing at char
+ following the eol. */
+ *input_line_pointer++ = eol;
+}
+
+/*****************************************************************************
+ parse_regop: parse a register operand.
+
+ In case of illegal operand, issue a message and return some valid
+ information so instruction processing can continue.
+ *************************************************************************** */
+static
+void
+parse_regop (regopP, optext, opdesc)
+ struct regop *regopP; /* Where to put description of register operand */
+ char *optext; /* Text of operand */
+ char opdesc; /* Descriptor byte: what's legal for this operand */
+{
+ int n; /* Register number */
+ expressionS e; /* Parsed expression */
+
+ /* See if operand is a register */
+ n = get_regnum (optext);
+ if (n >= 0)
+ {
+ if (IS_RG_REG (n))
+ {
+ /* global or local register */
+ if (!REG_ALIGN (opdesc, n))
+ {
+ as_bad (_("unaligned register"));
+ }
+ regopP->n = n;
+ regopP->mode = 0;
+ regopP->special = 0;
+ return;
+ }
+ else if (IS_FP_REG (n) && FP_OK (opdesc))
+ {
+ /* Floating point register, and it's allowed */
+ regopP->n = n - FP0;
+ regopP->mode = 1;
+ regopP->special = 0;
+ return;
+ }
+ else if (IS_SF_REG (n) && SFR_OK (opdesc))
+ {
+ /* Special-function register, and it's allowed */
+ regopP->n = n - SF0;
+ regopP->mode = 0;
+ regopP->special = 1;
+ if (!targ_has_sfr (regopP->n))
+ {
+ as_bad (_("no such sfr in this architecture"));
+ }
+ return;
+ }
+ }
+ else if (LIT_OK (opdesc))
+ {
+ /* How about a literal? */
+ regopP->mode = 1;
+ regopP->special = 0;
+ if (FP_OK (opdesc))
+ { /* floating point literal acceptable */
+ /* Skip over 0f, 0d, or 0e prefix */
+ if ((optext[0] == '0')
+ && (optext[1] >= 'd')
+ && (optext[1] <= 'f'))
+ {
+ optext += 2;
+ }
+
+ if (!strcmp (optext, "0.0") || !strcmp (optext, "0"))
+ {
+ regopP->n = 0x10;
+ return;
+ }
+ if (!strcmp (optext, "1.0") || !strcmp (optext, "1"))
+ {
+ regopP->n = 0x16;
+ return;
+ }
+
+ }
+ else
+ { /* fixed point literal acceptable */
+ parse_expr (optext, &e);
+ if (e.X_op != O_constant
+ || (offs (e) < 0) || (offs (e) > 31))
+ {
+ as_bad (_("illegal literal"));
+ offs (e) = 0;
+ }
+ regopP->n = offs (e);
+ return;
+ }
+ }
+
+ /* Nothing worked */
+ syntax ();
+ regopP->mode = 0; /* Register r0 is always a good one */
+ regopP->n = 0;
+ regopP->special = 0;
+} /* parse_regop() */
+
+/*****************************************************************************
+ reg_fmt: generate a REG-format instruction
+
+ *************************************************************************** */
+static void
+reg_fmt (args, oP)
+ char *args[]; /* args[0]->opcode mnemonic, args[1-3]->operands */
+ struct i960_opcode *oP; /* Pointer to description of instruction */
+{
+ long instr; /* Binary to be output */
+ struct regop regop; /* Description of register operand */
+ int n_ops; /* Number of operands */
+
+
+ instr = oP->opcode;
+ n_ops = oP->num_ops;
+
+ if (n_ops >= 1)
+ {
+ parse_regop (&regop, args[1], oP->operand[0]);
+
+ if ((n_ops == 1) && !(instr & M3))
+ {
+ /* 1-operand instruction in which the dst field should
+ * be used (instead of src1).
+ */
+ regop.n <<= 19;
+ if (regop.special)
+ {
+ regop.mode = regop.special;
+ }
+ regop.mode <<= 13;
+ regop.special = 0;
+ }
+ else
+ {
+ /* regop.n goes in bit 0, needs no shifting */
+ regop.mode <<= 11;
+ regop.special <<= 5;
+ }
+ instr |= regop.n | regop.mode | regop.special;
+ }
+
+ if (n_ops >= 2)
+ {
+ parse_regop (&regop, args[2], oP->operand[1]);
+
+ if ((n_ops == 2) && !(instr & M3))
+ {
+ /* 2-operand instruction in which the dst field should
+ * be used instead of src2).
+ */
+ regop.n <<= 19;
+ if (regop.special)
+ {
+ regop.mode = regop.special;
+ }
+ regop.mode <<= 13;
+ regop.special = 0;
+ }
+ else
+ {
+ regop.n <<= 14;
+ regop.mode <<= 12;
+ regop.special <<= 6;
+ }
+ instr |= regop.n | regop.mode | regop.special;
+ }
+ if (n_ops == 3)
+ {
+ parse_regop (&regop, args[3], oP->operand[2]);
+ if (regop.special)
+ {
+ regop.mode = regop.special;
+ }
+ instr |= (regop.n <<= 19) | (regop.mode <<= 13);
+ }
+ emit (instr);
+}
+
+
+/*****************************************************************************
+ relax_cobr:
+ Replace cobr instruction in a code fragment with equivalent branch and
+ compare instructions, so it can reach beyond a 13-bit displacement.
+ Set up an address fix/relocation for the new branch instruction.
+
+ *************************************************************************** */
+
+/* This "conditional jump" table maps cobr instructions into
+ equivalent compare and branch opcodes. */
+static const
+struct
+{
+ long compare;
+ long branch;
+}
+
+coj[] =
+{ /* COBR OPCODE: */
+ { CHKBIT, BNO }, /* 0x30 - bbc */
+ { CMPO, BG }, /* 0x31 - cmpobg */
+ { CMPO, BE }, /* 0x32 - cmpobe */
+ { CMPO, BGE }, /* 0x33 - cmpobge */
+ { CMPO, BL }, /* 0x34 - cmpobl */
+ { CMPO, BNE }, /* 0x35 - cmpobne */
+ { CMPO, BLE }, /* 0x36 - cmpoble */
+ { CHKBIT, BO }, /* 0x37 - bbs */
+ { CMPI, BNO }, /* 0x38 - cmpibno */
+ { CMPI, BG }, /* 0x39 - cmpibg */
+ { CMPI, BE }, /* 0x3a - cmpibe */
+ { CMPI, BGE }, /* 0x3b - cmpibge */
+ { CMPI, BL }, /* 0x3c - cmpibl */
+ { CMPI, BNE }, /* 0x3d - cmpibne */
+ { CMPI, BLE }, /* 0x3e - cmpible */
+ { CMPI, BO }, /* 0x3f - cmpibo */
+};
+
+static
+void
+relax_cobr (fragP)
+ register fragS *fragP; /* fragP->fr_opcode is assumed to point to
+ * the cobr instruction, which comes at the
+ * end of the code fragment.
+ */
+{
+ int opcode, src1, src2, m1, s2;
+ /* Bit fields from cobr instruction */
+ long bp_bits; /* Branch prediction bits from cobr instruction */
+ long instr; /* A single i960 instruction */
+ /* ->instruction to be replaced */
+ char *iP;
+ fixS *fixP; /* Relocation that can be done at assembly time */
+
+ /* PICK UP & PARSE COBR INSTRUCTION */
+ iP = fragP->fr_opcode;
+ instr = md_chars_to_number (iP, 4);
+ opcode = ((instr >> 24) & 0xff) - 0x30; /* "-0x30" for table index */
+ src1 = (instr >> 19) & 0x1f;
+ m1 = (instr >> 13) & 1;
+ s2 = instr & 1;
+ src2 = (instr >> 14) & 0x1f;
+ bp_bits = instr & BP_MASK;
+
+ /* GENERATE AND OUTPUT COMPARE INSTRUCTION */
+ instr = coj[opcode].compare
+ | src1 | (m1 << 11) | (s2 << 6) | (src2 << 14);
+ md_number_to_chars (iP, instr, 4);
+
+ /* OUTPUT BRANCH INSTRUCTION */
+ md_number_to_chars (iP + 4, coj[opcode].branch | bp_bits, 4);
+
+ /* SET UP ADDRESS FIXUP/RELOCATION */
+ fixP = fix_new (fragP,
+ iP + 4 - fragP->fr_literal,
+ 4,
+ fragP->fr_symbol,
+ fragP->fr_offset,
+ 1,
+ NO_RELOC);
+
+ fixP->fx_bit_fixP = (bit_fixS *) 24; /* Store size of bit field */
+
+ fragP->fr_fix += 4;
+ frag_wane (fragP);
+}
+
+
+/*****************************************************************************
+ reloc_callj: Relocate a 'callj' instruction
+
+ This is a "non-(GNU)-standard" machine-dependent hook. The base
+ assembler calls it when it decides it can relocate an address at
+ assembly time instead of emitting a relocation directive.
+
+ Check to see if the relocation involves a 'callj' instruction to a:
+ sysproc: Replace the default 'call' instruction with a 'calls'
+ leafproc: Replace the default 'call' instruction with a 'bal'.
+ other proc: Do nothing.
+
+ See b.out.h for details on the 'n_other' field in a symbol structure.
+
+ IMPORTANT!:
+ Assumes the caller has already figured out, in the case of a leafproc,
+ to use the 'bal' entry point, and has substituted that symbol into the
+ passed fixup structure.
+
+ *************************************************************************** */
+void
+reloc_callj (fixP)
+ /* Relocation that can be done at assembly time */
+ fixS *fixP;
+{
+ /* Points to the binary for the instruction being relocated. */
+ char *where;
+
+ if (!fixP->fx_tcbit)
+ {
+ /* This wasn't a callj instruction in the first place */
+ return;
+ }
+
+ where = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ if (TC_S_IS_SYSPROC (fixP->fx_addsy))
+ {
+ /* Symbol is a .sysproc: replace 'call' with 'calls'. System
+ procedure number is (other-1). */
+ md_number_to_chars (where, CALLS | TC_S_GET_SYSPROC (fixP->fx_addsy), 4);
+
+ /* Nothing else needs to be done for this instruction. Make
+ sure 'md_number_to_field()' will perform a no-op. */
+ fixP->fx_bit_fixP = (bit_fixS *) 1;
+
+ }
+ else if (TC_S_IS_CALLNAME (fixP->fx_addsy))
+ {
+ /* Should not happen: see block comment above */
+ as_fatal (_("Trying to 'bal' to %s"), S_GET_NAME (fixP->fx_addsy));
+ }
+ else if (TC_S_IS_BALNAME (fixP->fx_addsy))
+ {
+ /* Replace 'call' with 'bal'; both instructions have the same
+ format, so calling code should complete relocation as if
+ nothing happened here. */
+ md_number_to_chars (where, BAL, 4);
+ }
+ else if (TC_S_IS_BADPROC (fixP->fx_addsy))
+ {
+ as_bad (_("Looks like a proc, but can't tell what kind.\n"));
+ } /* switch on proc type */
+
+ /* else Symbol is neither a sysproc nor a leafproc */
+}
+
+
+/*****************************************************************************
+ s_leafproc: process .leafproc pseudo-op
+
+ .leafproc takes two arguments, the second one is optional:
+ arg[1]: name of 'call' entry point to leaf procedure
+ arg[2]: name of 'bal' entry point to leaf procedure
+
+ If the two arguments are identical, or if the second one is missing,
+ the first argument is taken to be the 'bal' entry point.
+
+ If there are 2 distinct arguments, we must make sure that the 'bal'
+ entry point immediately follows the 'call' entry point in the linked
+ list of symbols.
+
+ *************************************************************************** */
+static void
+s_leafproc (n_ops, args)
+ int n_ops; /* Number of operands */
+ char *args[]; /* args[1]->1st operand, args[2]->2nd operand */
+{
+ symbolS *callP; /* Pointer to leafproc 'call' entry point symbol */
+ symbolS *balP; /* Pointer to leafproc 'bal' entry point symbol */
+
+ if ((n_ops != 1) && (n_ops != 2))
+ {
+ as_bad (_("should have 1 or 2 operands"));
+ return;
+ } /* Check number of arguments */
+
+ /* Find or create symbol for 'call' entry point. */
+ callP = symbol_find_or_make (args[1]);
+
+ if (TC_S_IS_CALLNAME (callP))
+ {
+ as_warn (_("Redefining leafproc %s"), S_GET_NAME (callP));
+ } /* is leafproc */
+
+ /* If that was the only argument, use it as the 'bal' entry point.
+ * Otherwise, mark it as the 'call' entry point and find or create
+ * another symbol for the 'bal' entry point.
+ */
+ if ((n_ops == 1) || !strcmp (args[1], args[2]))
+ {
+ TC_S_FORCE_TO_BALNAME (callP);
+
+ }
+ else
+ {
+ TC_S_FORCE_TO_CALLNAME (callP);
+
+ balP = symbol_find_or_make (args[2]);
+ if (TC_S_IS_CALLNAME (balP))
+ {
+ as_warn (_("Redefining leafproc %s"), S_GET_NAME (balP));
+ }
+ TC_S_FORCE_TO_BALNAME (balP);
+
+ tc_set_bal_of_call (callP, balP);
+ } /* if only one arg, or the args are the same */
+}
+
+
+/*
+ s_sysproc: process .sysproc pseudo-op
+
+ .sysproc takes two arguments:
+ arg[1]: name of entry point to system procedure
+ arg[2]: 'entry_num' (index) of system procedure in the range
+ [0,31] inclusive.
+
+ For [ab].out, we store the 'entrynum' in the 'n_other' field of
+ the symbol. Since that entry is normally 0, we bias 'entrynum'
+ by adding 1 to it. It must be unbiased before it is used. */
+static void
+s_sysproc (n_ops, args)
+ int n_ops; /* Number of operands */
+ char *args[]; /* args[1]->1st operand, args[2]->2nd operand */
+{
+ expressionS exp;
+ symbolS *symP;
+
+ if (n_ops != 2)
+ {
+ as_bad (_("should have two operands"));
+ return;
+ } /* bad arg count */
+
+ /* Parse "entry_num" argument and check it for validity. */
+ parse_expr (args[2], &exp);
+ if (exp.X_op != O_constant
+ || (offs (exp) < 0)
+ || (offs (exp) > 31))
+ {
+ as_bad (_("'entry_num' must be absolute number in [0,31]"));
+ return;
+ }
+
+ /* Find/make symbol and stick entry number (biased by +1) into it */
+ symP = symbol_find_or_make (args[1]);
+
+ if (TC_S_IS_SYSPROC (symP))
+ {
+ as_warn (_("Redefining entrynum for sysproc %s"), S_GET_NAME (symP));
+ } /* redefining */
+
+ TC_S_SET_SYSPROC (symP, offs (exp)); /* encode entry number */
+ TC_S_FORCE_TO_SYSPROC (symP);
+}
+
+
+/*****************************************************************************
+ shift_ok:
+ Determine if a "shlo" instruction can be used to implement a "ldconst".
+ This means that some number X < 32 can be shifted left to produce the
+ constant of interest.
+
+ Return the shift count, or 0 if we can't do it.
+ Caller calculates X by shifting original constant right 'shift' places.
+
+ *************************************************************************** */
+static
+int
+shift_ok (n)
+ int n; /* The constant of interest */
+{
+ int shift; /* The shift count */
+
+ if (n <= 0)
+ {
+ /* Can't do it for negative numbers */
+ return 0;
+ }
+
+ /* Shift 'n' right until a 1 is about to be lost */
+ for (shift = 0; (n & 1) == 0; shift++)
+ {
+ n >>= 1;
+ }
+
+ if (n >= 32)
+ {
+ return 0;
+ }
+ return shift;
+}
+
+
+/* syntax: issue syntax error */
+
+static void
+syntax ()
+{
+ as_bad (_("syntax error"));
+} /* syntax() */
+
+
+/* targ_has_sfr:
+
+ Return TRUE iff the target architecture supports the specified
+ special-function register (sfr). */
+
+static
+int
+targ_has_sfr (n)
+ int n; /* Number (0-31) of sfr */
+{
+ switch (architecture)
+ {
+ case ARCH_KA:
+ case ARCH_KB:
+ case ARCH_MC:
+ case ARCH_JX:
+ return 0;
+ case ARCH_HX:
+ return ((0 <= n) && (n <= 4));
+ case ARCH_CA:
+ default:
+ return ((0 <= n) && (n <= 2));
+ }
+}
+
+
+/* targ_has_iclass:
+
+ Return TRUE iff the target architecture supports the indicated
+ class of instructions. */
+static
+int
+targ_has_iclass (ic)
+ /* Instruction class; one of:
+ I_BASE, I_CX, I_DEC, I_KX, I_FP, I_MIL, I_CASIM, I_CX2, I_HX, I_HX2
+ */
+ int ic;
+{
+ iclasses_seen |= ic;
+ switch (architecture)
+ {
+ case ARCH_KA:
+ return ic & (I_BASE | I_KX);
+ case ARCH_KB:
+ return ic & (I_BASE | I_KX | I_FP | I_DEC);
+ case ARCH_MC:
+ return ic & (I_BASE | I_KX | I_FP | I_DEC | I_MIL);
+ case ARCH_CA:
+ return ic & (I_BASE | I_CX | I_CX2 | I_CASIM);
+ case ARCH_JX:
+ return ic & (I_BASE | I_CX2 | I_JX);
+ case ARCH_HX:
+ return ic & (I_BASE | I_CX2 | I_JX | I_HX);
+ default:
+ if ((iclasses_seen & (I_KX | I_FP | I_DEC | I_MIL))
+ && (iclasses_seen & (I_CX | I_CX2)))
+ {
+ as_warn (_("architecture of opcode conflicts with that of earlier instruction(s)"));
+ iclasses_seen &= ~ic;
+ }
+ return 1;
+ }
+}
+
+/* Handle the MRI .endian pseudo-op. */
+
+static void
+s_endian (ignore)
+ int ignore;
+{
+ char *name;
+ char c;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ if (strcasecmp (name, "little") == 0)
+ ;
+ else if (strcasecmp (name, "big") == 0)
+ as_bad (_("big endian mode is not supported"));
+ else
+ as_warn (_("ignoring unrecognized .endian type `%s'"), name);
+
+ *input_line_pointer = c;
+
+ demand_empty_rest_of_line ();
+}
+
+/* We have no need to default values of symbols. */
+
+/* ARGSUSED */
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ return 0;
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the i960, they're relative to the address of the instruction,
+ which we have set up as the address of the fixup too. */
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ return fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+void
+md_apply_fix (fixP, val)
+ fixS *fixP;
+ long val;
+{
+ char *place = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ if (!fixP->fx_bit_fixP)
+ {
+ /* For callx, we always want to write out zero, and emit a
+ symbolic relocation. */
+ if (fixP->fx_bsr)
+ val = 0;
+
+ fixP->fx_addnumber = val;
+ md_number_to_imm (place, val, fixP->fx_size, fixP);
+ }
+ else
+ md_number_to_field (place, val, fixP->fx_bit_fixP);
+}
+
+#if defined(OBJ_AOUT) | defined(OBJ_BOUT)
+void
+tc_bout_fix_to_chars (where, fixP, segment_address_in_file)
+ char *where;
+ fixS *fixP;
+ relax_addressT segment_address_in_file;
+{
+ static const unsigned char nbytes_r_length[] = {42, 0, 1, 42, 2};
+ struct relocation_info ri;
+ symbolS *symbolP;
+
+ memset ((char *) &ri, '\0', sizeof (ri));
+ symbolP = fixP->fx_addsy;
+ know (symbolP != 0 || fixP->fx_r_type != NO_RELOC);
+ ri.r_bsr = fixP->fx_bsr; /*SAC LD RELAX HACK */
+ /* These two 'cuz of NS32K */
+ ri.r_callj = fixP->fx_tcbit;
+ if (fixP->fx_bit_fixP)
+ ri.r_length = 2;
+ else
+ ri.r_length = nbytes_r_length[fixP->fx_size];
+ ri.r_pcrel = fixP->fx_pcrel;
+ ri.r_address = fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file;
+
+ if (fixP->fx_r_type != NO_RELOC)
+ {
+ switch (fixP->fx_r_type)
+ {
+ case rs_align:
+ ri.r_index = -2;
+ ri.r_pcrel = 1;
+ ri.r_length = fixP->fx_size - 1;
+ break;
+ case rs_org:
+ ri.r_index = -2;
+ ri.r_pcrel = 0;
+ break;
+ case rs_fill:
+ ri.r_index = -1;
+ break;
+ default:
+ abort ();
+ }
+ ri.r_extern = 0;
+ }
+ else if (linkrelax || !S_IS_DEFINED (symbolP) || fixP->fx_bsr)
+ {
+ ri.r_extern = 1;
+ ri.r_index = symbolP->sy_number;
+ }
+ else
+ {
+ ri.r_extern = 0;
+ ri.r_index = S_GET_TYPE (symbolP);
+ }
+
+ /* Output the relocation information in machine-dependent form. */
+ md_ri_to_chars (where, &ri);
+}
+
+#endif /* OBJ_AOUT or OBJ_BOUT */
+
+#if defined (OBJ_COFF) && defined (BFD)
+short
+tc_coff_fix2rtype (fixP)
+ fixS *fixP;
+{
+ if (fixP->fx_bsr)
+ abort ();
+
+ if (fixP->fx_pcrel == 0 && fixP->fx_size == 4)
+ return R_RELLONG;
+
+ if (fixP->fx_pcrel != 0 && fixP->fx_size == 4)
+ return R_IPRMED;
+
+ abort ();
+ return 0;
+}
+
+int
+tc_coff_sizemachdep (frag)
+ fragS *frag;
+{
+ if (frag->fr_next)
+ return frag->fr_next->fr_address - frag->fr_address;
+ else
+ return 0;
+}
+#endif
+
+/* Align an address by rounding it up to the specified boundary. */
+valueT
+md_section_align (seg, addr)
+ segT seg;
+ valueT addr; /* Address to be rounded up */
+{
+ return ((addr + (1 << section_alignment[(int) seg]) - 1) & (-1 << section_alignment[(int) seg]));
+} /* md_section_align() */
+
+extern int coff_flags;
+
+#ifdef OBJ_COFF
+void
+tc_headers_hook (headers)
+ object_headers *headers;
+{
+ switch (architecture)
+ {
+ case ARCH_KA:
+ coff_flags |= F_I960KA;
+ break;
+
+ case ARCH_KB:
+ coff_flags |= F_I960KB;
+ break;
+
+ case ARCH_MC:
+ coff_flags |= F_I960MC;
+ break;
+
+ case ARCH_CA:
+ coff_flags |= F_I960CA;
+ break;
+
+ case ARCH_JX:
+ coff_flags |= F_I960JX;
+ break;
+
+ case ARCH_HX:
+ coff_flags |= F_I960HX;
+ break;
+
+ default:
+ if (iclasses_seen == I_BASE)
+ coff_flags |= F_I960CORE;
+ else if (iclasses_seen & I_CX)
+ coff_flags |= F_I960CA;
+ else if (iclasses_seen & I_HX)
+ coff_flags |= F_I960HX;
+ else if (iclasses_seen & I_JX)
+ coff_flags |= F_I960JX;
+ else if (iclasses_seen & I_CX2)
+ coff_flags |= F_I960CA;
+ else if (iclasses_seen & I_MIL)
+ coff_flags |= F_I960MC;
+ else if (iclasses_seen & (I_DEC | I_FP))
+ coff_flags |= F_I960KB;
+ else
+ coff_flags |= F_I960KA;
+ break;
+ }
+
+ if (flag_readonly_data_in_text)
+ {
+ headers->filehdr.f_magic = I960RWMAGIC;
+ headers->aouthdr.magic = OMAGIC;
+ }
+ else
+ {
+ headers->filehdr.f_magic = I960ROMAGIC;
+ headers->aouthdr.magic = NMAGIC;
+ } /* set magic numbers */
+}
+
+#endif /* OBJ_COFF */
+
+/* Things going on here:
+
+ For bout, We need to assure a couple of simplifying
+ assumptions about leafprocs for the linker: the leafproc
+ entry symbols will be defined in the same assembly in
+ which they're declared with the '.leafproc' directive;
+ and if a leafproc has both 'call' and 'bal' entry points
+ they are both global or both local.
+
+ For coff, the call symbol has a second aux entry that
+ contains the bal entry point. The bal symbol becomes a
+ label.
+
+ For coff representation, the call symbol has a second aux entry that
+ contains the bal entry point. The bal symbol becomes a label. */
+
+void
+tc_crawl_symbol_chain (headers)
+ object_headers *headers;
+{
+ symbolS *symbolP;
+
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next (symbolP))
+ {
+#ifdef OBJ_COFF
+ if (TC_S_IS_SYSPROC (symbolP))
+ {
+ /* second aux entry already contains the sysproc number */
+ S_SET_NUMBER_AUXILIARY (symbolP, 2);
+ S_SET_STORAGE_CLASS (symbolP, C_SCALL);
+ S_SET_DATA_TYPE (symbolP, S_GET_DATA_TYPE (symbolP) | (DT_FCN << N_BTSHFT));
+ continue;
+ } /* rewrite sysproc */
+#endif /* OBJ_COFF */
+
+ if (!TC_S_IS_BALNAME (symbolP) && !TC_S_IS_CALLNAME (symbolP))
+ {
+ continue;
+ } /* Not a leafproc symbol */
+
+ if (!S_IS_DEFINED (symbolP))
+ {
+ as_bad (_("leafproc symbol '%s' undefined"), S_GET_NAME (symbolP));
+ } /* undefined leaf */
+
+ if (TC_S_IS_CALLNAME (symbolP))
+ {
+ symbolS *balP = tc_get_bal_of_call (symbolP);
+ if (S_IS_EXTERNAL (symbolP) != S_IS_EXTERNAL (balP))
+ {
+ S_SET_EXTERNAL (symbolP);
+ S_SET_EXTERNAL (balP);
+ as_warn (_("Warning: making leafproc entries %s and %s both global\n"),
+ S_GET_NAME (symbolP), S_GET_NAME (balP));
+ } /* externality mismatch */
+ } /* if callname */
+ } /* walk the symbol chain */
+}
+
+/* For aout or bout, the bal immediately follows the call.
+
+ For coff, we cheat and store a pointer to the bal symbol in the
+ second aux entry of the call. */
+
+#undef OBJ_ABOUT
+#ifdef OBJ_AOUT
+#define OBJ_ABOUT
+#endif
+#ifdef OBJ_BOUT
+#define OBJ_ABOUT
+#endif
+
+void
+tc_set_bal_of_call (callP, balP)
+ symbolS *callP;
+ symbolS *balP;
+{
+ know (TC_S_IS_CALLNAME (callP));
+ know (TC_S_IS_BALNAME (balP));
+
+#ifdef OBJ_COFF
+
+ callP->sy_tc = balP;
+ S_SET_NUMBER_AUXILIARY (callP, 2);
+
+#else /* ! OBJ_COFF */
+#ifdef OBJ_ABOUT
+
+ /* If the 'bal' entry doesn't immediately follow the 'call'
+ * symbol, unlink it from the symbol list and re-insert it.
+ */
+ if (symbol_next (callP) != balP)
+ {
+ symbol_remove (balP, &symbol_rootP, &symbol_lastP);
+ symbol_append (balP, callP, &symbol_rootP, &symbol_lastP);
+ } /* if not in order */
+
+#else /* ! OBJ_ABOUT */
+ (as yet unwritten.);
+#endif /* ! OBJ_ABOUT */
+#endif /* ! OBJ_COFF */
+}
+
+symbolS *
+tc_get_bal_of_call (callP)
+ symbolS *callP;
+{
+ symbolS *retval;
+
+ know (TC_S_IS_CALLNAME (callP));
+
+#ifdef OBJ_COFF
+ retval = callP->sy_tc;
+#else
+#ifdef OBJ_ABOUT
+ retval = symbol_next (callP);
+#else
+ (as yet unwritten.);
+#endif /* ! OBJ_ABOUT */
+#endif /* ! OBJ_COFF */
+
+ know (TC_S_IS_BALNAME (retval));
+ return retval;
+} /* _tc_get_bal_of_call() */
+
+void
+tc_coff_symbol_emit_hook (symbolP)
+ symbolS *symbolP;
+{
+ if (TC_S_IS_CALLNAME (symbolP))
+ {
+#ifdef OBJ_COFF
+ symbolS *balP = tc_get_bal_of_call (symbolP);
+
+#if 0
+ /* second aux entry contains the bal entry point */
+ S_SET_NUMBER_AUXILIARY (symbolP, 2);
+#endif
+ symbolP->sy_symbol.ost_auxent[1].x_bal.x_balntry = S_GET_VALUE (balP);
+ if (S_GET_STORAGE_CLASS (symbolP) == C_EXT)
+ S_SET_STORAGE_CLASS (symbolP, C_LEAFEXT);
+ else
+ S_SET_STORAGE_CLASS (symbolP, C_LEAFSTAT);
+ S_SET_DATA_TYPE (symbolP, S_GET_DATA_TYPE (symbolP) | (DT_FCN << N_BTSHFT));
+ /* fix up the bal symbol */
+ S_SET_STORAGE_CLASS (balP, C_LABEL);
+#endif /* OBJ_COFF */
+ } /* only on calls */
+}
+
+void
+i960_handle_align (fragp)
+ fragS *fragp;
+{
+ if (!linkrelax)
+ return;
+
+#ifndef OBJ_BOUT
+
+ as_bad (_("option --link-relax is only supported in b.out format"));
+ linkrelax = 0;
+ return;
+
+#else
+
+ /* The text section "ends" with another alignment reloc, to which we
+ aren't adding padding. */
+ if (fragp->fr_next == text_last_frag
+ || fragp->fr_next == data_last_frag)
+ return;
+
+ /* alignment directive */
+ fix_new (fragp, fragp->fr_fix, fragp->fr_offset, 0, 0, 0,
+ (int) fragp->fr_type);
+#endif /* OBJ_BOUT */
+}
+
+int
+i960_validate_fix (fixP, this_segment_type, add_symbolPP)
+ fixS *fixP;
+ segT this_segment_type;
+ symbolS **add_symbolPP;
+{
+#define add_symbolP (*add_symbolPP)
+ if (fixP->fx_tcbit && TC_S_IS_CALLNAME (add_symbolP))
+ {
+ /* Relocation should be done via the associated 'bal'
+ entry point symbol. */
+
+ if (!TC_S_IS_BALNAME (tc_get_bal_of_call (add_symbolP)))
+ {
+ as_bad (_("No 'bal' entry point for leafproc %s"),
+ S_GET_NAME (add_symbolP));
+ return 1;
+ }
+ fixP->fx_addsy = add_symbolP = tc_get_bal_of_call (add_symbolP);
+ }
+#if 0
+ /* Still have to work out other conditions for these tests. */
+ {
+ if (fixP->fx_tcbit)
+ {
+ as_bad (_("callj to difference of two symbols"));
+ return 1;
+ }
+ reloc_callj (fixP);
+ if ((int) fixP->fx_bit_fixP == 13)
+ {
+ /* This is a COBR instruction. They have only a 13-bit
+ displacement and are only to be used for local branches:
+ flag as error, don't generate relocation. */
+ as_bad (_("can't use COBR format with external label"));
+ fixP->fx_addsy = NULL; /* No relocations please. */
+ return 1;
+ }
+ }
+#endif
+#undef add_symbolP
+ return 0;
+}
+
+/* end of tc-i960.c */
diff --git a/gas/config/tc-i960.h b/gas/config/tc-i960.h
new file mode 100644
index 0000000..f606967
--- /dev/null
+++ b/gas/config/tc-i960.h
@@ -0,0 +1,171 @@
+/* tc-i960.h - Basic 80960 instruction formats.
+ Copyright (C) 1989, 90, 91, 92, 93, 94, 95, 96, 97, 1998
+ Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifndef TC_I960
+#define TC_I960 1
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define WORKING_DOT_WORD
+
+/*
+ * The 'COJ' instructions are actually COBR instructions with the 'b' in
+ * the mnemonic replaced by a 'j'; they are ALWAYS "de-optimized" if necessary:
+ * if the displacement will not fit in 13 bits, the assembler will replace them
+ * with the corresponding compare and branch instructions.
+ *
+ * All of the 'MEMn' instructions are the same format; the 'n' in the name
+ * indicates the default index scale factor (the size of the datum operated on).
+ *
+ * The FBRA formats are not actually an instruction format. They are the
+ * "convenience directives" for branching on floating-point comparisons,
+ * each of which generates 2 instructions (a 'bno' and one other branch).
+ *
+ * The CALLJ format is not actually an instruction format. It indicates that
+ * the instruction generated (a CTRL-format 'call') should have its relocation
+ * specially flagged for link-time replacement with a 'bal' or 'calls' if
+ * appropriate.
+ */
+
+/* tailor gas */
+#define SYMBOLS_NEED_BACKPOINTERS
+#define LOCAL_LABELS_FB 1
+#define BITFIELD_CONS_EXPRESSIONS
+
+/* tailor the coff format */
+#define BFD_ARCH bfd_arch_i960
+#define COFF_FLAGS F_AR32WR
+#define COFF_MAGIC I960ROMAGIC
+#define OBJ_COFF_SECTION_HEADER_HAS_ALIGNMENT
+#define OBJ_COFF_MAX_AUXENTRIES (2)
+#define TC_COUNT_RELOC(FIXP) (!(FIXP)->fx_done)
+#define TC_COFF_FIX2RTYPE(FIXP) tc_coff_fix2rtype(FIXP)
+#define TC_COFF_SIZEMACHDEP(FRAGP) tc_coff_sizemachdep(FRAGP)
+#define TC_COFF_SET_MACHINE(HDRS) tc_headers_hook (HDRS)
+extern void tc_headers_hook ();
+extern short tc_coff_fix2rtype ();
+extern int tc_coff_sizemachdep ();
+
+/* MEANING OF 'n_other' in the symbol record.
+ *
+ * If non-zero, the 'n_other' fields indicates either a leaf procedure or
+ * a system procedure, as follows:
+ *
+ * 1 <= n_other <= 32 :
+ * The symbol is the entry point to a system procedure.
+ * 'n_value' is the address of the entry, as for any other
+ * procedure. The system procedure number (which can be used in
+ * a 'calls' instruction) is (n_other-1). These entries come from
+ * '.sysproc' directives.
+ *
+ * n_other == N_CALLNAME
+ * the symbol is the 'call' entry point to a leaf procedure.
+ * The *next* symbol in the symbol table must be the corresponding
+ * 'bal' entry point to the procedure (see following). These
+ * entries come from '.leafproc' directives in which two different
+ * symbols are specified (the first one is represented here).
+ *
+ *
+ * n_other == N_BALNAME
+ * the symbol is the 'bal' entry point to a leaf procedure.
+ * These entries result from '.leafproc' directives in which only
+ * one symbol is specified, or in which the same symbol is
+ * specified twice.
+ *
+ * Note that an N_CALLNAME entry *must* have a corresponding N_BALNAME entry,
+ * but not every N_BALNAME entry must have an N_CALLNAME entry.
+ */
+#define N_CALLNAME ((char)-1)
+#define N_BALNAME ((char)-2)
+
+/* i960 uses a custom relocation record. */
+
+/* let obj-aout.h know */
+#define CUSTOM_RELOC_FORMAT 1
+/* let aout_gnu.h know */
+#define N_RELOCATION_INFO_DECLARED 1
+struct relocation_info
+ {
+ int r_address; /* File address of item to be relocated */
+ unsigned
+ r_index:24, /* Index of symbol on which relocation is based*/
+ r_pcrel:1, /* 1 => relocate PC-relative; else absolute
+ * On i960, pc-relative implies 24-bit
+ * address, absolute implies 32-bit.
+ */
+ r_length:2, /* Number of bytes to relocate:
+ * 0 => 1 byte
+ * 1 => 2 bytes
+ * 2 => 4 bytes -- only value used for i960
+ */
+ r_extern:1, r_bsr:1, /* Something for the GNU NS32K assembler */
+ r_disp:1, /* Something for the GNU NS32K assembler */
+ r_callj:1, /* 1 if relocation target is an i960 'callj' */
+ nuthin:1; /* Unused */
+ };
+
+#ifdef OBJ_COFF
+
+/* We store the bal information in the sy_tc field. */
+#define TC_SYMFIELD_TYPE struct symbol *
+
+#define TC_ADJUST_RELOC_COUNT(FIXP,COUNT) \
+ { fixS *tcfixp = (FIXP); \
+ for (;tcfixp;tcfixp=tcfixp->fx_next) \
+ if (tcfixp->fx_tcbit && tcfixp->fx_addsy != 0) \
+ ++(COUNT); \
+ }
+#endif
+
+extern int i960_validate_fix PARAMS ((struct fix *, segT, struct symbol **));
+#define TC_VALIDATE_FIX(FIXP,SEGTYPE,LABEL) \
+ if (i960_validate_fix (FIXP, SEGTYPE, &add_symbolP) != 0) goto LABEL
+
+#define tc_fix_adjustable(FIXP) ((FIXP)->fx_bsr == 0)
+
+extern void brtab_emit PARAMS ((void));
+#define md_end() brtab_emit ()
+
+extern void reloc_callj ();
+
+extern void tc_set_bal_of_call PARAMS ((struct symbol *, struct symbol *));
+
+extern struct symbol *tc_get_bal_of_call PARAMS ((struct symbol *));
+
+extern void i960_handle_align ();
+#define HANDLE_ALIGN(FRAG) i960_handle_align (FRAG)
+#define NEED_FX_R_TYPE
+#define NO_RELOC -1
+
+#define md_operand(x)
+
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+#define LINKER_RELAXING_SHRINKS_ONLY
+
+#define TC_FIX_TYPE struct { unsigned bsr : 1; }
+#define fx_bsr tc_fix_data.bsr
+#define TC_INIT_FIX_DATA(F) ((F)->tc_fix_data.bsr = 0)
+
+#endif
+
+/* end of tc-i960.h */
diff --git a/gas/config/tc-m32r.c b/gas/config/tc-m32r.c
new file mode 100644
index 0000000..997e623
--- /dev/null
+++ b/gas/config/tc-m32r.c
@@ -0,0 +1,1300 @@
+/* tc-m32r.c -- Assembler for the Mitsubishi M32R.
+ Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "as.h"
+#include "subsegs.h"
+#include "symcat.h"
+#include "opcodes/m32r-desc.h"
+#include "opcodes/m32r-opc.h"
+#include "cgen.h"
+
+/* Linked list of symbols that are debugging symbols to be defined as the
+ beginning of the current instruction. */
+typedef struct sym_link
+{
+ struct sym_link *next;
+ symbolS *symbol;
+} sym_linkS;
+
+static sym_linkS *debug_sym_link = (sym_linkS *)0;
+
+/* Structure to hold all of the different components describing
+ an individual instruction. */
+typedef struct
+{
+ const CGEN_INSN * insn;
+ const CGEN_INSN * orig_insn;
+ CGEN_FIELDS fields;
+#if CGEN_INT_INSN_P
+ CGEN_INSN_INT buffer [1];
+#define INSN_VALUE(buf) (*(buf))
+#else
+ unsigned char buffer [CGEN_MAX_INSN_SIZE];
+#define INSN_VALUE(buf) (buf)
+#endif
+ char * addr;
+ fragS * frag;
+ int num_fixups;
+ fixS * fixups [GAS_CGEN_MAX_FIXUPS];
+ int indices [MAX_OPERAND_INSTANCES];
+ sym_linkS *debug_sym_link;
+}
+m32r_insn;
+
+/* prev_insn.insn is non-null if last insn was a 16 bit insn on a 32 bit
+ boundary (i.e. was the first of two 16 bit insns). */
+static m32r_insn prev_insn;
+
+/* Non-zero if we've seen a relaxable insn since the last 32 bit
+ alignment request. */
+static int seen_relaxable_p = 0;
+
+/* Non-zero if -relax specified, in which case sufficient relocs are output
+ for the linker to do relaxing.
+ We do simple forms of relaxing internally, but they are always done.
+ This flag does not apply to them. */
+static int m32r_relax;
+
+#if 0 /* not supported yet */
+/* If non-NULL, pointer to cpu description file to read.
+ This allows runtime additions to the assembler. */
+static const char * m32r_cpu_desc;
+#endif
+
+/* Non-zero if warn when a high/shigh reloc has no matching low reloc.
+ Each high/shigh reloc must be paired with it's low cousin in order to
+ properly calculate the addend in a relocatable link (since there is a
+ potential carry from the low to the high/shigh).
+ This option is off by default though for user-written assembler code it
+ might make sense to make the default be on (i.e. have gcc pass a flag
+ to turn it off). This warning must not be on for GCC created code as
+ optimization may delete the low but not the high/shigh (at least we
+ shouldn't assume or require it to). */
+static int warn_unmatched_high = 0;
+
+
+/* stuff for .scomm symbols. */
+static segT sbss_section;
+static asection scom_section;
+static asymbol scom_symbol;
+
+const char comment_chars[] = ";";
+const char line_comment_chars[] = "#";
+const char line_separator_chars[] = "";
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "dD";
+
+/* Relocations against symbols are done in two
+ parts, with a HI relocation and a LO relocation. Each relocation
+ has only 16 bits of space to store an addend. This means that in
+ order for the linker to handle carries correctly, it must be able
+ to locate both the HI and the LO relocation. This means that the
+ relocations must appear in order in the relocation table.
+
+ In order to implement this, we keep track of each unmatched HI
+ relocation. We then sort them so that they immediately precede the
+ corresponding LO relocation. */
+
+struct m32r_hi_fixup
+{
+ struct m32r_hi_fixup * next; /* Next HI fixup. */
+ fixS * fixp; /* This fixup. */
+ segT seg; /* The section this fixup is in. */
+
+};
+
+/* The list of unmatched HI relocs. */
+
+static struct m32r_hi_fixup * m32r_hi_fixup_list;
+
+
+
+#define M32R_SHORTOPTS ""
+const char * md_shortopts = M32R_SHORTOPTS;
+
+struct option md_longopts[] =
+{
+
+ /* Sigh. I guess all warnings must now have both variants. */
+#define OPTION_WARN_UNMATCHED (OPTION_MD_BASE + 4)
+ {"warn-unmatched-high", OPTION_WARN_UNMATCHED},
+ {"Wuh", OPTION_WARN_UNMATCHED},
+#define OPTION_NO_WARN_UNMATCHED (OPTION_MD_BASE + 5)
+ {"no-warn-unmatched-high", OPTION_WARN_UNMATCHED},
+ {"Wnuh", OPTION_WARN_UNMATCHED},
+
+#if 0 /* not supported yet */
+#define OPTION_RELAX (OPTION_MD_BASE + 6)
+ {"relax", no_argument, NULL, OPTION_RELAX},
+#define OPTION_CPU_DESC (OPTION_MD_BASE + 7)
+ {"cpu-desc", required_argument, NULL, OPTION_CPU_DESC},
+#endif
+
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (c, arg)
+ int c;
+ char * arg;
+{
+ switch (c)
+ {
+
+ case OPTION_WARN_UNMATCHED:
+ warn_unmatched_high = 1;
+ break;
+
+ case OPTION_NO_WARN_UNMATCHED:
+ warn_unmatched_high = 0;
+ break;
+
+#if 0 /* not supported yet */
+ case OPTION_RELAX:
+ m32r_relax = 1;
+ break;
+ case OPTION_CPU_DESC:
+ m32r_cpu_desc = arg;
+ break;
+#endif
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+void
+md_show_usage (stream)
+ FILE * stream;
+{
+ fprintf (stream, _(" M32R specific command line options:\n"));
+
+
+ fprintf (stream, _("\
+ -warn-unmatched-high warn when an (s)high reloc has no matching low reloc\n"));
+ fprintf (stream, _("\
+ -no-warn-unmatched-high do not warn about missing low relocs\n"));
+ fprintf (stream, _("\
+ -Wuh synonym for -warn-unmatched-high\n"));
+ fprintf (stream, _("\
+ -Wnuh synonym for -no-warn-unmatched-high\n"));
+
+#if 0
+ fprintf (stream, _("\
+ -relax create linker relaxable code\n"));
+ fprintf (stream, _("\
+ -cpu-desc provide runtime cpu description file\n"));
+#endif
+}
+
+static void fill_insn PARAMS ((int));
+static void m32r_scomm PARAMS ((int));
+static void debug_sym PARAMS ((int));
+static void expand_debug_syms PARAMS ((sym_linkS *, int));
+
+/* Set by md_assemble for use by m32r_fill_insn. */
+static subsegT prev_subseg;
+static segT prev_seg;
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "word", cons, 4 },
+ { "fillinsn", fill_insn, 0 },
+ { "scomm", m32r_scomm, 0 },
+ { "debugsym", debug_sym, 0 },
+ { NULL, NULL, 0 }
+};
+
+/* FIXME: Should be machine generated. */
+#define NOP_INSN 0x7000
+#define PAR_NOP_INSN 0xf000 /* can only be used in 2nd slot */
+
+/* When we align the .text section, insert the correct NOP pattern.
+ N is the power of 2 alignment. LEN is the length of pattern FILL.
+ MAX is the maximum number of characters to skip when doing the alignment,
+ or 0 if there is no maximum. */
+
+int
+m32r_do_align (n, fill, len, max)
+ int n;
+ const char * fill;
+ int len;
+ int max;
+{
+ /* Only do this if the fill pattern wasn't specified. */
+ if (fill == NULL
+ && (now_seg->flags & SEC_CODE) != 0
+ /* Only do this special handling if aligning to at least a
+ 4 byte boundary. */
+ && n > 1
+ /* Only do this special handling if we're allowed to emit at
+ least two bytes. */
+ && (max == 0 || max > 1))
+ {
+ static const unsigned char nop_pattern[] = { 0xf0, 0x00 };
+
+#if 0
+ /* First align to a 2 byte boundary, in case there is an odd .byte. */
+ /* FIXME: How much memory will cause gas to use when assembling a big
+ program? Perhaps we can avoid the frag_align call? */
+ frag_align (1, 0, 0);
+#endif
+ /* Next align to a 4 byte boundary (we know n >= 2) using a parallel
+ nop. */
+ frag_align_pattern (2, nop_pattern, sizeof nop_pattern, 0);
+ /* If doing larger alignments use a repeating sequence of appropriate
+ nops. */
+ if (n > 2)
+ {
+ static const unsigned char multi_nop_pattern[] =
+ { 0x70, 0x00, 0xf0, 0x00 };
+ frag_align_pattern (n, multi_nop_pattern, sizeof multi_nop_pattern,
+ max ? max - 2 : 0);
+ }
+
+ prev_insn.insn = NULL;
+ return 1;
+ }
+
+ return 0;
+}
+
+/* If the last instruction was the first of 2 16 bit insns,
+ output a nop to move the PC to a 32 bit boundary.
+
+ This is done via an alignment specification since branch relaxing
+ may make it unnecessary.
+
+ Internally, we need to output one of these each time a 32 bit insn is
+ seen after an insn that is relaxable. */
+
+static void
+fill_insn (ignore)
+ int ignore;
+{
+ (void) m32r_do_align (2, NULL, 0, 0);
+ prev_insn.insn = NULL;
+ seen_relaxable_p = 0;
+}
+
+/* Record the symbol so that when we output the insn, we can create
+ a symbol that is at the start of the instruction. This is used
+ to emit the label for the start of a breakpoint without causing
+ the assembler to emit a NOP if the previous instruction was a
+ 16 bit instruction. */
+
+static void
+debug_sym (ignore)
+ int ignore;
+{
+ register char *name;
+ register char delim;
+ register char *end_name;
+ register symbolS *symbolP;
+ register sym_linkS *link;
+
+ name = input_line_pointer;
+ delim = get_symbol_end ();
+ end_name = input_line_pointer;
+
+ if ((symbolP = symbol_find (name)) == NULL
+ && (symbolP = md_undefined_symbol (name)) == NULL)
+ {
+ symbolP = symbol_new (name, undefined_section, 0, &zero_address_frag);
+ }
+
+ symbol_table_insert (symbolP);
+ if (S_IS_DEFINED (symbolP) && S_GET_SEGMENT (symbolP) != reg_section)
+ /* xgettext:c-format */
+ as_bad (_("symbol `%s' already defined"), S_GET_NAME (symbolP));
+
+ else
+ {
+ link = (sym_linkS *) xmalloc (sizeof (sym_linkS));
+ link->symbol = symbolP;
+ link->next = debug_sym_link;
+ debug_sym_link = link;
+ symbolP->local = 1;
+ }
+
+ *end_name = delim;
+ demand_empty_rest_of_line ();
+}
+
+/* Second pass to expanding the debug symbols, go through linked
+ list of symbols and reassign the address. */
+
+static void
+expand_debug_syms (syms, align)
+ sym_linkS *syms;
+ int align;
+{
+ char *save_input_line = input_line_pointer;
+ sym_linkS *next_syms;
+
+ if (!syms)
+ return;
+
+ (void) m32r_do_align (align, NULL, 0, 0);
+ for (; syms != (sym_linkS *)0; syms = next_syms)
+ {
+ symbolS *symbolP = syms->symbol;
+ next_syms = syms->next;
+ input_line_pointer = ".\n";
+ pseudo_set (symbolP);
+ free ((char *)syms);
+ }
+
+ input_line_pointer = save_input_line;
+}
+
+/* Cover function to fill_insn called after a label and at end of assembly.
+ The result is always 1: we're called in a conditional to see if the
+ current line is a label. */
+
+int
+m32r_fill_insn (done)
+ int done;
+{
+ if (prev_seg != NULL)
+ {
+ segT seg = now_seg;
+ subsegT subseg = now_subseg;
+
+ subseg_set (prev_seg, prev_subseg);
+
+ fill_insn (0);
+
+ subseg_set (seg, subseg);
+ }
+
+ if (done && debug_sym_link)
+ {
+ expand_debug_syms (debug_sym_link, 1);
+ debug_sym_link = (sym_linkS *)0;
+ }
+
+ return 1;
+}
+
+void
+md_begin ()
+{
+ flagword applicable;
+ segT seg;
+ subsegT subseg;
+
+ /* Initialize the `cgen' interface. */
+
+ /* Set the machine number and endian. */
+ gas_cgen_cpu_desc = m32r_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, 0,
+ CGEN_CPU_OPEN_ENDIAN,
+ CGEN_ENDIAN_BIG,
+ CGEN_CPU_OPEN_END);
+ m32r_cgen_init_asm (gas_cgen_cpu_desc);
+
+ /* The operand instance table is used during optimization to determine
+ which insns can be executed in parallel. It is also used to give
+ warnings regarding operand interference in parallel insns. */
+ m32r_cgen_init_opinst_table (gas_cgen_cpu_desc);
+
+ /* This is a callback from cgen to gas to parse operands. */
+ cgen_set_parse_operand_fn (gas_cgen_cpu_desc, gas_cgen_parse_operand);
+
+#if 0 /* not supported yet */
+ /* If a runtime cpu description file was provided, parse it. */
+ if (m32r_cpu_desc != NULL)
+ {
+ const char * errmsg;
+
+ errmsg = cgen_read_cpu_file (gas_cgen_cpu_desc, m32r_cpu_desc);
+ if (errmsg != NULL)
+ as_bad ("%s: %s", m32r_cpu_desc, errmsg);
+ }
+#endif
+
+ /* Save the current subseg so we can restore it [it's the default one and
+ we don't want the initial section to be .sbss]. */
+ seg = now_seg;
+ subseg = now_subseg;
+
+ /* The sbss section is for local .scomm symbols. */
+ sbss_section = subseg_new (".sbss", 0);
+
+ /* This is copied from perform_an_assembly_pass. */
+ applicable = bfd_applicable_section_flags (stdoutput);
+ bfd_set_section_flags (stdoutput, sbss_section, applicable & SEC_ALLOC);
+
+#if 0 /* What does this do? [see perform_an_assembly_pass] */
+ seg_info (bss_section)->bss = 1;
+#endif
+
+ subseg_set (seg, subseg);
+
+ /* We must construct a fake section similar to bfd_com_section
+ but with the name .scommon. */
+ scom_section = bfd_com_section;
+ scom_section.name = ".scommon";
+ scom_section.output_section = & scom_section;
+ scom_section.symbol = & scom_symbol;
+ scom_section.symbol_ptr_ptr = & scom_section.symbol;
+ scom_symbol = * bfd_com_section.symbol;
+ scom_symbol.name = ".scommon";
+ scom_symbol.section = & scom_section;
+
+}
+
+
+
+void
+md_assemble (str)
+ char * str;
+{
+ m32r_insn insn;
+ char * errmsg;
+ char * str2 = NULL;
+
+ /* Initialize GAS's cgen interface for a new instruction. */
+ gas_cgen_init_parse ();
+
+
+ insn.debug_sym_link = debug_sym_link;
+ debug_sym_link = (sym_linkS *)0;
+
+ insn.insn = m32r_cgen_assemble_insn
+ (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg);
+
+ if (!insn.insn)
+ {
+ as_bad (errmsg);
+ return;
+ }
+
+
+ if (CGEN_INSN_BITSIZE (insn.insn) == 32)
+ {
+ /* 32 bit insns must live on 32 bit boundaries. */
+ if (prev_insn.insn || seen_relaxable_p)
+ {
+ /* ??? If calling fill_insn too many times turns us into a memory
+ pig, can we call a fn to assemble a nop instead of
+ !seen_relaxable_p? */
+ fill_insn (0);
+ }
+
+ expand_debug_syms (insn.debug_sym_link, 2);
+
+ /* Doesn't really matter what we pass for RELAX_P here. */
+ gas_cgen_finish_insn (insn.insn, insn.buffer,
+ CGEN_FIELDS_BITSIZE (& insn.fields), 1, NULL);
+ }
+ else
+ {
+ int on_32bit_boundary_p;
+
+ if (CGEN_INSN_BITSIZE (insn.insn) != 16)
+ abort();
+
+ insn.orig_insn = insn.insn;
+
+ /* Compute whether we're on a 32 bit boundary or not.
+ prev_insn.insn is NULL when we're on a 32 bit boundary. */
+ on_32bit_boundary_p = prev_insn.insn == NULL;
+
+
+ expand_debug_syms (insn.debug_sym_link, 1);
+
+ {
+ int i;
+ finished_insnS fi;
+
+ /* Ensure each pair of 16 bit insns is in the same frag. */
+ frag_grow (4);
+
+ gas_cgen_finish_insn (insn.orig_insn, insn.buffer,
+ CGEN_FIELDS_BITSIZE (& insn.fields),
+ 1 /*relax_p*/, &fi);
+ insn.addr = fi.addr;
+ insn.frag = fi.frag;
+ insn.num_fixups = fi.num_fixups;
+ for (i = 0; i < fi.num_fixups; ++i)
+ insn.fixups[i] = fi.fixups[i];
+ }
+
+
+ /* Keep track of whether we've seen a pair of 16 bit insns.
+ prev_insn.insn is NULL when we're on a 32 bit boundary. */
+ if (on_32bit_boundary_p)
+ prev_insn = insn;
+ else
+ prev_insn.insn = NULL;
+
+ /* If the insn needs the following one to be on a 32 bit boundary
+ (e.g. subroutine calls), fill this insn's slot. */
+ if (on_32bit_boundary_p
+ && CGEN_INSN_ATTR_VALUE (insn.orig_insn, CGEN_INSN_FILL_SLOT) != 0)
+ fill_insn (0);
+
+ /* If this is a relaxable insn (can be replaced with a larger version)
+ mark the fact so that we can emit an alignment directive for a
+ following 32 bit insn if we see one. */
+ if (CGEN_INSN_ATTR_VALUE (insn.orig_insn, CGEN_INSN_RELAXABLE) != 0)
+ seen_relaxable_p = 1;
+ }
+
+ /* Set these so m32r_fill_insn can use them. */
+ prev_seg = now_seg;
+ prev_subseg = now_subseg;
+}
+
+/* The syntax in the manual says constants begin with '#'.
+ We just ignore it. */
+
+void
+md_operand (expressionP)
+ expressionS * expressionP;
+{
+ if (* input_line_pointer == '#')
+ {
+ input_line_pointer ++;
+ expression (expressionP);
+ }
+}
+
+valueT
+md_section_align (segment, size)
+ segT segment;
+ valueT size;
+{
+ int align = bfd_get_section_alignment (stdoutput, segment);
+ return ((size + (1 << align) - 1) & (-1 << align));
+}
+
+symbolS *
+md_undefined_symbol (name)
+ char * name;
+{
+ return 0;
+}
+
+/* .scomm pseudo-op handler.
+
+ This is a new pseudo-op to handle putting objects in .scommon.
+ By doing this the linker won't need to do any work and more importantly
+ it removes the implicit -G arg necessary to correctly link the object file.
+*/
+
+static void
+m32r_scomm (ignore)
+ int ignore;
+{
+ register char * name;
+ register char c;
+ register char * p;
+ offsetT size;
+ register symbolS * symbolP;
+ offsetT align;
+ int align2;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ /* just after name is now '\0' */
+ p = input_line_pointer;
+ * p = c;
+ SKIP_WHITESPACE ();
+ if (* input_line_pointer != ',')
+ {
+ as_bad (_("Expected comma after symbol-name: rest of line ignored."));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ input_line_pointer ++; /* skip ',' */
+ if ((size = get_absolute_expression ()) < 0)
+ {
+ /* xgettext:c-format */
+ as_warn (_(".SCOMMon length (%ld.) <0! Ignored."), (long) size);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* The third argument to .scomm is the alignment. */
+ if (* input_line_pointer != ',')
+ align = 8;
+ else
+ {
+ ++ input_line_pointer;
+ align = get_absolute_expression ();
+ if (align <= 0)
+ {
+ as_warn (_("ignoring bad alignment"));
+ align = 8;
+ }
+ }
+ /* Convert to a power of 2 alignment. */
+ if (align)
+ {
+ for (align2 = 0; (align & 1) == 0; align >>= 1, ++ align2)
+ continue;
+ if (align != 1)
+ {
+ as_bad (_("Common alignment not a power of 2"));
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ else
+ align2 = 0;
+
+ * p = 0;
+ symbolP = symbol_find_or_make (name);
+ * p = c;
+
+ if (S_IS_DEFINED (symbolP))
+ {
+ /* xgettext:c-format */
+ as_bad (_("Ignoring attempt to re-define symbol `%s'."),
+ S_GET_NAME (symbolP));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (S_GET_VALUE (symbolP) && S_GET_VALUE (symbolP) != (valueT) size)
+ {
+ /* xgettext:c-format */
+ as_bad (_("Length of .scomm \"%s\" is already %ld. Not changed to %ld."),
+ S_GET_NAME (symbolP),
+ (long) S_GET_VALUE (symbolP),
+ (long) size);
+
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (symbolP->local)
+ {
+ segT old_sec = now_seg;
+ int old_subsec = now_subseg;
+ char * pfrag;
+
+ record_alignment (sbss_section, align2);
+ subseg_set (sbss_section, 0);
+
+ if (align2)
+ frag_align (align2, 0, 0);
+
+ if (S_GET_SEGMENT (symbolP) == sbss_section)
+ symbolP->sy_frag->fr_symbol = 0;
+
+ symbolP->sy_frag = frag_now;
+
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP, size,
+ (char *) 0);
+ * pfrag = 0;
+ S_SET_SIZE (symbolP, size);
+ S_SET_SEGMENT (symbolP, sbss_section);
+ S_CLEAR_EXTERNAL (symbolP);
+ subseg_set (old_sec, old_subsec);
+ }
+ else
+ {
+ S_SET_VALUE (symbolP, (valueT) size);
+ S_SET_ALIGN (symbolP, align2);
+ S_SET_EXTERNAL (symbolP);
+ S_SET_SEGMENT (symbolP, & scom_section);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Interface to relax_segment. */
+
+/* FIXME: Build table by hand, get it working, then machine generate. */
+
+const relax_typeS md_relax_table[] =
+{
+/* The fields are:
+ 1) most positive reach of this state,
+ 2) most negative reach of this state,
+ 3) how many bytes this mode will add to the size of the current frag
+ 4) which index into the table to try if we can't fit into this one. */
+
+ /* The first entry must be unused because an `rlx_more' value of zero ends
+ each list. */
+ {1, 1, 0, 0},
+
+ /* The displacement used by GAS is from the end of the 2 byte insn,
+ so we subtract 2 from the following. */
+ /* 16 bit insn, 8 bit disp -> 10 bit range.
+ This doesn't handle a branch in the right slot at the border:
+ the "& -4" isn't taken into account. It's not important enough to
+ complicate things over it, so we subtract an extra 2 (or + 2 in -ve
+ case). */
+ {511 - 2 - 2, -512 - 2 + 2, 0, 2 },
+ /* 32 bit insn, 24 bit disp -> 26 bit range. */
+ {0x2000000 - 1 - 2, -0x2000000 - 2, 2, 0 },
+ /* Same thing, but with leading nop for alignment. */
+ {0x2000000 - 1 - 2, -0x2000000 - 2, 4, 0 }
+};
+
+long
+m32r_relax_frag (fragP, stretch)
+ fragS * fragP;
+ long stretch;
+{
+ /* Address of branch insn. */
+ long address = fragP->fr_address + fragP->fr_fix - 2;
+ long growth = 0;
+
+ /* Keep 32 bit insns aligned on 32 bit boundaries. */
+ if (fragP->fr_subtype == 2)
+ {
+ if ((address & 3) != 0)
+ {
+ fragP->fr_subtype = 3;
+ growth = 2;
+ }
+ }
+ else if (fragP->fr_subtype == 3)
+ {
+ if ((address & 3) == 0)
+ {
+ fragP->fr_subtype = 2;
+ growth = -2;
+ }
+ }
+ else
+ {
+ growth = relax_frag (fragP, stretch);
+
+ /* Long jump on odd halfword boundary? */
+ if (fragP->fr_subtype == 2 && (address & 3) != 0)
+ {
+ fragP->fr_subtype = 3;
+ growth += 2;
+ }
+ }
+
+ return growth;
+}
+
+/* Return an initial guess of the length by which a fragment must grow to
+ hold a branch to reach its destination.
+ Also updates fr_type/fr_subtype as necessary.
+
+ Called just before doing relaxation.
+ Any symbol that is now undefined will not become defined.
+ The guess for fr_var is ACTUALLY the growth beyond fr_fix.
+ Whatever we do to grow fr_fix or fr_var contributes to our returned value.
+ Although it may not be explicit in the frag, pretend fr_var starts with a
+ 0 value. */
+
+int
+md_estimate_size_before_relax (fragP, segment)
+ fragS * fragP;
+ segT segment;
+{
+ int old_fr_fix = fragP->fr_fix;
+
+ /* The only thing we have to handle here are symbols outside of the
+ current segment. They may be undefined or in a different segment in
+ which case linker scripts may place them anywhere.
+ However, we can't finish the fragment here and emit the reloc as insn
+ alignment requirements may move the insn about. */
+
+ if (S_GET_SEGMENT (fragP->fr_symbol) != segment)
+ {
+ /* The symbol is undefined in this segment.
+ Change the relaxation subtype to the max allowable and leave
+ all further handling to md_convert_frag. */
+ fragP->fr_subtype = 2;
+
+#if 0 /* Can't use this, but leave in for illustration. */
+ /* Change 16 bit insn to 32 bit insn. */
+ fragP->fr_opcode[0] |= 0x80;
+
+ /* Increase known (fixed) size of fragment. */
+ fragP->fr_fix += 2;
+
+ /* Create a relocation for it. */
+ fix_new (fragP, old_fr_fix, 4,
+ fragP->fr_symbol,
+ fragP->fr_offset, 1 /* pcrel */,
+ /* FIXME: Can't use a real BFD reloc here.
+ gas_cgen_md_apply_fix3 can't handle it. */
+ BFD_RELOC_M32R_26_PCREL);
+
+ /* Mark this fragment as finished. */
+ frag_wane (fragP);
+#else
+ {
+ const CGEN_INSN * insn;
+ int i;
+
+ /* Update the recorded insn.
+ Fortunately we don't have to look very far.
+ FIXME: Change this to record in the instruction the next higher
+ relaxable insn to use. */
+ for (i = 0, insn = fragP->fr_cgen.insn; i < 4; i++, insn++)
+ {
+ if ((strcmp (CGEN_INSN_MNEMONIC (insn),
+ CGEN_INSN_MNEMONIC (fragP->fr_cgen.insn))
+ == 0)
+ && CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_RELAX))
+ break;
+ }
+ if (i == 4)
+ abort ();
+
+ fragP->fr_cgen.insn = insn;
+ return 2;
+ }
+#endif
+ }
+
+ return (fragP->fr_var + fragP->fr_fix - old_fr_fix);
+}
+
+/* *fragP has been relaxed to its final size, and now needs to have
+ the bytes inside it modified to conform to the new size.
+
+ Called after relaxation is finished.
+ fragP->fr_type == rs_machine_dependent.
+ fragP->fr_subtype is the subtype of what the address relaxed to. */
+
+void
+md_convert_frag (abfd, sec, fragP)
+ bfd * abfd;
+ segT sec;
+ fragS * fragP;
+{
+ char * opcode;
+ char * displacement;
+ int target_address;
+ int opcode_address;
+ int extension;
+ int addend;
+
+ opcode = fragP->fr_opcode;
+
+ /* Address opcode resides at in file space. */
+ opcode_address = fragP->fr_address + fragP->fr_fix - 2;
+
+ switch (fragP->fr_subtype)
+ {
+ case 1 :
+ extension = 0;
+ displacement = & opcode[1];
+ break;
+ case 2 :
+ opcode[0] |= 0x80;
+ extension = 2;
+ displacement = & opcode[1];
+ break;
+ case 3 :
+ opcode[2] = opcode[0] | 0x80;
+ md_number_to_chars (opcode, PAR_NOP_INSN, 2);
+ opcode_address += 2;
+ extension = 4;
+ displacement = & opcode[3];
+ break;
+ default :
+ abort ();
+ }
+
+ if (S_GET_SEGMENT (fragP->fr_symbol) != sec)
+ {
+ /* symbol must be resolved by linker */
+ if (fragP->fr_offset & 3)
+ as_warn (_("Addend to unresolved symbol not on word boundary."));
+ addend = fragP->fr_offset >> 2;
+ }
+ else
+ {
+ /* Address we want to reach in file space. */
+ target_address = S_GET_VALUE (fragP->fr_symbol) + fragP->fr_offset;
+ target_address += fragP->fr_symbol->sy_frag->fr_address;
+ addend = (target_address - (opcode_address & -4)) >> 2;
+ }
+
+ /* Create a relocation for symbols that must be resolved by the linker.
+ Otherwise output the completed insn. */
+
+ if (S_GET_SEGMENT (fragP->fr_symbol) != sec)
+ {
+ assert (fragP->fr_subtype != 1);
+ assert (fragP->fr_cgen.insn != 0);
+ gas_cgen_record_fixup (fragP,
+ /* Offset of branch insn in frag. */
+ fragP->fr_fix + extension - 4,
+ fragP->fr_cgen.insn,
+ 4 /*length*/,
+ /* FIXME: quick hack */
+#if 0
+ cgen_operand_lookup_by_num (gas_cgen_cpu_desc,
+ fragP->fr_cgen.opindex),
+#else
+ cgen_operand_lookup_by_num (gas_cgen_cpu_desc,
+ M32R_OPERAND_DISP24),
+#endif
+ fragP->fr_cgen.opinfo,
+ fragP->fr_symbol, fragP->fr_offset);
+ }
+
+#define SIZE_FROM_RELAX_STATE(n) ((n) == 1 ? 1 : 3)
+
+ md_number_to_chars (displacement, (valueT) addend,
+ SIZE_FROM_RELAX_STATE (fragP->fr_subtype));
+
+ fragP->fr_fix += extension;
+}
+
+/* Functions concerning relocs. */
+
+/* The location from which a PC relative jump should be calculated,
+ given a PC relative reloc. */
+
+long
+md_pcrel_from_section (fixP, sec)
+ fixS * fixP;
+ segT sec;
+{
+ if (fixP->fx_addsy != (symbolS *) NULL
+ && (! S_IS_DEFINED (fixP->fx_addsy)
+ || S_GET_SEGMENT (fixP->fx_addsy) != sec))
+ {
+ /* The symbol is undefined (or is defined but not in this section).
+ Let the linker figure it out. */
+ return 0;
+ }
+
+ return (fixP->fx_frag->fr_address + fixP->fx_where) & -4L;
+}
+
+/* Return the bfd reloc type for OPERAND of INSN at fixup FIXP.
+ Returns BFD_RELOC_NONE if no reloc type can be found.
+ *FIXP may be modified if desired. */
+
+bfd_reloc_code_real_type
+md_cgen_lookup_reloc (insn, operand, fixP)
+ const CGEN_INSN * insn;
+ const CGEN_OPERAND * operand;
+ fixS * fixP;
+{
+ switch (operand->type)
+ {
+ case M32R_OPERAND_DISP8 : return BFD_RELOC_M32R_10_PCREL;
+ case M32R_OPERAND_DISP16 : return BFD_RELOC_M32R_18_PCREL;
+ case M32R_OPERAND_DISP24 : return BFD_RELOC_M32R_26_PCREL;
+ case M32R_OPERAND_UIMM24 : return BFD_RELOC_M32R_24;
+ case M32R_OPERAND_HI16 :
+ case M32R_OPERAND_SLO16 :
+ case M32R_OPERAND_ULO16 :
+ /* If low/high/shigh/sda was used, it is recorded in `opinfo'. */
+ if (fixP->fx_cgen.opinfo != 0)
+ return fixP->fx_cgen.opinfo;
+ break;
+ default : /* avoid -Wall warning */
+ break;
+ }
+ return BFD_RELOC_NONE;
+}
+
+/* Record a HI16 reloc for later matching with its LO16 cousin. */
+
+static void
+m32r_record_hi16 (reloc_type, fixP, seg)
+ int reloc_type;
+ fixS * fixP;
+ segT seg;
+{
+ struct m32r_hi_fixup * hi_fixup;
+
+ assert (reloc_type == BFD_RELOC_M32R_HI16_SLO
+ || reloc_type == BFD_RELOC_M32R_HI16_ULO);
+
+ hi_fixup = ((struct m32r_hi_fixup *)
+ xmalloc (sizeof (struct m32r_hi_fixup)));
+ hi_fixup->fixp = fixP;
+ hi_fixup->seg = now_seg;
+ hi_fixup->next = m32r_hi_fixup_list;
+
+ m32r_hi_fixup_list = hi_fixup;
+}
+
+/* Called while parsing an instruction to create a fixup.
+ We need to check for HI16 relocs and queue them up for later sorting. */
+
+fixS *
+m32r_cgen_record_fixup_exp (frag, where, insn, length, operand, opinfo, exp)
+ fragS * frag;
+ int where;
+ const CGEN_INSN * insn;
+ int length;
+ const CGEN_OPERAND * operand;
+ int opinfo;
+ expressionS * exp;
+{
+ fixS * fixP = gas_cgen_record_fixup_exp (frag, where, insn, length,
+ operand, opinfo, exp);
+
+ switch (operand->type)
+ {
+ case M32R_OPERAND_HI16 :
+ /* If low/high/shigh/sda was used, it is recorded in `opinfo'. */
+ if (fixP->fx_cgen.opinfo == BFD_RELOC_M32R_HI16_SLO
+ || fixP->fx_cgen.opinfo == BFD_RELOC_M32R_HI16_ULO)
+ m32r_record_hi16 (fixP->fx_cgen.opinfo, fixP, now_seg);
+ break;
+ default : /* avoid -Wall warning */
+ break;
+ }
+
+ return fixP;
+}
+
+/* Return BFD reloc type from opinfo field in a fixS.
+ It's tricky using fx_r_type in m32r_frob_file because the values
+ are BFD_RELOC_UNUSED + operand number. */
+#define FX_OPINFO_R_TYPE(f) ((f)->fx_cgen.opinfo)
+
+/* Sort any unmatched HI16 relocs so that they immediately precede
+ the corresponding LO16 reloc. This is called before md_apply_fix and
+ tc_gen_reloc. */
+
+void
+m32r_frob_file ()
+{
+ struct m32r_hi_fixup * l;
+
+ for (l = m32r_hi_fixup_list; l != NULL; l = l->next)
+ {
+ segment_info_type * seginfo;
+ int pass;
+
+ assert (FX_OPINFO_R_TYPE (l->fixp) == BFD_RELOC_M32R_HI16_SLO
+ || FX_OPINFO_R_TYPE (l->fixp) == BFD_RELOC_M32R_HI16_ULO);
+
+ /* Check quickly whether the next fixup happens to be a matching low. */
+ if (l->fixp->fx_next != NULL
+ && FX_OPINFO_R_TYPE (l->fixp->fx_next) == BFD_RELOC_M32R_LO16
+ && l->fixp->fx_addsy == l->fixp->fx_next->fx_addsy
+ && l->fixp->fx_offset == l->fixp->fx_next->fx_offset)
+ continue;
+
+ /* Look through the fixups for this segment for a matching `low'.
+ When we find one, move the high/shigh just in front of it. We do
+ this in two passes. In the first pass, we try to find a
+ unique `low'. In the second pass, we permit multiple high's
+ relocs for a single `low'. */
+ seginfo = seg_info (l->seg);
+ for (pass = 0; pass < 2; pass++)
+ {
+ fixS * f;
+ fixS * prev;
+
+ prev = NULL;
+ for (f = seginfo->fix_root; f != NULL; f = f->fx_next)
+ {
+ /* Check whether this is a `low' fixup which matches l->fixp. */
+ if (FX_OPINFO_R_TYPE (f) == BFD_RELOC_M32R_LO16
+ && f->fx_addsy == l->fixp->fx_addsy
+ && f->fx_offset == l->fixp->fx_offset
+ && (pass == 1
+ || prev == NULL
+ || (FX_OPINFO_R_TYPE (prev) != BFD_RELOC_M32R_HI16_SLO
+ && FX_OPINFO_R_TYPE (prev) != BFD_RELOC_M32R_HI16_ULO)
+ || prev->fx_addsy != f->fx_addsy
+ || prev->fx_offset != f->fx_offset))
+ {
+ fixS ** pf;
+
+ /* Move l->fixp before f. */
+ for (pf = &seginfo->fix_root;
+ * pf != l->fixp;
+ pf = & (* pf)->fx_next)
+ assert (* pf != NULL);
+
+ * pf = l->fixp->fx_next;
+
+ l->fixp->fx_next = f;
+ if (prev == NULL)
+ seginfo->fix_root = l->fixp;
+ else
+ prev->fx_next = l->fixp;
+
+ break;
+ }
+
+ prev = f;
+ }
+
+ if (f != NULL)
+ break;
+
+ if (pass == 1
+ && warn_unmatched_high)
+ as_warn_where (l->fixp->fx_file, l->fixp->fx_line,
+ _("Unmatched high/shigh reloc"));
+ }
+ }
+}
+
+/* See whether we need to force a relocation into the output file.
+ This is used to force out switch and PC relative relocations when
+ relaxing. */
+
+int
+m32r_force_relocation (fix)
+ fixS * fix;
+{
+ if (fix->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fix->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 1;
+
+ if (! m32r_relax)
+ return 0;
+
+ return (fix->fx_pcrel
+ || 0 /* ??? */);
+}
+
+/* Write a value out to the object file, using the appropriate endianness. */
+
+void
+md_number_to_chars (buf, val, n)
+ char * buf;
+ valueT val;
+ int n;
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+/* Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP . An error message is returned, or NULL on OK.
+*/
+
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+char *
+md_atof (type, litP, sizeP)
+ char type;
+ char *litP;
+ int *sizeP;
+{
+ int i;
+ int prec;
+ LITTLENUM_TYPE words [MAX_LITTLENUMS];
+ char * t;
+ char * atof_ieee ();
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ /* FIXME: Some targets allow other format chars for bigger sizes here. */
+
+ default:
+ * sizeP = 0;
+ return _("Bad call to md_atof()");
+ }
+
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+ * sizeP = prec * sizeof (LITTLENUM_TYPE);
+
+ if (target_big_endian)
+ {
+ for (i = 0; i < prec; i++)
+ {
+ md_number_to_chars (litP, (valueT) words[i],
+ sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ }
+ else
+ {
+ for (i = prec - 1; i >= 0; i--)
+ {
+ md_number_to_chars (litP, (valueT) words[i],
+ sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ }
+
+ return 0;
+}
+
+void
+m32r_elf_section_change_hook ()
+{
+ /* If we have reached the end of a section and we have just emitted a
+ 16 bit insn, then emit a nop to make sure that the section ends on
+ a 32 bit boundary. */
+
+ if (prev_insn.insn || seen_relaxable_p)
+ (void) m32r_fill_insn (0);
+}
+
+boolean
+m32r_fix_adjustable (fixP)
+ fixS *fixP;
+{
+
+ if (fixP->fx_addsy == NULL)
+ return 1;
+
+ /* Prevent all adjustments to global symbols. */
+ if (S_IS_EXTERN (fixP->fx_addsy))
+ return 0;
+ if (S_IS_WEAK (fixP->fx_addsy))
+ return 0;
+
+ /* We need the symbol name for the VTABLE entries */
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ return 1;
+}
diff --git a/gas/config/tc-m32r.h b/gas/config/tc-m32r.h
new file mode 100644
index 0000000..ebcfca1
--- /dev/null
+++ b/gas/config/tc-m32r.h
@@ -0,0 +1,102 @@
+/* tc-m32r.h -- Header file for tc-m32r.c.
+ Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#define TC_M32R
+
+#ifndef BFD_ASSEMBLER
+/* leading space so will compile with cc */
+ #error M32R support requires BFD_ASSEMBLER
+#endif
+
+#define LISTING_HEADER "M32R GAS "
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_m32r
+
+#define TARGET_FORMAT "elf32-m32r"
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+/* call md_pcrel_from_section, not md_pcrel_from */
+long md_pcrel_from_section PARAMS ((struct fix *, segT));
+#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section(FIXP, SEC)
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+#define DIFF_EXPR_OK /* .-foo gets turned into PC relative relocs */
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+/* For 8 vs 16 vs 32 bit branch selection. */
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+#if 0
+extern void m32r_prepare_relax_scan ();
+#define md_prepare_relax_scan(fragP, address, aim, this_state, this_type) \
+m32r_prepare_relax_scan (fragP, address, aim, this_state, this_type)
+#else
+extern long m32r_relax_frag PARAMS ((fragS *, long));
+#define md_relax_frag(fragP, stretch) \
+m32r_relax_frag (fragP, stretch)
+#endif
+/* Account for nop if 32 bit insn falls on odd halfword boundary. */
+#define TC_CGEN_MAX_RELAX(insn, len) (6)
+
+/* Alignments are used to ensure 32 bit insns live on 32 bit boundaries, so
+ we use a special alignment function to insert the correct nop pattern. */
+extern int m32r_do_align PARAMS ((int, const char *, int, int));
+#define md_do_align(n, fill, len, max, l) \
+if (m32r_do_align (n, fill, len, max)) goto l
+
+#define MD_APPLY_FIX3
+#define md_apply_fix3 gas_cgen_md_apply_fix3
+
+#define obj_fix_adjustable(fixP) m32r_fix_adjustable(fixP)
+
+/* After creating a fixup for an instruction operand, we need to check for
+ HI16 relocs and queue them up for later sorting. */
+#define md_cgen_record_fixup_exp m32r_cgen_record_fixup_exp
+
+#define TC_HANDLES_FX_DONE
+
+#define tc_gen_reloc gas_cgen_tc_gen_reloc
+
+#define tc_frob_file() m32r_frob_file ()
+extern void m32r_frob_file PARAMS ((void));
+
+/* When relaxing, we need to emit various relocs we otherwise wouldn't. */
+#define TC_FORCE_RELOCATION(fix) m32r_force_relocation (fix)
+extern int m32r_force_relocation ();
+
+/* Ensure insns at labels are aligned to 32 bit boundaries. */
+int m32r_fill_insn PARAMS ((int));
+#define md_after_pass_hook() m32r_fill_insn (1)
+#define TC_START_LABEL(ch, ptr) (ch == ':' && m32r_fill_insn (0))
+
+/* Add extra M32R sections. */
+#define ELF_TC_SPECIAL_SECTIONS \
+ { ".sdata", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE }, \
+ { ".sbss", SHT_NOBITS, SHF_ALLOC + SHF_WRITE },
+
+#define md_cleanup m32r_elf_section_change_hook
+#define md_elf_section_change_hook m32r_elf_section_change_hook
+extern void m32r_elf_section_change_hook ();
diff --git a/gas/config/tc-m68851.h b/gas/config/tc-m68851.h
new file mode 100644
index 0000000..0f6d741
--- /dev/null
+++ b/gas/config/tc-m68851.h
@@ -0,0 +1,304 @@
+/* This file is tc-m68851.h
+
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/*
+ * pmmu.h
+ */
+
+/* I suppose we have to copyright this file. Someone on the net sent it
+ to us as part of the changes for the m68851 Memory Management Unit */
+
+/* Copyright (C) 1987 Free Software Foundation, Inc.
+
+ This file is part of Gas, the GNU Assembler.
+
+ The GNU assembler is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY. No author or distributor
+ accepts responsibility to anyone for the consequences of using it
+ or for whether it serves any particular purpose or works at all,
+ unless he says so in writing. Refer to the GNU Assembler General
+ Public License for full details.
+
+ Everyone is granted permission to copy, modify and redistribute
+ the GNU Assembler, but only under the conditions described in the
+ GNU Assembler General Public License. A copy of this license is
+ supposed to have been given to you along with the GNU Assembler
+ so you can know your rights and responsibilities. It should be
+ in a file named COPYING. Among other things, the copyright
+ notice and this notice must be preserved on all copies. */
+
+#ifdef m68851
+
+/*
+ I didn't use much imagination in choosing the
+ following codes, so many of them aren't very
+ mnemonic. -rab
+
+ P pmmu register
+ Possible values:
+ 000 TC Translation Control reg
+ 100 CAL Current Access Level
+ 101 VAL Validate Access Level
+ 110 SCC Stack Change Control
+ 111 AC Access Control
+
+ W wide pmmu registers
+ Possible values:
+ 001 DRP Dma Root Pointer
+ 010 SRP Supervisor Root Pointer
+ 011 CRP Cpu Root Pointer
+
+ f function code register
+ 0 SFC
+ 1 DFC
+
+ V VAL register only
+
+ X BADx, BACx
+ 100 BAD Breakpoint Acknowledge Data
+ 101 BAC Breakpoint Acknowledge Control
+
+ Y PSR
+ Z PCSR
+
+ | memory (modes 2-6, 7.*)
+
+ */
+
+/*
+ * these defines should be in m68k.c but
+ * i put them here to keep all the m68851 stuff
+ * together -rab
+ * JF--Make sure these #s don't clash with the ones in m68k.c
+ * That would be BAD.
+ */
+#define TC (FPS+1) /* 48 */
+#define DRP (TC+1) /* 49 */
+#define SRP (DRP+1) /* 50 */
+#define CRP (SRP+1) /* 51 */
+#define CAL (CRP+1) /* 52 */
+#define VAL (CAL+1) /* 53 */
+#define SCC (VAL+1) /* 54 */
+#define AC (SCC+1) /* 55 */
+#define BAD (AC+1) /* 56,57,58,59, 60,61,62,63 */
+#define BAC (BAD+8) /* 64,65,66,67, 68,69,70,71 */
+#define PSR (BAC+8) /* 72 */
+#define PCSR (PSR+1) /* 73 */
+
+/* name */ /* opcode */ /* match */ /* args */
+
+{"pbac", one(0xf0c7), one(0xffbf), "Bc"},
+{"pbacw", one(0xf087), one(0xffbf), "Bc"},
+{"pbas", one(0xf0c6), one(0xffbf), "Bc"},
+{"pbasw", one(0xf086), one(0xffbf), "Bc"},
+{"pbbc", one(0xf0c1), one(0xffbf), "Bc"},
+{"pbbcw", one(0xf081), one(0xffbf), "Bc"},
+{"pbbs", one(0xf0c0), one(0xffbf), "Bc"},
+{"pbbsw", one(0xf080), one(0xffbf), "Bc"},
+{"pbcc", one(0xf0cf), one(0xffbf), "Bc"},
+{"pbccw", one(0xf08f), one(0xffbf), "Bc"},
+{"pbcs", one(0xf0ce), one(0xffbf), "Bc"},
+{"pbcsw", one(0xf08e), one(0xffbf), "Bc"},
+{"pbgc", one(0xf0cd), one(0xffbf), "Bc"},
+{"pbgcw", one(0xf08d), one(0xffbf), "Bc"},
+{"pbgs", one(0xf0cc), one(0xffbf), "Bc"},
+{"pbgsw", one(0xf08c), one(0xffbf), "Bc"},
+{"pbic", one(0xf0cb), one(0xffbf), "Bc"},
+{"pbicw", one(0xf08b), one(0xffbf), "Bc"},
+{"pbis", one(0xf0ca), one(0xffbf), "Bc"},
+{"pbisw", one(0xf08a), one(0xffbf), "Bc"},
+{"pblc", one(0xf0c3), one(0xffbf), "Bc"},
+{"pblcw", one(0xf083), one(0xffbf), "Bc"},
+{"pbls", one(0xf0c2), one(0xffbf), "Bc"},
+{"pblsw", one(0xf082), one(0xffbf), "Bc"},
+{"pbsc", one(0xf0c5), one(0xffbf), "Bc"},
+{"pbscw", one(0xf085), one(0xffbf), "Bc"},
+{"pbss", one(0xf0c4), one(0xffbf), "Bc"},
+{"pbssw", one(0xf084), one(0xffbf), "Bc"},
+{"pbwc", one(0xf0c9), one(0xffbf), "Bc"},
+{"pbwcw", one(0xf089), one(0xffbf), "Bc"},
+{"pbws", one(0xf0c8), one(0xffbf), "Bc"},
+{"pbwsw", one(0xf088), one(0xffbf), "Bc"},
+
+
+{"pdbac", two(0xf048, 0x0007), two(0xfff8, 0xffff), "DsBw"},
+{"pdbas", two(0xf048, 0x0006), two(0xfff8, 0xffff), "DsBw"},
+{"pdbbc", two(0xf048, 0x0001), two(0xfff8, 0xffff), "DsBw"},
+{"pdbbs", two(0xf048, 0x0000), two(0xfff8, 0xffff), "DsBw"},
+{"pdbcc", two(0xf048, 0x000f), two(0xfff8, 0xffff), "DsBw"},
+{"pdbcs", two(0xf048, 0x000e), two(0xfff8, 0xffff), "DsBw"},
+{"pdbgc", two(0xf048, 0x000d), two(0xfff8, 0xffff), "DsBw"},
+{"pdbgs", two(0xf048, 0x000c), two(0xfff8, 0xffff), "DsBw"},
+{"pdbic", two(0xf048, 0x000b), two(0xfff8, 0xffff), "DsBw"},
+{"pdbis", two(0xf048, 0x000a), two(0xfff8, 0xffff), "DsBw"},
+{"pdblc", two(0xf048, 0x0003), two(0xfff8, 0xffff), "DsBw"},
+{"pdbls", two(0xf048, 0x0002), two(0xfff8, 0xffff), "DsBw"},
+{"pdbsc", two(0xf048, 0x0005), two(0xfff8, 0xffff), "DsBw"},
+{"pdbss", two(0xf048, 0x0004), two(0xfff8, 0xffff), "DsBw"},
+{"pdbwc", two(0xf048, 0x0009), two(0xfff8, 0xffff), "DsBw"},
+{"pdbws", two(0xf048, 0x0008), two(0xfff8, 0xffff), "DsBw"},
+
+{"pflusha", two(0xf000, 0x2400), two(0xffff, 0xffff), "" },
+
+{"pflush", two(0xf000, 0x3010), two(0xffc0, 0xfe10), "T3T9" },
+{"pflush", two(0xf000, 0x3810), two(0xffc0, 0xfe10), "T3T9&s" },
+{"pflush", two(0xf000, 0x3008), two(0xffc0, 0xfe18), "D3T9" },
+{"pflush", two(0xf000, 0x3808), two(0xffc0, 0xfe18), "D3T9&s" },
+{"pflush", two(0xf000, 0x3000), two(0xffc0, 0xfe1e), "f3T9" },
+{"pflush", two(0xf000, 0x3800), two(0xffc0, 0xfe1e), "f3T9&s" },
+
+{"pflushs", two(0xf000, 0x3410), two(0xfff8, 0xfe10), "T3T9" },
+{"pflushs", two(0xf000, 0x3c00), two(0xfff8, 0xfe00), "T3T9&s" },
+{"pflushs", two(0xf000, 0x3408), two(0xfff8, 0xfe18), "D3T9" },
+{"pflushs", two(0xf000, 0x3c08), two(0xfff8, 0xfe18), "D3T9&s" },
+{"pflushs", two(0xf000, 0x3400), two(0xfff8, 0xfe1e), "f3T9" },
+{"pflushs", two(0xf000, 0x3c00), two(0xfff8, 0xfe1e), "f3T9&s"},
+
+{"pflushr", two(0xf000, 0xa000), two(0xffc0, 0xffff), "|s" },
+
+{"ploadr", two(0xf000, 0x2210), two(0xffc0, 0xfff0), "T3&s" },
+{"ploadr", two(0xf000, 0x2208), two(0xffc0, 0xfff8), "D3&s" },
+{"ploadr", two(0xf000, 0x2200), two(0xffc0, 0xfffe), "f3&s" },
+{"ploadw", two(0xf000, 0x2010), two(0xffc0, 0xfff0), "T3&s" },
+{"ploadw", two(0xf000, 0x2008), two(0xffc0, 0xfff8), "D3&s" },
+{"ploadw", two(0xf000, 0x2000), two(0xffc0, 0xfffe), "f3&s" },
+
+ /* TC, CRP, DRP, SRP, CAL, VAL, SCC, AC */
+{"pmove", two(0xf000, 0x4000), two(0xffc0, 0xe3ff), "*sP8" },
+{"pmove", two(0xf000, 0x4200), two(0xffc0, 0xe3ff), "P8%s" },
+{"pmove", two(0xf000, 0x4000), two(0xffc0, 0xe3ff), "|sW8" },
+{"pmove", two(0xf000, 0x4200), two(0xffc0, 0xe3ff), "W8~s" },
+
+ /* BADx, BACx */
+{"pmove", two(0xf000, 0x6200), two(0xffc0, 0xe3e3), "*sX3" },
+{"pmove", two(0xf000, 0x6000), two(0xffc0, 0xe3e3), "X3%s" },
+
+ /* PSR, PCSR */
+ /* {"pmove", two(0xf000, 0x6100), two(oxffc0, oxffff), "*sZ8" }, */
+{"pmove", two(0xf000, 0x6000), two(0xffc0, 0xffff), "*sY8" },
+{"pmove", two(0xf000, 0x6200), two(0xffc0, 0xffff), "Y8%s" },
+{"pmove", two(0xf000, 0x6600), two(0xffc0, 0xffff), "Z8%s" },
+
+{"prestore", one(0xf140), one(0xffc0), "&s"},
+{"prestore", one(0xf158), one(0xfff8), "+s"},
+{"psave", one(0xf100), one(0xffc0), "&s"},
+{"psave", one(0xf100), one(0xffc0), "+s"},
+
+{"psac", two(0xf040, 0x0007), two(0xffc0, 0xffff), "@s"},
+{"psas", two(0xf040, 0x0006), two(0xffc0, 0xffff), "@s"},
+{"psbc", two(0xf040, 0x0001), two(0xffc0, 0xffff), "@s"},
+{"psbs", two(0xf040, 0x0000), two(0xffc0, 0xffff), "@s"},
+{"pscc", two(0xf040, 0x000f), two(0xffc0, 0xffff), "@s"},
+{"pscs", two(0xf040, 0x000e), two(0xffc0, 0xffff), "@s"},
+{"psgc", two(0xf040, 0x000d), two(0xffc0, 0xffff), "@s"},
+{"psgs", two(0xf040, 0x000c), two(0xffc0, 0xffff), "@s"},
+{"psic", two(0xf040, 0x000b), two(0xffc0, 0xffff), "@s"},
+{"psis", two(0xf040, 0x000a), two(0xffc0, 0xffff), "@s"},
+{"pslc", two(0xf040, 0x0003), two(0xffc0, 0xffff), "@s"},
+{"psls", two(0xf040, 0x0002), two(0xffc0, 0xffff), "@s"},
+{"pssc", two(0xf040, 0x0005), two(0xffc0, 0xffff), "@s"},
+{"psss", two(0xf040, 0x0004), two(0xffc0, 0xffff), "@s"},
+{"pswc", two(0xf040, 0x0009), two(0xffc0, 0xffff), "@s"},
+{"psws", two(0xf040, 0x0008), two(0xffc0, 0xffff), "@s"},
+
+{"ptestr", two(0xf000, 0x8210), two(0xffc0, 0xe3f0), "T3&sQ8" },
+{"ptestr", two(0xf000, 0x8310), two(0xffc0, 0xe310), "T3&sQ8A9" },
+{"ptestr", two(0xf000, 0x8208), two(0xffc0, 0xe3f8), "D3&sQ8" },
+{"ptestr", two(0xf000, 0x8308), two(0xffc0, 0xe318), "D3&sQ8A9" },
+{"ptestr", two(0xf000, 0x8200), two(0xffc0, 0xe3fe), "f3&sQ8" },
+{"ptestr", two(0xf000, 0x8300), two(0xffc0, 0xe31e), "f3&sQ8A9" },
+
+{"ptestw", two(0xf000, 0x8010), two(0xffc0, 0xe3f0), "T3&sQ8" },
+{"ptestw", two(0xf000, 0x8110), two(0xffc0, 0xe310), "T3&sQ8A9" },
+{"ptestw", two(0xf000, 0x8008), two(0xffc0, 0xe3f8), "D3&sQ8" },
+{"ptestw", two(0xf000, 0x8108), two(0xffc0, 0xe318), "D3&sQ8A9" },
+{"ptestw", two(0xf000, 0x8000), two(0xffc0, 0xe3fe), "f3&sQ8" },
+{"ptestw", two(0xf000, 0x8100), two(0xffc0, 0xe31e), "f3&sQ8A9" },
+
+{"ptrapacw", two(0xf07a, 0x0007), two(0xffff, 0xffff), "#w"},
+{"ptrapacl", two(0xf07b, 0x0007), two(0xffff, 0xffff), "#l"},
+{"ptrapac", two(0xf07c, 0x0007), two(0xffff, 0xffff), ""},
+
+{"ptrapasw", two(0xf07a, 0x0006), two(0xffff, 0xffff), "#w"},
+{"ptrapasl", two(0xf07b, 0x0006), two(0xffff, 0xffff), "#l"},
+{"ptrapas", two(0xf07c, 0x0006), two(0xffff, 0xffff), ""},
+
+{"ptrapbcw", two(0xf07a, 0x0001), two(0xffff, 0xffff), "#w"},
+{"ptrapbcl", two(0xf07b, 0x0001), two(0xffff, 0xffff), "#l"},
+{"ptrapbc", two(0xf07c, 0x0001), two(0xffff, 0xffff), ""},
+
+{"ptrapbsw", two(0xf07a, 0x0000), two(0xffff, 0xffff), "#w"},
+{"ptrapbsl", two(0xf07b, 0x0000), two(0xffff, 0xffff), "#l"},
+{"ptrapbs", two(0xf07c, 0x0000), two(0xffff, 0xffff), ""},
+
+{"ptrapccw", two(0xf07a, 0x000f), two(0xffff, 0xffff), "#w"},
+{"ptrapccl", two(0xf07b, 0x000f), two(0xffff, 0xffff), "#l"},
+{"ptrapcc", two(0xf07c, 0x000f), two(0xffff, 0xffff), ""},
+
+{"ptrapcsw", two(0xf07a, 0x000e), two(0xffff, 0xffff), "#w"},
+{"ptrapcsl", two(0xf07b, 0x000e), two(0xffff, 0xffff), "#l"},
+{"ptrapcs", two(0xf07c, 0x000e), two(0xffff, 0xffff), ""},
+
+{"ptrapgcw", two(0xf07a, 0x000d), two(0xffff, 0xffff), "#w"},
+{"ptrapgcl", two(0xf07b, 0x000d), two(0xffff, 0xffff), "#l"},
+{"ptrapgc", two(0xf07c, 0x000d), two(0xffff, 0xffff), ""},
+
+{"ptrapgsw", two(0xf07a, 0x000c), two(0xffff, 0xffff), "#w"},
+{"ptrapgsl", two(0xf07b, 0x000c), two(0xffff, 0xffff), "#l"},
+{"ptrapgs", two(0xf07c, 0x000c), two(0xffff, 0xffff), ""},
+
+{"ptrapicw", two(0xf07a, 0x000b), two(0xffff, 0xffff), "#w"},
+{"ptrapicl", two(0xf07b, 0x000b), two(0xffff, 0xffff), "#l"},
+{"ptrapic", two(0xf07c, 0x000b), two(0xffff, 0xffff), ""},
+
+{"ptrapisw", two(0xf07a, 0x000a), two(0xffff, 0xffff), "#w"},
+{"ptrapisl", two(0xf07b, 0x000a), two(0xffff, 0xffff), "#l"},
+{"ptrapis", two(0xf07c, 0x000a), two(0xffff, 0xffff), ""},
+
+{"ptraplcw", two(0xf07a, 0x0003), two(0xffff, 0xffff), "#w"},
+{"ptraplcl", two(0xf07b, 0x0003), two(0xffff, 0xffff), "#l"},
+{"ptraplc", two(0xf07c, 0x0003), two(0xffff, 0xffff), ""},
+
+{"ptraplsw", two(0xf07a, 0x0002), two(0xffff, 0xffff), "#w"},
+{"ptraplsl", two(0xf07b, 0x0002), two(0xffff, 0xffff), "#l"},
+{"ptrapls", two(0xf07c, 0x0002), two(0xffff, 0xffff), ""},
+
+{"ptrapscw", two(0xf07a, 0x0005), two(0xffff, 0xffff), "#w"},
+{"ptrapscl", two(0xf07b, 0x0005), two(0xffff, 0xffff), "#l"},
+{"ptrapsc", two(0xf07c, 0x0005), two(0xffff, 0xffff), ""},
+
+{"ptrapssw", two(0xf07a, 0x0004), two(0xffff, 0xffff), "#w"},
+{"ptrapssl", two(0xf07b, 0x0004), two(0xffff, 0xffff), "#l"},
+{"ptrapss", two(0xf07c, 0x0004), two(0xffff, 0xffff), ""},
+
+{"ptrapwcw", two(0xf07a, 0x0009), two(0xffff, 0xffff), "#w"},
+{"ptrapwcl", two(0xf07b, 0x0009), two(0xffff, 0xffff), "#l"},
+{"ptrapwc", two(0xf07c, 0x0009), two(0xffff, 0xffff), ""},
+
+{"ptrapwsw", two(0xf07a, 0x0008), two(0xffff, 0xffff), "#w"},
+{"ptrapwsl", two(0xf07b, 0x0008), two(0xffff, 0xffff), "#l"},
+{"ptrapws", two(0xf07c, 0x0008), two(0xffff, 0xffff), ""},
+
+{"pvalid", two(0xf000, 0x2800), two(0xffc0, 0xffff), "Vs&s"},
+{"pvalid", two(0xf000, 0x2c00), two(0xffc0, 0xfff8), "A3&s" },
+
+#endif /* m68851 */
+
+/* end of tc-m68851.h */
diff --git a/gas/config/tc-m68k.c b/gas/config/tc-m68k.c
new file mode 100644
index 0000000..856dd4e
--- /dev/null
+++ b/gas/config/tc-m68k.c
@@ -0,0 +1,7009 @@
+/* tc-m68k.c -- Assemble for the m68k family
+ Copyright (C) 1987, 91, 92, 93, 94, 95, 96, 97, 1998
+ Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#include <ctype.h>
+#include "as.h"
+#include "obstack.h"
+#include "subsegs.h"
+
+#include "opcode/m68k.h"
+#include "m68k-parse.h"
+
+/* This string holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful. The macro
+ tc_comment_chars points to this. We use this, rather than the
+ usual comment_chars, so that the --bitwise-or option will work. */
+#if defined (TE_SVR4) || defined (TE_DELTA)
+const char *m68k_comment_chars = "|#";
+#else
+const char *m68k_comment_chars = "|";
+#endif
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that comments like this one will always work. */
+const char line_comment_chars[] = "#*";
+
+const char line_separator_chars[] = "";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+CONST char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant, as
+ in "0f12.456" or "0d1.2345e12". */
+
+CONST char FLT_CHARS[] = "rRsSfFdDxXeEpP";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c . Ideally it shouldn't have to know about it at all,
+ but nothing is ideal around here. */
+
+const int md_reloc_size = 8; /* Size of relocation record */
+
+/* Are we trying to generate PIC code? If so, absolute references
+ ought to be made into linkage table references or pc-relative
+ references. Not implemented. For ELF there are other means
+ to denote pic relocations. */
+int flag_want_pic;
+
+static int flag_short_refs; /* -l option */
+static int flag_long_jumps; /* -S option */
+
+#ifdef REGISTER_PREFIX_OPTIONAL
+int flag_reg_prefix_optional = REGISTER_PREFIX_OPTIONAL;
+#else
+int flag_reg_prefix_optional;
+#endif
+
+/* Whether --register-prefix-optional was used on the command line. */
+static int reg_prefix_optional_seen;
+
+/* The floating point coprocessor to use by default. */
+static enum m68k_register m68k_float_copnum = COP1;
+
+/* If this is non-zero, then references to number(%pc) will be taken
+ to refer to number, rather than to %pc + number. */
+static int m68k_abspcadd;
+
+/* If this is non-zero, then the quick forms of the move, add, and sub
+ instructions are used when possible. */
+static int m68k_quick = 1;
+
+/* If this is non-zero, then if the size is not specified for a base
+ or outer displacement, the assembler assumes that the size should
+ be 32 bits. */
+static int m68k_rel32 = 1;
+
+/* This is non-zero if m68k_rel32 was set from the command line. */
+static int m68k_rel32_from_cmdline;
+
+/* The default width to use for an index register when using a base
+ displacement. */
+static enum m68k_size m68k_index_width_default = SIZE_LONG;
+
+/* We want to warn if any text labels are misaligned. In order to get
+ the right line number, we need to record the line number for each
+ label. */
+
+struct label_line
+{
+ struct label_line *next;
+ symbolS *label;
+ char *file;
+ unsigned int line;
+ int text;
+};
+
+/* The list of labels. */
+
+static struct label_line *labels;
+
+/* The current label. */
+
+static struct label_line *current_label;
+
+/* Its an arbitrary name: This means I don't approve of it */
+/* See flames below */
+static struct obstack robyn;
+
+#define TAB(x,y) (((x)<<2)+(y))
+#define TABTYPE(xy) ((xy) >> 2)
+#define BYTE 0
+#define SHORT 1
+#define LONG 2
+#define SZ_UNDEF 3
+#undef BRANCH
+/* Case `g' except when BCC68000 is applicable. */
+#define ABRANCH 1
+/* Coprocessor branches. */
+#define FBRANCH 2
+/* Mode 7.2 -- program counter indirect with (16-bit) displacement,
+ supported on all cpus. Widens to 32-bit absolute. */
+#define PCREL 3
+/* For inserting an extra jmp instruction with long offset on 68000,
+ for expanding conditional branches. (Not bsr or bra.) Since the
+ 68000 doesn't support 32-bit displacements for conditional
+ branches, we fake it by reversing the condition and branching
+ around a jmp with an absolute long operand. */
+#define BCC68000 4
+/* For the DBcc "instructions". If the displacement requires 32 bits,
+ the branch-around-a-jump game is played here too. */
+#define DBCC 5
+/* Not currently used? */
+#define PCLEA 6
+/* Mode AINDX (apc-relative) using PC, with variable target, might fit
+ in 16 or 8 bits. */
+#define PCINDEX 7
+
+struct m68k_incant
+ {
+ const char *m_operands;
+ unsigned long m_opcode;
+ short m_opnum;
+ short m_codenum;
+ int m_arch;
+ struct m68k_incant *m_next;
+ };
+
+#define getone(x) ((((x)->m_opcode)>>16)&0xffff)
+#define gettwo(x) (((x)->m_opcode)&0xffff)
+
+static const enum m68k_register m68000_control_regs[] = { 0 };
+static const enum m68k_register m68010_control_regs[] = {
+ SFC, DFC, USP, VBR,
+ 0
+};
+static const enum m68k_register m68020_control_regs[] = {
+ SFC, DFC, USP, VBR, CACR, CAAR, MSP, ISP,
+ 0
+};
+static const enum m68k_register m68040_control_regs[] = {
+ SFC, DFC, CACR, TC, ITT0, ITT1, DTT0, DTT1,
+ USP, VBR, MSP, ISP, MMUSR, URP, SRP,
+ 0
+};
+static const enum m68k_register m68060_control_regs[] = {
+ SFC, DFC, CACR, TC, ITT0, ITT1, DTT0, DTT1, BUSCR,
+ USP, VBR, URP, SRP, PCR,
+ 0
+};
+static const enum m68k_register mcf5200_control_regs[] = {
+ CACR, TC, ITT0, ITT1, DTT0, DTT1, VBR, ROMBAR,
+ RAMBAR0, RAMBAR1, MBAR,
+ 0
+};
+#define cpu32_control_regs m68010_control_regs
+
+static const enum m68k_register *control_regs;
+
+/* internal form of a 68020 instruction */
+struct m68k_it
+{
+ const char *error;
+ const char *args; /* list of opcode info */
+ int numargs;
+
+ int numo; /* Number of shorts in opcode */
+ short opcode[11];
+
+ struct m68k_op operands[6];
+
+ int nexp; /* number of exprs in use */
+ struct m68k_exp exprs[4];
+
+ int nfrag; /* Number of frags we have to produce */
+ struct
+ {
+ int fragoff; /* Where in the current opcode the frag ends */
+ symbolS *fadd;
+ offsetT foff;
+ int fragty;
+ }
+ fragb[4];
+
+ int nrel; /* Num of reloc strucs in use */
+ struct
+ {
+ int n;
+ expressionS exp;
+ char wid;
+ char pcrel;
+ /* In a pc relative address the difference between the address
+ of the offset and the address that the offset is relative
+ to. This depends on the addressing mode. Basically this
+ is the value to put in the offset field to address the
+ first byte of the offset, without regarding the special
+ significance of some values (in the branch instruction, for
+ example). */
+ int pcrel_fix;
+#ifdef OBJ_ELF
+ /* Whether this expression needs special pic relocation, and if
+ so, which. */
+ enum pic_relocation pic_reloc;
+#endif
+ }
+ reloc[5]; /* Five is enough??? */
+};
+
+#define cpu_of_arch(x) ((x) & (m68000up|mcf5200))
+#define float_of_arch(x) ((x) & mfloat)
+#define mmu_of_arch(x) ((x) & mmmu)
+
+/* Macros for determining if cpu supports a specific addressing mode */
+#define HAVE_LONG_BRANCH(x) ((x) & (m68020|m68030|m68040|m68060|cpu32))
+
+static struct m68k_it the_ins; /* the instruction being assembled */
+
+#define op(ex) ((ex)->exp.X_op)
+#define adds(ex) ((ex)->exp.X_add_symbol)
+#define subs(ex) ((ex)->exp.X_op_symbol)
+#define offs(ex) ((ex)->exp.X_add_number)
+
+/* Macros for adding things to the m68k_it struct */
+
+#define addword(w) the_ins.opcode[the_ins.numo++]=(w)
+
+/* Static functions. */
+
+static void insop PARAMS ((int, const struct m68k_incant *));
+static void add_fix PARAMS ((int, struct m68k_exp *, int, int));
+static void add_frag PARAMS ((symbolS *, offsetT, int));
+
+/* Like addword, but goes BEFORE general operands */
+static void
+insop (w, opcode)
+ int w;
+ const struct m68k_incant *opcode;
+{
+ int z;
+ for(z=the_ins.numo;z>opcode->m_codenum;--z)
+ the_ins.opcode[z]=the_ins.opcode[z-1];
+ for(z=0;z<the_ins.nrel;z++)
+ the_ins.reloc[z].n+=2;
+ for (z = 0; z < the_ins.nfrag; z++)
+ the_ins.fragb[z].fragoff++;
+ the_ins.opcode[opcode->m_codenum]=w;
+ the_ins.numo++;
+}
+
+/* The numo+1 kludge is so we can hit the low order byte of the prev word.
+ Blecch. */
+static void
+add_fix (width, exp, pc_rel, pc_fix)
+ int width;
+ struct m68k_exp *exp;
+ int pc_rel;
+ int pc_fix;
+{
+ the_ins.reloc[the_ins.nrel].n = ((width == 'B' || width == '3')
+ ? (the_ins.numo*2-1)
+ : (((width)=='b')
+ ? (the_ins.numo*2+1)
+ : (the_ins.numo*2)));
+ the_ins.reloc[the_ins.nrel].exp = exp->exp;
+ the_ins.reloc[the_ins.nrel].wid = width;
+ the_ins.reloc[the_ins.nrel].pcrel_fix = pc_fix;
+#ifdef OBJ_ELF
+ the_ins.reloc[the_ins.nrel].pic_reloc = exp->pic_reloc;
+#endif
+ the_ins.reloc[the_ins.nrel++].pcrel = pc_rel;
+}
+
+/* Cause an extra frag to be generated here, inserting up to 10 bytes
+ (that value is chosen in the frag_var call in md_assemble). TYPE
+ is the subtype of the frag to be generated; its primary type is
+ rs_machine_dependent.
+
+ The TYPE parameter is also used by md_convert_frag_1 and
+ md_estimate_size_before_relax. The appropriate type of fixup will
+ be emitted by md_convert_frag_1.
+
+ ADD becomes the FR_SYMBOL field of the frag, and OFF the FR_OFFSET. */
+static void
+add_frag (add, off, type)
+ symbolS *add;
+ offsetT off;
+ int type;
+{
+ the_ins.fragb[the_ins.nfrag].fragoff=the_ins.numo;
+ the_ins.fragb[the_ins.nfrag].fadd=add;
+ the_ins.fragb[the_ins.nfrag].foff=off;
+ the_ins.fragb[the_ins.nfrag++].fragty=type;
+}
+
+#define isvar(ex) \
+ (op (ex) != O_constant && op (ex) != O_big)
+
+static char *crack_operand PARAMS ((char *str, struct m68k_op *opP));
+static int get_num PARAMS ((struct m68k_exp *exp, int ok));
+static void m68k_ip PARAMS ((char *));
+static void insert_reg PARAMS ((const char *, int));
+static void select_control_regs PARAMS ((void));
+static void init_regtable PARAMS ((void));
+static int reverse_16_bits PARAMS ((int in));
+static int reverse_8_bits PARAMS ((int in));
+static void install_gen_operand PARAMS ((int mode, int val));
+static void install_operand PARAMS ((int mode, int val));
+static void s_bss PARAMS ((int));
+static void s_data1 PARAMS ((int));
+static void s_data2 PARAMS ((int));
+static void s_even PARAMS ((int));
+static void s_proc PARAMS ((int));
+static void mri_chip PARAMS ((void));
+static void s_chip PARAMS ((int));
+static void s_fopt PARAMS ((int));
+static void s_opt PARAMS ((int));
+static void s_reg PARAMS ((int));
+static void s_restore PARAMS ((int));
+static void s_save PARAMS ((int));
+static void s_mri_if PARAMS ((int));
+static void s_mri_else PARAMS ((int));
+static void s_mri_endi PARAMS ((int));
+static void s_mri_break PARAMS ((int));
+static void s_mri_next PARAMS ((int));
+static void s_mri_for PARAMS ((int));
+static void s_mri_endf PARAMS ((int));
+static void s_mri_repeat PARAMS ((int));
+static void s_mri_until PARAMS ((int));
+static void s_mri_while PARAMS ((int));
+static void s_mri_endw PARAMS ((int));
+static void md_apply_fix_2 PARAMS ((fixS *, offsetT));
+static void md_convert_frag_1 PARAMS ((fragS *));
+
+static int current_architecture;
+
+struct m68k_cpu {
+ unsigned long arch;
+ const char *name;
+ int alias;
+};
+
+static const struct m68k_cpu archs[] = {
+ { m68000, "68000", 0 },
+ { m68010, "68010", 0 },
+ { m68020, "68020", 0 },
+ { m68030, "68030", 0 },
+ { m68040, "68040", 0 },
+ { m68060, "68060", 0 },
+ { cpu32, "cpu32", 0 },
+ { m68881, "68881", 0 },
+ { m68851, "68851", 0 },
+ { mcf5200, "5200", 0 },
+ /* Aliases (effectively, so far as gas is concerned) for the above
+ cpus. */
+ { m68020, "68k", 1 },
+ { m68000, "68008", 1 },
+ { m68000, "68302", 1 },
+ { m68000, "68306", 1 },
+ { m68000, "68307", 1 },
+ { m68000, "68322", 1 },
+ { m68000, "68356", 1 },
+ { m68000, "68ec000", 1 },
+ { m68000, "68hc000", 1 },
+ { m68000, "68hc001", 1 },
+ { m68020, "68ec020", 1 },
+ { m68030, "68ec030", 1 },
+ { m68040, "68ec040", 1 },
+ { m68060, "68ec060", 1 },
+ { cpu32, "68330", 1 },
+ { cpu32, "68331", 1 },
+ { cpu32, "68332", 1 },
+ { cpu32, "68333", 1 },
+ { cpu32, "68334", 1 },
+ { cpu32, "68336", 1 },
+ { cpu32, "68340", 1 },
+ { cpu32, "68341", 1 },
+ { cpu32, "68349", 1 },
+ { cpu32, "68360", 1 },
+ { m68881, "68882", 1 },
+};
+
+static const int n_archs = sizeof (archs) / sizeof (archs[0]);
+
+/* BCC68000 is for patching in an extra jmp instruction for long offsets
+ on the 68000. The 68000 doesn't support long branches with branchs */
+
+/* This table desribes how you change sizes for the various types of variable
+ size expressions. This version only supports two kinds. */
+
+/* Note that calls to frag_var need to specify the maximum expansion
+ needed; this is currently 10 bytes for DBCC. */
+
+/* The fields are:
+ How far Forward this mode will reach:
+ How far Backward this mode will reach:
+ How many bytes this mode will add to the size of the frag
+ Which mode to go to if the offset won't fit in this one
+ */
+relax_typeS md_relax_table[] =
+{
+ {1, 1, 0, 0}, /* First entries aren't used */
+ {1, 1, 0, 0}, /* For no good reason except */
+ {1, 1, 0, 0}, /* that the VAX doesn't either */
+ {1, 1, 0, 0},
+
+ {(127), (-128), 0, TAB (ABRANCH, SHORT)},
+ {(32767), (-32768), 2, TAB (ABRANCH, LONG)},
+ {0, 0, 4, 0},
+ {1, 1, 0, 0},
+
+ {1, 1, 0, 0}, /* FBRANCH doesn't come BYTE */
+ {(32767), (-32768), 2, TAB (FBRANCH, LONG)},
+ {0, 0, 4, 0},
+ {1, 1, 0, 0},
+
+ {1, 1, 0, 0}, /* PCREL doesn't come BYTE */
+ {(32767), (-32768), 2, TAB (PCREL, LONG)},
+ {0, 0, 4, 0},
+ {1, 1, 0, 0},
+
+ {(127), (-128), 0, TAB (BCC68000, SHORT)},
+ {(32767), (-32768), 2, TAB (BCC68000, LONG)},
+ {0, 0, 6, 0}, /* jmp long space */
+ {1, 1, 0, 0},
+
+ {1, 1, 0, 0}, /* DBCC doesn't come BYTE */
+ {(32767), (-32768), 2, TAB (DBCC, LONG)},
+ {0, 0, 10, 0}, /* bra/jmp long space */
+ {1, 1, 0, 0},
+
+ {1, 1, 0, 0}, /* PCLEA doesn't come BYTE */
+ {32767, -32768, 2, TAB (PCLEA, LONG)},
+ {0, 0, 6, 0},
+ {1, 1, 0, 0},
+
+ /* For, e.g., jmp pcrel indexed. */
+ {125, -130, 0, TAB (PCINDEX, SHORT)},
+ {32765, -32770, 2, TAB (PCINDEX, LONG)},
+ {0, 0, 4, 0},
+ {1, 1, 0, 0},
+};
+
+/* These are the machine dependent pseudo-ops. These are included so
+ the assembler can work on the output from the SUN C compiler, which
+ generates these.
+ */
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ Integer arg to pass to the function
+ */
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"data1", s_data1, 0},
+ {"data2", s_data2, 0},
+ {"bss", s_bss, 0},
+ {"even", s_even, 0},
+ {"skip", s_space, 0},
+ {"proc", s_proc, 0},
+#if defined (TE_SUN3) || defined (OBJ_ELF)
+ {"align", s_align_bytes, 0},
+#endif
+#ifdef OBJ_ELF
+ {"swbeg", s_ignore, 0},
+#endif
+ {"extend", float_cons, 'x'},
+ {"ldouble", float_cons, 'x'},
+
+ /* The following pseudo-ops are supported for MRI compatibility. */
+ {"chip", s_chip, 0},
+ {"comline", s_space, 1},
+ {"fopt", s_fopt, 0},
+ {"mask2", s_ignore, 0},
+ {"opt", s_opt, 0},
+ {"reg", s_reg, 0},
+ {"restore", s_restore, 0},
+ {"save", s_save, 0},
+
+ {"if", s_mri_if, 0},
+ {"if.b", s_mri_if, 'b'},
+ {"if.w", s_mri_if, 'w'},
+ {"if.l", s_mri_if, 'l'},
+ {"else", s_mri_else, 0},
+ {"else.s", s_mri_else, 's'},
+ {"else.l", s_mri_else, 'l'},
+ {"endi", s_mri_endi, 0},
+ {"break", s_mri_break, 0},
+ {"break.s", s_mri_break, 's'},
+ {"break.l", s_mri_break, 'l'},
+ {"next", s_mri_next, 0},
+ {"next.s", s_mri_next, 's'},
+ {"next.l", s_mri_next, 'l'},
+ {"for", s_mri_for, 0},
+ {"for.b", s_mri_for, 'b'},
+ {"for.w", s_mri_for, 'w'},
+ {"for.l", s_mri_for, 'l'},
+ {"endf", s_mri_endf, 0},
+ {"repeat", s_mri_repeat, 0},
+ {"until", s_mri_until, 0},
+ {"until.b", s_mri_until, 'b'},
+ {"until.w", s_mri_until, 'w'},
+ {"until.l", s_mri_until, 'l'},
+ {"while", s_mri_while, 0},
+ {"while.b", s_mri_while, 'b'},
+ {"while.w", s_mri_while, 'w'},
+ {"while.l", s_mri_while, 'l'},
+ {"endw", s_mri_endw, 0},
+
+ {0, 0, 0}
+};
+
+
+/* The mote pseudo ops are put into the opcode table, since they
+ don't start with a . they look like opcodes to gas.
+ */
+
+#ifdef M68KCOFF
+extern void obj_coff_section PARAMS ((int));
+#endif
+
+CONST pseudo_typeS mote_pseudo_table[] =
+{
+
+ {"dcl", cons, 4},
+ {"dc", cons, 2},
+ {"dcw", cons, 2},
+ {"dcb", cons, 1},
+
+ {"dsl", s_space, 4},
+ {"ds", s_space, 2},
+ {"dsw", s_space, 2},
+ {"dsb", s_space, 1},
+
+ {"xdef", s_globl, 0},
+#ifdef OBJ_ELF
+ {"align", s_align_bytes, 0},
+#else
+ {"align", s_align_ptwo, 0},
+#endif
+#ifdef M68KCOFF
+ {"sect", obj_coff_section, 0},
+ {"section", obj_coff_section, 0},
+#endif
+ {0, 0, 0}
+};
+
+#define issbyte(x) ((x)>=-128 && (x)<=127)
+#define isubyte(x) ((x)>=0 && (x)<=255)
+#define issword(x) ((x)>=-32768 && (x)<=32767)
+#define isuword(x) ((x)>=0 && (x)<=65535)
+
+#define isbyte(x) ((x)>= -255 && (x)<=255)
+#define isword(x) ((x)>=-65536 && (x)<=65535)
+#define islong(x) (1)
+
+extern char *input_line_pointer;
+
+static char mklower_table[256];
+#define mklower(c) (mklower_table[(unsigned char)(c)])
+static char notend_table[256];
+static char alt_notend_table[256];
+#define notend(s) \
+ (! (notend_table[(unsigned char) *s] \
+ || (*s == ':' \
+ && alt_notend_table[(unsigned char) s[1]])))
+
+#if defined (M68KCOFF) && !defined (BFD_ASSEMBLER)
+
+#ifdef NO_PCREL_RELOCS
+
+int
+make_pcrel_absolute(fixP, add_number)
+ fixS *fixP;
+ long *add_number;
+{
+ register unsigned char *opcode = fixP->fx_frag->fr_opcode;
+
+ /* rewrite the PC relative instructions to absolute address ones.
+ * these are rumoured to be faster, and the apollo linker refuses
+ * to deal with the PC relative relocations.
+ */
+ if (opcode[0] == 0x60 && opcode[1] == 0xff) /* BRA -> JMP */
+ {
+ opcode[0] = 0x4e;
+ opcode[1] = 0xf9;
+ }
+ else if (opcode[0] == 0x61 && opcode[1] == 0xff) /* BSR -> JSR */
+ {
+ opcode[0] = 0x4e;
+ opcode[1] = 0xb9;
+ }
+ else
+ as_fatal (_("Unknown PC relative instruction"));
+ *add_number -= 4;
+ return 0;
+}
+
+#endif /* NO_PCREL_RELOCS */
+
+short
+tc_coff_fix2rtype (fixP)
+ fixS *fixP;
+{
+ if (fixP->fx_tcbit && fixP->fx_size == 4)
+ return R_RELLONG_NEG;
+#ifdef NO_PCREL_RELOCS
+ know (fixP->fx_pcrel == 0);
+ return (fixP->fx_size == 1 ? R_RELBYTE
+ : fixP->fx_size == 2 ? R_DIR16
+ : R_DIR32);
+#else
+ return (fixP->fx_pcrel ?
+ (fixP->fx_size == 1 ? R_PCRBYTE :
+ fixP->fx_size == 2 ? R_PCRWORD :
+ R_PCRLONG) :
+ (fixP->fx_size == 1 ? R_RELBYTE :
+ fixP->fx_size == 2 ? R_RELWORD :
+ R_RELLONG));
+#endif
+}
+
+#endif
+
+#ifdef OBJ_ELF
+
+/* Compute the relocation code for a fixup of SIZE bytes, using pc
+ relative relocation if PCREL is non-zero. PIC says whether a special
+ pic relocation was requested. */
+
+static bfd_reloc_code_real_type get_reloc_code
+ PARAMS ((int, int, enum pic_relocation));
+
+static bfd_reloc_code_real_type
+get_reloc_code (size, pcrel, pic)
+ int size;
+ int pcrel;
+ enum pic_relocation pic;
+{
+ switch (pic)
+ {
+ case pic_got_pcrel:
+ switch (size)
+ {
+ case 1:
+ return BFD_RELOC_8_GOT_PCREL;
+ case 2:
+ return BFD_RELOC_16_GOT_PCREL;
+ case 4:
+ return BFD_RELOC_32_GOT_PCREL;
+ }
+ break;
+
+ case pic_got_off:
+ switch (size)
+ {
+ case 1:
+ return BFD_RELOC_8_GOTOFF;
+ case 2:
+ return BFD_RELOC_16_GOTOFF;
+ case 4:
+ return BFD_RELOC_32_GOTOFF;
+ }
+ break;
+
+ case pic_plt_pcrel:
+ switch (size)
+ {
+ case 1:
+ return BFD_RELOC_8_PLT_PCREL;
+ case 2:
+ return BFD_RELOC_16_PLT_PCREL;
+ case 4:
+ return BFD_RELOC_32_PLT_PCREL;
+ }
+ break;
+
+ case pic_plt_off:
+ switch (size)
+ {
+ case 1:
+ return BFD_RELOC_8_PLTOFF;
+ case 2:
+ return BFD_RELOC_16_PLTOFF;
+ case 4:
+ return BFD_RELOC_32_PLTOFF;
+ }
+ break;
+
+ case pic_none:
+ if (pcrel)
+ {
+ switch (size)
+ {
+ case 1:
+ return BFD_RELOC_8_PCREL;
+ case 2:
+ return BFD_RELOC_16_PCREL;
+ case 4:
+ return BFD_RELOC_32_PCREL;
+ }
+ }
+ else
+ {
+ switch (size)
+ {
+ case 1:
+ return BFD_RELOC_8;
+ case 2:
+ return BFD_RELOC_16;
+ case 4:
+ return BFD_RELOC_32;
+ }
+ }
+ }
+
+ if (pcrel)
+ {
+ if (pic == pic_none)
+ as_bad (_("Can not do %d byte pc-relative relocation"), size);
+ else
+ as_bad (_("Can not do %d byte pc-relative pic relocation"), size);
+ }
+ else
+ {
+ if (pic == pic_none)
+ as_bad (_("Can not do %d byte relocation"), size);
+ else
+ as_bad (_("Can not do %d byte pic relocation"), size);
+ }
+
+ return BFD_RELOC_NONE;
+}
+
+/* Here we decide which fixups can be adjusted to make them relative
+ to the beginning of the section instead of the symbol. Basically
+ we need to make sure that the dynamic relocations are done
+ correctly, so in some cases we force the original symbol to be
+ used. */
+int
+tc_m68k_fix_adjustable (fixP)
+ fixS *fixP;
+{
+ /* Prevent all adjustments to global symbols. */
+ if (S_IS_EXTERNAL (fixP->fx_addsy)
+ || S_IS_WEAK (fixP->fx_addsy))
+ return 0;
+
+ /* adjust_reloc_syms doesn't know about the GOT */
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8_GOT_PCREL:
+ case BFD_RELOC_16_GOT_PCREL:
+ case BFD_RELOC_32_GOT_PCREL:
+ case BFD_RELOC_8_GOTOFF:
+ case BFD_RELOC_16_GOTOFF:
+ case BFD_RELOC_32_GOTOFF:
+ case BFD_RELOC_8_PLT_PCREL:
+ case BFD_RELOC_16_PLT_PCREL:
+ case BFD_RELOC_32_PLT_PCREL:
+ case BFD_RELOC_8_PLTOFF:
+ case BFD_RELOC_16_PLTOFF:
+ case BFD_RELOC_32_PLTOFF:
+ return 0;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ return 0;
+
+ default:
+ return 1;
+ }
+}
+
+#else /* !OBJ_ELF */
+
+#define get_reloc_code(SIZE,PCREL,OTHER) NO_RELOC
+
+#endif /* OBJ_ELF */
+
+#ifdef BFD_ASSEMBLER
+
+arelent *
+tc_gen_reloc (section, fixp)
+ asection *section;
+ fixS *fixp;
+{
+ arelent *reloc;
+ bfd_reloc_code_real_type code;
+
+ if (fixp->fx_tcbit)
+ abort ();
+
+ if (fixp->fx_r_type != BFD_RELOC_NONE)
+ {
+ code = fixp->fx_r_type;
+
+ /* Since DIFF_EXPR_OK is defined in tc-m68k.h, it is possible
+ that fixup_segment converted a non-PC relative reloc into a
+ PC relative reloc. In such a case, we need to convert the
+ reloc code. */
+ if (fixp->fx_pcrel)
+ {
+ switch (code)
+ {
+ case BFD_RELOC_8:
+ code = BFD_RELOC_8_PCREL;
+ break;
+ case BFD_RELOC_16:
+ code = BFD_RELOC_16_PCREL;
+ break;
+ case BFD_RELOC_32:
+ code = BFD_RELOC_32_PCREL;
+ break;
+ case BFD_RELOC_8_PCREL:
+ case BFD_RELOC_16_PCREL:
+ case BFD_RELOC_32_PCREL:
+ case BFD_RELOC_8_GOT_PCREL:
+ case BFD_RELOC_16_GOT_PCREL:
+ case BFD_RELOC_32_GOT_PCREL:
+ case BFD_RELOC_8_GOTOFF:
+ case BFD_RELOC_16_GOTOFF:
+ case BFD_RELOC_32_GOTOFF:
+ case BFD_RELOC_8_PLT_PCREL:
+ case BFD_RELOC_16_PLT_PCREL:
+ case BFD_RELOC_32_PLT_PCREL:
+ case BFD_RELOC_8_PLTOFF:
+ case BFD_RELOC_16_PLTOFF:
+ case BFD_RELOC_32_PLTOFF:
+ break;
+ default:
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Cannot make %s relocation PC relative"),
+ bfd_get_reloc_code_name (code));
+ }
+ }
+ }
+ else
+ {
+#define F(SZ,PCREL) (((SZ) << 1) + (PCREL))
+ switch (F (fixp->fx_size, fixp->fx_pcrel))
+ {
+#define MAP(SZ,PCREL,TYPE) case F(SZ,PCREL): code = (TYPE); break
+ MAP (1, 0, BFD_RELOC_8);
+ MAP (2, 0, BFD_RELOC_16);
+ MAP (4, 0, BFD_RELOC_32);
+ MAP (1, 1, BFD_RELOC_8_PCREL);
+ MAP (2, 1, BFD_RELOC_16_PCREL);
+ MAP (4, 1, BFD_RELOC_32_PCREL);
+ default:
+ abort ();
+ }
+ }
+#undef F
+#undef MAP
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+#ifndef OBJ_ELF
+ if (fixp->fx_pcrel)
+ reloc->addend = fixp->fx_addnumber;
+ else
+ reloc->addend = 0;
+#else
+ if (!fixp->fx_pcrel)
+ reloc->addend = fixp->fx_addnumber;
+ else
+ reloc->addend = (section->vma
+ + (fixp->fx_pcrel_adjust == 64
+ ? -1 : fixp->fx_pcrel_adjust)
+ + fixp->fx_addnumber
+ + md_pcrel_from (fixp));
+#endif
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+ assert (reloc->howto != 0);
+
+ return reloc;
+}
+
+#endif /* BFD_ASSEMBLER */
+
+/* Return zero if the reference to SYMBOL from within the same segment may
+ be relaxed. */
+#ifdef OBJ_ELF
+
+/* On an ELF system, we can't relax an externally visible symbol,
+ because it may be overridden by a shared library. However, if
+ TARGET_OS is "elf", then we presume that we are assembling for an
+ embedded system, in which case we don't have to worry about shared
+ libraries, and we can relax anything. */
+
+#define relaxable_symbol(symbol) \
+ (strcmp (TARGET_OS, "elf") == 0 \
+ || (! S_IS_EXTERNAL (symbol) \
+ && ! S_IS_WEAK (symbol)))
+
+#else
+
+#define relaxable_symbol(symbol) 1
+
+#endif
+
+/* Handle of the OPCODE hash table. NULL means any use before
+ m68k_ip_begin() will crash. */
+static struct hash_control *op_hash;
+
+/* Assemble an m68k instruction. */
+
+static void
+m68k_ip (instring)
+ char *instring;
+{
+ register char *p;
+ register struct m68k_op *opP;
+ register const struct m68k_incant *opcode;
+ register const char *s;
+ register int tmpreg = 0, baseo = 0, outro = 0, nextword;
+ char *pdot, *pdotmove;
+ enum m68k_size siz1, siz2;
+ char c;
+ int losing;
+ int opsfound;
+ LITTLENUM_TYPE words[6];
+ LITTLENUM_TYPE *wordp;
+ unsigned long ok_arch = 0;
+
+ if (*instring == ' ')
+ instring++; /* skip leading whitespace */
+
+ /* Scan up to end of operation-code, which MUST end in end-of-string
+ or exactly 1 space. */
+ pdot = 0;
+ for (p = instring; *p != '\0'; p++)
+ {
+ if (*p == ' ')
+ break;
+ if (*p == '.')
+ pdot = p;
+ }
+
+ if (p == instring)
+ {
+ the_ins.error = _("No operator");
+ return;
+ }
+
+ /* p now points to the end of the opcode name, probably whitespace.
+ Make sure the name is null terminated by clobbering the
+ whitespace, look it up in the hash table, then fix it back.
+ Remove a dot, first, since the opcode tables have none. */
+ if (pdot != NULL)
+ {
+ for (pdotmove = pdot; pdotmove < p; pdotmove++)
+ *pdotmove = pdotmove[1];
+ p--;
+ }
+
+ c = *p;
+ *p = '\0';
+ opcode = (const struct m68k_incant *) hash_find (op_hash, instring);
+ *p = c;
+
+ if (pdot != NULL)
+ {
+ for (pdotmove = p; pdotmove > pdot; pdotmove--)
+ *pdotmove = pdotmove[-1];
+ *pdot = '.';
+ ++p;
+ }
+
+ if (opcode == NULL)
+ {
+ the_ins.error = _("Unknown operator");
+ return;
+ }
+
+ /* found a legitimate opcode, start matching operands */
+ while (*p == ' ')
+ ++p;
+
+ if (opcode->m_operands == 0)
+ {
+ char *old = input_line_pointer;
+ *old = '\n';
+ input_line_pointer = p;
+ /* Ahh - it's a motorola style psuedo op */
+ mote_pseudo_table[opcode->m_opnum].poc_handler
+ (mote_pseudo_table[opcode->m_opnum].poc_val);
+ input_line_pointer = old;
+ *old = 0;
+
+ return;
+ }
+
+ if (flag_mri && opcode->m_opnum == 0)
+ {
+ /* In MRI mode, random garbage is allowed after an instruction
+ which accepts no operands. */
+ the_ins.args = opcode->m_operands;
+ the_ins.numargs = opcode->m_opnum;
+ the_ins.numo = opcode->m_codenum;
+ the_ins.opcode[0] = getone (opcode);
+ the_ins.opcode[1] = gettwo (opcode);
+ return;
+ }
+
+ for (opP = &the_ins.operands[0]; *p; opP++)
+ {
+ p = crack_operand (p, opP);
+
+ if (opP->error)
+ {
+ the_ins.error = opP->error;
+ return;
+ }
+ }
+
+ opsfound = opP - &the_ins.operands[0];
+
+ /* This ugly hack is to support the floating pt opcodes in their
+ standard form. Essentially, we fake a first enty of type COP#1 */
+ if (opcode->m_operands[0] == 'I')
+ {
+ int n;
+
+ for (n = opsfound; n > 0; --n)
+ the_ins.operands[n] = the_ins.operands[n - 1];
+
+ memset ((char *) (&the_ins.operands[0]), '\0',
+ sizeof (the_ins.operands[0]));
+ the_ins.operands[0].mode = CONTROL;
+ the_ins.operands[0].reg = m68k_float_copnum;
+ opsfound++;
+ }
+
+ /* We've got the operands. Find an opcode that'll accept them */
+ for (losing = 0;;)
+ {
+ /* If we didn't get the right number of ops, or we have no
+ common model with this pattern then reject this pattern. */
+
+ ok_arch |= opcode->m_arch;
+ if (opsfound != opcode->m_opnum
+ || ((opcode->m_arch & current_architecture) == 0))
+ ++losing;
+ else
+ {
+ for (s = opcode->m_operands, opP = &the_ins.operands[0];
+ *s && !losing;
+ s += 2, opP++)
+ {
+ /* Warning: this switch is huge! */
+ /* I've tried to organize the cases into this order:
+ non-alpha first, then alpha by letter. Lower-case
+ goes directly before uppercase counterpart. */
+ /* Code with multiple case ...: gets sorted by the lowest
+ case ... it belongs to. I hope this makes sense. */
+ switch (*s)
+ {
+ case '!':
+ switch (opP->mode)
+ {
+ case IMMED:
+ case DREG:
+ case AREG:
+ case FPREG:
+ case CONTROL:
+ case AINC:
+ case ADEC:
+ case REGLST:
+ losing++;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case '<':
+ switch (opP->mode)
+ {
+ case DREG:
+ case AREG:
+ case FPREG:
+ case CONTROL:
+ case IMMED:
+ case ADEC:
+ case REGLST:
+ losing++;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case '>':
+ switch (opP->mode)
+ {
+ case DREG:
+ case AREG:
+ case FPREG:
+ case CONTROL:
+ case IMMED:
+ case AINC:
+ case REGLST:
+ losing++;
+ break;
+ case ABSL:
+ break;
+ default:
+ if (opP->reg == PC
+ || opP->reg == ZPC)
+ losing++;
+ break;
+ }
+ break;
+
+ case 'm':
+ switch (opP->mode)
+ {
+ case DREG:
+ case AREG:
+ case AINDR:
+ case AINC:
+ case ADEC:
+ break;
+ default:
+ losing++;
+ }
+ break;
+
+ case 'n':
+ switch (opP->mode)
+ {
+ case DISP:
+ break;
+ default:
+ losing++;
+ }
+ break;
+
+ case 'o':
+ switch (opP->mode)
+ {
+ case BASE:
+ case ABSL:
+ case IMMED:
+ break;
+ default:
+ losing++;
+ }
+ break;
+
+ case 'p':
+ switch (opP->mode)
+ {
+ case DREG:
+ case AREG:
+ case AINDR:
+ case AINC:
+ case ADEC:
+ break;
+ case DISP:
+ if (opP->reg == PC || opP->reg == ZPC)
+ losing++;
+ break;
+ default:
+ losing++;
+ }
+ break;
+
+ case 'q':
+ switch (opP->mode)
+ {
+ case DREG:
+ case AINDR:
+ case AINC:
+ case ADEC:
+ break;
+ case DISP:
+ if (opP->reg == PC || opP->reg == ZPC)
+ losing++;
+ break;
+ default:
+ losing++;
+ break;
+ }
+ break;
+
+ case 'v':
+ switch (opP->mode)
+ {
+ case DREG:
+ case AINDR:
+ case AINC:
+ case ADEC:
+ case ABSL:
+ break;
+ case DISP:
+ if (opP->reg == PC || opP->reg == ZPC)
+ losing++;
+ break;
+ default:
+ losing++;
+ break;
+ }
+ break;
+
+ case '#':
+ if (opP->mode != IMMED)
+ losing++;
+ else if (s[1] == 'b'
+ && ! isvar (&opP->disp)
+ && (opP->disp.exp.X_op != O_constant
+ || ! isbyte (opP->disp.exp.X_add_number)))
+ losing++;
+ else if (s[1] == 'B'
+ && ! isvar (&opP->disp)
+ && (opP->disp.exp.X_op != O_constant
+ || ! issbyte (opP->disp.exp.X_add_number)))
+ losing++;
+ else if (s[1] == 'w'
+ && ! isvar (&opP->disp)
+ && (opP->disp.exp.X_op != O_constant
+ || ! isword (opP->disp.exp.X_add_number)))
+ losing++;
+ else if (s[1] == 'W'
+ && ! isvar (&opP->disp)
+ && (opP->disp.exp.X_op != O_constant
+ || ! issword (opP->disp.exp.X_add_number)))
+ losing++;
+ break;
+
+ case '^':
+ case 'T':
+ if (opP->mode != IMMED)
+ losing++;
+ break;
+
+ case '$':
+ if (opP->mode == AREG
+ || opP->mode == CONTROL
+ || opP->mode == FPREG
+ || opP->mode == IMMED
+ || opP->mode == REGLST
+ || (opP->mode != ABSL
+ && (opP->reg == PC
+ || opP->reg == ZPC)))
+ losing++;
+ break;
+
+ case '%':
+ if (opP->mode == CONTROL
+ || opP->mode == FPREG
+ || opP->mode == REGLST
+ || opP->mode == IMMED
+ || (opP->mode != ABSL
+ && (opP->reg == PC
+ || opP->reg == ZPC)))
+ losing++;
+ break;
+
+ case '&':
+ switch (opP->mode)
+ {
+ case DREG:
+ case AREG:
+ case FPREG:
+ case CONTROL:
+ case IMMED:
+ case AINC:
+ case ADEC:
+ case REGLST:
+ losing++;
+ break;
+ case ABSL:
+ break;
+ default:
+ if (opP->reg == PC
+ || opP->reg == ZPC)
+ losing++;
+ break;
+ }
+ break;
+
+ case '*':
+ if (opP->mode == CONTROL
+ || opP->mode == FPREG
+ || opP->mode == REGLST)
+ losing++;
+ break;
+
+ case '+':
+ if (opP->mode != AINC)
+ losing++;
+ break;
+
+ case '-':
+ if (opP->mode != ADEC)
+ losing++;
+ break;
+
+ case '/':
+ switch (opP->mode)
+ {
+ case AREG:
+ case CONTROL:
+ case FPREG:
+ case AINC:
+ case ADEC:
+ case IMMED:
+ case REGLST:
+ losing++;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case ';':
+ switch (opP->mode)
+ {
+ case AREG:
+ case CONTROL:
+ case FPREG:
+ case REGLST:
+ losing++;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case '?':
+ switch (opP->mode)
+ {
+ case AREG:
+ case CONTROL:
+ case FPREG:
+ case AINC:
+ case ADEC:
+ case IMMED:
+ case REGLST:
+ losing++;
+ break;
+ case ABSL:
+ break;
+ default:
+ if (opP->reg == PC || opP->reg == ZPC)
+ losing++;
+ break;
+ }
+ break;
+
+ case '@':
+ switch (opP->mode)
+ {
+ case AREG:
+ case CONTROL:
+ case FPREG:
+ case IMMED:
+ case REGLST:
+ losing++;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case '~': /* For now! (JF FOO is this right?) */
+ switch (opP->mode)
+ {
+ case DREG:
+ case AREG:
+ case CONTROL:
+ case FPREG:
+ case IMMED:
+ case REGLST:
+ losing++;
+ break;
+ case ABSL:
+ break;
+ default:
+ if (opP->reg == PC
+ || opP->reg == ZPC)
+ losing++;
+ break;
+ }
+ break;
+
+ case '3':
+ if (opP->mode != CONTROL
+ || (opP->reg != TT0 && opP->reg != TT1))
+ losing++;
+ break;
+
+ case 'A':
+ if (opP->mode != AREG)
+ losing++;
+ break;
+
+ case 'a':
+ if (opP->mode != AINDR)
+ ++losing;
+ break;
+
+ case 'B': /* FOO */
+ if (opP->mode != ABSL
+ || (flag_long_jumps
+ && strncmp (instring, "jbsr", 4) == 0))
+ losing++;
+ break;
+
+ case 'C':
+ if (opP->mode != CONTROL || opP->reg != CCR)
+ losing++;
+ break;
+
+ case 'd':
+ if (opP->mode != DISP
+ || opP->reg < ADDR0
+ || opP->reg > ADDR7)
+ losing++;
+ break;
+
+ case 'D':
+ if (opP->mode != DREG)
+ losing++;
+ break;
+
+ case 'F':
+ if (opP->mode != FPREG)
+ losing++;
+ break;
+
+ case 'I':
+ if (opP->mode != CONTROL
+ || opP->reg < COP0
+ || opP->reg > COP7)
+ losing++;
+ break;
+
+ case 'J':
+ if (opP->mode != CONTROL
+ || opP->reg < USP
+ || opP->reg > last_movec_reg)
+ losing++;
+ else
+ {
+ const enum m68k_register *rp;
+ for (rp = control_regs; *rp; rp++)
+ if (*rp == opP->reg)
+ break;
+ if (*rp == 0)
+ losing++;
+ }
+ break;
+
+ case 'k':
+ if (opP->mode != IMMED)
+ losing++;
+ break;
+
+ case 'l':
+ case 'L':
+ if (opP->mode == DREG
+ || opP->mode == AREG
+ || opP->mode == FPREG)
+ {
+ if (s[1] == '8')
+ losing++;
+ else
+ {
+ switch (opP->mode)
+ {
+ case DREG:
+ opP->mask = 1 << (opP->reg - DATA0);
+ break;
+ case AREG:
+ opP->mask = 1 << (opP->reg - ADDR0 + 8);
+ break;
+ case FPREG:
+ opP->mask = 1 << (opP->reg - FP0 + 16);
+ break;
+ default:
+ abort ();
+ }
+ opP->mode = REGLST;
+ }
+ }
+ else if (opP->mode == CONTROL)
+ {
+ if (s[1] != '8')
+ losing++;
+ else
+ {
+ switch (opP->reg)
+ {
+ case FPI:
+ opP->mask = 1 << 24;
+ break;
+ case FPS:
+ opP->mask = 1 << 25;
+ break;
+ case FPC:
+ opP->mask = 1 << 26;
+ break;
+ default:
+ losing++;
+ break;
+ }
+ opP->mode = REGLST;
+ }
+ }
+ else if (opP->mode != REGLST)
+ losing++;
+ else if (s[1] == '8' && (opP->mask & 0x0ffffff) != 0)
+ losing++;
+ else if (s[1] == '3' && (opP->mask & 0x7000000) != 0)
+ losing++;
+ break;
+
+ case 'M':
+ if (opP->mode != IMMED)
+ losing++;
+ else if (opP->disp.exp.X_op != O_constant
+ || ! issbyte (opP->disp.exp.X_add_number))
+ losing++;
+ else if (! m68k_quick
+ && instring[3] != 'q'
+ && instring[4] != 'q')
+ losing++;
+ break;
+
+ case 'O':
+ if (opP->mode != DREG
+ && opP->mode != IMMED
+ && opP->mode != ABSL)
+ losing++;
+ break;
+
+ case 'Q':
+ if (opP->mode != IMMED)
+ losing++;
+ else if (opP->disp.exp.X_op != O_constant
+ || opP->disp.exp.X_add_number < 1
+ || opP->disp.exp.X_add_number > 8)
+ losing++;
+ else if (! m68k_quick
+ && (strncmp (instring, "add", 3) == 0
+ || strncmp (instring, "sub", 3) == 0)
+ && instring[3] != 'q')
+ losing++;
+ break;
+
+ case 'R':
+ if (opP->mode != DREG && opP->mode != AREG)
+ losing++;
+ break;
+
+ case 'r':
+ if (opP->mode != AINDR
+ && (opP->mode != BASE
+ || (opP->reg != 0
+ && opP->reg != ZADDR0)
+ || opP->disp.exp.X_op != O_absent
+ || ((opP->index.reg < DATA0
+ || opP->index.reg > DATA7)
+ && (opP->index.reg < ADDR0
+ || opP->index.reg > ADDR7))
+ || opP->index.size != SIZE_UNSPEC
+ || opP->index.scale != 1))
+ losing++;
+ break;
+
+ case 's':
+ if (opP->mode != CONTROL
+ || ! (opP->reg == FPI
+ || opP->reg == FPS
+ || opP->reg == FPC))
+ losing++;
+ break;
+
+ case 'S':
+ if (opP->mode != CONTROL || opP->reg != SR)
+ losing++;
+ break;
+
+ case 't':
+ if (opP->mode != IMMED)
+ losing++;
+ else if (opP->disp.exp.X_op != O_constant
+ || opP->disp.exp.X_add_number < 0
+ || opP->disp.exp.X_add_number > 7)
+ losing++;
+ break;
+
+ case 'U':
+ if (opP->mode != CONTROL || opP->reg != USP)
+ losing++;
+ break;
+
+ /* JF these are out of order. We could put them
+ in order if we were willing to put up with
+ bunches of #ifdef m68851s in the code.
+
+ Don't forget that you need these operands
+ to use 68030 MMU instructions. */
+#ifndef NO_68851
+ /* Memory addressing mode used by pflushr */
+ case '|':
+ if (opP->mode == CONTROL
+ || opP->mode == FPREG
+ || opP->mode == DREG
+ || opP->mode == AREG
+ || opP->mode == REGLST)
+ losing++;
+ /* We should accept immediate operands, but they
+ supposedly have to be quad word, and we don't
+ handle that. I would like to see what a Motorola
+ assembler does before doing something here. */
+ if (opP->mode == IMMED)
+ losing++;
+ break;
+
+ case 'f':
+ if (opP->mode != CONTROL
+ || (opP->reg != SFC && opP->reg != DFC))
+ losing++;
+ break;
+
+ case '0':
+ if (opP->mode != CONTROL || opP->reg != TC)
+ losing++;
+ break;
+
+ case '1':
+ if (opP->mode != CONTROL || opP->reg != AC)
+ losing++;
+ break;
+
+ case '2':
+ if (opP->mode != CONTROL
+ || (opP->reg != CAL
+ && opP->reg != VAL
+ && opP->reg != SCC))
+ losing++;
+ break;
+
+ case 'V':
+ if (opP->mode != CONTROL
+ || opP->reg != VAL)
+ losing++;
+ break;
+
+ case 'W':
+ if (opP->mode != CONTROL
+ || (opP->reg != DRP
+ && opP->reg != SRP
+ && opP->reg != CRP))
+ losing++;
+ break;
+
+ case 'X':
+ if (opP->mode != CONTROL
+ || (!(opP->reg >= BAD && opP->reg <= BAD + 7)
+ && !(opP->reg >= BAC && opP->reg <= BAC + 7)))
+ losing++;
+ break;
+
+ case 'Y':
+ if (opP->mode != CONTROL || opP->reg != PSR)
+ losing++;
+ break;
+
+ case 'Z':
+ if (opP->mode != CONTROL || opP->reg != PCSR)
+ losing++;
+ break;
+#endif
+ case 'c':
+ if (opP->mode != CONTROL
+ || (opP->reg != NC
+ && opP->reg != IC
+ && opP->reg != DC
+ && opP->reg != BC))
+ {
+ losing++;
+ } /* not a cache specifier. */
+ break;
+
+ case '_':
+ if (opP->mode != ABSL)
+ ++losing;
+ break;
+
+ default:
+ abort ();
+ } /* switch on type of operand */
+
+ if (losing)
+ break;
+ } /* for each operand */
+ } /* if immediately wrong */
+
+ if (!losing)
+ {
+ break;
+ } /* got it. */
+
+ opcode = opcode->m_next;
+
+ if (!opcode)
+ {
+ if (ok_arch
+ && !(ok_arch & current_architecture))
+ {
+ char buf[200], *cp;
+
+ strcpy (buf,
+ _("invalid instruction for this architecture; needs "));
+ cp = buf + strlen (buf);
+ switch (ok_arch)
+ {
+ case mfloat:
+ strcpy (cp, _("fpu (68040, 68060 or 68881/68882)"));
+ break;
+ case mmmu:
+ strcpy (cp, _("mmu (68030 or 68851)"));
+ break;
+ case m68020up:
+ strcpy (cp, _("68020 or higher"));
+ break;
+ case m68000up:
+ strcpy (cp, _("68000 or higher"));
+ break;
+ case m68010up:
+ strcpy (cp, _("68010 or higher"));
+ break;
+ default:
+ {
+ int got_one = 0, idx;
+ for (idx = 0; idx < sizeof (archs) / sizeof (archs[0]);
+ idx++)
+ {
+ if ((archs[idx].arch & ok_arch)
+ && ! archs[idx].alias)
+ {
+ if (got_one)
+ {
+ strcpy (cp, " or ");
+ cp += strlen (cp);
+ }
+ got_one = 1;
+ strcpy (cp, archs[idx].name);
+ cp += strlen (cp);
+ }
+ }
+ }
+ }
+ cp = xmalloc (strlen (buf) + 1);
+ strcpy (cp, buf);
+ the_ins.error = cp;
+ }
+ else
+ the_ins.error = _("operands mismatch");
+ return;
+ } /* Fell off the end */
+
+ losing = 0;
+ }
+
+ /* now assemble it */
+
+ the_ins.args = opcode->m_operands;
+ the_ins.numargs = opcode->m_opnum;
+ the_ins.numo = opcode->m_codenum;
+ the_ins.opcode[0] = getone (opcode);
+ the_ins.opcode[1] = gettwo (opcode);
+
+ for (s = the_ins.args, opP = &the_ins.operands[0]; *s; s += 2, opP++)
+ {
+ /* This switch is a doozy.
+ Watch the first step; its a big one! */
+ switch (s[0])
+ {
+
+ case '*':
+ case '~':
+ case '%':
+ case ';':
+ case '@':
+ case '!':
+ case '&':
+ case '$':
+ case '?':
+ case '/':
+ case '<':
+ case '>':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'v':
+#ifndef NO_68851
+ case '|':
+#endif
+ switch (opP->mode)
+ {
+ case IMMED:
+ tmpreg = 0x3c; /* 7.4 */
+ if (strchr ("bwl", s[1]))
+ nextword = get_num (&opP->disp, 80);
+ else
+ nextword = get_num (&opP->disp, 0);
+ if (isvar (&opP->disp))
+ add_fix (s[1], &opP->disp, 0, 0);
+ switch (s[1])
+ {
+ case 'b':
+ if (!isbyte (nextword))
+ opP->error = _("operand out of range");
+ addword (nextword);
+ baseo = 0;
+ break;
+ case 'w':
+ if (!isword (nextword))
+ opP->error = _("operand out of range");
+ addword (nextword);
+ baseo = 0;
+ break;
+ case 'W':
+ if (!issword (nextword))
+ opP->error = _("operand out of range");
+ addword (nextword);
+ baseo = 0;
+ break;
+ case 'l':
+ addword (nextword >> 16);
+ addword (nextword);
+ baseo = 0;
+ break;
+
+ case 'f':
+ baseo = 2;
+ outro = 8;
+ break;
+ case 'F':
+ baseo = 4;
+ outro = 11;
+ break;
+ case 'x':
+ baseo = 6;
+ outro = 15;
+ break;
+ case 'p':
+ baseo = 6;
+ outro = -1;
+ break;
+ default:
+ abort ();
+ }
+ if (!baseo)
+ break;
+
+ /* We gotta put out some float */
+ if (op (&opP->disp) != O_big)
+ {
+ valueT val;
+ int gencnt;
+
+ /* Can other cases happen here? */
+ if (op (&opP->disp) != O_constant)
+ abort ();
+
+ val = (valueT) offs (&opP->disp);
+ gencnt = 0;
+ do
+ {
+ generic_bignum[gencnt] = (LITTLENUM_TYPE) val;
+ val >>= LITTLENUM_NUMBER_OF_BITS;
+ ++gencnt;
+ }
+ while (val != 0);
+ offs (&opP->disp) = gencnt;
+ }
+ if (offs (&opP->disp) > 0)
+ {
+ if (offs (&opP->disp) > baseo)
+ {
+ as_warn (_("Bignum too big for %c format; truncated"),
+ s[1]);
+ offs (&opP->disp) = baseo;
+ }
+ baseo -= offs (&opP->disp);
+ while (baseo--)
+ addword (0);
+ for (wordp = generic_bignum + offs (&opP->disp) - 1;
+ offs (&opP->disp)--;
+ --wordp)
+ addword (*wordp);
+ break;
+ }
+ gen_to_words (words, baseo, (long) outro);
+ for (wordp = words; baseo--; wordp++)
+ addword (*wordp);
+ break;
+ case DREG:
+ tmpreg = opP->reg - DATA; /* 0.dreg */
+ break;
+ case AREG:
+ tmpreg = 0x08 + opP->reg - ADDR; /* 1.areg */
+ break;
+ case AINDR:
+ tmpreg = 0x10 + opP->reg - ADDR; /* 2.areg */
+ break;
+ case ADEC:
+ tmpreg = 0x20 + opP->reg - ADDR; /* 4.areg */
+ break;
+ case AINC:
+ tmpreg = 0x18 + opP->reg - ADDR; /* 3.areg */
+ break;
+ case DISP:
+
+ nextword = get_num (&opP->disp, 80);
+
+ if (opP->reg == PC
+ && ! isvar (&opP->disp)
+ && m68k_abspcadd)
+ {
+ opP->disp.exp.X_op = O_symbol;
+#ifndef BFD_ASSEMBLER
+ opP->disp.exp.X_add_symbol = &abs_symbol;
+#else
+ opP->disp.exp.X_add_symbol =
+ section_symbol (absolute_section);
+#endif
+ }
+
+ /* Force into index mode. Hope this works */
+
+ /* We do the first bit for 32-bit displacements, and the
+ second bit for 16 bit ones. It is possible that we
+ should make the default be WORD instead of LONG, but
+ I think that'd break GCC, so we put up with a little
+ inefficiency for the sake of working output. */
+
+ if (!issword (nextword)
+ || (isvar (&opP->disp)
+ && ((opP->disp.size == SIZE_UNSPEC
+ && flag_short_refs == 0
+ && cpu_of_arch (current_architecture) >= m68020
+ && cpu_of_arch (current_architecture) != mcf5200)
+ || opP->disp.size == SIZE_LONG)))
+ {
+ if (cpu_of_arch (current_architecture) < m68020
+ || cpu_of_arch (current_architecture) == mcf5200)
+ opP->error =
+ _("displacement too large for this architecture; needs 68020 or higher");
+ if (opP->reg == PC)
+ tmpreg = 0x3B; /* 7.3 */
+ else
+ tmpreg = 0x30 + opP->reg - ADDR; /* 6.areg */
+ if (isvar (&opP->disp))
+ {
+ if (opP->reg == PC)
+ {
+ if (opP->disp.size == SIZE_LONG
+#ifdef OBJ_ELF
+ /* If the displacement needs pic
+ relocation it cannot be relaxed. */
+ || opP->disp.pic_reloc != pic_none
+#endif
+ )
+ {
+ addword (0x0170);
+ add_fix ('l', &opP->disp, 1, 2);
+ }
+ else
+ {
+ add_frag (adds (&opP->disp),
+ offs (&opP->disp),
+ TAB (PCLEA, SZ_UNDEF));
+ break;
+ }
+ }
+ else
+ {
+ addword (0x0170);
+ add_fix ('l', &opP->disp, 0, 0);
+ }
+ }
+ else
+ addword (0x0170);
+ addword (nextword >> 16);
+ }
+ else
+ {
+ if (opP->reg == PC)
+ tmpreg = 0x3A; /* 7.2 */
+ else
+ tmpreg = 0x28 + opP->reg - ADDR; /* 5.areg */
+
+ if (isvar (&opP->disp))
+ {
+ if (opP->reg == PC)
+ {
+ add_fix ('w', &opP->disp, 1, 0);
+ }
+ else
+ add_fix ('w', &opP->disp, 0, 0);
+ }
+ }
+ addword (nextword);
+ break;
+
+ case POST:
+ case PRE:
+ case BASE:
+ nextword = 0;
+ baseo = get_num (&opP->disp, 80);
+ if (opP->mode == POST || opP->mode == PRE)
+ outro = get_num (&opP->odisp, 80);
+ /* Figure out the `addressing mode'.
+ Also turn on the BASE_DISABLE bit, if needed. */
+ if (opP->reg == PC || opP->reg == ZPC)
+ {
+ tmpreg = 0x3b; /* 7.3 */
+ if (opP->reg == ZPC)
+ nextword |= 0x80;
+ }
+ else if (opP->reg == 0)
+ {
+ nextword |= 0x80;
+ tmpreg = 0x30; /* 6.garbage */
+ }
+ else if (opP->reg >= ZADDR0 && opP->reg <= ZADDR7)
+ {
+ nextword |= 0x80;
+ tmpreg = 0x30 + opP->reg - ZADDR0;
+ }
+ else
+ tmpreg = 0x30 + opP->reg - ADDR; /* 6.areg */
+
+ siz1 = opP->disp.size;
+ if (opP->mode == POST || opP->mode == PRE)
+ siz2 = opP->odisp.size;
+ else
+ siz2 = SIZE_UNSPEC;
+
+ /* Index register stuff */
+ if (opP->index.reg != 0
+ && opP->index.reg >= DATA
+ && opP->index.reg <= ADDR7)
+ {
+ nextword |= (opP->index.reg - DATA) << 12;
+
+ if (opP->index.size == SIZE_LONG
+ || (opP->index.size == SIZE_UNSPEC
+ && m68k_index_width_default == SIZE_LONG))
+ nextword |= 0x800;
+
+ if ((opP->index.scale != 1
+ && cpu_of_arch (current_architecture) < m68020)
+ || (opP->index.scale == 8
+ && current_architecture == mcf5200))
+ {
+ opP->error =
+ _("scale factor invalid on this architecture; needs cpu32 or 68020 or higher");
+ }
+
+ switch (opP->index.scale)
+ {
+ case 1:
+ break;
+ case 2:
+ nextword |= 0x200;
+ break;
+ case 4:
+ nextword |= 0x400;
+ break;
+ case 8:
+ nextword |= 0x600;
+ break;
+ default:
+ abort ();
+ }
+ /* IF its simple,
+ GET US OUT OF HERE! */
+
+ /* Must be INDEX, with an index register. Address
+ register cannot be ZERO-PC, and either :b was
+ forced, or we know it will fit. For a 68000 or
+ 68010, force this mode anyways, because the
+ larger modes aren't supported. */
+ if (opP->mode == BASE
+ && ((opP->reg >= ADDR0
+ && opP->reg <= ADDR7)
+ || opP->reg == PC))
+ {
+ if (siz1 == SIZE_BYTE
+ || cpu_of_arch (current_architecture) < m68020
+ || cpu_of_arch (current_architecture) == mcf5200
+ || (siz1 == SIZE_UNSPEC
+ && ! isvar (&opP->disp)
+ && issbyte (baseo)))
+ {
+ nextword += baseo & 0xff;
+ addword (nextword);
+ if (isvar (&opP->disp))
+ {
+ /* Do a byte relocation. If it doesn't
+ fit (possible on m68000) let the
+ fixup processing complain later. */
+ if (opP->reg == PC)
+ add_fix ('B', &opP->disp, 1, 1);
+ else
+ add_fix ('B', &opP->disp, 0, 0);
+ }
+ else if (siz1 != SIZE_BYTE)
+ {
+ if (siz1 != SIZE_UNSPEC)
+ as_warn (_("Forcing byte displacement"));
+ if (! issbyte (baseo))
+ opP->error = _("byte displacement out of range");
+ }
+
+ break;
+ }
+ else if (siz1 == SIZE_UNSPEC
+ && opP->reg == PC
+ && isvar (&opP->disp)
+ && subs (&opP->disp) == NULL
+#ifdef OBJ_ELF
+ /* If the displacement needs pic
+ relocation it cannot be relaxed. */
+ && opP->disp.pic_reloc == pic_none
+#endif
+ )
+ {
+ /* The code in md_convert_frag_1 needs to be
+ able to adjust nextword. Call frag_grow
+ to ensure that we have enough space in
+ the frag obstack to make all the bytes
+ contiguous. */
+ frag_grow (14);
+ nextword += baseo & 0xff;
+ addword (nextword);
+ add_frag (adds (&opP->disp), offs (&opP->disp),
+ TAB (PCINDEX, SZ_UNDEF));
+
+ break;
+ }
+ }
+ }
+ else
+ {
+ nextword |= 0x40; /* No index reg */
+ if (opP->index.reg >= ZDATA0
+ && opP->index.reg <= ZDATA7)
+ nextword |= (opP->index.reg - ZDATA0) << 12;
+ else if (opP->index.reg >= ZADDR0
+ || opP->index.reg <= ZADDR7)
+ nextword |= (opP->index.reg - ZADDR0 + 8) << 12;
+ }
+
+ /* It isn't simple. */
+
+ if (cpu_of_arch (current_architecture) < m68020
+ || cpu_of_arch (current_architecture) == mcf5200)
+ opP->error =
+ _("invalid operand mode for this architecture; needs 68020 or higher");
+
+ nextword |= 0x100;
+ /* If the guy specified a width, we assume that it is
+ wide enough. Maybe it isn't. If so, we lose. */
+ switch (siz1)
+ {
+ case SIZE_UNSPEC:
+ if (isvar (&opP->disp)
+ ? m68k_rel32
+ : ! issword (baseo))
+ {
+ siz1 = SIZE_LONG;
+ nextword |= 0x30;
+ }
+ else if (! isvar (&opP->disp) && baseo == 0)
+ nextword |= 0x10;
+ else
+ {
+ nextword |= 0x20;
+ siz1 = SIZE_WORD;
+ }
+ break;
+ case SIZE_BYTE:
+ as_warn (_(":b not permitted; defaulting to :w"));
+ /* Fall through. */
+ case SIZE_WORD:
+ nextword |= 0x20;
+ break;
+ case SIZE_LONG:
+ nextword |= 0x30;
+ break;
+ }
+
+ /* Figure out innner displacement stuff */
+ if (opP->mode == POST || opP->mode == PRE)
+ {
+ if (cpu_of_arch (current_architecture) & cpu32)
+ opP->error = _("invalid operand mode for this architecture; needs 68020 or higher");
+ switch (siz2)
+ {
+ case SIZE_UNSPEC:
+ if (isvar (&opP->odisp)
+ ? m68k_rel32
+ : ! issword (outro))
+ {
+ siz2 = SIZE_LONG;
+ nextword |= 0x3;
+ }
+ else if (! isvar (&opP->odisp) && outro == 0)
+ nextword |= 0x1;
+ else
+ {
+ nextword |= 0x2;
+ siz2 = SIZE_WORD;
+ }
+ break;
+ case 1:
+ as_warn (_(":b not permitted; defaulting to :w"));
+ /* Fall through. */
+ case 2:
+ nextword |= 0x2;
+ break;
+ case 3:
+ nextword |= 0x3;
+ break;
+ }
+ if (opP->mode == POST
+ && (nextword & 0x40) == 0)
+ nextword |= 0x04;
+ }
+ addword (nextword);
+
+ if (siz1 != SIZE_UNSPEC && isvar (&opP->disp))
+ {
+ if (opP->reg == PC || opP->reg == ZPC)
+ add_fix (siz1 == SIZE_LONG ? 'l' : 'w', &opP->disp, 1, 2);
+ else
+ add_fix (siz1 == SIZE_LONG ? 'l' : 'w', &opP->disp, 0, 0);
+ }
+ if (siz1 == SIZE_LONG)
+ addword (baseo >> 16);
+ if (siz1 != SIZE_UNSPEC)
+ addword (baseo);
+
+ if (siz2 != SIZE_UNSPEC && isvar (&opP->odisp))
+ add_fix (siz2 == SIZE_LONG ? 'l' : 'w', &opP->odisp, 0, 0);
+ if (siz2 == SIZE_LONG)
+ addword (outro >> 16);
+ if (siz2 != SIZE_UNSPEC)
+ addword (outro);
+
+ break;
+
+ case ABSL:
+ nextword = get_num (&opP->disp, 80);
+ switch (opP->disp.size)
+ {
+ default:
+ abort ();
+ case SIZE_UNSPEC:
+ if (!isvar (&opP->disp) && issword (offs (&opP->disp)))
+ {
+ tmpreg = 0x38; /* 7.0 */
+ addword (nextword);
+ break;
+ }
+ /* Don't generate pc relative code on 68010 and
+ 68000. */
+ if (isvar (&opP->disp)
+ && !subs (&opP->disp)
+ && adds (&opP->disp)
+#ifdef OBJ_ELF
+ /* If the displacement needs pic relocation it
+ cannot be relaxed. */
+ && opP->disp.pic_reloc == pic_none
+#endif
+ && S_GET_SEGMENT (adds (&opP->disp)) == now_seg
+ && relaxable_symbol (adds (&opP->disp))
+ && HAVE_LONG_BRANCH(current_architecture)
+ && !flag_long_jumps
+ && !strchr ("~%&$?", s[0]))
+ {
+ tmpreg = 0x3A; /* 7.2 */
+ add_frag (adds (&opP->disp),
+ offs (&opP->disp),
+ TAB (PCREL, SZ_UNDEF));
+ break;
+ }
+ /* Fall through into long */
+ case SIZE_LONG:
+ if (isvar (&opP->disp))
+ add_fix ('l', &opP->disp, 0, 0);
+
+ tmpreg = 0x39;/* 7.1 mode */
+ addword (nextword >> 16);
+ addword (nextword);
+ break;
+
+ case SIZE_BYTE:
+ as_bad (_("unsupported byte value; use a different suffix"));
+ /* Fall through. */
+ case SIZE_WORD: /* Word */
+ if (isvar (&opP->disp))
+ add_fix ('w', &opP->disp, 0, 0);
+
+ tmpreg = 0x38;/* 7.0 mode */
+ addword (nextword);
+ break;
+ }
+ break;
+ case CONTROL:
+ case FPREG:
+ default:
+ as_bad (_("unknown/incorrect operand"));
+ /* abort(); */
+ }
+ install_gen_operand (s[1], tmpreg);
+ break;
+
+ case '#':
+ case '^':
+ switch (s[1])
+ { /* JF: I hate floating point! */
+ case 'j':
+ tmpreg = 70;
+ break;
+ case '8':
+ tmpreg = 20;
+ break;
+ case 'C':
+ tmpreg = 50;
+ break;
+ case '3':
+ default:
+ tmpreg = 80;
+ break;
+ }
+ tmpreg = get_num (&opP->disp, tmpreg);
+ if (isvar (&opP->disp))
+ add_fix (s[1], &opP->disp, 0, 0);
+ switch (s[1])
+ {
+ case 'b': /* Danger: These do no check for
+ certain types of overflow.
+ user beware! */
+ if (!isbyte (tmpreg))
+ opP->error = _("out of range");
+ insop (tmpreg, opcode);
+ if (isvar (&opP->disp))
+ the_ins.reloc[the_ins.nrel - 1].n =
+ (opcode->m_codenum) * 2 + 1;
+ break;
+ case 'B':
+ if (!issbyte (tmpreg))
+ opP->error = _("out of range");
+ the_ins.opcode[the_ins.numo - 1] |= tmpreg & 0xff;
+ if (isvar (&opP->disp))
+ the_ins.reloc[the_ins.nrel - 1].n = opcode->m_codenum * 2 - 1;
+ break;
+ case 'w':
+ if (!isword (tmpreg))
+ opP->error = _("out of range");
+ insop (tmpreg, opcode);
+ if (isvar (&opP->disp))
+ the_ins.reloc[the_ins.nrel - 1].n = (opcode->m_codenum) * 2;
+ break;
+ case 'W':
+ if (!issword (tmpreg))
+ opP->error = _("out of range");
+ insop (tmpreg, opcode);
+ if (isvar (&opP->disp))
+ the_ins.reloc[the_ins.nrel - 1].n = (opcode->m_codenum) * 2;
+ break;
+ case 'l':
+ /* Because of the way insop works, we put these two out
+ backwards. */
+ insop (tmpreg, opcode);
+ insop (tmpreg >> 16, opcode);
+ if (isvar (&opP->disp))
+ the_ins.reloc[the_ins.nrel - 1].n = (opcode->m_codenum) * 2;
+ break;
+ case '3':
+ tmpreg &= 0xFF;
+ case '8':
+ case 'C':
+ case 'j':
+ install_operand (s[1], tmpreg);
+ break;
+ default:
+ abort ();
+ }
+ break;
+
+ case '+':
+ case '-':
+ case 'A':
+ case 'a':
+ install_operand (s[1], opP->reg - ADDR);
+ break;
+
+ case 'B':
+ tmpreg = get_num (&opP->disp, 80);
+ switch (s[1])
+ {
+ case 'B':
+ /* The pc_fix argument winds up in fx_pcrel_adjust,
+ which is a char, and may therefore be unsigned. We
+ want to pass -1, but we pass 64 instead, and convert
+ back in md_pcrel_from. */
+ add_fix ('B', &opP->disp, 1, 64);
+ break;
+ case 'W':
+ add_fix ('w', &opP->disp, 1, 0);
+ addword (0);
+ break;
+ case 'L':
+ long_branch:
+ if (!HAVE_LONG_BRANCH(current_architecture))
+ as_warn (_("Can't use long branches on 68000/68010/5200"));
+ the_ins.opcode[the_ins.numo - 1] |= 0xff;
+ add_fix ('l', &opP->disp, 1, 0);
+ addword (0);
+ addword (0);
+ break;
+ case 'g':
+ if (subs (&opP->disp)) /* We can't relax it */
+ goto long_branch;
+
+#ifdef OBJ_ELF
+ /* If the displacement needs pic relocation it cannot be
+ relaxed. */
+ if (opP->disp.pic_reloc != pic_none)
+ goto long_branch;
+#endif
+
+ /* This could either be a symbol, or an absolute
+ address. No matter, the frag hacking will finger it
+ out. Not quite: it can't switch from BRANCH to
+ BCC68000 for the case where opnd is absolute (it
+ needs to use the 68000 hack since no conditional abs
+ jumps). */
+ if (( !HAVE_LONG_BRANCH(current_architecture)
+ || (0 == adds (&opP->disp)))
+ && (the_ins.opcode[0] >= 0x6200)
+ && (the_ins.opcode[0] <= 0x6f00))
+ add_frag (adds (&opP->disp), offs (&opP->disp),
+ TAB (BCC68000, SZ_UNDEF));
+ else
+ add_frag (adds (&opP->disp), offs (&opP->disp),
+ TAB (ABRANCH, SZ_UNDEF));
+ break;
+ case 'w':
+ if (isvar (&opP->disp))
+ {
+#if 1
+ /* check for DBcc instruction */
+ if ((the_ins.opcode[0] & 0xf0f8) == 0x50c8)
+ {
+ /* size varies if patch */
+ /* needed for long form */
+ add_frag (adds (&opP->disp), offs (&opP->disp),
+ TAB (DBCC, SZ_UNDEF));
+ break;
+ }
+#endif
+ add_fix ('w', &opP->disp, 1, 0);
+ }
+ addword (0);
+ break;
+ case 'C': /* Fixed size LONG coproc branches */
+ add_fix ('l', &opP->disp, 1, 0);
+ addword (0);
+ addword (0);
+ break;
+ case 'c': /* Var size Coprocesssor branches */
+ if (subs (&opP->disp))
+ {
+ add_fix ('l', &opP->disp, 1, 0);
+ add_frag ((symbolS *) 0, (offsetT) 0, TAB (FBRANCH, LONG));
+ }
+ else if (adds (&opP->disp))
+ add_frag (adds (&opP->disp), offs (&opP->disp),
+ TAB (FBRANCH, SZ_UNDEF));
+ else
+ {
+ /* add_frag ((symbolS *) 0, offs (&opP->disp),
+ TAB(FBRANCH,SHORT)); */
+ the_ins.opcode[the_ins.numo - 1] |= 0x40;
+ add_fix ('l', &opP->disp, 1, 0);
+ addword (0);
+ addword (0);
+ }
+ break;
+ default:
+ abort ();
+ }
+ break;
+
+ case 'C': /* Ignore it */
+ break;
+
+ case 'd': /* JF this is a kludge */
+ install_operand ('s', opP->reg - ADDR);
+ tmpreg = get_num (&opP->disp, 80);
+ if (!issword (tmpreg))
+ {
+ as_warn (_("Expression out of range, using 0"));
+ tmpreg = 0;
+ }
+ addword (tmpreg);
+ break;
+
+ case 'D':
+ install_operand (s[1], opP->reg - DATA);
+ break;
+
+ case 'F':
+ install_operand (s[1], opP->reg - FP0);
+ break;
+
+ case 'I':
+ tmpreg = opP->reg - COP0;
+ install_operand (s[1], tmpreg);
+ break;
+
+ case 'J': /* JF foo */
+ switch (opP->reg)
+ {
+ case SFC:
+ tmpreg = 0x000;
+ break;
+ case DFC:
+ tmpreg = 0x001;
+ break;
+ case CACR:
+ tmpreg = 0x002;
+ break;
+ case TC:
+ tmpreg = 0x003;
+ break;
+ case ITT0:
+ tmpreg = 0x004;
+ break;
+ case ITT1:
+ tmpreg = 0x005;
+ break;
+ case DTT0:
+ tmpreg = 0x006;
+ break;
+ case DTT1:
+ tmpreg = 0x007;
+ break;
+ case BUSCR:
+ tmpreg = 0x008;
+ break;
+
+ case USP:
+ tmpreg = 0x800;
+ break;
+ case VBR:
+ tmpreg = 0x801;
+ break;
+ case CAAR:
+ tmpreg = 0x802;
+ break;
+ case MSP:
+ tmpreg = 0x803;
+ break;
+ case ISP:
+ tmpreg = 0x804;
+ break;
+ case MMUSR:
+ tmpreg = 0x805;
+ break;
+ case URP:
+ tmpreg = 0x806;
+ break;
+ case SRP:
+ tmpreg = 0x807;
+ break;
+ case PCR:
+ tmpreg = 0x808;
+ break;
+ case ROMBAR:
+ tmpreg = 0xC00;
+ break;
+ case RAMBAR0:
+ tmpreg = 0xC04;
+ break;
+ case RAMBAR1:
+ tmpreg = 0xC05;
+ break;
+ case MBAR:
+ tmpreg = 0xC0F;
+ break;
+ default:
+ abort ();
+ }
+ install_operand (s[1], tmpreg);
+ break;
+
+ case 'k':
+ tmpreg = get_num (&opP->disp, 55);
+ install_operand (s[1], tmpreg & 0x7f);
+ break;
+
+ case 'l':
+ tmpreg = opP->mask;
+ if (s[1] == 'w')
+ {
+ if (tmpreg & 0x7FF0000)
+ as_bad (_("Floating point register in register list"));
+ insop (reverse_16_bits (tmpreg), opcode);
+ }
+ else
+ {
+ if (tmpreg & 0x700FFFF)
+ as_bad (_("Wrong register in floating-point reglist"));
+ install_operand (s[1], reverse_8_bits (tmpreg >> 16));
+ }
+ break;
+
+ case 'L':
+ tmpreg = opP->mask;
+ if (s[1] == 'w')
+ {
+ if (tmpreg & 0x7FF0000)
+ as_bad (_("Floating point register in register list"));
+ insop (tmpreg, opcode);
+ }
+ else if (s[1] == '8')
+ {
+ if (tmpreg & 0x0FFFFFF)
+ as_bad (_("incorrect register in reglist"));
+ install_operand (s[1], tmpreg >> 24);
+ }
+ else
+ {
+ if (tmpreg & 0x700FFFF)
+ as_bad (_("wrong register in floating-point reglist"));
+ else
+ install_operand (s[1], tmpreg >> 16);
+ }
+ break;
+
+ case 'M':
+ install_operand (s[1], get_num (&opP->disp, 60));
+ break;
+
+ case 'O':
+ tmpreg = ((opP->mode == DREG)
+ ? 0x20 + opP->reg - DATA
+ : (get_num (&opP->disp, 40) & 0x1F));
+ install_operand (s[1], tmpreg);
+ break;
+
+ case 'Q':
+ tmpreg = get_num (&opP->disp, 10);
+ if (tmpreg == 8)
+ tmpreg = 0;
+ install_operand (s[1], tmpreg);
+ break;
+
+ case 'R':
+ /* This depends on the fact that ADDR registers are eight
+ more than their corresponding DATA regs, so the result
+ will have the ADDR_REG bit set */
+ install_operand (s[1], opP->reg - DATA);
+ break;
+
+ case 'r':
+ if (opP->mode == AINDR)
+ install_operand (s[1], opP->reg - DATA);
+ else
+ install_operand (s[1], opP->index.reg - DATA);
+ break;
+
+ case 's':
+ if (opP->reg == FPI)
+ tmpreg = 0x1;
+ else if (opP->reg == FPS)
+ tmpreg = 0x2;
+ else if (opP->reg == FPC)
+ tmpreg = 0x4;
+ else
+ abort ();
+ install_operand (s[1], tmpreg);
+ break;
+
+ case 'S': /* Ignore it */
+ break;
+
+ case 'T':
+ install_operand (s[1], get_num (&opP->disp, 30));
+ break;
+
+ case 'U': /* Ignore it */
+ break;
+
+ case 'c':
+ switch (opP->reg)
+ {
+ case NC:
+ tmpreg = 0;
+ break;
+ case DC:
+ tmpreg = 1;
+ break;
+ case IC:
+ tmpreg = 2;
+ break;
+ case BC:
+ tmpreg = 3;
+ break;
+ default:
+ as_fatal (_("failed sanity check"));
+ } /* switch on cache token */
+ install_operand (s[1], tmpreg);
+ break;
+#ifndef NO_68851
+ /* JF: These are out of order, I fear. */
+ case 'f':
+ switch (opP->reg)
+ {
+ case SFC:
+ tmpreg = 0;
+ break;
+ case DFC:
+ tmpreg = 1;
+ break;
+ default:
+ abort ();
+ }
+ install_operand (s[1], tmpreg);
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ switch (opP->reg)
+ {
+ case TC:
+ tmpreg = 0;
+ break;
+ case CAL:
+ tmpreg = 4;
+ break;
+ case VAL:
+ tmpreg = 5;
+ break;
+ case SCC:
+ tmpreg = 6;
+ break;
+ case AC:
+ tmpreg = 7;
+ break;
+ default:
+ abort ();
+ }
+ install_operand (s[1], tmpreg);
+ break;
+
+ case 'V':
+ if (opP->reg == VAL)
+ break;
+ abort ();
+
+ case 'W':
+ switch (opP->reg)
+ {
+ case DRP:
+ tmpreg = 1;
+ break;
+ case SRP:
+ tmpreg = 2;
+ break;
+ case CRP:
+ tmpreg = 3;
+ break;
+ default:
+ abort ();
+ }
+ install_operand (s[1], tmpreg);
+ break;
+
+ case 'X':
+ switch (opP->reg)
+ {
+ case BAD:
+ case BAD + 1:
+ case BAD + 2:
+ case BAD + 3:
+ case BAD + 4:
+ case BAD + 5:
+ case BAD + 6:
+ case BAD + 7:
+ tmpreg = (4 << 10) | ((opP->reg - BAD) << 2);
+ break;
+
+ case BAC:
+ case BAC + 1:
+ case BAC + 2:
+ case BAC + 3:
+ case BAC + 4:
+ case BAC + 5:
+ case BAC + 6:
+ case BAC + 7:
+ tmpreg = (5 << 10) | ((opP->reg - BAC) << 2);
+ break;
+
+ default:
+ abort ();
+ }
+ install_operand (s[1], tmpreg);
+ break;
+ case 'Y':
+ know (opP->reg == PSR);
+ break;
+ case 'Z':
+ know (opP->reg == PCSR);
+ break;
+#endif /* m68851 */
+ case '3':
+ switch (opP->reg)
+ {
+ case TT0:
+ tmpreg = 2;
+ break;
+ case TT1:
+ tmpreg = 3;
+ break;
+ default:
+ abort ();
+ }
+ install_operand (s[1], tmpreg);
+ break;
+ case 't':
+ tmpreg = get_num (&opP->disp, 20);
+ install_operand (s[1], tmpreg);
+ break;
+ case '_': /* used only for move16 absolute 32-bit address */
+ if (isvar (&opP->disp))
+ add_fix ('l', &opP->disp, 0, 0);
+ tmpreg = get_num (&opP->disp, 80);
+ addword (tmpreg >> 16);
+ addword (tmpreg & 0xFFFF);
+ break;
+ default:
+ abort ();
+ }
+ }
+
+ /* By the time whe get here (FINALLY) the_ins contains the complete
+ instruction, ready to be emitted. . . */
+}
+
+static int
+reverse_16_bits (in)
+ int in;
+{
+ int out = 0;
+ int n;
+
+ static int mask[16] =
+ {
+ 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
+ 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000
+ };
+ for (n = 0; n < 16; n++)
+ {
+ if (in & mask[n])
+ out |= mask[15 - n];
+ }
+ return out;
+} /* reverse_16_bits() */
+
+static int
+reverse_8_bits (in)
+ int in;
+{
+ int out = 0;
+ int n;
+
+ static int mask[8] =
+ {
+ 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
+ };
+
+ for (n = 0; n < 8; n++)
+ {
+ if (in & mask[n])
+ out |= mask[7 - n];
+ }
+ return out;
+} /* reverse_8_bits() */
+
+/* Cause an extra frag to be generated here, inserting up to 10 bytes
+ (that value is chosen in the frag_var call in md_assemble). TYPE
+ is the subtype of the frag to be generated; its primary type is
+ rs_machine_dependent.
+
+ The TYPE parameter is also used by md_convert_frag_1 and
+ md_estimate_size_before_relax. The appropriate type of fixup will
+ be emitted by md_convert_frag_1.
+
+ ADD becomes the FR_SYMBOL field of the frag, and OFF the FR_OFFSET. */
+static void
+install_operand (mode, val)
+ int mode;
+ int val;
+{
+ switch (mode)
+ {
+ case 's':
+ the_ins.opcode[0] |= val & 0xFF; /* JF FF is for M kludge */
+ break;
+ case 'd':
+ the_ins.opcode[0] |= val << 9;
+ break;
+ case '1':
+ the_ins.opcode[1] |= val << 12;
+ break;
+ case '2':
+ the_ins.opcode[1] |= val << 6;
+ break;
+ case '3':
+ the_ins.opcode[1] |= val;
+ break;
+ case '4':
+ the_ins.opcode[2] |= val << 12;
+ break;
+ case '5':
+ the_ins.opcode[2] |= val << 6;
+ break;
+ case '6':
+ /* DANGER! This is a hack to force cas2l and cas2w cmds to be
+ three words long! */
+ the_ins.numo++;
+ the_ins.opcode[2] |= val;
+ break;
+ case '7':
+ the_ins.opcode[1] |= val << 7;
+ break;
+ case '8':
+ the_ins.opcode[1] |= val << 10;
+ break;
+#ifndef NO_68851
+ case '9':
+ the_ins.opcode[1] |= val << 5;
+ break;
+#endif
+
+ case 't':
+ the_ins.opcode[1] |= (val << 10) | (val << 7);
+ break;
+ case 'D':
+ the_ins.opcode[1] |= (val << 12) | val;
+ break;
+ case 'g':
+ the_ins.opcode[0] |= val = 0xff;
+ break;
+ case 'i':
+ the_ins.opcode[0] |= val << 9;
+ break;
+ case 'C':
+ the_ins.opcode[1] |= val;
+ break;
+ case 'j':
+ the_ins.opcode[1] |= val;
+ the_ins.numo++; /* What a hack */
+ break;
+ case 'k':
+ the_ins.opcode[1] |= val << 4;
+ break;
+ case 'b':
+ case 'w':
+ case 'W':
+ case 'l':
+ break;
+ case 'e':
+ the_ins.opcode[0] |= (val << 6);
+ break;
+ case 'L':
+ the_ins.opcode[1] = (val >> 16);
+ the_ins.opcode[2] = val & 0xffff;
+ break;
+ case 'c':
+ default:
+ as_fatal (_("failed sanity check."));
+ }
+} /* install_operand() */
+
+static void
+install_gen_operand (mode, val)
+ int mode;
+ int val;
+{
+ switch (mode)
+ {
+ case 's':
+ the_ins.opcode[0] |= val;
+ break;
+ case 'd':
+ /* This is a kludge!!! */
+ the_ins.opcode[0] |= (val & 0x07) << 9 | (val & 0x38) << 3;
+ break;
+ case 'b':
+ case 'w':
+ case 'l':
+ case 'f':
+ case 'F':
+ case 'x':
+ case 'p':
+ the_ins.opcode[0] |= val;
+ break;
+ /* more stuff goes here */
+ default:
+ as_fatal (_("failed sanity check."));
+ }
+} /* install_gen_operand() */
+
+/*
+ * verify that we have some number of paren pairs, do m68k_ip_op(), and
+ * then deal with the bitfield hack.
+ */
+
+static char *
+crack_operand (str, opP)
+ register char *str;
+ register struct m68k_op *opP;
+{
+ register int parens;
+ register int c;
+ register char *beg_str;
+ int inquote = 0;
+
+ if (!str)
+ {
+ return str;
+ }
+ beg_str = str;
+ for (parens = 0; *str && (parens > 0 || inquote || notend (str)); str++)
+ {
+ if (! inquote)
+ {
+ if (*str == '(')
+ parens++;
+ else if (*str == ')')
+ {
+ if (!parens)
+ { /* ERROR */
+ opP->error = _("Extra )");
+ return str;
+ }
+ --parens;
+ }
+ }
+ if (flag_mri && *str == '\'')
+ inquote = ! inquote;
+ }
+ if (!*str && parens)
+ { /* ERROR */
+ opP->error = _("Missing )");
+ return str;
+ }
+ c = *str;
+ *str = '\0';
+ if (m68k_ip_op (beg_str, opP) != 0)
+ {
+ *str = c;
+ return str;
+ }
+ *str = c;
+ if (c == '}')
+ c = *++str; /* JF bitfield hack */
+ if (c)
+ {
+ c = *++str;
+ if (!c)
+ as_bad (_("Missing operand"));
+ }
+
+ /* Detect MRI REG symbols and convert them to REGLSTs. */
+ if (opP->mode == CONTROL && (int)opP->reg < 0)
+ {
+ opP->mode = REGLST;
+ opP->mask = ~(int)opP->reg;
+ opP->reg = 0;
+ }
+
+ return str;
+}
+
+/* This is the guts of the machine-dependent assembler. STR points to a
+ machine dependent instruction. This function is supposed to emit
+ the frags/bytes it assembles to.
+ */
+
+static void
+insert_reg (regname, regnum)
+ const char *regname;
+ int regnum;
+{
+ char buf[100];
+ int i;
+
+#ifdef REGISTER_PREFIX
+ if (!flag_reg_prefix_optional)
+ {
+ buf[0] = REGISTER_PREFIX;
+ strcpy (buf + 1, regname);
+ regname = buf;
+ }
+#endif
+
+ symbol_table_insert (symbol_new (regname, reg_section, regnum,
+ &zero_address_frag));
+
+ for (i = 0; regname[i]; i++)
+ buf[i] = islower (regname[i]) ? toupper (regname[i]) : regname[i];
+ buf[i] = '\0';
+
+ symbol_table_insert (symbol_new (buf, reg_section, regnum,
+ &zero_address_frag));
+}
+
+struct init_entry
+ {
+ const char *name;
+ int number;
+ };
+
+static const struct init_entry init_table[] =
+{
+ { "d0", DATA0 },
+ { "d1", DATA1 },
+ { "d2", DATA2 },
+ { "d3", DATA3 },
+ { "d4", DATA4 },
+ { "d5", DATA5 },
+ { "d6", DATA6 },
+ { "d7", DATA7 },
+ { "a0", ADDR0 },
+ { "a1", ADDR1 },
+ { "a2", ADDR2 },
+ { "a3", ADDR3 },
+ { "a4", ADDR4 },
+ { "a5", ADDR5 },
+ { "a6", ADDR6 },
+ { "fp", ADDR6 },
+ { "a7", ADDR7 },
+ { "sp", ADDR7 },
+ { "ssp", ADDR7 },
+ { "fp0", FP0 },
+ { "fp1", FP1 },
+ { "fp2", FP2 },
+ { "fp3", FP3 },
+ { "fp4", FP4 },
+ { "fp5", FP5 },
+ { "fp6", FP6 },
+ { "fp7", FP7 },
+ { "fpi", FPI },
+ { "fpiar", FPI },
+ { "fpc", FPI },
+ { "fps", FPS },
+ { "fpsr", FPS },
+ { "fpc", FPC },
+ { "fpcr", FPC },
+ { "control", FPC },
+ { "status", FPS },
+ { "iaddr", FPI },
+
+ { "cop0", COP0 },
+ { "cop1", COP1 },
+ { "cop2", COP2 },
+ { "cop3", COP3 },
+ { "cop4", COP4 },
+ { "cop5", COP5 },
+ { "cop6", COP6 },
+ { "cop7", COP7 },
+ { "pc", PC },
+ { "zpc", ZPC },
+ { "sr", SR },
+
+ { "ccr", CCR },
+ { "cc", CCR },
+
+ /* control registers */
+ { "sfc", SFC }, /* Source Function Code */
+ { "sfcr", SFC },
+ { "dfc", DFC }, /* Destination Function Code */
+ { "dfcr", DFC },
+ { "cacr", CACR }, /* Cache Control Register */
+ { "caar", CAAR }, /* Cache Address Register */
+
+ { "usp", USP }, /* User Stack Pointer */
+ { "vbr", VBR }, /* Vector Base Register */
+ { "msp", MSP }, /* Master Stack Pointer */
+ { "isp", ISP }, /* Interrupt Stack Pointer */
+
+ { "itt0", ITT0 }, /* Instruction Transparent Translation Reg 0 */
+ { "itt1", ITT1 }, /* Instruction Transparent Translation Reg 1 */
+ { "dtt0", DTT0 }, /* Data Transparent Translation Register 0 */
+ { "dtt1", DTT1 }, /* Data Transparent Translation Register 1 */
+
+ /* 68ec040 versions of same */
+ { "iacr0", ITT0 }, /* Instruction Access Control Register 0 */
+ { "iacr1", ITT1 }, /* Instruction Access Control Register 0 */
+ { "dacr0", DTT0 }, /* Data Access Control Register 0 */
+ { "dacr1", DTT1 }, /* Data Access Control Register 0 */
+
+ /* mcf5200 versions of same. The ColdFire programmer's reference
+ manual indicated that the order is 2,3,0,1, but Ken Rose
+ <rose@netcom.com> says that 0,1,2,3 is the correct order. */
+ { "acr0", ITT0 }, /* Access Control Unit 0 */
+ { "acr1", ITT1 }, /* Access Control Unit 1 */
+ { "acr2", DTT0 }, /* Access Control Unit 2 */
+ { "acr3", DTT1 }, /* Access Control Unit 3 */
+
+ { "tc", TC }, /* MMU Translation Control Register */
+ { "tcr", TC },
+
+ { "mmusr", MMUSR }, /* MMU Status Register */
+ { "srp", SRP }, /* User Root Pointer */
+ { "urp", URP }, /* Supervisor Root Pointer */
+
+ { "buscr", BUSCR },
+ { "pcr", PCR },
+
+ { "rombar", ROMBAR }, /* ROM Base Address Register */
+ { "rambar0", RAMBAR0 }, /* ROM Base Address Register */
+ { "rambar1", RAMBAR1 }, /* ROM Base Address Register */
+ { "mbar", MBAR }, /* Module Base Address Register */
+ /* end of control registers */
+
+ { "ac", AC },
+ { "bc", BC },
+ { "cal", CAL },
+ { "crp", CRP },
+ { "drp", DRP },
+ { "pcsr", PCSR },
+ { "psr", PSR },
+ { "scc", SCC },
+ { "val", VAL },
+ { "bad0", BAD0 },
+ { "bad1", BAD1 },
+ { "bad2", BAD2 },
+ { "bad3", BAD3 },
+ { "bad4", BAD4 },
+ { "bad5", BAD5 },
+ { "bad6", BAD6 },
+ { "bad7", BAD7 },
+ { "bac0", BAC0 },
+ { "bac1", BAC1 },
+ { "bac2", BAC2 },
+ { "bac3", BAC3 },
+ { "bac4", BAC4 },
+ { "bac5", BAC5 },
+ { "bac6", BAC6 },
+ { "bac7", BAC7 },
+
+ { "ic", IC },
+ { "dc", DC },
+ { "nc", NC },
+
+ { "tt0", TT0 },
+ { "tt1", TT1 },
+ /* 68ec030 versions of same */
+ { "ac0", TT0 },
+ { "ac1", TT1 },
+ /* 68ec030 access control unit, identical to 030 MMU status reg */
+ { "acusr", PSR },
+
+ /* Suppressed data and address registers. */
+ { "zd0", ZDATA0 },
+ { "zd1", ZDATA1 },
+ { "zd2", ZDATA2 },
+ { "zd3", ZDATA3 },
+ { "zd4", ZDATA4 },
+ { "zd5", ZDATA5 },
+ { "zd6", ZDATA6 },
+ { "zd7", ZDATA7 },
+ { "za0", ZADDR0 },
+ { "za1", ZADDR1 },
+ { "za2", ZADDR2 },
+ { "za3", ZADDR3 },
+ { "za4", ZADDR4 },
+ { "za5", ZADDR5 },
+ { "za6", ZADDR6 },
+ { "za7", ZADDR7 },
+
+ { 0, 0 }
+};
+
+static void
+init_regtable ()
+{
+ int i;
+ for (i = 0; init_table[i].name; i++)
+ insert_reg (init_table[i].name, init_table[i].number);
+}
+
+static int no_68851, no_68881;
+
+#ifdef OBJ_AOUT
+/* a.out machine type. Default to 68020. */
+int m68k_aout_machtype = 2;
+#endif
+
+void
+md_assemble (str)
+ char *str;
+{
+ const char *er;
+ short *fromP;
+ char *toP = NULL;
+ int m, n = 0;
+ char *to_beg_P;
+ int shorts_this_frag;
+ fixS *fixP;
+
+ /* In MRI mode, the instruction and operands are separated by a
+ space. Anything following the operands is a comment. The label
+ has already been removed. */
+ if (flag_mri)
+ {
+ char *s;
+ int fields = 0;
+ int infield = 0;
+ int inquote = 0;
+
+ for (s = str; *s != '\0'; s++)
+ {
+ if ((*s == ' ' || *s == '\t') && ! inquote)
+ {
+ if (infield)
+ {
+ ++fields;
+ if (fields >= 2)
+ {
+ *s = '\0';
+ break;
+ }
+ infield = 0;
+ }
+ }
+ else
+ {
+ if (! infield)
+ infield = 1;
+ if (*s == '\'')
+ inquote = ! inquote;
+ }
+ }
+ }
+
+ memset ((char *) (&the_ins), '\0', sizeof (the_ins));
+ m68k_ip (str);
+ er = the_ins.error;
+ if (!er)
+ {
+ for (n = 0; n < the_ins.numargs; n++)
+ if (the_ins.operands[n].error)
+ {
+ er = the_ins.operands[n].error;
+ break;
+ }
+ }
+ if (er)
+ {
+ as_bad (_("%s -- statement `%s' ignored"), er, str);
+ return;
+ }
+
+ /* If there is a current label, record that it marks an instruction. */
+ if (current_label != NULL)
+ {
+ current_label->text = 1;
+ current_label = NULL;
+ }
+
+ if (the_ins.nfrag == 0)
+ {
+ /* No frag hacking involved; just put it out */
+ toP = frag_more (2 * the_ins.numo);
+ fromP = &the_ins.opcode[0];
+ for (m = the_ins.numo; m; --m)
+ {
+ md_number_to_chars (toP, (long) (*fromP), 2);
+ toP += 2;
+ fromP++;
+ }
+ /* put out symbol-dependent info */
+ for (m = 0; m < the_ins.nrel; m++)
+ {
+ switch (the_ins.reloc[m].wid)
+ {
+ case 'B':
+ n = 1;
+ break;
+ case 'b':
+ n = 1;
+ break;
+ case '3':
+ n = 1;
+ break;
+ case 'w':
+ case 'W':
+ n = 2;
+ break;
+ case 'l':
+ n = 4;
+ break;
+ default:
+ as_fatal (_("Don't know how to figure width of %c in md_assemble()"),
+ the_ins.reloc[m].wid);
+ }
+
+ fixP = fix_new_exp (frag_now,
+ ((toP - frag_now->fr_literal)
+ - the_ins.numo * 2 + the_ins.reloc[m].n),
+ n,
+ &the_ins.reloc[m].exp,
+ the_ins.reloc[m].pcrel,
+ get_reloc_code (n, the_ins.reloc[m].pcrel,
+ the_ins.reloc[m].pic_reloc));
+ fixP->fx_pcrel_adjust = the_ins.reloc[m].pcrel_fix;
+ if (the_ins.reloc[m].wid == 'B')
+ fixP->fx_signed = 1;
+ }
+ return;
+ }
+
+ /* There's some frag hacking */
+ for (n = 0, fromP = &the_ins.opcode[0]; n < the_ins.nfrag; n++)
+ {
+ int wid;
+
+ if (n == 0)
+ wid = 2 * the_ins.fragb[n].fragoff;
+ else
+ wid = 2 * (the_ins.numo - the_ins.fragb[n - 1].fragoff);
+ toP = frag_more (wid);
+ to_beg_P = toP;
+ shorts_this_frag = 0;
+ for (m = wid / 2; m; --m)
+ {
+ md_number_to_chars (toP, (long) (*fromP), 2);
+ toP += 2;
+ fromP++;
+ shorts_this_frag++;
+ }
+ for (m = 0; m < the_ins.nrel; m++)
+ {
+ if ((the_ins.reloc[m].n) >= 2 * shorts_this_frag)
+ {
+ the_ins.reloc[m].n -= 2 * shorts_this_frag;
+ break;
+ }
+ wid = the_ins.reloc[m].wid;
+ if (wid == 0)
+ continue;
+ the_ins.reloc[m].wid = 0;
+ wid = (wid == 'b') ? 1 : (wid == 'w') ? 2 : (wid == 'l') ? 4 : 4000;
+
+ fixP = fix_new_exp (frag_now,
+ ((toP - frag_now->fr_literal)
+ - the_ins.numo * 2 + the_ins.reloc[m].n),
+ wid,
+ &the_ins.reloc[m].exp,
+ the_ins.reloc[m].pcrel,
+ get_reloc_code (wid, the_ins.reloc[m].pcrel,
+ the_ins.reloc[m].pic_reloc));
+ fixP->fx_pcrel_adjust = the_ins.reloc[m].pcrel_fix;
+ }
+ (void) frag_var (rs_machine_dependent, 10, 0,
+ (relax_substateT) (the_ins.fragb[n].fragty),
+ the_ins.fragb[n].fadd, the_ins.fragb[n].foff, to_beg_P);
+ }
+ n = (the_ins.numo - the_ins.fragb[n - 1].fragoff);
+ shorts_this_frag = 0;
+ if (n)
+ {
+ toP = frag_more (n * sizeof (short));
+ while (n--)
+ {
+ md_number_to_chars (toP, (long) (*fromP), 2);
+ toP += 2;
+ fromP++;
+ shorts_this_frag++;
+ }
+ }
+ for (m = 0; m < the_ins.nrel; m++)
+ {
+ int wid;
+
+ wid = the_ins.reloc[m].wid;
+ if (wid == 0)
+ continue;
+ the_ins.reloc[m].wid = 0;
+ wid = (wid == 'b') ? 1 : (wid == 'w') ? 2 : (wid == 'l') ? 4 : 4000;
+
+ fixP = fix_new_exp (frag_now,
+ ((the_ins.reloc[m].n + toP - frag_now->fr_literal)
+ - shorts_this_frag * 2),
+ wid,
+ &the_ins.reloc[m].exp,
+ the_ins.reloc[m].pcrel,
+ get_reloc_code (wid, the_ins.reloc[m].pcrel,
+ the_ins.reloc[m].pic_reloc));
+ fixP->fx_pcrel_adjust = the_ins.reloc[m].pcrel_fix;
+ }
+}
+
+void
+md_begin ()
+{
+ /*
+ * md_begin -- set up hash tables with 68000 instructions.
+ * similar to what the vax assembler does. ---phr
+ */
+ /* RMS claims the thing to do is take the m68k-opcode.h table, and make
+ a copy of it at runtime, adding in the information we want but isn't
+ there. I think it'd be better to have an awk script hack the table
+ at compile time. Or even just xstr the table and use it as-is. But
+ my lord ghod hath spoken, so we do it this way. Excuse the ugly var
+ names. */
+
+ register const struct m68k_opcode *ins;
+ register struct m68k_incant *hack, *slak;
+ register const char *retval = 0; /* empty string, or error msg text */
+ register unsigned int i;
+ register char c;
+
+ if (flag_mri)
+ {
+ flag_reg_prefix_optional = 1;
+ m68k_abspcadd = 1;
+ if (! m68k_rel32_from_cmdline)
+ m68k_rel32 = 0;
+ }
+
+ op_hash = hash_new ();
+
+ obstack_begin (&robyn, 4000);
+ for (i = 0; i < m68k_numopcodes; i++)
+ {
+ hack = slak = (struct m68k_incant *) obstack_alloc (&robyn, sizeof (struct m68k_incant));
+ do
+ {
+ ins = &m68k_opcodes[i];
+ /* We *could* ignore insns that don't match our arch here
+ but just leaving them out of the hash. */
+ slak->m_operands = ins->args;
+ slak->m_opnum = strlen (slak->m_operands) / 2;
+ slak->m_arch = ins->arch;
+ slak->m_opcode = ins->opcode;
+ /* This is kludgey */
+ slak->m_codenum = ((ins->match) & 0xffffL) ? 2 : 1;
+ if (i + 1 != m68k_numopcodes
+ && !strcmp (ins->name, m68k_opcodes[i + 1].name))
+ {
+ slak->m_next = (struct m68k_incant *) obstack_alloc (&robyn, sizeof (struct m68k_incant));
+ i++;
+ }
+ else
+ slak->m_next = 0;
+ slak = slak->m_next;
+ }
+ while (slak);
+
+ retval = hash_insert (op_hash, ins->name, (char *) hack);
+ if (retval)
+ as_fatal (_("Internal Error: Can't hash %s: %s"), ins->name, retval);
+ }
+
+ for (i = 0; i < m68k_numaliases; i++)
+ {
+ const char *name = m68k_opcode_aliases[i].primary;
+ const char *alias = m68k_opcode_aliases[i].alias;
+ PTR val = hash_find (op_hash, name);
+ if (!val)
+ as_fatal (_("Internal Error: Can't find %s in hash table"), name);
+ retval = hash_insert (op_hash, alias, val);
+ if (retval)
+ as_fatal (_("Internal Error: Can't hash %s: %s"), alias, retval);
+ }
+
+ /* In MRI mode, all unsized branches are variable sized. Normally,
+ they are word sized. */
+ if (flag_mri)
+ {
+ static struct m68k_opcode_alias mri_aliases[] =
+ {
+ { "bhi", "jhi", },
+ { "bls", "jls", },
+ { "bcc", "jcc", },
+ { "bcs", "jcs", },
+ { "bne", "jne", },
+ { "beq", "jeq", },
+ { "bvc", "jvc", },
+ { "bvs", "jvs", },
+ { "bpl", "jpl", },
+ { "bmi", "jmi", },
+ { "bge", "jge", },
+ { "blt", "jlt", },
+ { "bgt", "jgt", },
+ { "ble", "jle", },
+ { "bra", "jra", },
+ { "bsr", "jbsr", },
+ };
+
+ for (i = 0; i < sizeof mri_aliases / sizeof mri_aliases[0]; i++)
+ {
+ const char *name = mri_aliases[i].primary;
+ const char *alias = mri_aliases[i].alias;
+ PTR val = hash_find (op_hash, name);
+ if (!val)
+ as_fatal (_("Internal Error: Can't find %s in hash table"), name);
+ retval = hash_jam (op_hash, alias, val);
+ if (retval)
+ as_fatal (_("Internal Error: Can't hash %s: %s"), alias, retval);
+ }
+ }
+
+ for (i = 0; i < sizeof (mklower_table); i++)
+ mklower_table[i] = (isupper (c = (char) i)) ? tolower (c) : c;
+
+ for (i = 0; i < sizeof (notend_table); i++)
+ {
+ notend_table[i] = 0;
+ alt_notend_table[i] = 0;
+ }
+ notend_table[','] = 1;
+ notend_table['{'] = 1;
+ notend_table['}'] = 1;
+ alt_notend_table['a'] = 1;
+ alt_notend_table['A'] = 1;
+ alt_notend_table['d'] = 1;
+ alt_notend_table['D'] = 1;
+ alt_notend_table['#'] = 1;
+ alt_notend_table['&'] = 1;
+ alt_notend_table['f'] = 1;
+ alt_notend_table['F'] = 1;
+#ifdef REGISTER_PREFIX
+ alt_notend_table[REGISTER_PREFIX] = 1;
+#endif
+
+ /* We need to put '(' in alt_notend_table to handle
+ cas2 %d0:%d2,%d3:%d4,(%a0):(%a1)
+ */
+ alt_notend_table['('] = 1;
+
+ /* We need to put '@' in alt_notend_table to handle
+ cas2 %d0:%d2,%d3:%d4,@(%d0):@(%d1)
+ */
+ alt_notend_table['@'] = 1;
+
+ /* We need to put digits in alt_notend_table to handle
+ bfextu %d0{24:1},%d0
+ */
+ alt_notend_table['0'] = 1;
+ alt_notend_table['1'] = 1;
+ alt_notend_table['2'] = 1;
+ alt_notend_table['3'] = 1;
+ alt_notend_table['4'] = 1;
+ alt_notend_table['5'] = 1;
+ alt_notend_table['6'] = 1;
+ alt_notend_table['7'] = 1;
+ alt_notend_table['8'] = 1;
+ alt_notend_table['9'] = 1;
+
+#ifndef MIT_SYNTAX_ONLY
+ /* Insert pseudo ops, these have to go into the opcode table since
+ gas expects pseudo ops to start with a dot */
+ {
+ int n = 0;
+ while (mote_pseudo_table[n].poc_name)
+ {
+ hack = (struct m68k_incant *)
+ obstack_alloc (&robyn, sizeof (struct m68k_incant));
+ hash_insert (op_hash,
+ mote_pseudo_table[n].poc_name, (char *) hack);
+ hack->m_operands = 0;
+ hack->m_opnum = n;
+ n++;
+ }
+ }
+#endif
+
+ init_regtable ();
+
+#ifdef OBJ_ELF
+ record_alignment (text_section, 2);
+ record_alignment (data_section, 2);
+ record_alignment (bss_section, 2);
+#endif
+}
+
+static void
+select_control_regs ()
+{
+ /* Note which set of "movec" control registers is available. */
+ switch (cpu_of_arch (current_architecture))
+ {
+ case m68000:
+ control_regs = m68000_control_regs;
+ break;
+ case m68010:
+ control_regs = m68010_control_regs;
+ break;
+ case m68020:
+ case m68030:
+ control_regs = m68020_control_regs;
+ break;
+ case m68040:
+ control_regs = m68040_control_regs;
+ break;
+ case m68060:
+ control_regs = m68060_control_regs;
+ break;
+ case cpu32:
+ control_regs = cpu32_control_regs;
+ break;
+ case mcf5200:
+ control_regs = mcf5200_control_regs;
+ break;
+ default:
+ abort ();
+ }
+}
+
+void
+m68k_init_after_args ()
+{
+ if (cpu_of_arch (current_architecture) == 0)
+ {
+ int i;
+ const char *default_cpu = TARGET_CPU;
+
+ if (*default_cpu == 'm')
+ default_cpu++;
+ for (i = 0; i < n_archs; i++)
+ if (strcasecmp (default_cpu, archs[i].name) == 0)
+ break;
+ if (i == n_archs)
+ {
+ as_bad (_("unrecognized default cpu `%s' ???"), TARGET_CPU);
+ current_architecture |= m68020;
+ }
+ else
+ current_architecture |= archs[i].arch;
+ }
+ /* Permit m68881 specification with all cpus; those that can't work
+ with a coprocessor could be doing emulation. */
+ if (current_architecture & m68851)
+ {
+ if (current_architecture & m68040)
+ {
+ as_warn (_("68040 and 68851 specified; mmu instructions may assemble incorrectly"));
+ }
+ }
+ /* What other incompatibilities could we check for? */
+
+ /* Toss in some default assumptions about coprocessors. */
+ if (!no_68881
+ && (cpu_of_arch (current_architecture)
+ /* Can CPU32 have a 68881 coprocessor?? */
+ & (m68020 | m68030 | cpu32)))
+ {
+ current_architecture |= m68881;
+ }
+ if (!no_68851
+ && (cpu_of_arch (current_architecture) & m68020up) != 0
+ && (cpu_of_arch (current_architecture) & m68040up) == 0)
+ {
+ current_architecture |= m68851;
+ }
+ if (no_68881 && (current_architecture & m68881))
+ as_bad (_("options for 68881 and no-68881 both given"));
+ if (no_68851 && (current_architecture & m68851))
+ as_bad (_("options for 68851 and no-68851 both given"));
+
+#ifdef OBJ_AOUT
+ /* Work out the magic number. This isn't very general. */
+ if (current_architecture & m68000)
+ m68k_aout_machtype = 0;
+ else if (current_architecture & m68010)
+ m68k_aout_machtype = 1;
+ else if (current_architecture & m68020)
+ m68k_aout_machtype = 2;
+ else
+ m68k_aout_machtype = 2;
+#endif
+
+ /* Note which set of "movec" control registers is available. */
+ select_control_regs ();
+
+ if (cpu_of_arch (current_architecture) < m68020
+ || cpu_of_arch (current_architecture) == mcf5200)
+ md_relax_table[TAB (PCINDEX, BYTE)].rlx_more = 0;
+}
+
+/* This is called when a label is defined. */
+
+void
+m68k_frob_label (sym)
+ symbolS *sym;
+{
+ struct label_line *n;
+
+ n = (struct label_line *) xmalloc (sizeof *n);
+ n->next = labels;
+ n->label = sym;
+ as_where (&n->file, &n->line);
+ n->text = 0;
+ labels = n;
+ current_label = n;
+}
+
+/* This is called when a value that is not an instruction is emitted. */
+
+void
+m68k_flush_pending_output ()
+{
+ current_label = NULL;
+}
+
+/* This is called at the end of the assembly, when the final value of
+ the label is known. We warn if this is a text symbol aligned at an
+ odd location. */
+
+void
+m68k_frob_symbol (sym)
+ symbolS *sym;
+{
+ if (S_GET_SEGMENT (sym) == reg_section
+ && (int) S_GET_VALUE (sym) < 0)
+ {
+ S_SET_SEGMENT (sym, absolute_section);
+ S_SET_VALUE (sym, ~(int)S_GET_VALUE (sym));
+ }
+ else if ((S_GET_VALUE (sym) & 1) != 0)
+ {
+ struct label_line *l;
+
+ for (l = labels; l != NULL; l = l->next)
+ {
+ if (l->label == sym)
+ {
+ if (l->text)
+ as_warn_where (l->file, l->line,
+ _("text label `%s' aligned to odd boundary"),
+ S_GET_NAME (sym));
+ break;
+ }
+ }
+ }
+}
+
+/* This is called if we go in or out of MRI mode because of the .mri
+ pseudo-op. */
+
+void
+m68k_mri_mode_change (on)
+ int on;
+{
+ if (on)
+ {
+ if (! flag_reg_prefix_optional)
+ {
+ flag_reg_prefix_optional = 1;
+#ifdef REGISTER_PREFIX
+ init_regtable ();
+#endif
+ }
+ m68k_abspcadd = 1;
+ if (! m68k_rel32_from_cmdline)
+ m68k_rel32 = 0;
+ }
+ else
+ {
+ if (! reg_prefix_optional_seen)
+ {
+#ifdef REGISTER_PREFIX_OPTIONAL
+ flag_reg_prefix_optional = REGISTER_PREFIX_OPTIONAL;
+#else
+ flag_reg_prefix_optional = 0;
+#endif
+#ifdef REGISTER_PREFIX
+ init_regtable ();
+#endif
+ }
+ m68k_abspcadd = 0;
+ if (! m68k_rel32_from_cmdline)
+ m68k_rel32 = 1;
+ }
+}
+
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+/* Turn a string in input_line_pointer into a floating point constant
+ of type type, and store the appropriate bytes in *litP. The number
+ of LITTLENUMS emitted is stored in *sizeP . An error message is
+ returned, or NULL on OK. */
+
+char *
+md_atof (type, litP, sizeP)
+ char type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("Bad call to MD_ATOF()");
+ }
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+
+ *sizeP = prec * sizeof (LITTLENUM_TYPE);
+ for (wordP = words; prec--;)
+ {
+ md_number_to_chars (litP, (long) (*wordP++), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ return 0;
+}
+
+void
+md_number_to_chars (buf, val, n)
+ char *buf;
+ valueT val;
+ int n;
+{
+ number_to_chars_bigendian (buf, val, n);
+}
+
+static void
+md_apply_fix_2 (fixP, val)
+ fixS *fixP;
+ offsetT val;
+{
+ addressT upper_limit;
+ offsetT lower_limit;
+
+ /* This is unnecessary but it convinces the native rs6000 compiler
+ to generate the code we want. */
+ char *buf = fixP->fx_frag->fr_literal;
+ buf += fixP->fx_where;
+ /* end ibm compiler workaround */
+
+ if (val & 0x80000000)
+ val |= ~(addressT)0x7fffffff;
+ else
+ val &= 0x7fffffff;
+
+#ifdef OBJ_ELF
+ if (fixP->fx_addsy)
+ {
+ memset (buf, 0, fixP->fx_size);
+ fixP->fx_addnumber = val; /* Remember value for emit_reloc */
+
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ && !S_IS_DEFINED (fixP->fx_addsy)
+ && !S_IS_WEAK (fixP->fx_addsy))
+ S_SET_WEAK (fixP->fx_addsy);
+ return;
+ }
+#endif
+
+#ifdef BFD_ASSEMBLER
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return;
+#endif
+
+ switch (fixP->fx_size)
+ {
+ /* The cast to offsetT below are necessary to make code correct for
+ machines where ints are smaller than offsetT */
+ case 1:
+ *buf++ = val;
+ upper_limit = 0x7f;
+ lower_limit = - (offsetT) 0x80;
+ break;
+ case 2:
+ *buf++ = (val >> 8);
+ *buf++ = val;
+ upper_limit = 0x7fff;
+ lower_limit = - (offsetT) 0x8000;
+ break;
+ case 4:
+ *buf++ = (val >> 24);
+ *buf++ = (val >> 16);
+ *buf++ = (val >> 8);
+ *buf++ = val;
+ upper_limit = 0x7fffffff;
+ lower_limit = - (offsetT) 0x7fffffff - 1; /* avoid constant overflow */
+ break;
+ default:
+ BAD_CASE (fixP->fx_size);
+ }
+
+ /* Fix up a negative reloc. */
+ if (fixP->fx_addsy == NULL && fixP->fx_subsy != NULL)
+ {
+ fixP->fx_addsy = fixP->fx_subsy;
+ fixP->fx_subsy = NULL;
+ fixP->fx_tcbit = 1;
+ }
+
+ /* For non-pc-relative values, it's conceivable we might get something
+ like "0xff" for a byte field. So extend the upper part of the range
+ to accept such numbers. We arbitrarily disallow "-0xff" or "0xff+0xff",
+ so that we can do any range checking at all. */
+ if (! fixP->fx_pcrel && ! fixP->fx_signed)
+ upper_limit = upper_limit * 2 + 1;
+
+ if ((addressT) val > upper_limit
+ && (val > 0 || val < lower_limit))
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("value out of range"));
+
+ /* A one byte PC-relative reloc means a short branch. We can't use
+ a short branch with a value of 0 or -1, because those indicate
+ different opcodes (branches with longer offsets). fixup_segment
+ in write.c may have clobbered fx_pcrel, so we need to examine the
+ reloc type. */
+ if ((fixP->fx_pcrel
+#ifdef BFD_ASSEMBLER
+ || fixP->fx_r_type == BFD_RELOC_8_PCREL
+#endif
+ )
+ && fixP->fx_size == 1
+ && (fixP->fx_addsy == NULL
+ || S_IS_DEFINED (fixP->fx_addsy))
+ && (val == 0 || val == -1))
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("invalid byte branch offset"));
+}
+
+#ifdef BFD_ASSEMBLER
+int
+md_apply_fix (fixP, valp)
+ fixS *fixP;
+ valueT *valp;
+{
+ md_apply_fix_2 (fixP, (addressT) *valp);
+ return 1;
+}
+#else
+void md_apply_fix (fixP, val)
+ fixS *fixP;
+ long val;
+{
+ md_apply_fix_2 (fixP, (addressT) val);
+}
+#endif
+
+/* *fragP has been relaxed to its final size, and now needs to have
+ the bytes inside it modified to conform to the new size There is UGLY
+ MAGIC here. ..
+ */
+static void
+md_convert_frag_1 (fragP)
+ register fragS *fragP;
+{
+ long disp;
+ long ext = 0;
+ fixS *fixP;
+
+ /* Address in object code of the displacement. */
+ register int object_address = fragP->fr_fix + fragP->fr_address;
+
+ /* Address in gas core of the place to store the displacement. */
+ /* This convinces the native rs6000 compiler to generate the code we
+ want. */
+ register char *buffer_address = fragP->fr_literal;
+ buffer_address += fragP->fr_fix;
+ /* end ibm compiler workaround */
+
+ /* The displacement of the address, from current location. */
+ disp = fragP->fr_symbol ? S_GET_VALUE (fragP->fr_symbol) : 0;
+ disp = (disp + fragP->fr_offset) - object_address;
+
+#ifdef BFD_ASSEMBLER
+ disp += fragP->fr_symbol->sy_frag->fr_address;
+#endif
+
+ switch (fragP->fr_subtype)
+ {
+ case TAB (BCC68000, BYTE):
+ case TAB (ABRANCH, BYTE):
+ know (issbyte (disp));
+ if (disp == 0)
+ as_bad (_("short branch with zero offset: use :w"));
+ fragP->fr_opcode[1] = disp;
+ ext = 0;
+ break;
+ case TAB (DBCC, SHORT):
+ know (issword (disp));
+ ext = 2;
+ break;
+ case TAB (BCC68000, SHORT):
+ case TAB (ABRANCH, SHORT):
+ know (issword (disp));
+ fragP->fr_opcode[1] = 0x00;
+ ext = 2;
+ break;
+ case TAB (ABRANCH, LONG):
+ if (!HAVE_LONG_BRANCH(current_architecture))
+ {
+ if (fragP->fr_opcode[0] == 0x61)
+ /* BSR */
+ {
+ fragP->fr_opcode[0] = 0x4E;
+ fragP->fr_opcode[1] = (char) 0xB9; /* JBSR with ABSL LONG offset */
+
+ fix_new (fragP,
+ fragP->fr_fix,
+ 4,
+ fragP->fr_symbol,
+ fragP->fr_offset,
+ 0,
+ NO_RELOC);
+
+ fragP->fr_fix += 4;
+ ext = 0;
+ }
+ /* BRA */
+ else if (fragP->fr_opcode[0] == 0x60)
+ {
+ fragP->fr_opcode[0] = 0x4E;
+ fragP->fr_opcode[1] = (char) 0xF9; /* JMP with ABSL LONG offset */
+ fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol,
+ fragP->fr_offset, 0, NO_RELOC);
+ fragP->fr_fix += 4;
+ ext = 0;
+ }
+ else
+ {
+ as_bad (_("Long branch offset not supported."));
+ }
+ }
+ else
+ {
+ fragP->fr_opcode[1] = (char) 0xff;
+ ext = 4;
+ }
+ break;
+ case TAB (BCC68000, LONG):
+ /* only Bcc 68000 instructions can come here */
+ /* change bcc into b!cc/jmp absl long */
+ fragP->fr_opcode[0] ^= 0x01; /* invert bcc */
+ fragP->fr_opcode[1] = 0x6;/* branch offset = 6 */
+
+ /* JF: these used to be fr_opcode[2,3], but they may be in a
+ different frag, in which case refering to them is a no-no.
+ Only fr_opcode[0,1] are guaranteed to work. */
+ *buffer_address++ = 0x4e; /* put in jmp long (0x4ef9) */
+ *buffer_address++ = (char) 0xf9;
+ fragP->fr_fix += 2; /* account for jmp instruction */
+ fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol,
+ fragP->fr_offset, 0, NO_RELOC);
+ fragP->fr_fix += 4;
+ ext = 0;
+ break;
+ case TAB (DBCC, LONG):
+ /* only DBcc 68000 instructions can come here */
+ /* change dbcc into dbcc/jmp absl long */
+ /* JF: these used to be fr_opcode[2-7], but that's wrong */
+ *buffer_address++ = 0x00; /* branch offset = 4 */
+ *buffer_address++ = 0x04;
+ *buffer_address++ = 0x60; /* put in bra pc+6 */
+ *buffer_address++ = 0x06;
+ *buffer_address++ = 0x4e; /* put in jmp long (0x4ef9) */
+ *buffer_address++ = (char) 0xf9;
+
+ fragP->fr_fix += 6; /* account for bra/jmp instructions */
+ fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol,
+ fragP->fr_offset, 0, NO_RELOC);
+ fragP->fr_fix += 4;
+ ext = 0;
+ break;
+ case TAB (FBRANCH, SHORT):
+ know ((fragP->fr_opcode[1] & 0x40) == 0);
+ ext = 2;
+ break;
+ case TAB (FBRANCH, LONG):
+ fragP->fr_opcode[1] |= 0x40; /* Turn on LONG bit */
+ ext = 4;
+ break;
+ case TAB (PCREL, SHORT):
+ ext = 2;
+ break;
+ case TAB (PCREL, LONG):
+ /* The thing to do here is force it to ABSOLUTE LONG, since
+ PCREL is really trying to shorten an ABSOLUTE address anyway */
+ /* JF FOO This code has not been tested */
+ fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol, fragP->fr_offset,
+ 0, NO_RELOC);
+ if ((fragP->fr_opcode[1] & 0x3F) != 0x3A)
+ as_bad (_("Internal error (long PC-relative operand) for insn 0x%04x at 0x%lx"),
+ (unsigned) fragP->fr_opcode[0],
+ (unsigned long) fragP->fr_address);
+ fragP->fr_opcode[1] &= ~0x3F;
+ fragP->fr_opcode[1] |= 0x39; /* Mode 7.1 */
+ fragP->fr_fix += 4;
+ ext = 0;
+ break;
+ case TAB (PCLEA, SHORT):
+ fix_new (fragP, (int) (fragP->fr_fix), 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ fragP->fr_opcode[1] &= ~0x3F;
+ fragP->fr_opcode[1] |= 0x3A; /* 072 - mode 7.2 */
+ ext = 2;
+ break;
+ case TAB (PCLEA, LONG):
+ fixP = fix_new (fragP, (int) (fragP->fr_fix) + 2, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ fixP->fx_pcrel_adjust = 2;
+ /* Already set to mode 7.3; this indicates: PC indirect with
+ suppressed index, 32-bit displacement. */
+ *buffer_address++ = 0x01;
+ *buffer_address++ = 0x70;
+ fragP->fr_fix += 2;
+ ext = 4;
+ break;
+
+ case TAB (PCINDEX, BYTE):
+ disp += 2;
+ if (!issbyte (disp))
+ {
+ as_bad (_("displacement doesn't fit in one byte"));
+ disp = 0;
+ }
+ assert (fragP->fr_fix >= 2);
+ buffer_address[-2] &= ~1;
+ buffer_address[-1] = disp;
+ ext = 0;
+ break;
+ case TAB (PCINDEX, SHORT):
+ disp += 2;
+ assert (issword (disp));
+ assert (fragP->fr_fix >= 2);
+ buffer_address[-2] |= 0x1;
+ buffer_address[-1] = 0x20;
+ fixP = fix_new (fragP, (int) (fragP->fr_fix), 2, fragP->fr_symbol,
+ fragP->fr_offset, (fragP->fr_opcode[1] & 077) == 073,
+ NO_RELOC);
+ fixP->fx_pcrel_adjust = 2;
+ ext = 2;
+ break;
+ case TAB (PCINDEX, LONG):
+ disp += 2;
+ fixP = fix_new (fragP, (int) (fragP->fr_fix), 4, fragP->fr_symbol,
+ fragP->fr_offset, (fragP->fr_opcode[1] & 077) == 073,
+ NO_RELOC);
+ fixP->fx_pcrel_adjust = 2;
+ assert (fragP->fr_fix >= 2);
+ buffer_address[-2] |= 0x1;
+ buffer_address[-1] = 0x30;
+ ext = 4;
+ break;
+ }
+
+ if (ext)
+ {
+ md_number_to_chars (buffer_address, (long) disp, (int) ext);
+ fragP->fr_fix += ext;
+ }
+}
+
+#ifndef BFD_ASSEMBLER
+
+void
+md_convert_frag (headers, sec, fragP)
+ object_headers *headers;
+ segT sec;
+ fragS *fragP;
+{
+ md_convert_frag_1 (fragP);
+}
+
+#else
+
+void
+md_convert_frag (abfd, sec, fragP)
+ bfd *abfd;
+ segT sec;
+ fragS *fragP;
+{
+ md_convert_frag_1 (fragP);
+}
+#endif
+
+/* Force truly undefined symbols to their maximum size, and generally set up
+ the frag list to be relaxed
+ */
+int
+md_estimate_size_before_relax (fragP, segment)
+ register fragS *fragP;
+ segT segment;
+{
+ int old_fix;
+ register char *buffer_address = fragP->fr_fix + fragP->fr_literal;
+
+ old_fix = fragP->fr_fix;
+
+ /* handle SZ_UNDEF first, it can be changed to BYTE or SHORT */
+ switch (fragP->fr_subtype)
+ {
+
+ case TAB (ABRANCH, SZ_UNDEF):
+ {
+ if ((fragP->fr_symbol != NULL) /* Not absolute */
+ && S_GET_SEGMENT (fragP->fr_symbol) == segment
+ && relaxable_symbol (fragP->fr_symbol))
+ {
+ fragP->fr_subtype = TAB (TABTYPE (fragP->fr_subtype), BYTE);
+ break;
+ }
+ else if ((fragP->fr_symbol == 0) || !HAVE_LONG_BRANCH(current_architecture))
+ {
+ /* On 68000, or for absolute value, switch to abs long */
+ /* FIXME, we should check abs val, pick short or long */
+ if (fragP->fr_opcode[0] == 0x61)
+ {
+ fragP->fr_opcode[0] = 0x4E;
+ fragP->fr_opcode[1] = (char) 0xB9; /* JBSR with ABSL LONG offset */
+ fix_new (fragP, fragP->fr_fix, 4,
+ fragP->fr_symbol, fragP->fr_offset, 0, NO_RELOC);
+ fragP->fr_fix += 4;
+ frag_wane (fragP);
+ }
+ else if (fragP->fr_opcode[0] == 0x60)
+ {
+ fragP->fr_opcode[0] = 0x4E;
+ fragP->fr_opcode[1] = (char) 0xF9; /* JMP with ABSL LONG offset */
+ fix_new (fragP, fragP->fr_fix, 4,
+ fragP->fr_symbol, fragP->fr_offset, 0, NO_RELOC);
+ fragP->fr_fix += 4;
+ frag_wane (fragP);
+ }
+ else
+ {
+ as_warn (_("Long branch offset to extern symbol not supported."));
+ }
+ }
+ else
+ { /* Symbol is still undefined. Make it simple */
+ fix_new (fragP, (int) (fragP->fr_fix), 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ fragP->fr_fix += 4;
+ fragP->fr_opcode[1] = (char) 0xff;
+ frag_wane (fragP);
+ break;
+ }
+
+ break;
+ } /* case TAB(ABRANCH,SZ_UNDEF) */
+
+ case TAB (FBRANCH, SZ_UNDEF):
+ {
+ if ((S_GET_SEGMENT (fragP->fr_symbol) == segment
+ && relaxable_symbol (fragP->fr_symbol))
+ || flag_short_refs)
+ {
+ fragP->fr_subtype = TAB (FBRANCH, SHORT);
+ fragP->fr_var += 2;
+ }
+ else
+ {
+ fix_new (fragP, (int) fragP->fr_fix, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ fragP->fr_fix += 4;
+ fragP->fr_opcode[1] |= 0x40; /* Turn on LONG bit */
+ frag_wane (fragP);
+ }
+ break;
+ } /* TAB(FBRANCH,SZ_UNDEF) */
+
+ case TAB (PCREL, SZ_UNDEF):
+ {
+ if ((S_GET_SEGMENT (fragP->fr_symbol) == segment
+ && relaxable_symbol (fragP->fr_symbol))
+ || flag_short_refs
+ || cpu_of_arch (current_architecture) < m68020
+ || cpu_of_arch (current_architecture) == mcf5200)
+ {
+ fragP->fr_subtype = TAB (PCREL, SHORT);
+ fragP->fr_var += 2;
+ }
+ else
+ {
+ fragP->fr_subtype = TAB (PCREL, LONG);
+ fragP->fr_var += 4;
+ }
+ break;
+ } /* TAB(PCREL,SZ_UNDEF) */
+
+ case TAB (BCC68000, SZ_UNDEF):
+ {
+ if ((fragP->fr_symbol != NULL)
+ && S_GET_SEGMENT (fragP->fr_symbol) == segment
+ && relaxable_symbol (fragP->fr_symbol))
+ {
+ fragP->fr_subtype = TAB (BCC68000, BYTE);
+ break;
+ }
+ /* only Bcc 68000 instructions can come here */
+ /* change bcc into b!cc/jmp absl long */
+ fragP->fr_opcode[0] ^= 0x01; /* invert bcc */
+ if (flag_short_refs)
+ {
+ fragP->fr_opcode[1] = 0x04; /* branch offset = 6 */
+ /* JF: these were fr_opcode[2,3] */
+ buffer_address[0] = 0x4e; /* put in jmp long (0x4ef9) */
+ buffer_address[1] = (char) 0xf8;
+ fragP->fr_fix += 2; /* account for jmp instruction */
+ fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+ fragP->fr_offset, 0, NO_RELOC);
+ fragP->fr_fix += 2;
+ }
+ else
+ {
+ fragP->fr_opcode[1] = 0x06; /* branch offset = 6 */
+ /* JF: these were fr_opcode[2,3] */
+ buffer_address[0] = 0x4e; /* put in jmp long (0x4ef9) */
+ buffer_address[1] = (char) 0xf9;
+ fragP->fr_fix += 2; /* account for jmp instruction */
+ fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol,
+ fragP->fr_offset, 0, NO_RELOC);
+ fragP->fr_fix += 4;
+ }
+ frag_wane (fragP);
+ break;
+ } /* case TAB(BCC68000,SZ_UNDEF) */
+
+ case TAB (DBCC, SZ_UNDEF):
+ {
+ if (fragP->fr_symbol != NULL
+ && S_GET_SEGMENT (fragP->fr_symbol) == segment
+ && relaxable_symbol (fragP->fr_symbol))
+ {
+ fragP->fr_subtype = TAB (DBCC, SHORT);
+ fragP->fr_var += 2;
+ break;
+ }
+ /* only DBcc 68000 instructions can come here */
+ /* change dbcc into dbcc/jmp absl long */
+ /* JF: these used to be fr_opcode[2-4], which is wrong. */
+ buffer_address[0] = 0x00; /* branch offset = 4 */
+ buffer_address[1] = 0x04;
+ buffer_address[2] = 0x60; /* put in bra pc + ... */
+
+ if (flag_short_refs)
+ {
+ /* JF: these were fr_opcode[5-7] */
+ buffer_address[3] = 0x04; /* plus 4 */
+ buffer_address[4] = 0x4e; /* Put in Jump Word */
+ buffer_address[5] = (char) 0xf8;
+ fragP->fr_fix += 6; /* account for bra/jmp instruction */
+ fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+ fragP->fr_offset, 0, NO_RELOC);
+ fragP->fr_fix += 2;
+ }
+ else
+ {
+ /* JF: these were fr_opcode[5-7] */
+ buffer_address[3] = 0x06; /* Plus 6 */
+ buffer_address[4] = 0x4e; /* put in jmp long (0x4ef9) */
+ buffer_address[5] = (char) 0xf9;
+ fragP->fr_fix += 6; /* account for bra/jmp instruction */
+ fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol,
+ fragP->fr_offset, 0, NO_RELOC);
+ fragP->fr_fix += 4;
+ }
+
+ frag_wane (fragP);
+ break;
+ } /* case TAB(DBCC,SZ_UNDEF) */
+
+ case TAB (PCLEA, SZ_UNDEF):
+ {
+ if (((S_GET_SEGMENT (fragP->fr_symbol)) == segment
+ && relaxable_symbol (fragP->fr_symbol))
+ || flag_short_refs
+ || cpu_of_arch (current_architecture) < m68020
+ || cpu_of_arch (current_architecture) == mcf5200)
+ {
+ fragP->fr_subtype = TAB (PCLEA, SHORT);
+ fragP->fr_var += 2;
+ }
+ else
+ {
+ fragP->fr_subtype = TAB (PCLEA, LONG);
+ fragP->fr_var += 6;
+ }
+ break;
+ } /* TAB(PCLEA,SZ_UNDEF) */
+
+ case TAB (PCINDEX, SZ_UNDEF):
+ if ((S_GET_SEGMENT (fragP->fr_symbol) == segment
+ && relaxable_symbol (fragP->fr_symbol))
+ || cpu_of_arch (current_architecture) < m68020
+ || cpu_of_arch (current_architecture) == mcf5200)
+ {
+ fragP->fr_subtype = TAB (PCINDEX, BYTE);
+ }
+ else
+ {
+ fragP->fr_subtype = TAB (PCINDEX, LONG);
+ fragP->fr_var += 4;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /* now that SZ_UNDEF are taken care of, check others */
+ switch (fragP->fr_subtype)
+ {
+ case TAB (BCC68000, BYTE):
+ case TAB (ABRANCH, BYTE):
+ /* We can't do a short jump to the next instruction, so in that
+ case we force word mode. At this point S_GET_VALUE should
+ return the offset of the symbol within its frag. If the
+ symbol is at the start of a frag, and it is the next frag
+ with any data in it (usually this is just the next frag, but
+ assembler listings may introduce empty frags), we must use
+ word mode. */
+ if (fragP->fr_symbol && S_GET_VALUE (fragP->fr_symbol) == 0)
+ {
+ fragS *l;
+
+ for (l = fragP->fr_next;
+ l != fragP->fr_symbol->sy_frag;
+ l = l->fr_next)
+ if (l->fr_fix + l->fr_var != 0)
+ break;
+ if (l == fragP->fr_symbol->sy_frag)
+ {
+ fragP->fr_subtype = TAB (TABTYPE (fragP->fr_subtype), SHORT);
+ fragP->fr_var += 2;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return fragP->fr_var + fragP->fr_fix - old_fix;
+}
+
+#if defined(OBJ_AOUT) | defined(OBJ_BOUT)
+/* the bit-field entries in the relocation_info struct plays hell
+ with the byte-order problems of cross-assembly. So as a hack,
+ I added this mach. dependent ri twiddler. Ugly, but it gets
+ you there. -KWK */
+/* on m68k: first 4 bytes are normal unsigned long, next three bytes
+ are symbolnum, most sig. byte first. Last byte is broken up with
+ bit 7 as pcrel, bits 6 & 5 as length, bit 4 as pcrel, and the lower
+ nibble as nuthin. (on Sun 3 at least) */
+/* Translate the internal relocation information into target-specific
+ format. */
+#ifdef comment
+void
+md_ri_to_chars (the_bytes, ri)
+ char *the_bytes;
+ struct reloc_info_generic *ri;
+{
+ /* this is easy */
+ md_number_to_chars (the_bytes, ri->r_address, 4);
+ /* now the fun stuff */
+ the_bytes[4] = (ri->r_symbolnum >> 16) & 0x0ff;
+ the_bytes[5] = (ri->r_symbolnum >> 8) & 0x0ff;
+ the_bytes[6] = ri->r_symbolnum & 0x0ff;
+ the_bytes[7] = (((ri->r_pcrel << 7) & 0x80) | ((ri->r_length << 5) & 0x60) |
+ ((ri->r_extern << 4) & 0x10));
+}
+
+#endif /* comment */
+
+#ifndef BFD_ASSEMBLER
+void
+tc_aout_fix_to_chars (where, fixP, segment_address_in_file)
+ char *where;
+ fixS *fixP;
+ relax_addressT segment_address_in_file;
+{
+ /*
+ * In: length of relocation (or of address) in chars: 1, 2 or 4.
+ * Out: GNU LD relocation length code: 0, 1, or 2.
+ */
+
+ static CONST unsigned char nbytes_r_length[] = {42, 0, 1, 42, 2};
+ long r_symbolnum;
+
+ know (fixP->fx_addsy != NULL);
+
+ md_number_to_chars (where,
+ fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file,
+ 4);
+
+ r_symbolnum = (S_IS_DEFINED (fixP->fx_addsy)
+ ? S_GET_TYPE (fixP->fx_addsy)
+ : fixP->fx_addsy->sy_number);
+
+ where[4] = (r_symbolnum >> 16) & 0x0ff;
+ where[5] = (r_symbolnum >> 8) & 0x0ff;
+ where[6] = r_symbolnum & 0x0ff;
+ where[7] = (((fixP->fx_pcrel << 7) & 0x80) | ((nbytes_r_length[fixP->fx_size] << 5) & 0x60) |
+ (((!S_IS_DEFINED (fixP->fx_addsy)) << 4) & 0x10));
+}
+#endif
+
+#endif /* OBJ_AOUT or OBJ_BOUT */
+
+#ifndef WORKING_DOT_WORD
+CONST int md_short_jump_size = 4;
+CONST int md_long_jump_size = 6;
+
+void
+md_create_short_jump (ptr, from_addr, to_addr, frag, to_symbol)
+ char *ptr;
+ addressT from_addr, to_addr;
+ fragS *frag;
+ symbolS *to_symbol;
+{
+ valueT offset;
+
+ offset = to_addr - (from_addr + 2);
+
+ md_number_to_chars (ptr, (valueT) 0x6000, 2);
+ md_number_to_chars (ptr + 2, (valueT) offset, 2);
+}
+
+void
+md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol)
+ char *ptr;
+ addressT from_addr, to_addr;
+ fragS *frag;
+ symbolS *to_symbol;
+{
+ valueT offset;
+
+ if (!HAVE_LONG_BRANCH(current_architecture))
+ {
+ offset = to_addr - S_GET_VALUE (to_symbol);
+ md_number_to_chars (ptr, (valueT) 0x4EF9, 2);
+ md_number_to_chars (ptr + 2, (valueT) offset, 4);
+ fix_new (frag, (ptr + 2) - frag->fr_literal, 4, to_symbol, (offsetT) 0,
+ 0, NO_RELOC);
+ }
+ else
+ {
+ offset = to_addr - (from_addr + 2);
+ md_number_to_chars (ptr, (valueT) 0x60ff, 2);
+ md_number_to_chars (ptr + 2, (valueT) offset, 4);
+ }
+}
+
+#endif
+
+/* Different values of OK tell what its OK to return. Things that
+ aren't OK are an error (what a shock, no?)
+
+ 0: Everything is OK
+ 10: Absolute 1:8 only
+ 20: Absolute 0:7 only
+ 30: absolute 0:15 only
+ 40: Absolute 0:31 only
+ 50: absolute 0:127 only
+ 55: absolute -64:63 only
+ 60: absolute -128:127 only
+ 70: absolute 0:4095 only
+ 80: No bignums
+
+ */
+
+static int
+get_num (exp, ok)
+ struct m68k_exp *exp;
+ int ok;
+{
+ if (exp->exp.X_op == O_absent)
+ {
+ /* Do the same thing the VAX asm does */
+ op (exp) = O_constant;
+ adds (exp) = 0;
+ subs (exp) = 0;
+ offs (exp) = 0;
+ if (ok == 10)
+ {
+ as_warn (_("expression out of range: defaulting to 1"));
+ offs (exp) = 1;
+ }
+ }
+ else if (exp->exp.X_op == O_constant)
+ {
+ switch (ok)
+ {
+ case 10:
+ if (offs (exp) < 1 || offs (exp) > 8)
+ {
+ as_warn (_("expression out of range: defaulting to 1"));
+ offs (exp) = 1;
+ }
+ break;
+ case 20:
+ if (offs (exp) < 0 || offs (exp) > 7)
+ goto outrange;
+ break;
+ case 30:
+ if (offs (exp) < 0 || offs (exp) > 15)
+ goto outrange;
+ break;
+ case 40:
+ if (offs (exp) < 0 || offs (exp) > 32)
+ goto outrange;
+ break;
+ case 50:
+ if (offs (exp) < 0 || offs (exp) > 127)
+ goto outrange;
+ break;
+ case 55:
+ if (offs (exp) < -64 || offs (exp) > 63)
+ goto outrange;
+ break;
+ case 60:
+ if (offs (exp) < -128 || offs (exp) > 127)
+ goto outrange;
+ break;
+ case 70:
+ if (offs (exp) < 0 || offs (exp) > 4095)
+ {
+ outrange:
+ as_warn (_("expression out of range: defaulting to 0"));
+ offs (exp) = 0;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ else if (exp->exp.X_op == O_big)
+ {
+ if (offs (exp) <= 0 /* flonum */
+ && (ok == 80 /* no bignums */
+ || (ok > 10 /* small-int ranges including 0 ok */
+ /* If we have a flonum zero, a zero integer should
+ do as well (e.g., in moveq). */
+ && generic_floating_point_number.exponent == 0
+ && generic_floating_point_number.low[0] == 0)))
+ {
+ /* HACK! Turn it into a long */
+ LITTLENUM_TYPE words[6];
+
+ gen_to_words (words, 2, 8L); /* These numbers are magic! */
+ op (exp) = O_constant;
+ adds (exp) = 0;
+ subs (exp) = 0;
+ offs (exp) = words[1] | (words[0] << 16);
+ }
+ else if (ok != 0)
+ {
+ op (exp) = O_constant;
+ adds (exp) = 0;
+ subs (exp) = 0;
+ offs (exp) = (ok == 10) ? 1 : 0;
+ as_warn (_("Can't deal with expression; defaulting to %ld"),
+ offs (exp));
+ }
+ }
+ else
+ {
+ if (ok >= 10 && ok <= 70)
+ {
+ op (exp) = O_constant;
+ adds (exp) = 0;
+ subs (exp) = 0;
+ offs (exp) = (ok == 10) ? 1 : 0;
+ as_warn (_("Can't deal with expression; defaulting to %ld"),
+ offs (exp));
+ }
+ }
+
+ if (exp->size != SIZE_UNSPEC)
+ {
+ switch (exp->size)
+ {
+ case SIZE_UNSPEC:
+ case SIZE_LONG:
+ break;
+ case SIZE_BYTE:
+ if (!isbyte (offs (exp)))
+ as_warn (_("expression doesn't fit in BYTE"));
+ break;
+ case SIZE_WORD:
+ if (!isword (offs (exp)))
+ as_warn (_("expression doesn't fit in WORD"));
+ break;
+ }
+ }
+
+ return offs (exp);
+}
+
+/* These are the back-ends for the various machine dependent pseudo-ops. */
+
+static void
+s_data1 (ignore)
+ int ignore;
+{
+ subseg_set (data_section, 1);
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_data2 (ignore)
+ int ignore;
+{
+ subseg_set (data_section, 2);
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_bss (ignore)
+ int ignore;
+{
+ /* We don't support putting frags in the BSS segment, we fake it
+ by marking in_bss, then looking at s_skip for clues. */
+
+ subseg_set (bss_section, 0);
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_even (ignore)
+ int ignore;
+{
+ register int temp;
+ register long temp_fill;
+
+ temp = 1; /* JF should be 2? */
+ temp_fill = get_absolute_expression ();
+ if (!need_pass_2) /* Never make frag if expect extra pass. */
+ frag_align (temp, (int) temp_fill, 0);
+ demand_empty_rest_of_line ();
+ record_alignment (now_seg, temp);
+}
+
+static void
+s_proc (ignore)
+ int ignore;
+{
+ demand_empty_rest_of_line ();
+}
+
+/* Pseudo-ops handled for MRI compatibility. */
+
+/* This function returns non-zero if the argument is a conditional
+ pseudo-op. This is called when checking whether a pending
+ alignment is needed. */
+
+int
+m68k_conditional_pseudoop (pop)
+ pseudo_typeS *pop;
+{
+ return (pop->poc_handler == s_mri_if
+ || pop->poc_handler == s_mri_else);
+}
+
+/* Handle an MRI style chip specification. */
+
+static void
+mri_chip ()
+{
+ char *s;
+ char c;
+ int i;
+
+ s = input_line_pointer;
+ /* We can't use get_symbol_end since the processor names are not proper
+ symbols. */
+ while (is_part_of_name (c = *input_line_pointer++))
+ ;
+ *--input_line_pointer = 0;
+ for (i = 0; i < n_archs; i++)
+ if (strcasecmp (s, archs[i].name) == 0)
+ break;
+ if (i >= n_archs)
+ {
+ as_bad (_("%s: unrecognized processor name"), s);
+ *input_line_pointer = c;
+ ignore_rest_of_line ();
+ return;
+ }
+ *input_line_pointer = c;
+
+ if (*input_line_pointer == '/')
+ current_architecture = 0;
+ else
+ current_architecture &= m68881 | m68851;
+ current_architecture |= archs[i].arch;
+
+ while (*input_line_pointer == '/')
+ {
+ ++input_line_pointer;
+ s = input_line_pointer;
+ /* We can't use get_symbol_end since the processor names are not
+ proper symbols. */
+ while (is_part_of_name (c = *input_line_pointer++))
+ ;
+ *--input_line_pointer = 0;
+ if (strcmp (s, "68881") == 0)
+ current_architecture |= m68881;
+ else if (strcmp (s, "68851") == 0)
+ current_architecture |= m68851;
+ *input_line_pointer = c;
+ }
+
+ /* Update info about available control registers. */
+ select_control_regs ();
+}
+
+/* The MRI CHIP pseudo-op. */
+
+static void
+s_chip (ignore)
+ int ignore;
+{
+ char *stop = NULL;
+ char stopc;
+
+ if (flag_mri)
+ stop = mri_comment_field (&stopc);
+ mri_chip ();
+ if (flag_mri)
+ mri_comment_end (stop, stopc);
+ demand_empty_rest_of_line ();
+}
+
+/* The MRI FOPT pseudo-op. */
+
+static void
+s_fopt (ignore)
+ int ignore;
+{
+ SKIP_WHITESPACE ();
+
+ if (strncasecmp (input_line_pointer, "ID=", 3) == 0)
+ {
+ int temp;
+
+ input_line_pointer += 3;
+ temp = get_absolute_expression ();
+ if (temp < 0 || temp > 7)
+ as_bad (_("bad coprocessor id"));
+ else
+ m68k_float_copnum = COP0 + temp;
+ }
+ else
+ {
+ as_bad (_("unrecognized fopt option"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* The structure used to handle the MRI OPT pseudo-op. */
+
+struct opt_action
+{
+ /* The name of the option. */
+ const char *name;
+
+ /* If this is not NULL, just call this function. The first argument
+ is the ARG field of this structure, the second argument is
+ whether the option was negated. */
+ void (*pfn) PARAMS ((int arg, int on));
+
+ /* If this is not NULL, and the PFN field is NULL, set the variable
+ this points to. Set it to the ARG field if the option was not
+ negated, and the NOTARG field otherwise. */
+ int *pvar;
+
+ /* The value to pass to PFN or to assign to *PVAR. */
+ int arg;
+
+ /* The value to assign to *PVAR if the option is negated. If PFN is
+ NULL, and PVAR is not NULL, and ARG and NOTARG are the same, then
+ the option may not be negated. */
+ int notarg;
+};
+
+/* The table used to handle the MRI OPT pseudo-op. */
+
+static void skip_to_comma PARAMS ((int, int));
+static void opt_nest PARAMS ((int, int));
+static void opt_chip PARAMS ((int, int));
+static void opt_list PARAMS ((int, int));
+static void opt_list_symbols PARAMS ((int, int));
+
+static const struct opt_action opt_table[] =
+{
+ { "abspcadd", 0, &m68k_abspcadd, 1, 0 },
+
+ /* We do relaxing, so there is little use for these options. */
+ { "b", 0, 0, 0, 0 },
+ { "brs", 0, 0, 0, 0 },
+ { "brb", 0, 0, 0, 0 },
+ { "brl", 0, 0, 0, 0 },
+ { "brw", 0, 0, 0, 0 },
+
+ { "c", 0, 0, 0, 0 },
+ { "cex", 0, 0, 0, 0 },
+ { "case", 0, &symbols_case_sensitive, 1, 0 },
+ { "cl", 0, 0, 0, 0 },
+ { "cre", 0, 0, 0, 0 },
+ { "d", 0, &flag_keep_locals, 1, 0 },
+ { "e", 0, 0, 0, 0 },
+ { "f", 0, &flag_short_refs, 1, 0 },
+ { "frs", 0, &flag_short_refs, 1, 0 },
+ { "frl", 0, &flag_short_refs, 0, 1 },
+ { "g", 0, 0, 0, 0 },
+ { "i", 0, 0, 0, 0 },
+ { "m", 0, 0, 0, 0 },
+ { "mex", 0, 0, 0, 0 },
+ { "mc", 0, 0, 0, 0 },
+ { "md", 0, 0, 0, 0 },
+ { "nest", opt_nest, 0, 0, 0 },
+ { "next", skip_to_comma, 0, 0, 0 },
+ { "o", 0, 0, 0, 0 },
+ { "old", 0, 0, 0, 0 },
+ { "op", skip_to_comma, 0, 0, 0 },
+ { "pco", 0, 0, 0, 0 },
+ { "p", opt_chip, 0, 0, 0 },
+ { "pcr", 0, 0, 0, 0 },
+ { "pcs", 0, 0, 0, 0 },
+ { "r", 0, 0, 0, 0 },
+ { "quick", 0, &m68k_quick, 1, 0 },
+ { "rel32", 0, &m68k_rel32, 1, 0 },
+ { "s", opt_list, 0, 0, 0 },
+ { "t", opt_list_symbols, 0, 0, 0 },
+ { "w", 0, &flag_no_warnings, 0, 1 },
+ { "x", 0, 0, 0, 0 }
+};
+
+#define OPTCOUNT (sizeof opt_table / sizeof opt_table[0])
+
+/* The MRI OPT pseudo-op. */
+
+static void
+s_opt (ignore)
+ int ignore;
+{
+ do
+ {
+ int t;
+ char *s;
+ char c;
+ int i;
+ const struct opt_action *o;
+
+ SKIP_WHITESPACE ();
+
+ t = 1;
+ if (*input_line_pointer == '-')
+ {
+ ++input_line_pointer;
+ t = 0;
+ }
+ else if (strncasecmp (input_line_pointer, "NO", 2) == 0)
+ {
+ input_line_pointer += 2;
+ t = 0;
+ }
+
+ s = input_line_pointer;
+ c = get_symbol_end ();
+
+ for (i = 0, o = opt_table; i < OPTCOUNT; i++, o++)
+ {
+ if (strcasecmp (s, o->name) == 0)
+ {
+ if (o->pfn)
+ {
+ /* Restore input_line_pointer now in case the option
+ takes arguments. */
+ *input_line_pointer = c;
+ (*o->pfn) (o->arg, t);
+ }
+ else if (o->pvar != NULL)
+ {
+ if (! t && o->arg == o->notarg)
+ as_bad (_("option `%s' may not be negated"), s);
+ *input_line_pointer = c;
+ *o->pvar = t ? o->arg : o->notarg;
+ }
+ else
+ *input_line_pointer = c;
+ break;
+ }
+ }
+ if (i >= OPTCOUNT)
+ {
+ as_bad (_("option `%s' not recognized"), s);
+ *input_line_pointer = c;
+ }
+ }
+ while (*input_line_pointer++ == ',');
+
+ /* Move back to terminating character. */
+ --input_line_pointer;
+ demand_empty_rest_of_line ();
+}
+
+/* Skip ahead to a comma. This is used for OPT options which we do
+ not suppor tand which take arguments. */
+
+static void
+skip_to_comma (arg, on)
+ int arg;
+ int on;
+{
+ while (*input_line_pointer != ','
+ && ! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+}
+
+/* Handle the OPT NEST=depth option. */
+
+static void
+opt_nest (arg, on)
+ int arg;
+ int on;
+{
+ if (*input_line_pointer != '=')
+ {
+ as_bad (_("bad format of OPT NEST=depth"));
+ return;
+ }
+
+ ++input_line_pointer;
+ max_macro_nest = get_absolute_expression ();
+}
+
+/* Handle the OPT P=chip option. */
+
+static void
+opt_chip (arg, on)
+ int arg;
+ int on;
+{
+ if (*input_line_pointer != '=')
+ {
+ /* This is just OPT P, which we do not support. */
+ return;
+ }
+
+ ++input_line_pointer;
+ mri_chip ();
+}
+
+/* Handle the OPT S option. */
+
+static void
+opt_list (arg, on)
+ int arg;
+ int on;
+{
+ listing_list (on);
+}
+
+/* Handle the OPT T option. */
+
+static void
+opt_list_symbols (arg, on)
+ int arg;
+ int on;
+{
+ if (on)
+ listing |= LISTING_SYMBOLS;
+ else
+ listing &=~ LISTING_SYMBOLS;
+}
+
+/* Handle the MRI REG pseudo-op. */
+
+static void
+s_reg (ignore)
+ int ignore;
+{
+ char *s;
+ int c;
+ struct m68k_op rop;
+ int mask;
+ char *stop = NULL;
+ char stopc;
+
+ if (line_label == NULL)
+ {
+ as_bad (_("missing label"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (flag_mri)
+ stop = mri_comment_field (&stopc);
+
+ SKIP_WHITESPACE ();
+
+ s = input_line_pointer;
+ while (isalnum ((unsigned char) *input_line_pointer)
+#ifdef REGISTER_PREFIX
+ || *input_line_pointer == REGISTER_PREFIX
+#endif
+ || *input_line_pointer == '/'
+ || *input_line_pointer == '-')
+ ++input_line_pointer;
+ c = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ if (m68k_ip_op (s, &rop) != 0)
+ {
+ if (rop.error == NULL)
+ as_bad (_("bad register list"));
+ else
+ as_bad (_("bad register list: %s"), rop.error);
+ *input_line_pointer = c;
+ ignore_rest_of_line ();
+ return;
+ }
+
+ *input_line_pointer = c;
+
+ if (rop.mode == REGLST)
+ mask = rop.mask;
+ else if (rop.mode == DREG)
+ mask = 1 << (rop.reg - DATA0);
+ else if (rop.mode == AREG)
+ mask = 1 << (rop.reg - ADDR0 + 8);
+ else if (rop.mode == FPREG)
+ mask = 1 << (rop.reg - FP0 + 16);
+ else if (rop.mode == CONTROL
+ && rop.reg == FPI)
+ mask = 1 << 24;
+ else if (rop.mode == CONTROL
+ && rop.reg == FPS)
+ mask = 1 << 25;
+ else if (rop.mode == CONTROL
+ && rop.reg == FPC)
+ mask = 1 << 26;
+ else
+ {
+ as_bad (_("bad register list"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ S_SET_SEGMENT (line_label, reg_section);
+ S_SET_VALUE (line_label, ~mask);
+ line_label->sy_frag = &zero_address_frag;
+
+ if (flag_mri)
+ mri_comment_end (stop, stopc);
+
+ demand_empty_rest_of_line ();
+}
+
+/* This structure is used for the MRI SAVE and RESTORE pseudo-ops. */
+
+struct save_opts
+{
+ struct save_opts *next;
+ int abspcadd;
+ int symbols_case_sensitive;
+ int keep_locals;
+ int short_refs;
+ int architecture;
+ int quick;
+ int rel32;
+ int listing;
+ int no_warnings;
+ /* FIXME: We don't save OPT S. */
+};
+
+/* This variable holds the stack of saved options. */
+
+static struct save_opts *save_stack;
+
+/* The MRI SAVE pseudo-op. */
+
+static void
+s_save (ignore)
+ int ignore;
+{
+ struct save_opts *s;
+
+ s = (struct save_opts *) xmalloc (sizeof (struct save_opts));
+ s->abspcadd = m68k_abspcadd;
+ s->symbols_case_sensitive = symbols_case_sensitive;
+ s->keep_locals = flag_keep_locals;
+ s->short_refs = flag_short_refs;
+ s->architecture = current_architecture;
+ s->quick = m68k_quick;
+ s->rel32 = m68k_rel32;
+ s->listing = listing;
+ s->no_warnings = flag_no_warnings;
+
+ s->next = save_stack;
+ save_stack = s;
+
+ demand_empty_rest_of_line ();
+}
+
+/* The MRI RESTORE pseudo-op. */
+
+static void
+s_restore (ignore)
+ int ignore;
+{
+ struct save_opts *s;
+
+ if (save_stack == NULL)
+ {
+ as_bad (_("restore without save"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ s = save_stack;
+ save_stack = s->next;
+
+ m68k_abspcadd = s->abspcadd;
+ symbols_case_sensitive = s->symbols_case_sensitive;
+ flag_keep_locals = s->keep_locals;
+ flag_short_refs = s->short_refs;
+ current_architecture = s->architecture;
+ m68k_quick = s->quick;
+ m68k_rel32 = s->rel32;
+ listing = s->listing;
+ flag_no_warnings = s->no_warnings;
+
+ free (s);
+
+ demand_empty_rest_of_line ();
+}
+
+/* Types of MRI structured control directives. */
+
+enum mri_control_type
+{
+ mri_for,
+ mri_if,
+ mri_repeat,
+ mri_while
+};
+
+/* This structure is used to stack the MRI structured control
+ directives. */
+
+struct mri_control_info
+{
+ /* The directive within which this one is enclosed. */
+ struct mri_control_info *outer;
+
+ /* The type of directive. */
+ enum mri_control_type type;
+
+ /* Whether an ELSE has been in an IF. */
+ int else_seen;
+
+ /* The add or sub statement at the end of a FOR. */
+ char *incr;
+
+ /* The label of the top of a FOR or REPEAT loop. */
+ char *top;
+
+ /* The label to jump to for the next iteration, or the else
+ expression of a conditional. */
+ char *next;
+
+ /* The label to jump to to break out of the loop, or the label past
+ the end of a conditional. */
+ char *bottom;
+};
+
+/* The stack of MRI structured control directives. */
+
+static struct mri_control_info *mri_control_stack;
+
+/* The current MRI structured control directive index number, used to
+ generate label names. */
+
+static int mri_control_index;
+
+/* Some function prototypes. */
+
+static void mri_assemble PARAMS ((char *));
+static char *mri_control_label PARAMS ((void));
+static struct mri_control_info *push_mri_control
+ PARAMS ((enum mri_control_type));
+static void pop_mri_control PARAMS ((void));
+static int parse_mri_condition PARAMS ((int *));
+static int parse_mri_control_operand
+ PARAMS ((int *, char **, char **, char **, char **));
+static int swap_mri_condition PARAMS ((int));
+static int reverse_mri_condition PARAMS ((int));
+static void build_mri_control_operand
+ PARAMS ((int, int, char *, char *, char *, char *, const char *,
+ const char *, int));
+static void parse_mri_control_expression
+ PARAMS ((char *, int, const char *, const char *, int));
+
+/* Assemble an instruction for an MRI structured control directive. */
+
+static void
+mri_assemble (str)
+ char *str;
+{
+ char *s;
+
+ /* md_assemble expects the opcode to be in lower case. */
+ for (s = str; *s != ' ' && *s != '\0'; s++)
+ {
+ if (isupper ((unsigned char) *s))
+ *s = tolower ((unsigned char) *s);
+ }
+
+ md_assemble (str);
+}
+
+/* Generate a new MRI label structured control directive label name. */
+
+static char *
+mri_control_label ()
+{
+ char *n;
+
+ n = (char *) xmalloc (20);
+ sprintf (n, "%smc%d", FAKE_LABEL_NAME, mri_control_index);
+ ++mri_control_index;
+ return n;
+}
+
+/* Create a new MRI structured control directive. */
+
+static struct mri_control_info *
+push_mri_control (type)
+ enum mri_control_type type;
+{
+ struct mri_control_info *n;
+
+ n = (struct mri_control_info *) xmalloc (sizeof (struct mri_control_info));
+
+ n->type = type;
+ n->else_seen = 0;
+ if (type == mri_if || type == mri_while)
+ n->top = NULL;
+ else
+ n->top = mri_control_label ();
+ n->next = mri_control_label ();
+ n->bottom = mri_control_label ();
+
+ n->outer = mri_control_stack;
+ mri_control_stack = n;
+
+ return n;
+}
+
+/* Pop off the stack of MRI structured control directives. */
+
+static void
+pop_mri_control ()
+{
+ struct mri_control_info *n;
+
+ n = mri_control_stack;
+ mri_control_stack = n->outer;
+ if (n->top != NULL)
+ free (n->top);
+ free (n->next);
+ free (n->bottom);
+ free (n);
+}
+
+/* Recognize a condition code in an MRI structured control expression. */
+
+static int
+parse_mri_condition (pcc)
+ int *pcc;
+{
+ char c1, c2;
+
+ know (*input_line_pointer == '<');
+
+ ++input_line_pointer;
+ c1 = *input_line_pointer++;
+ c2 = *input_line_pointer++;
+
+ if (*input_line_pointer != '>')
+ {
+ as_bad (_("syntax error in structured control directive"));
+ return 0;
+ }
+
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+
+ if (isupper (c1))
+ c1 = tolower (c1);
+ if (isupper (c2))
+ c2 = tolower (c2);
+
+ *pcc = (c1 << 8) | c2;
+
+ return 1;
+}
+
+/* Parse a single operand in an MRI structured control expression. */
+
+static int
+parse_mri_control_operand (pcc, leftstart, leftstop, rightstart, rightstop)
+ int *pcc;
+ char **leftstart;
+ char **leftstop;
+ char **rightstart;
+ char **rightstop;
+{
+ char *s;
+
+ SKIP_WHITESPACE ();
+
+ *pcc = -1;
+ *leftstart = NULL;
+ *leftstop = NULL;
+ *rightstart = NULL;
+ *rightstop = NULL;
+
+ if (*input_line_pointer == '<')
+ {
+ /* It's just a condition code. */
+ return parse_mri_condition (pcc);
+ }
+
+ /* Look ahead for the condition code. */
+ for (s = input_line_pointer; *s != '\0'; ++s)
+ {
+ if (*s == '<' && s[1] != '\0' && s[2] != '\0' && s[3] == '>')
+ break;
+ }
+ if (*s == '\0')
+ {
+ as_bad (_("missing condition code in structured control directive"));
+ return 0;
+ }
+
+ *leftstart = input_line_pointer;
+ *leftstop = s;
+ if (*leftstop > *leftstart
+ && ((*leftstop)[-1] == ' ' || (*leftstop)[-1] == '\t'))
+ --*leftstop;
+
+ input_line_pointer = s;
+ if (! parse_mri_condition (pcc))
+ return 0;
+
+ /* Look ahead for AND or OR or end of line. */
+ for (s = input_line_pointer; *s != '\0'; ++s)
+ {
+ if ((strncasecmp (s, "AND", 3) == 0
+ && (s[3] == '.' || ! is_part_of_name (s[3])))
+ || (strncasecmp (s, "OR", 2) == 0
+ && (s[2] == '.' || ! is_part_of_name (s[2]))))
+ break;
+ }
+
+ *rightstart = input_line_pointer;
+ *rightstop = s;
+ if (*rightstop > *rightstart
+ && ((*rightstop)[-1] == ' ' || (*rightstop)[-1] == '\t'))
+ --*rightstop;
+
+ input_line_pointer = s;
+
+ return 1;
+}
+
+#define MCC(b1, b2) (((b1) << 8) | (b2))
+
+/* Swap the sense of a condition. This changes the condition so that
+ it generates the same result when the operands are swapped. */
+
+static int
+swap_mri_condition (cc)
+ int cc;
+{
+ switch (cc)
+ {
+ case MCC ('h', 'i'): return MCC ('c', 's');
+ case MCC ('l', 's'): return MCC ('c', 'c');
+ case MCC ('c', 'c'): return MCC ('l', 's');
+ case MCC ('c', 's'): return MCC ('h', 'i');
+ case MCC ('p', 'l'): return MCC ('m', 'i');
+ case MCC ('m', 'i'): return MCC ('p', 'l');
+ case MCC ('g', 'e'): return MCC ('l', 'e');
+ case MCC ('l', 't'): return MCC ('g', 't');
+ case MCC ('g', 't'): return MCC ('l', 't');
+ case MCC ('l', 'e'): return MCC ('g', 'e');
+ }
+ return cc;
+}
+
+/* Reverse the sense of a condition. */
+
+static int
+reverse_mri_condition (cc)
+ int cc;
+{
+ switch (cc)
+ {
+ case MCC ('h', 'i'): return MCC ('l', 's');
+ case MCC ('l', 's'): return MCC ('h', 'i');
+ case MCC ('c', 'c'): return MCC ('c', 's');
+ case MCC ('c', 's'): return MCC ('c', 'c');
+ case MCC ('n', 'e'): return MCC ('e', 'q');
+ case MCC ('e', 'q'): return MCC ('n', 'e');
+ case MCC ('v', 'c'): return MCC ('v', 's');
+ case MCC ('v', 's'): return MCC ('v', 'c');
+ case MCC ('p', 'l'): return MCC ('m', 'i');
+ case MCC ('m', 'i'): return MCC ('p', 'l');
+ case MCC ('g', 'e'): return MCC ('l', 't');
+ case MCC ('l', 't'): return MCC ('g', 'e');
+ case MCC ('g', 't'): return MCC ('l', 'e');
+ case MCC ('l', 'e'): return MCC ('g', 't');
+ }
+ return cc;
+}
+
+/* Build an MRI structured control expression. This generates test
+ and branch instructions. It goes to TRUELAB if the condition is
+ true, and to FALSELAB if the condition is false. Exactly one of
+ TRUELAB and FALSELAB will be NULL, meaning to fall through. QUAL
+ is the size qualifier for the expression. EXTENT is the size to
+ use for the branch. */
+
+static void
+build_mri_control_operand (qual, cc, leftstart, leftstop, rightstart,
+ rightstop, truelab, falselab, extent)
+ int qual;
+ int cc;
+ char *leftstart;
+ char *leftstop;
+ char *rightstart;
+ char *rightstop;
+ const char *truelab;
+ const char *falselab;
+ int extent;
+{
+ char *buf;
+ char *s;
+
+ if (leftstart != NULL)
+ {
+ struct m68k_op leftop, rightop;
+ char c;
+
+ /* Swap the compare operands, if necessary, to produce a legal
+ m68k compare instruction. Comparing a register operand with
+ a non-register operand requires the register to be on the
+ right (cmp, cmpa). Comparing an immediate value with
+ anything requires the immediate value to be on the left
+ (cmpi). */
+
+ c = *leftstop;
+ *leftstop = '\0';
+ (void) m68k_ip_op (leftstart, &leftop);
+ *leftstop = c;
+
+ c = *rightstop;
+ *rightstop = '\0';
+ (void) m68k_ip_op (rightstart, &rightop);
+ *rightstop = c;
+
+ if (rightop.mode == IMMED
+ || ((leftop.mode == DREG || leftop.mode == AREG)
+ && (rightop.mode != DREG && rightop.mode != AREG)))
+ {
+ char *temp;
+
+ cc = swap_mri_condition (cc);
+ temp = leftstart;
+ leftstart = rightstart;
+ rightstart = temp;
+ temp = leftstop;
+ leftstop = rightstop;
+ rightstop = temp;
+ }
+ }
+
+ if (truelab == NULL)
+ {
+ cc = reverse_mri_condition (cc);
+ truelab = falselab;
+ }
+
+ if (leftstart != NULL)
+ {
+ buf = (char *) xmalloc (20
+ + (leftstop - leftstart)
+ + (rightstop - rightstart));
+ s = buf;
+ *s++ = 'c';
+ *s++ = 'm';
+ *s++ = 'p';
+ if (qual != '\0')
+ *s++ = qual;
+ *s++ = ' ';
+ memcpy (s, leftstart, leftstop - leftstart);
+ s += leftstop - leftstart;
+ *s++ = ',';
+ memcpy (s, rightstart, rightstop - rightstart);
+ s += rightstop - rightstart;
+ *s = '\0';
+ mri_assemble (buf);
+ free (buf);
+ }
+
+ buf = (char *) xmalloc (20 + strlen (truelab));
+ s = buf;
+ *s++ = 'b';
+ *s++ = cc >> 8;
+ *s++ = cc & 0xff;
+ if (extent != '\0')
+ *s++ = extent;
+ *s++ = ' ';
+ strcpy (s, truelab);
+ mri_assemble (buf);
+ free (buf);
+}
+
+/* Parse an MRI structured control expression. This generates test
+ and branch instructions. STOP is where the expression ends. It
+ goes to TRUELAB if the condition is true, and to FALSELAB if the
+ condition is false. Exactly one of TRUELAB and FALSELAB will be
+ NULL, meaning to fall through. QUAL is the size qualifier for the
+ expression. EXTENT is the size to use for the branch. */
+
+static void
+parse_mri_control_expression (stop, qual, truelab, falselab, extent)
+ char *stop;
+ int qual;
+ const char *truelab;
+ const char *falselab;
+ int extent;
+{
+ int c;
+ int cc;
+ char *leftstart;
+ char *leftstop;
+ char *rightstart;
+ char *rightstop;
+
+ c = *stop;
+ *stop = '\0';
+
+ if (! parse_mri_control_operand (&cc, &leftstart, &leftstop,
+ &rightstart, &rightstop))
+ {
+ *stop = c;
+ return;
+ }
+
+ if (strncasecmp (input_line_pointer, "AND", 3) == 0)
+ {
+ const char *flab;
+
+ if (falselab != NULL)
+ flab = falselab;
+ else
+ flab = mri_control_label ();
+
+ build_mri_control_operand (qual, cc, leftstart, leftstop, rightstart,
+ rightstop, (const char *) NULL, flab, extent);
+
+ input_line_pointer += 3;
+ if (*input_line_pointer != '.'
+ || input_line_pointer[1] == '\0')
+ qual = '\0';
+ else
+ {
+ qual = input_line_pointer[1];
+ input_line_pointer += 2;
+ }
+
+ if (! parse_mri_control_operand (&cc, &leftstart, &leftstop,
+ &rightstart, &rightstop))
+ {
+ *stop = c;
+ return;
+ }
+
+ build_mri_control_operand (qual, cc, leftstart, leftstop, rightstart,
+ rightstop, truelab, falselab, extent);
+
+ if (falselab == NULL)
+ colon (flab);
+ }
+ else if (strncasecmp (input_line_pointer, "OR", 2) == 0)
+ {
+ const char *tlab;
+
+ if (truelab != NULL)
+ tlab = truelab;
+ else
+ tlab = mri_control_label ();
+
+ build_mri_control_operand (qual, cc, leftstart, leftstop, rightstart,
+ rightstop, tlab, (const char *) NULL, extent);
+
+ input_line_pointer += 2;
+ if (*input_line_pointer != '.'
+ || input_line_pointer[1] == '\0')
+ qual = '\0';
+ else
+ {
+ qual = input_line_pointer[1];
+ input_line_pointer += 2;
+ }
+
+ if (! parse_mri_control_operand (&cc, &leftstart, &leftstop,
+ &rightstart, &rightstop))
+ {
+ *stop = c;
+ return;
+ }
+
+ build_mri_control_operand (qual, cc, leftstart, leftstop, rightstart,
+ rightstop, truelab, falselab, extent);
+
+ if (truelab == NULL)
+ colon (tlab);
+ }
+ else
+ {
+ build_mri_control_operand (qual, cc, leftstart, leftstop, rightstart,
+ rightstop, truelab, falselab, extent);
+ }
+
+ *stop = c;
+ if (input_line_pointer != stop)
+ as_bad (_("syntax error in structured control directive"));
+}
+
+/* Handle the MRI IF pseudo-op. This may be a structured control
+ directive, or it may be a regular assembler conditional, depending
+ on its operands. */
+
+static void
+s_mri_if (qual)
+ int qual;
+{
+ char *s;
+ int c;
+ struct mri_control_info *n;
+
+ /* A structured control directive must end with THEN with an
+ optional qualifier. */
+ s = input_line_pointer;
+ while (! is_end_of_line[(unsigned char) *s]
+ && (! flag_mri || *s != '*'))
+ ++s;
+ --s;
+ while (s > input_line_pointer && (*s == ' ' || *s == '\t'))
+ --s;
+
+ if (s - input_line_pointer > 1
+ && s[-1] == '.')
+ s -= 2;
+
+ if (s - input_line_pointer < 3
+ || strncasecmp (s - 3, "THEN", 4) != 0)
+ {
+ if (qual != '\0')
+ {
+ as_bad (_("missing then"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* It's a conditional. */
+ s_if (O_ne);
+ return;
+ }
+
+ /* Since this might be a conditional if, this pseudo-op will be
+ called even if we are supported to be ignoring input. Double
+ check now. Clobber *input_line_pointer so that ignore_input
+ thinks that this is not a special pseudo-op. */
+ c = *input_line_pointer;
+ *input_line_pointer = 0;
+ if (ignore_input ())
+ {
+ *input_line_pointer = c;
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ demand_empty_rest_of_line ();
+ return;
+ }
+ *input_line_pointer = c;
+
+ n = push_mri_control (mri_if);
+
+ parse_mri_control_expression (s - 3, qual, (const char *) NULL,
+ n->next, s[1] == '.' ? s[2] : '\0');
+
+ if (s[1] == '.')
+ input_line_pointer = s + 3;
+ else
+ input_line_pointer = s + 1;
+
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the MRI else pseudo-op. If we are currently doing an MRI
+ structured IF, associate the ELSE with the IF. Otherwise, assume
+ it is a conditional else. */
+
+static void
+s_mri_else (qual)
+ int qual;
+{
+ int c;
+ char *buf;
+ char q[2];
+
+ if (qual == '\0'
+ && (mri_control_stack == NULL
+ || mri_control_stack->type != mri_if
+ || mri_control_stack->else_seen))
+ {
+ s_else (0);
+ return;
+ }
+
+ c = *input_line_pointer;
+ *input_line_pointer = 0;
+ if (ignore_input ())
+ {
+ *input_line_pointer = c;
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ demand_empty_rest_of_line ();
+ return;
+ }
+ *input_line_pointer = c;
+
+ if (mri_control_stack == NULL
+ || mri_control_stack->type != mri_if
+ || mri_control_stack->else_seen)
+ {
+ as_bad (_("else without matching if"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ mri_control_stack->else_seen = 1;
+
+ buf = (char *) xmalloc (20 + strlen (mri_control_stack->bottom));
+ q[0] = qual;
+ q[1] = '\0';
+ sprintf (buf, "bra%s %s", q, mri_control_stack->bottom);
+ mri_assemble (buf);
+ free (buf);
+
+ colon (mri_control_stack->next);
+
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the MRI ENDI pseudo-op. */
+
+static void
+s_mri_endi (ignore)
+ int ignore;
+{
+ if (mri_control_stack == NULL
+ || mri_control_stack->type != mri_if)
+ {
+ as_bad (_("endi without matching if"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* ignore_input will not return true for ENDI, so we don't need to
+ worry about checking it again here. */
+
+ if (! mri_control_stack->else_seen)
+ colon (mri_control_stack->next);
+ colon (mri_control_stack->bottom);
+
+ pop_mri_control ();
+
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the MRI BREAK pseudo-op. */
+
+static void
+s_mri_break (extent)
+ int extent;
+{
+ struct mri_control_info *n;
+ char *buf;
+ char ex[2];
+
+ n = mri_control_stack;
+ while (n != NULL
+ && n->type != mri_for
+ && n->type != mri_repeat
+ && n->type != mri_while)
+ n = n->outer;
+ if (n == NULL)
+ {
+ as_bad (_("break outside of structured loop"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ buf = (char *) xmalloc (20 + strlen (n->bottom));
+ ex[0] = extent;
+ ex[1] = '\0';
+ sprintf (buf, "bra%s %s", ex, n->bottom);
+ mri_assemble (buf);
+ free (buf);
+
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the MRI NEXT pseudo-op. */
+
+static void
+s_mri_next (extent)
+ int extent;
+{
+ struct mri_control_info *n;
+ char *buf;
+ char ex[2];
+
+ n = mri_control_stack;
+ while (n != NULL
+ && n->type != mri_for
+ && n->type != mri_repeat
+ && n->type != mri_while)
+ n = n->outer;
+ if (n == NULL)
+ {
+ as_bad (_("next outside of structured loop"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ buf = (char *) xmalloc (20 + strlen (n->next));
+ ex[0] = extent;
+ ex[1] = '\0';
+ sprintf (buf, "bra%s %s", ex, n->next);
+ mri_assemble (buf);
+ free (buf);
+
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the MRI FOR pseudo-op. */
+
+static void
+s_mri_for (qual)
+ int qual;
+{
+ const char *varstart, *varstop;
+ const char *initstart, *initstop;
+ const char *endstart, *endstop;
+ const char *bystart, *bystop;
+ int up;
+ int by;
+ int extent;
+ struct mri_control_info *n;
+ char *buf;
+ char *s;
+ char ex[2];
+
+ /* The syntax is
+ FOR.q var = init { TO | DOWNTO } end [ BY by ] DO.e
+ */
+
+ SKIP_WHITESPACE ();
+ varstart = input_line_pointer;
+
+ /* Look for the '='. */
+ while (! is_end_of_line[(unsigned char) *input_line_pointer]
+ && *input_line_pointer != '=')
+ ++input_line_pointer;
+ if (*input_line_pointer != '=')
+ {
+ as_bad (_("missing ="));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ varstop = input_line_pointer;
+ if (varstop > varstart
+ && (varstop[-1] == ' ' || varstop[-1] == '\t'))
+ --varstop;
+
+ ++input_line_pointer;
+
+ initstart = input_line_pointer;
+
+ /* Look for TO or DOWNTO. */
+ up = 1;
+ initstop = NULL;
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ if (strncasecmp (input_line_pointer, "TO", 2) == 0
+ && ! is_part_of_name (input_line_pointer[2]))
+ {
+ initstop = input_line_pointer;
+ input_line_pointer += 2;
+ break;
+ }
+ if (strncasecmp (input_line_pointer, "DOWNTO", 6) == 0
+ && ! is_part_of_name (input_line_pointer[6]))
+ {
+ initstop = input_line_pointer;
+ up = 0;
+ input_line_pointer += 6;
+ break;
+ }
+ ++input_line_pointer;
+ }
+ if (initstop == NULL)
+ {
+ as_bad (_("missing to or downto"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (initstop > initstart
+ && (initstop[-1] == ' ' || initstop[-1] == '\t'))
+ --initstop;
+
+ SKIP_WHITESPACE ();
+ endstart = input_line_pointer;
+
+ /* Look for BY or DO. */
+ by = 0;
+ endstop = NULL;
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ if (strncasecmp (input_line_pointer, "BY", 2) == 0
+ && ! is_part_of_name (input_line_pointer[2]))
+ {
+ endstop = input_line_pointer;
+ by = 1;
+ input_line_pointer += 2;
+ break;
+ }
+ if (strncasecmp (input_line_pointer, "DO", 2) == 0
+ && (input_line_pointer[2] == '.'
+ || ! is_part_of_name (input_line_pointer[2])))
+ {
+ endstop = input_line_pointer;
+ input_line_pointer += 2;
+ break;
+ }
+ ++input_line_pointer;
+ }
+ if (endstop == NULL)
+ {
+ as_bad (_("missing do"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (endstop > endstart
+ && (endstop[-1] == ' ' || endstop[-1] == '\t'))
+ --endstop;
+
+ if (! by)
+ {
+ bystart = "#1";
+ bystop = bystart + 2;
+ }
+ else
+ {
+ SKIP_WHITESPACE ();
+ bystart = input_line_pointer;
+
+ /* Look for DO. */
+ bystop = NULL;
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ if (strncasecmp (input_line_pointer, "DO", 2) == 0
+ && (input_line_pointer[2] == '.'
+ || ! is_part_of_name (input_line_pointer[2])))
+ {
+ bystop = input_line_pointer;
+ input_line_pointer += 2;
+ break;
+ }
+ ++input_line_pointer;
+ }
+ if (bystop == NULL)
+ {
+ as_bad (_("missing do"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (bystop > bystart
+ && (bystop[-1] == ' ' || bystop[-1] == '\t'))
+ --bystop;
+ }
+
+ if (*input_line_pointer != '.')
+ extent = '\0';
+ else
+ {
+ extent = input_line_pointer[1];
+ input_line_pointer += 2;
+ }
+
+ /* We have fully parsed the FOR operands. Now build the loop. */
+
+ n = push_mri_control (mri_for);
+
+ buf = (char *) xmalloc (50 + (input_line_pointer - varstart));
+
+ /* move init,var */
+ s = buf;
+ *s++ = 'm';
+ *s++ = 'o';
+ *s++ = 'v';
+ *s++ = 'e';
+ if (qual != '\0')
+ *s++ = qual;
+ *s++ = ' ';
+ memcpy (s, initstart, initstop - initstart);
+ s += initstop - initstart;
+ *s++ = ',';
+ memcpy (s, varstart, varstop - varstart);
+ s += varstop - varstart;
+ *s = '\0';
+ mri_assemble (buf);
+
+ colon (n->top);
+
+ /* cmp end,var */
+ s = buf;
+ *s++ = 'c';
+ *s++ = 'm';
+ *s++ = 'p';
+ if (qual != '\0')
+ *s++ = qual;
+ *s++ = ' ';
+ memcpy (s, endstart, endstop - endstart);
+ s += endstop - endstart;
+ *s++ = ',';
+ memcpy (s, varstart, varstop - varstart);
+ s += varstop - varstart;
+ *s = '\0';
+ mri_assemble (buf);
+
+ /* bcc bottom */
+ ex[0] = extent;
+ ex[1] = '\0';
+ if (up)
+ sprintf (buf, "blt%s %s", ex, n->bottom);
+ else
+ sprintf (buf, "bgt%s %s", ex, n->bottom);
+ mri_assemble (buf);
+
+ /* Put together the add or sub instruction used by ENDF. */
+ s = buf;
+ if (up)
+ strcpy (s, "add");
+ else
+ strcpy (s, "sub");
+ s += 3;
+ if (qual != '\0')
+ *s++ = qual;
+ *s++ = ' ';
+ memcpy (s, bystart, bystop - bystart);
+ s += bystop - bystart;
+ *s++ = ',';
+ memcpy (s, varstart, varstop - varstart);
+ s += varstop - varstart;
+ *s = '\0';
+ n->incr = buf;
+
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the MRI ENDF pseudo-op. */
+
+static void
+s_mri_endf (ignore)
+ int ignore;
+{
+ if (mri_control_stack == NULL
+ || mri_control_stack->type != mri_for)
+ {
+ as_bad (_("endf without for"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ colon (mri_control_stack->next);
+
+ mri_assemble (mri_control_stack->incr);
+
+ sprintf (mri_control_stack->incr, "bra %s", mri_control_stack->top);
+ mri_assemble (mri_control_stack->incr);
+
+ free (mri_control_stack->incr);
+
+ colon (mri_control_stack->bottom);
+
+ pop_mri_control ();
+
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the MRI REPEAT pseudo-op. */
+
+static void
+s_mri_repeat (ignore)
+ int ignore;
+{
+ struct mri_control_info *n;
+
+ n = push_mri_control (mri_repeat);
+ colon (n->top);
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the MRI UNTIL pseudo-op. */
+
+static void
+s_mri_until (qual)
+ int qual;
+{
+ char *s;
+
+ if (mri_control_stack == NULL
+ || mri_control_stack->type != mri_repeat)
+ {
+ as_bad (_("until without repeat"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ colon (mri_control_stack->next);
+
+ for (s = input_line_pointer; ! is_end_of_line[(unsigned char) *s]; s++)
+ ;
+
+ parse_mri_control_expression (s, qual, (const char *) NULL,
+ mri_control_stack->top, '\0');
+
+ colon (mri_control_stack->bottom);
+
+ input_line_pointer = s;
+
+ pop_mri_control ();
+
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the MRI WHILE pseudo-op. */
+
+static void
+s_mri_while (qual)
+ int qual;
+{
+ char *s;
+
+ struct mri_control_info *n;
+
+ s = input_line_pointer;
+ while (! is_end_of_line[(unsigned char) *s]
+ && (! flag_mri || *s != '*'))
+ s++;
+ --s;
+ while (*s == ' ' || *s == '\t')
+ --s;
+ if (s - input_line_pointer > 1
+ && s[-1] == '.')
+ s -= 2;
+ if (s - input_line_pointer < 2
+ || strncasecmp (s - 1, "DO", 2) != 0)
+ {
+ as_bad (_("missing do"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ n = push_mri_control (mri_while);
+
+ colon (n->next);
+
+ parse_mri_control_expression (s - 1, qual, (const char *) NULL, n->bottom,
+ s[1] == '.' ? s[2] : '\0');
+
+ input_line_pointer = s + 1;
+ if (*input_line_pointer == '.')
+ input_line_pointer += 2;
+
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the MRI ENDW pseudo-op. */
+
+static void
+s_mri_endw (ignore)
+ int ignore;
+{
+ char *buf;
+
+ if (mri_control_stack == NULL
+ || mri_control_stack->type != mri_while)
+ {
+ as_bad (_("endw without while"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ buf = (char *) xmalloc (20 + strlen (mri_control_stack->next));
+ sprintf (buf, "bra %s", mri_control_stack->next);
+ mri_assemble (buf);
+ free (buf);
+
+ colon (mri_control_stack->bottom);
+
+ pop_mri_control ();
+
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/*
+ * md_parse_option
+ * Invocation line includes a switch not recognized by the base assembler.
+ * See if it's a processor-specific option. These are:
+ *
+ * -[A]m[c]68000, -[A]m[c]68008, -[A]m[c]68010, -[A]m[c]68020, -[A]m[c]68030, -[A]m[c]68040
+ * -[A]m[c]68881, -[A]m[c]68882, -[A]m[c]68851
+ * Select the architecture. Instructions or features not
+ * supported by the selected architecture cause fatal
+ * errors. More than one may be specified. The default is
+ * -m68020 -m68851 -m68881. Note that -m68008 is a synonym
+ * for -m68000, and -m68882 is a synonym for -m68881.
+ * -[A]m[c]no-68851, -[A]m[c]no-68881
+ * Don't accept 688?1 instructions. (The "c" is kind of silly,
+ * so don't use or document it, but that's the way the parsing
+ * works).
+ *
+ * -pic Indicates PIC.
+ * -k Indicates PIC. (Sun 3 only.)
+ *
+ * --bitwise-or
+ * Permit `|' to be used in expressions.
+ *
+ */
+
+#ifdef OBJ_ELF
+CONST char *md_shortopts = "lSA:m:kQ:V";
+#else
+CONST char *md_shortopts = "lSA:m:k";
+#endif
+
+struct option md_longopts[] = {
+#define OPTION_PIC (OPTION_MD_BASE)
+ {"pic", no_argument, NULL, OPTION_PIC},
+#define OPTION_REGISTER_PREFIX_OPTIONAL (OPTION_MD_BASE + 1)
+ {"register-prefix-optional", no_argument, NULL,
+ OPTION_REGISTER_PREFIX_OPTIONAL},
+#define OPTION_BITWISE_OR (OPTION_MD_BASE + 2)
+ {"bitwise-or", no_argument, NULL, OPTION_BITWISE_OR},
+#define OPTION_BASE_SIZE_DEFAULT_16 (OPTION_MD_BASE + 3)
+ {"base-size-default-16", no_argument, NULL, OPTION_BASE_SIZE_DEFAULT_16},
+#define OPTION_BASE_SIZE_DEFAULT_32 (OPTION_MD_BASE + 4)
+ {"base-size-default-32", no_argument, NULL, OPTION_BASE_SIZE_DEFAULT_32},
+#define OPTION_DISP_SIZE_DEFAULT_16 (OPTION_MD_BASE + 5)
+ {"disp-size-default-16", no_argument, NULL, OPTION_DISP_SIZE_DEFAULT_16},
+#define OPTION_DISP_SIZE_DEFAULT_32 (OPTION_MD_BASE + 6)
+ {"disp-size-default-32", no_argument, NULL, OPTION_DISP_SIZE_DEFAULT_32},
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ switch (c)
+ {
+ case 'l': /* -l means keep external to 2 bit offset
+ rather than 16 bit one */
+ flag_short_refs = 1;
+ break;
+
+ case 'S': /* -S means that jbsr's always turn into
+ jsr's. */
+ flag_long_jumps = 1;
+ break;
+
+ case 'A':
+ if (*arg == 'm')
+ arg++;
+ /* intentional fall-through */
+ case 'm':
+
+ if (arg[0] == 'n' && arg[1] == 'o' && arg[2] == '-')
+ {
+ int i;
+ unsigned long arch;
+ const char *oarg = arg;
+
+ arg += 3;
+ if (*arg == 'm')
+ {
+ arg++;
+ if (arg[0] == 'c' && arg[1] == '6')
+ arg++;
+ }
+ for (i = 0; i < n_archs; i++)
+ if (!strcmp (arg, archs[i].name))
+ break;
+ if (i == n_archs)
+ {
+ unknown:
+ as_bad (_("unrecognized option `%s'"), oarg);
+ return 0;
+ }
+ arch = archs[i].arch;
+ if (arch == m68881)
+ no_68881 = 1;
+ else if (arch == m68851)
+ no_68851 = 1;
+ else
+ goto unknown;
+ }
+ else
+ {
+ int i;
+
+ if (arg[0] == 'c' && arg[1] == '6')
+ arg++;
+
+ for (i = 0; i < n_archs; i++)
+ if (!strcmp (arg, archs[i].name))
+ {
+ unsigned long arch = archs[i].arch;
+ if (cpu_of_arch (arch))
+ /* It's a cpu spec. */
+ {
+ current_architecture &= ~m68000up;
+ current_architecture |= arch;
+ }
+ else if (arch == m68881)
+ {
+ current_architecture |= m68881;
+ no_68881 = 0;
+ }
+ else if (arch == m68851)
+ {
+ current_architecture |= m68851;
+ no_68851 = 0;
+ }
+ else
+ /* ??? */
+ abort ();
+ break;
+ }
+ if (i == n_archs)
+ {
+ as_bad (_("unrecognized architecture specification `%s'"), arg);
+ return 0;
+ }
+ }
+ break;
+
+ case OPTION_PIC:
+ case 'k':
+ flag_want_pic = 1;
+ break; /* -pic, Position Independent Code */
+
+ case OPTION_REGISTER_PREFIX_OPTIONAL:
+ flag_reg_prefix_optional = 1;
+ reg_prefix_optional_seen = 1;
+ break;
+
+ /* -V: SVR4 argument to print version ID. */
+ case 'V':
+ print_version_id ();
+ break;
+
+ /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section
+ should be emitted or not. FIXME: Not implemented. */
+ case 'Q':
+ break;
+
+ case OPTION_BITWISE_OR:
+ {
+ char *n, *t;
+ const char *s;
+
+ n = (char *) xmalloc (strlen (m68k_comment_chars) + 1);
+ t = n;
+ for (s = m68k_comment_chars; *s != '\0'; s++)
+ if (*s != '|')
+ *t++ = *s;
+ *t = '\0';
+ m68k_comment_chars = n;
+ }
+ break;
+
+ case OPTION_BASE_SIZE_DEFAULT_16:
+ m68k_index_width_default = SIZE_WORD;
+ break;
+
+ case OPTION_BASE_SIZE_DEFAULT_32:
+ m68k_index_width_default = SIZE_LONG;
+ break;
+
+ case OPTION_DISP_SIZE_DEFAULT_16:
+ m68k_rel32 = 0;
+ m68k_rel32_from_cmdline = 1;
+ break;
+
+ case OPTION_DISP_SIZE_DEFAULT_32:
+ m68k_rel32 = 1;
+ m68k_rel32_from_cmdline = 1;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ fprintf(stream, _("\
+680X0 options:\n\
+-l use 1 word for refs to undefined symbols [default 2]\n\
+-m68000 | -m68008 | -m68010 | -m68020 | -m68030 | -m68040 | -m68060\n\
+ | -m68302 | -m68331 | -m68332 | -m68333 | -m68340 | -m68360\n\
+ | -mcpu32 | -m5200\n\
+ specify variant of 680X0 architecture [default 68020]\n\
+-m68881 | -m68882 | -mno-68881 | -mno-68882\n\
+ target has/lacks floating-point coprocessor\n\
+ [default yes for 68020, 68030, and cpu32]\n"));
+ fprintf(stream, _("\
+-m68851 | -mno-68851\n\
+ target has/lacks memory-management unit coprocessor\n\
+ [default yes for 68020 and up]\n\
+-pic, -k generate position independent code\n\
+-S turn jbsr into jsr\n\
+--register-prefix-optional\n\
+ recognize register names without prefix character\n\
+--bitwise-or do not treat `|' as a comment character\n"));
+ fprintf (stream, _("\
+--base-size-default-16 base reg without size is 16 bits\n\
+--base-size-default-32 base reg without size is 32 bits (default)\n\
+--disp-size-default-16 displacement with unknown size is 16 bits\n\
+--disp-size-default-32 displacement with unknown size is 32 bits (default)\n"));
+}
+
+#ifdef TEST2
+
+/* TEST2: Test md_assemble() */
+/* Warning, this routine probably doesn't work anymore */
+
+main ()
+{
+ struct m68k_it the_ins;
+ char buf[120];
+ char *cp;
+ int n;
+
+ m68k_ip_begin ();
+ for (;;)
+ {
+ if (!gets (buf) || !*buf)
+ break;
+ if (buf[0] == '|' || buf[1] == '.')
+ continue;
+ for (cp = buf; *cp; cp++)
+ if (*cp == '\t')
+ *cp = ' ';
+ if (is_label (buf))
+ continue;
+ memset (&the_ins, '\0', sizeof (the_ins));
+ m68k_ip (&the_ins, buf);
+ if (the_ins.error)
+ {
+ printf (_("Error %s in %s\n"), the_ins.error, buf);
+ }
+ else
+ {
+ printf (_("Opcode(%d.%s): "), the_ins.numo, the_ins.args);
+ for (n = 0; n < the_ins.numo; n++)
+ printf (" 0x%x", the_ins.opcode[n] & 0xffff);
+ printf (" ");
+ print_the_insn (&the_ins.opcode[0], stdout);
+ (void) putchar ('\n');
+ }
+ for (n = 0; n < strlen (the_ins.args) / 2; n++)
+ {
+ if (the_ins.operands[n].error)
+ {
+ printf ("op%d Error %s in %s\n", n, the_ins.operands[n].error, buf);
+ continue;
+ }
+ printf ("mode %d, reg %d, ", the_ins.operands[n].mode, the_ins.operands[n].reg);
+ if (the_ins.operands[n].b_const)
+ printf ("Constant: '%.*s', ", 1 + the_ins.operands[n].e_const - the_ins.operands[n].b_const, the_ins.operands[n].b_const);
+ printf ("ireg %d, isiz %d, imul %d, ", the_ins.operands[n].ireg, the_ins.operands[n].isiz, the_ins.operands[n].imul);
+ if (the_ins.operands[n].b_iadd)
+ printf ("Iadd: '%.*s',", 1 + the_ins.operands[n].e_iadd - the_ins.operands[n].b_iadd, the_ins.operands[n].b_iadd);
+ (void) putchar ('\n');
+ }
+ }
+ m68k_ip_end ();
+ return 0;
+}
+
+is_label (str)
+ char *str;
+{
+ while (*str == ' ')
+ str++;
+ while (*str && *str != ' ')
+ str++;
+ if (str[-1] == ':' || str[1] == '=')
+ return 1;
+ return 0;
+}
+
+#endif
+
+/* Possible states for relaxation:
+
+ 0 0 branch offset byte (bra, etc)
+ 0 1 word
+ 0 2 long
+
+ 1 0 indexed offsets byte a0@(32,d4:w:1) etc
+ 1 1 word
+ 1 2 long
+
+ 2 0 two-offset index word-word a0@(32,d4)@(45) etc
+ 2 1 word-long
+ 2 2 long-word
+ 2 3 long-long
+
+ */
+
+/* We have no need to default values of symbols. */
+
+/* ARGSUSED */
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ return 0;
+}
+
+/* Round up a section size to the appropriate boundary. */
+valueT
+md_section_align (segment, size)
+ segT segment;
+ valueT size;
+{
+#ifdef OBJ_AOUT
+#ifdef BFD_ASSEMBLER
+ /* For a.out, force the section size to be aligned. If we don't do
+ this, BFD will align it for us, but it will not write out the
+ final bytes of the section. This may be a bug in BFD, but it is
+ easier to fix it here since that is how the other a.out targets
+ work. */
+ int align;
+
+ align = bfd_get_section_alignment (stdoutput, segment);
+ size = ((size + (1 << align) - 1) & ((valueT) -1 << align));
+#endif
+#endif
+
+ return size;
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the 68k, it is relative to the address of the first extension
+ word. The difference between the addresses of the offset and the
+ first extension word is stored in fx_pcrel_adjust. */
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ int adjust;
+
+ /* Because fx_pcrel_adjust is a char, and may be unsigned, we store
+ -1 as 64. */
+ adjust = fixP->fx_pcrel_adjust;
+ if (adjust == 64)
+ adjust = -1;
+ return fixP->fx_where + fixP->fx_frag->fr_address - adjust;
+}
+
+#ifndef BFD_ASSEMBLER
+#ifdef OBJ_COFF
+
+/*ARGSUSED*/
+void
+tc_coff_symbol_emit_hook (ignore)
+ symbolS *ignore;
+{
+}
+
+int
+tc_coff_sizemachdep (frag)
+ fragS *frag;
+{
+ switch (frag->fr_subtype & 0x3)
+ {
+ case BYTE:
+ return 1;
+ case SHORT:
+ return 2;
+ case LONG:
+ return 4;
+ default:
+ abort ();
+ return 0;
+ }
+}
+
+#endif
+#endif
+
+/* end of tc-m68k.c */
diff --git a/gas/config/tc-m68k.h b/gas/config/tc-m68k.h
new file mode 100644
index 0000000..bc8308a
--- /dev/null
+++ b/gas/config/tc-m68k.h
@@ -0,0 +1,216 @@
+/* This file is tc-m68k.h
+ Copyright (C) 1987, 89, 90, 91, 92, 93, 94, 95, 96, 97, 1998
+ Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#define TC_M68K 1
+
+#ifdef ANSI_PROTOTYPES
+struct symbol;
+struct fix;
+#endif
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+#ifdef OBJ_AOUT
+#ifdef TE_SUN3
+#define TARGET_FORMAT "a.out-sunos-big"
+#endif
+#ifdef TE_NetBSD
+#define TARGET_FORMAT "a.out-m68k-netbsd"
+#endif
+#ifdef TE_LINUX
+#define TARGET_FORMAT "a.out-m68k-linux"
+#endif
+#ifndef TARGET_FORMAT
+#define TARGET_FORMAT "a.out-zero-big"
+#endif
+#endif
+
+#ifdef OBJ_ELF
+#define TARGET_FORMAT "elf32-m68k"
+#endif
+
+#ifdef TE_APOLLO
+#define COFF_MAGIC APOLLOM68KMAGIC
+#define COFF_AOUTHDR_MAGIC APOLLO_COFF_VERSION_NUMBER
+#undef OBJ_COFF_OMIT_OPTIONAL_HEADER
+#endif
+
+#ifdef TE_LYNX
+#define TARGET_FORMAT "coff-m68k-lynx"
+#endif
+#ifdef TE_AUX
+#define TARGET_FORMAT "coff-m68k-aux"
+#endif
+#ifdef TE_DELTA
+#define TARGET_FORMAT "coff-m68k-sysv"
+#endif
+
+#ifndef COFF_MAGIC
+#define COFF_MAGIC MC68MAGIC
+#endif
+#define BFD_ARCH bfd_arch_m68k /* for non-BFD_ASSEMBLER */
+#define TARGET_ARCH bfd_arch_m68k /* BFD_ASSEMBLER */
+#define COFF_FLAGS F_AR32W
+#define TC_COUNT_RELOC(x) ((x)->fx_addsy||(x)->fx_subsy)
+
+#define TC_COFF_FIX2RTYPE(fixP) tc_coff_fix2rtype(fixP)
+#define TC_COFF_SIZEMACHDEP(frag) tc_coff_sizemachdep(frag)
+extern int tc_coff_sizemachdep PARAMS ((struct frag *));
+#ifdef TE_SUN3
+/* This variable contains the value to write out at the beginning of
+ the a.out file. The 2<<16 means that this is a 68020 file instead
+ of an old-style 68000 file */
+
+#define DEFAULT_MAGIC_NUMBER_FOR_OBJECT_FILE (2<<16|OMAGIC); /* Magic byte for file header */
+#endif /* TE_SUN3 */
+
+#ifndef AOUT_MACHTYPE
+#define AOUT_MACHTYPE m68k_aout_machtype
+extern int m68k_aout_machtype;
+#endif
+
+#define tc_comment_chars m68k_comment_chars
+extern const char *m68k_comment_chars;
+
+#define tc_crawl_symbol_chain(a) {;} /* not used */
+#define tc_headers_hook(a) {;} /* not used */
+#define tc_aout_pre_write_hook(x) {;} /* not used */
+
+#define LISTING_WORD_SIZE 2 /* A word is 2 bytes */
+#define LISTING_LHS_WIDTH 2 /* One word on the first line */
+#define LISTING_LHS_WIDTH_SECOND 2 /* One word on the second line */
+#define LISTING_LHS_CONT_LINES 4/* And 4 lines max */
+#define LISTING_HEADER "68K GAS "
+
+#ifndef REGISTER_PREFIX
+#define REGISTER_PREFIX '%'
+#endif
+
+#if !defined (REGISTER_PREFIX_OPTIONAL)
+#if defined (M68KCOFF) || defined (OBJ_ELF)
+#ifndef BFD_ASSEMBLER
+#define LOCAL_LABEL(name) (name[0] == '.' \
+ && (name[1] == 'L' || name[1] == '.'))
+#endif /* ! BFD_ASSEMBLER */
+#define REGISTER_PREFIX_OPTIONAL 0
+#else /* ! (COFF || ELF) */
+#define REGISTER_PREFIX_OPTIONAL 1
+#endif /* ! (COFF || ELF) */
+#endif /* not def REGISTER_PREFIX and not def OPTIONAL_REGISTER_PREFIX */
+
+#ifdef TE_DELTA
+/* On the Delta, `%' can occur within a label name, but not as the
+ initial character. */
+#define LEX_PCT LEX_NAME
+/* On the Delta, `~' can start a label name, but is converted to '.'. */
+#define LEX_TILDE LEX_BEGIN_NAME
+#define tc_canonicalize_symbol_name(s) ((*(s) == '~' ? *(s) = '.' : '.'), s)
+/* On the Delta, dots are not required before pseudo-ops. */
+#define NO_PSEUDO_DOT
+#ifndef BFD_ASSEMBLER
+#undef LOCAL_LABEL
+#define LOCAL_LABEL(name) \
+ (name[0] == '.' || (name[0] == 'L' && name[1] == '%'))
+#endif
+#endif
+
+extern void m68k_mri_mode_change PARAMS ((int));
+#define MRI_MODE_CHANGE(i) m68k_mri_mode_change (i)
+
+extern int m68k_conditional_pseudoop PARAMS ((pseudo_typeS *));
+#define tc_conditional_pseudoop(pop) m68k_conditional_pseudoop (pop)
+
+extern void m68k_frob_label PARAMS ((struct symbol *));
+#define tc_frob_label(sym) m68k_frob_label (sym)
+
+extern void m68k_flush_pending_output PARAMS ((void));
+#define md_flush_pending_output() m68k_flush_pending_output ()
+
+extern void m68k_frob_symbol PARAMS ((struct symbol *));
+
+#ifdef BFD_ASSEMBLER
+
+#define tc_frob_symbol(sym,punt) \
+do \
+ { \
+ if (S_GET_SEGMENT (sym) == reg_section) \
+ punt = 1; \
+ m68k_frob_symbol (sym); \
+ } \
+while (0)
+
+#define NO_RELOC BFD_RELOC_NONE
+
+#ifdef OBJ_ELF
+
+/* This expression evaluates to false if the relocation is for a local object
+ for which we still want to do the relocation at runtime. True if we
+ are willing to perform this relocation while building the .o file. If
+ the reloc is against an externally visible symbol, then the assembler
+ should never do the relocation. */
+
+#define TC_RELOC_RTSYM_LOC_FIXUP(FIX) \
+ ((FIX)->fx_addsy == NULL \
+ || (! S_IS_EXTERNAL ((FIX)->fx_addsy) \
+ && ! S_IS_WEAK ((FIX)->fx_addsy) \
+ && S_IS_DEFINED ((FIX)->fx_addsy) \
+ && ! S_IS_COMMON ((FIX)->fx_addsy)))
+
+#define tc_fix_adjustable(X) tc_m68k_fix_adjustable(X)
+extern int tc_m68k_fix_adjustable PARAMS ((struct fix *));
+#endif
+
+#define TC_FORCE_RELOCATION(FIX) \
+ ((FIX)->fx_r_type == BFD_RELOC_VTABLE_INHERIT \
+ || (FIX)->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+
+#else /* ! BFD_ASSEMBLER */
+
+#define tc_frob_coff_symbol(sym) m68k_frob_symbol (sym)
+
+#define NO_RELOC 0
+
+#endif /* ! BFD_ASSEMBLER */
+
+#define DIFF_EXPR_OK
+
+extern void m68k_init_after_args PARAMS ((void));
+#define tc_init_after_args m68k_init_after_args
+
+extern int m68k_parse_long_option PARAMS ((char *));
+#define md_parse_long_option m68k_parse_long_option
+
+#define md_operand(x)
+
+#define TARGET_WORD_SIZE 32
+#define TARGET_ARCH bfd_arch_m68k
+
+extern struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+/* Copied from write.c */
+/* This was formerly called M68K_AIM_KLUDGE. */
+#define md_prepare_relax_scan(fragP, address, aim, this_state, this_type) \
+ if (aim==0 && this_state== 4) { /* hard encoded from tc-m68k.c */ \
+ aim=this_type->rlx_forward+1; /* Force relaxation into word mode */ \
+ }
+
+/* end of tc-m68k.h */
diff --git a/gas/config/tc-m88k.c b/gas/config/tc-m88k.c
new file mode 100644
index 0000000..931a496a
--- /dev/null
+++ b/gas/config/tc-m88k.c
@@ -0,0 +1,1452 @@
+/* m88k.c -- Assembler for the Motorola 88000
+ Contributed by Devon Bowen of Buffalo University
+ and Torbjorn Granlund of the Swedish Institute of Computer Science.
+ Copyright (C) 1989, 90, 91, 92, 93, 94, 95, 96, 1997
+ Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
+
+#include <ctype.h>
+#include "as.h"
+#include "subsegs.h"
+#include "m88k-opcode.h"
+
+struct field_val_assoc
+{
+ char *name;
+ unsigned val;
+};
+
+struct field_val_assoc cr_regs[] =
+{
+ {"PID", 0},
+ {"PSR", 1},
+ {"EPSR", 2},
+ {"SSBR", 3},
+ {"SXIP", 4},
+ {"SNIP", 5},
+ {"SFIP", 6},
+ {"VBR", 7},
+ {"DMT0", 8},
+ {"DMD0", 9},
+ {"DMA0", 10},
+ {"DMT1", 11},
+ {"DMD1", 12},
+ {"DMA1", 13},
+ {"DMT2", 14},
+ {"DMD2", 15},
+ {"DMA2", 16},
+ {"SR0", 17},
+ {"SR1", 18},
+ {"SR2", 19},
+ {"SR3", 20},
+
+ {NULL, 0},
+};
+
+struct field_val_assoc fcr_regs[] =
+{
+ {"FPECR", 0},
+ {"FPHS1", 1},
+ {"FPLS1", 2},
+ {"FPHS2", 3},
+ {"FPLS2", 4},
+ {"FPPT", 5},
+ {"FPRH", 6},
+ {"FPRL", 7},
+ {"FPIT", 8},
+
+ {"FPSR", 62},
+ {"FPCR", 63},
+
+ {NULL, 0},
+};
+
+struct field_val_assoc cmpslot[] =
+{
+/* Integer Floating point */
+ {"nc", 0},
+ {"cp", 1},
+ {"eq", 2},
+ {"ne", 3},
+ {"gt", 4},
+ {"le", 5},
+ {"lt", 6},
+ {"ge", 7},
+ {"hi", 8}, {"ou", 8},
+ {"ls", 9}, {"ib", 9},
+ {"lo", 10}, {"in", 10},
+ {"hs", 11}, {"ob", 11},
+ {"be", 12}, {"ue", 12},
+ {"nb", 13}, {"lg", 13},
+ {"he", 14}, {"ug", 14},
+ {"nh", 15}, {"ule", 15},
+ {"ul", 16},
+ {"uge", 17},
+
+ {NULL, 0},
+};
+
+struct field_val_assoc cndmsk[] =
+{
+ {"gt0", 1},
+ {"eq0", 2},
+ {"ge0", 3},
+ {"lt0", 12},
+ {"ne0", 13},
+ {"le0", 14},
+
+ {NULL, 0},
+};
+
+struct m88k_insn
+{
+ unsigned long opcode;
+ expressionS exp;
+ enum reloc_type reloc;
+};
+
+static char *get_bf PARAMS ((char *param, unsigned *valp));
+static char *get_cmp PARAMS ((char *param, unsigned *valp));
+static char *get_cnd PARAMS ((char *param, unsigned *valp));
+static char *get_cr PARAMS ((char *param, unsigned *regnop));
+static char *get_fcr PARAMS ((char *param, unsigned *regnop));
+static char *get_imm16 PARAMS ((char *param, struct m88k_insn *insn));
+static char *get_o6 PARAMS ((char *param, unsigned *valp));
+static char *get_reg PARAMS ((char *param, unsigned *regnop, int reg_prefix));
+static char *get_vec9 PARAMS ((char *param, unsigned *valp));
+static char *getval PARAMS ((char *param, unsigned int *valp));
+
+static char *get_pcr PARAMS ((char *param, struct m88k_insn *insn,
+ enum reloc_type reloc));
+
+static int calcop PARAMS ((struct m88k_opcode *format,
+ char *param, struct m88k_insn *insn));
+
+
+extern char *myname;
+static struct hash_control *op_hash = NULL;
+
+/* These bits should be turned off in the first address of every segment */
+int md_seg_align = 7;
+
+/* These chars start a comment anywhere in a source file (except inside
+ another comment */
+const char comment_chars[] = ";";
+
+/* These chars only start a comment at the beginning of a line. */
+const char line_comment_chars[] = "#";
+
+const char line_separator_chars[] = "";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* as in 0f123.456 */
+/* or 0H1.234E-12 (see exp chars above) */
+const char FLT_CHARS[] = "dDfF";
+
+extern void float_cons (), cons (), s_globl (), s_space (),
+ s_set (), s_lcomm ();
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"align", s_align_bytes, 4},
+ {"def", s_set, 0},
+ {"dfloat", float_cons, 'd'},
+ {"ffloat", float_cons, 'f'},
+ {"global", s_globl, 0},
+ {"half", cons, 2},
+ {"bss", s_lcomm, 1},
+ {"string", stringer, 0},
+ {"word", cons, 4},
+ /* Force set to be treated as an instruction. */
+ {"set", NULL, 0},
+ {".set", s_set, 0},
+ {0}
+};
+
+void
+md_begin ()
+{
+ const char *retval = NULL;
+ unsigned int i = 0;
+
+ /* initialize hash table */
+
+ op_hash = hash_new ();
+
+ /* loop until you see the end of the list */
+
+ while (*m88k_opcodes[i].name)
+ {
+ char *name = m88k_opcodes[i].name;
+
+ /* hash each mnemonic and record its position */
+
+ retval = hash_insert (op_hash, name, &m88k_opcodes[i]);
+
+ if (retval != NULL)
+ as_fatal (_("Can't hash instruction '%s':%s"),
+ m88k_opcodes[i].name, retval);
+
+ /* skip to next unique mnemonic or end of list */
+
+ for (i++; !strcmp (m88k_opcodes[i].name, name); i++)
+ ;
+ }
+}
+
+CONST char *md_shortopts = "";
+struct option md_longopts[] = {
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ return 0;
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+}
+
+void
+md_assemble (op)
+ char *op;
+{
+ char *param, *thisfrag;
+ char c;
+ struct m88k_opcode *format;
+ struct m88k_insn insn;
+
+ assert (op);
+
+ /* skip over instruction to find parameters */
+
+ for (param = op; *param != 0 && !isspace (*param); param++)
+ ;
+ c = *param;
+ *param++ = '\0';
+
+ /* try to find the instruction in the hash table */
+
+ if ((format = (struct m88k_opcode *) hash_find (op_hash, op)) == NULL)
+ {
+ as_bad (_("Invalid mnemonic '%s'"), op);
+ return;
+ }
+
+ /* try parsing this instruction into insn */
+
+ insn.exp.X_add_symbol = 0;
+ insn.exp.X_op_symbol = 0;
+ insn.exp.X_add_number = 0;
+ insn.exp.X_op = O_illegal;
+ insn.reloc = NO_RELOC;
+
+ while (!calcop (format, param, &insn))
+ {
+ /* if it doesn't parse try the next instruction */
+
+ if (!strcmp (format[0].name, format[1].name))
+ format++;
+ else
+ {
+ as_fatal (_("Parameter syntax error"));
+ return;
+ }
+ }
+
+ /* grow the current frag and plop in the opcode */
+
+ thisfrag = frag_more (4);
+ md_number_to_chars (thisfrag, insn.opcode, 4);
+
+ /* if this instruction requires labels mark it for later */
+
+ switch (insn.reloc)
+ {
+ case NO_RELOC:
+ break;
+
+ case RELOC_LO16:
+ case RELOC_HI16:
+ fix_new_exp (frag_now,
+ thisfrag - frag_now->fr_literal + 2,
+ 2,
+ &insn.exp,
+ 0,
+ insn.reloc);
+ break;
+
+ case RELOC_IW16:
+ fix_new_exp (frag_now,
+ thisfrag - frag_now->fr_literal,
+ 4,
+ &insn.exp,
+ 0,
+ insn.reloc);
+ break;
+
+ case RELOC_PC16:
+ fix_new_exp (frag_now,
+ thisfrag - frag_now->fr_literal + 2,
+ 2,
+ &insn.exp,
+ 1,
+ insn.reloc);
+ break;
+
+ case RELOC_PC26:
+ fix_new_exp (frag_now,
+ thisfrag - frag_now->fr_literal,
+ 4,
+ &insn.exp,
+ 1,
+ insn.reloc);
+ break;
+
+ default:
+ as_fatal (_("Unknown relocation type"));
+ break;
+ }
+}
+
+static int
+calcop (format, param, insn)
+ struct m88k_opcode *format;
+ char *param;
+ struct m88k_insn *insn;
+{
+ char *fmt = format->op_spec;
+ int f;
+ unsigned val;
+ unsigned opcode;
+ int reg_prefix = 'r';
+
+ insn->opcode = format->opcode;
+ opcode = 0;
+
+ for (;;)
+ {
+ if (param == 0)
+ return 0;
+ f = *fmt++;
+ switch (f)
+ {
+ case 0:
+ insn->opcode |= opcode;
+ return (*param == 0 || *param == '\n');
+
+ default:
+ if (f != *param++)
+ return 0;
+ break;
+
+ case 'd':
+ param = get_reg (param, &val, reg_prefix);
+ reg_prefix = 'r';
+ opcode |= val << 21;
+ break;
+
+ case 'o':
+ param = get_o6 (param, &val);
+ opcode |= ((val >> 2) << 7);
+ break;
+
+ case 'x':
+ reg_prefix = 'x';
+ break;
+
+ case '1':
+ param = get_reg (param, &val, reg_prefix);
+ reg_prefix = 'r';
+ opcode |= val << 16;
+ break;
+
+ case '2':
+ param = get_reg (param, &val, reg_prefix);
+ reg_prefix = 'r';
+ opcode |= val;
+ break;
+
+ case '3':
+ param = get_reg (param, &val, 'r');
+ opcode |= (val << 16) | val;
+ break;
+
+ case 'I':
+ param = get_imm16 (param, insn);
+ break;
+
+ case 'b':
+ param = get_bf (param, &val);
+ opcode |= val;
+ break;
+
+ case 'p':
+ param = get_pcr (param, insn, RELOC_PC16);
+ break;
+
+ case 'P':
+ param = get_pcr (param, insn, RELOC_PC26);
+ break;
+
+ case 'B':
+ param = get_cmp (param, &val);
+ opcode |= val;
+ break;
+
+ case 'M':
+ param = get_cnd (param, &val);
+ opcode |= val;
+ break;
+
+ case 'c':
+ param = get_cr (param, &val);
+ opcode |= val << 5;
+ break;
+
+ case 'f':
+ param = get_fcr (param, &val);
+ opcode |= val << 5;
+ break;
+
+ case 'V':
+ param = get_vec9 (param, &val);
+ opcode |= val;
+ break;
+
+ case '?':
+ /* Having this here repeats the warning somtimes.
+ But can't we stand that? */
+ as_warn (_("Use of obsolete instruction"));
+ break;
+ }
+ }
+}
+
+static char *
+match_name (param, assoc_tab, valp)
+ char *param;
+ struct field_val_assoc *assoc_tab;
+ unsigned *valp;
+{
+ int i;
+ char *name;
+ int name_len;
+
+ for (i = 0;; i++)
+ {
+ name = assoc_tab[i].name;
+ if (name == NULL)
+ return NULL;
+ name_len = strlen (name);
+ if (!strncmp (param, name, name_len))
+ {
+ *valp = assoc_tab[i].val;
+ return param + name_len;
+ }
+ }
+}
+
+static char *
+get_reg (param, regnop, reg_prefix)
+ char *param;
+ unsigned *regnop;
+ int reg_prefix;
+{
+ unsigned c;
+ unsigned regno;
+
+ c = *param++;
+ if (c == reg_prefix)
+ {
+ regno = *param++ - '0';
+ if (regno < 10)
+ {
+ if (regno == 0)
+ {
+ *regnop = 0;
+ return param;
+ }
+ c = *param - '0';
+ if (c < 10)
+ {
+ regno = regno * 10 + c;
+ if (c < 32)
+ {
+ *regnop = regno;
+ return param + 1;
+ }
+ }
+ else
+ {
+ *regnop = regno;
+ return param;
+ }
+ }
+ return NULL;
+ }
+ else if (c == 's' && param[0] == 'p')
+ {
+ *regnop = 31;
+ return param + 1;
+ }
+
+ return 0;
+}
+
+static char *
+get_imm16 (param, insn)
+ char *param;
+ struct m88k_insn *insn;
+{
+ enum reloc_type reloc = NO_RELOC;
+ unsigned int val;
+ char *save_ptr;
+
+ if (!strncmp (param, "hi16", 4) && !isalnum (param[4]))
+ {
+ reloc = RELOC_HI16;
+ param += 4;
+ }
+ else if (!strncmp (param, "lo16", 4) && !isalnum (param[4]))
+ {
+ reloc = RELOC_LO16;
+ param += 4;
+ }
+ else if (!strncmp (param, "iw16", 4) && !isalnum (param[4]))
+ {
+ reloc = RELOC_IW16;
+ param += 4;
+ }
+
+ save_ptr = input_line_pointer;
+ input_line_pointer = param;
+ expression (&insn->exp);
+ param = input_line_pointer;
+ input_line_pointer = save_ptr;
+
+ val = insn->exp.X_add_number;
+
+ if (insn->exp.X_op == O_constant)
+ {
+ /* Insert the value now, and reset reloc to NO_RELOC. */
+ if (reloc == NO_RELOC)
+ {
+ /* Warn about too big expressions if not surrounded by xx16. */
+ if (val > 0xffff)
+ as_warn (_("Expression truncated to 16 bits"));
+ }
+
+ if (reloc == RELOC_HI16)
+ val >>= 16;
+
+ insn->opcode |= val & 0xffff;
+ reloc = NO_RELOC;
+ }
+ else if (reloc == NO_RELOC)
+ /* We accept a symbol even without lo16, hi16, etc, and assume
+ lo16 was intended. */
+ reloc = RELOC_LO16;
+
+ insn->reloc = reloc;
+
+ return param;
+}
+
+static char *
+get_pcr (param, insn, reloc)
+ char *param;
+ struct m88k_insn *insn;
+ enum reloc_type reloc;
+{
+ char *saveptr, *saveparam;
+
+ saveptr = input_line_pointer;
+ input_line_pointer = param;
+
+ expression (&insn->exp);
+
+ saveparam = input_line_pointer;
+ input_line_pointer = saveptr;
+
+ /* Botch: We should relocate now if O_constant. */
+ insn->reloc = reloc;
+
+ return saveparam;
+}
+
+static char *
+get_cmp (param, valp)
+ char *param;
+ unsigned *valp;
+{
+ unsigned int val;
+ char *save_ptr;
+
+ save_ptr = param;
+
+ param = match_name (param, cmpslot, valp);
+ val = *valp;
+
+ if (param == NULL)
+ {
+ param = save_ptr;
+
+ save_ptr = input_line_pointer;
+ input_line_pointer = param;
+ val = get_absolute_expression ();
+ param = input_line_pointer;
+ input_line_pointer = save_ptr;
+
+ if (val >= 32)
+ {
+ as_warn (_("Expression truncated to 5 bits"));
+ val %= 32;
+ }
+ }
+
+ *valp = val << 21;
+ return param;
+}
+
+static char *
+get_cnd (param, valp)
+ char *param;
+ unsigned *valp;
+{
+ unsigned int val;
+
+ if (isdigit (*param))
+ {
+ param = getval (param, &val);
+
+ if (val >= 32)
+ {
+ as_warn (_("Expression truncated to 5 bits"));
+ val %= 32;
+ }
+ }
+ else
+ {
+ if (isupper (*param))
+ *param = tolower (*param);
+
+ if (isupper (param[1]))
+ param[1] = tolower (param[1]);
+
+ param = match_name (param, cndmsk, valp);
+
+ if (param == NULL)
+ return NULL;
+
+ val = *valp;
+ }
+
+ *valp = val << 21;
+ return param;
+}
+
+static char *
+get_bf2 (param, bc)
+ char *param;
+ int bc;
+{
+ int depth = 0;
+ int c;
+
+ for (;;)
+ {
+ c = *param;
+ if (c == 0)
+ return param;
+ else if (c == '(')
+ depth++;
+ else if (c == ')')
+ depth--;
+ else if (c == bc && depth <= 0)
+ return param;
+ param++;
+ }
+}
+
+static char *
+get_bf_offset_expression (param, offsetp)
+ char *param;
+ unsigned *offsetp;
+{
+ unsigned offset;
+
+ if (isalpha (param[0]))
+ {
+ if (isupper (param[0]))
+ param[0] = tolower (param[0]);
+ if (isupper (param[1]))
+ param[1] = tolower (param[1]);
+
+ param = match_name (param, cmpslot, offsetp);
+
+ return param;
+ }
+ else
+ {
+ input_line_pointer = param;
+ offset = get_absolute_expression ();
+ param = input_line_pointer;
+ }
+
+ *offsetp = offset;
+ return param;
+}
+
+static char *
+get_bf (param, valp)
+ char *param;
+ unsigned *valp;
+{
+ unsigned offset = 0;
+ unsigned width = 0;
+ char *xp;
+ char *save_ptr;
+
+ xp = get_bf2 (param, '<');
+
+ save_ptr = input_line_pointer;
+ input_line_pointer = param;
+ if (*xp == 0)
+ {
+ /* We did not find '<'. We have an offset (width implicitly 32). */
+ param = get_bf_offset_expression (param, &offset);
+ input_line_pointer = save_ptr;
+ if (param == NULL)
+ return NULL;
+ }
+ else
+ {
+ *xp++ = 0; /* Overwrite the '<' */
+ param = get_bf2 (xp, '>');
+ if (*param == 0)
+ return NULL;
+ *param++ = 0; /* Overwrite the '>' */
+
+ width = get_absolute_expression ();
+ xp = get_bf_offset_expression (xp, &offset);
+ input_line_pointer = save_ptr;
+
+ if (xp + 1 != param)
+ return NULL;
+ }
+
+ *valp = ((width % 32) << 5) | (offset % 32);
+
+ return param;
+}
+
+static char *
+get_cr (param, regnop)
+ char *param;
+ unsigned *regnop;
+{
+ unsigned regno;
+ unsigned c;
+
+ if (!strncmp (param, "cr", 2))
+ {
+ param += 2;
+
+ regno = *param++ - '0';
+ if (regno < 10)
+ {
+ if (regno == 0)
+ {
+ *regnop = 0;
+ return param;
+ }
+ c = *param - '0';
+ if (c < 10)
+ {
+ regno = regno * 10 + c;
+ if (c < 64)
+ {
+ *regnop = regno;
+ return param + 1;
+ }
+ }
+ else
+ {
+ *regnop = regno;
+ return param;
+ }
+ }
+ return NULL;
+ }
+
+ param = match_name (param, cr_regs, regnop);
+
+ return param;
+}
+
+static char *
+get_fcr (param, regnop)
+ char *param;
+ unsigned *regnop;
+{
+ unsigned regno;
+ unsigned c;
+
+ if (!strncmp (param, "fcr", 3))
+ {
+ param += 3;
+
+ regno = *param++ - '0';
+ if (regno < 10)
+ {
+ if (regno == 0)
+ {
+ *regnop = 0;
+ return param;
+ }
+ c = *param - '0';
+ if (c < 10)
+ {
+ regno = regno * 10 + c;
+ if (c < 64)
+ {
+ *regnop = regno;
+ return param + 1;
+ }
+ }
+ else
+ {
+ *regnop = regno;
+ return param;
+ }
+ }
+ return NULL;
+ }
+
+ param = match_name (param, fcr_regs, regnop);
+
+ return param;
+}
+
+static char *
+get_vec9 (param, valp)
+ char *param;
+ unsigned *valp;
+{
+ unsigned val;
+ char *save_ptr;
+
+ save_ptr = input_line_pointer;
+ input_line_pointer = param;
+ val = get_absolute_expression ();
+ param = input_line_pointer;
+ input_line_pointer = save_ptr;
+
+ if (val >= 1 << 9)
+ as_warn (_("Expression truncated to 9 bits"));
+
+ *valp = val % (1 << 9);
+
+ return param;
+}
+
+static char *
+get_o6 (param, valp)
+ char *param;
+ unsigned *valp;
+{
+ unsigned val;
+ char *save_ptr;
+
+ save_ptr = input_line_pointer;
+ input_line_pointer = param;
+ val = get_absolute_expression ();
+ param = input_line_pointer;
+ input_line_pointer = save_ptr;
+
+ if (val & 0x3)
+ as_warn (_("Removed lower 2 bits of expression"));
+
+ *valp = val;
+
+ return(param);
+}
+
+#define hexval(z) \
+ (isdigit (z) ? (z) - '0' : \
+ islower (z) ? (z) - 'a' + 10 : \
+ isupper (z) ? (z) - 'A' + 10 : -1)
+
+static char *
+getval (param, valp)
+ char *param;
+ unsigned int *valp;
+{
+ unsigned int val = 0;
+ unsigned int c;
+
+ c = *param++;
+ if (c == '0')
+ {
+ c = *param++;
+ if (c == 'x' || c == 'X')
+ {
+ c = *param++;
+ c = hexval (c);
+ while (c < 16)
+ {
+ val = val * 16 + c;
+ c = *param++;
+ c = hexval (c);
+ }
+ }
+ else
+ {
+ c -= '0';
+ while (c < 8)
+ {
+ val = val * 8 + c;
+ c = *param++ - '0';
+ }
+ }
+ }
+ else
+ {
+ c -= '0';
+ while (c < 10)
+ {
+ val = val * 10 + c;
+ c = *param++ - '0';
+ }
+ }
+
+ *valp = val;
+ return param - 1;
+}
+
+void
+md_number_to_chars (buf, val, nbytes)
+ char *buf;
+ valueT val;
+ int nbytes;
+{
+ number_to_chars_bigendian (buf, val, nbytes);
+}
+
+#if 0
+
+/* This routine is never called. What is it for?
+ Ian Taylor, Cygnus Support 13 Jul 1993 */
+
+void
+md_number_to_imm (buf, val, nbytes, fixP, seg_type)
+ unsigned char *buf;
+ unsigned int val;
+ int nbytes;
+ fixS *fixP;
+ int seg_type;
+{
+ if (seg_type != N_TEXT || fixP->fx_r_type == NO_RELOC)
+ {
+ switch (nbytes)
+ {
+ case 4:
+ *buf++ = val >> 24;
+ *buf++ = val >> 16;
+ case 2:
+ *buf++ = val >> 8;
+ case 1:
+ *buf = val;
+ break;
+
+ default:
+ abort ();
+ }
+ return;
+ }
+
+ switch (fixP->fx_r_type)
+ {
+ case RELOC_IW16:
+ buf[2] = val >> 8;
+ buf[3] = val;
+ break;
+
+ case RELOC_LO16:
+ buf[0] = val >> 8;
+ buf[1] = val;
+ break;
+
+ case RELOC_HI16:
+ buf[0] = val >> 24;
+ buf[1] = val >> 16;
+ break;
+
+ case RELOC_PC16:
+ val += 4;
+ buf[0] = val >> 10;
+ buf[1] = val >> 2;
+ break;
+
+ case RELOC_PC26:
+ val += 4;
+ buf[0] |= (val >> 26) & 0x03;
+ buf[1] = val >> 18;
+ buf[2] = val >> 10;
+ buf[3] = val >> 2;
+ break;
+
+ case RELOC_32:
+ buf[0] = val >> 24;
+ buf[1] = val >> 16;
+ buf[2] = val >> 8;
+ buf[3] = val;
+ break;
+
+ default:
+ as_fatal (_("Bad relocation type"));
+ break;
+ }
+}
+
+#endif /* 0 */
+
+void
+md_number_to_disp (buf, val, nbytes)
+ char *buf;
+ int val;
+ int nbytes;
+{
+ as_fatal (_("md_number_to_disp not defined"));
+ md_number_to_chars (buf, val, nbytes);
+}
+
+void
+md_number_to_field (buf, val, nbytes)
+ char *buf;
+ int val;
+ int nbytes;
+{
+ as_fatal (_("md_number_to_field not defined"));
+ md_number_to_chars (buf, val, nbytes);
+}
+
+#define MAX_LITTLENUMS 6
+
+/* Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP . An error message is returned, or NULL on OK.
+ */
+char *
+md_atof (type, litP, sizeP)
+ char type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("Bad call to MD_ATOF()");
+ }
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+
+ *sizeP = prec * sizeof (LITTLENUM_TYPE);
+ for (wordP = words; prec--;)
+ {
+ md_number_to_chars (litP, (long) (*wordP++), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ return 0;
+}
+
+int md_short_jump_size = 4;
+
+void
+md_create_short_jump (ptr, from_addr, to_addr, frag, to_symbol)
+ char *ptr;
+ addressT from_addr, to_addr;
+ fragS *frag;
+ symbolS *to_symbol;
+{
+ ptr[0] = (char) 0xc0;
+ ptr[1] = 0x00;
+ ptr[2] = 0x00;
+ ptr[3] = 0x00;
+ fix_new (frag,
+ ptr - frag->fr_literal,
+ 4,
+ to_symbol,
+ (offsetT) 0,
+ 0,
+ RELOC_PC26); /* Botch: Shouldn't this be RELOC_PC16? */
+}
+
+int md_long_jump_size = 4;
+
+void
+md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol)
+ char *ptr;
+ addressT from_addr, to_addr;
+ fragS *frag;
+ symbolS *to_symbol;
+{
+ ptr[0] = (char) 0xc0;
+ ptr[1] = 0x00;
+ ptr[2] = 0x00;
+ ptr[3] = 0x00;
+ fix_new (frag,
+ ptr - frag->fr_literal,
+ 4,
+ to_symbol,
+ (offsetT) 0,
+ 0,
+ RELOC_PC26);
+}
+
+int
+md_estimate_size_before_relax (fragP, segment_type)
+ fragS *fragP;
+ segT segment_type;
+{
+ as_fatal (_("Relaxation should never occur"));
+ return (-1);
+}
+
+#if 0
+
+/* As far as I can tell, this routine is never called. What is it
+ doing here?
+ Ian Taylor, Cygnus Support 13 Jul 1993 */
+
+
+/*
+ * Risc relocations are completely different, so it needs
+ * this machine dependent routine to emit them.
+ */
+void
+emit_relocations (fixP, segment_address_in_file)
+ fixS *fixP;
+ relax_addressT segment_address_in_file;
+{
+ struct reloc_info_m88k ri;
+ symbolS *symbolP;
+ extern char *next_object_file_charP;
+
+ bzero ((char *) &ri, sizeof (ri));
+ for (; fixP; fixP = fixP->fx_next)
+ {
+ if (fixP->fx_r_type >= NO_RELOC)
+ {
+ fprintf (stderr, "fixP->fx_r_type = %d\n", fixP->fx_r_type);
+ abort ();
+ }
+
+ if ((symbolP = fixP->fx_addsy) != NULL)
+ {
+ ri.r_address = fixP->fx_frag->fr_address +
+ fixP->fx_where - segment_address_in_file;
+ if ((symbolP->sy_type & N_TYPE) == N_UNDF)
+ {
+ ri.r_extern = 1;
+ ri.r_symbolnum = symbolP->sy_number;
+ }
+ else
+ {
+ ri.r_extern = 0;
+ ri.r_symbolnum = symbolP->sy_type & N_TYPE;
+ }
+ if (symbolP && symbolP->sy_frag)
+ {
+ ri.r_addend = symbolP->sy_frag->fr_address;
+ }
+ ri.r_type = fixP->fx_r_type;
+ if (fixP->fx_pcrel)
+ {
+ ri.r_addend -= ri.r_address;
+ }
+ else
+ {
+ ri.r_addend = fixP->fx_addnumber;
+ }
+
+ append (&next_object_file_charP, (char *) &ri, sizeof (ri));
+ }
+ }
+}
+
+#endif /* 0 */
+
+#if 0
+
+/* This routine can be subsumed by s_lcomm in read.c.
+ Ian Taylor, Cygnus Support 13 Jul 1993 */
+
+
+static void
+s_bss ()
+{
+ char *name;
+ char c;
+ char *p;
+ int temp, bss_align;
+ symbolS *symbolP;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_warn (_("Expected comma after name"));
+ ignore_rest_of_line ();
+ return;
+ }
+ input_line_pointer++;
+ if ((temp = get_absolute_expression ()) < 0)
+ {
+ as_warn (_("BSS length (%d.) <0! Ignored."), temp);
+ ignore_rest_of_line ();
+ return;
+ }
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ bss_align = get_absolute_expression ();
+ }
+ else
+ bss_align = 0;
+
+ if (!S_IS_DEFINED(symbolP)
+ || S_GET_SEGMENT(symbolP) == SEG_BSS)
+ {
+ if (! need_pass_2)
+ {
+ char *p;
+ segT current_seg = now_seg;
+ subsegT current_subseg = now_subseg;
+
+ subseg_set (SEG_BSS, 1); /* switch to bss */
+
+ if (bss_align)
+ frag_align (bss_align, 0, 0);
+
+ /* detach from old frag */
+ if (symbolP->sy_type == N_BSS && symbolP->sy_frag != NULL)
+ symbolP->sy_frag->fr_symbol = NULL;
+
+ symbolP->sy_frag = frag_now;
+ p = frag_var (rs_org, 1, 1, (relax_substateT)0, symbolP,
+ (offsetT) temp, (char *)0);
+ *p = 0;
+ S_SET_SEGMENT (symbolP, SEG_BSS);
+
+ subseg_set (current_seg, current_subseg);
+ }
+ }
+ else
+ {
+ as_warn (_("Ignoring attempt to re-define symbol %s."), name);
+ }
+
+ while (!is_end_of_line[*input_line_pointer])
+ {
+ input_line_pointer++;
+ }
+}
+
+#endif /* 0 */
+
+#ifdef M88KCOFF
+
+/* These functions are needed if we are linking with obj-coffbfd.c.
+ That file may be replaced by a more BFD oriented version at some
+ point. If that happens, these functions should be rexamined.
+
+ Ian Lance Taylor, Cygnus Support, 13 July 1993. */
+
+/* Given a fixS structure (created by a call to fix_new, above),
+ return the BFD relocation type to use for it. */
+
+short
+tc_coff_fix2rtype (fixp)
+ fixS *fixp;
+{
+ switch (fixp->fx_r_type)
+ {
+ case RELOC_LO16:
+ return R_LVRT16;
+ case RELOC_HI16:
+ return R_HVRT16;
+ case RELOC_PC16:
+ return R_PCR16L;
+ case RELOC_PC26:
+ return R_PCR26L;
+ case RELOC_32:
+ return R_VRT32;
+ case RELOC_IW16:
+ return R_VRT16;
+ default:
+ abort ();
+ }
+}
+
+/* Apply a fixS to the object file. Since COFF does not use addends
+ in relocs, the addend is actually stored directly in the object
+ file itself. */
+
+void
+md_apply_fix (fixp, val)
+ fixS *fixp;
+ long val;
+{
+ char *buf;
+
+ buf = fixp->fx_frag->fr_literal + fixp->fx_where;
+ fixp->fx_offset = 0;
+
+ switch (fixp->fx_r_type)
+ {
+ case RELOC_IW16:
+ fixp->fx_offset = val >> 16;
+ buf[2] = val >> 8;
+ buf[3] = val;
+ break;
+
+ case RELOC_LO16:
+ fixp->fx_offset = val >> 16;
+ buf[0] = val >> 8;
+ buf[1] = val;
+ break;
+
+ case RELOC_HI16:
+ fixp->fx_offset = val >> 16;
+ buf[0] = val >> 8;
+ buf[1] = val;
+ break;
+
+ case RELOC_PC16:
+ buf[0] = val >> 10;
+ buf[1] = val >> 2;
+ break;
+
+ case RELOC_PC26:
+ buf[0] |= (val >> 26) & 0x03;
+ buf[1] = val >> 18;
+ buf[2] = val >> 10;
+ buf[3] = val >> 2;
+ break;
+
+ case RELOC_32:
+ buf[0] = val >> 24;
+ buf[1] = val >> 16;
+ buf[2] = val >> 8;
+ buf[3] = val;
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+/* Where a PC relative offset is calculated from. On the m88k they
+ are calculated from just after the instruction. */
+
+long
+md_pcrel_from (fixp)
+ fixS *fixp;
+{
+ switch (fixp->fx_r_type)
+ {
+ case RELOC_PC16:
+ return fixp->fx_frag->fr_address + fixp->fx_where - 2;
+ case RELOC_PC26:
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+ default:
+ abort ();
+ }
+ /*NOTREACHED*/
+}
+
+/* When we align the .init section, insert the correct NOP pattern. */
+
+int
+m88k_do_align (n, fill, max, len)
+ int n;
+ const char *fill;
+ int len;
+ int max;
+{
+ if (fill == NULL
+ && strcmp (obj_segment_name (now_seg), ".init") == 0)
+ {
+ static const unsigned char nop_pattern[] = { 0xf4, 0x00, 0x58, 0x00 };
+ frag_align_pattern (n, nop_pattern, sizeof (nop_pattern), max);
+ return 1;
+ }
+ return 0;
+}
+
+#endif /* M88KCOFF */
diff --git a/gas/config/tc-m88k.h b/gas/config/tc-m88k.h
new file mode 100644
index 0000000..426b697
--- /dev/null
+++ b/gas/config/tc-m88k.h
@@ -0,0 +1,108 @@
+/* m88k.h -- Assembler for the Motorola 88000
+ Contributed by Devon Bowen of Buffalo University
+ and Torbjorn Granlund of the Swedish Institute of Computer Science.
+ Copyright (C) 1989, 90, 91, 92, 93, 94, 95, 96, 1997
+ Free Software Foundation, Inc.
+
+This file is part of GAS, the GNU Assembler.
+
+GAS is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GAS is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GAS; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
+
+#define TC_M88K
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+#ifdef M88KCOFF
+#define COFF_MAGIC MC88OMAGIC
+#define BFD_ARCH bfd_arch_m88k
+#define COFF_FLAGS F_AR32W
+#endif
+
+#define NEED_FX_R_TYPE
+#define TC_KEEP_FX_OFFSET
+#define TC_CONS_RELOC RELOC_32
+
+/* different type of relocation available in the m88k */
+
+enum reloc_type
+{
+ RELOC_LO16, /* lo16(sym) */
+ RELOC_HI16, /* hi16(sym) */
+ RELOC_PC16, /* bb0, bb1, bcnd */
+ RELOC_PC26, /* br, bsr */
+ RELOC_32, /* jump tables, etc */
+ RELOC_IW16, /* global access through linker regs 28 */
+ NO_RELOC
+};
+
+struct reloc_info_m88k
+{
+ unsigned long int r_address;
+ unsigned int r_symbolnum:24;
+ unsigned int r_extern:1;
+ unsigned int r_pad:3;
+ enum reloc_type r_type:4;
+ long int r_addend;
+};
+
+#define relocation_info reloc_info_m88k
+
+/* The m88k uses '@' to start local labels. */
+#define LEX_AT (LEX_BEGIN_NAME | LEX_NAME)
+
+#ifndef BFD_ASSEMBLER
+#define LOCAL_LABEL(name) \
+ ((name[0] =='@' && (name [1] == 'L' || name [1] == '.')) \
+ || (name[0] == 'L' && name[1] == '0' && name[2] == '\001'))
+#endif
+
+/* The m88k uses pseudo-ops with no leading period. */
+#define NO_PSEUDO_DOT
+
+/* Don't warn on word overflow; it happens on %hi relocs. */
+#undef WARN_SIGNED_OVERFLOW_WORD
+
+#define md_convert_frag(b,s,f) {as_fatal (_("m88k convert_frag\n"));}
+
+/* We don't need to do anything special for undefined symbols. */
+#define md_undefined_symbol(s) 0
+
+/* We have no special operand handling. */
+#define md_operand(e)
+
+#ifdef M88KCOFF
+
+/* Whether a reloc should be output. */
+#define TC_COUNT_RELOC(fixp) ((fixp)->fx_addsy != NULL)
+
+/* Get the BFD reloc type to use for a gas fixS structure. */
+#define TC_COFF_FIX2RTYPE(fixp) tc_coff_fix2rtype (fixp)
+
+/* No special hook needed for symbols. */
+#define tc_coff_symbol_emit_hook(s)
+
+/* Align sections to a four byte boundary. */
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+#define SUB_SEGMENT_ALIGN(SEG) max (section_alignment[(int) (SEG)], 4)
+
+/* We use a special alignment function to insert the correct nop
+ pattern in .init. */
+extern int m88k_do_align PARAMS ((int, const char *, int, int));
+#define md_do_align(n,fill,len,max,l) if (m88k_do_align(n,fill,max,len)) goto l
+
+#endif /* M88KCOFF */
diff --git a/gas/config/tc-mcore.c b/gas/config/tc-mcore.c
new file mode 100644
index 0000000..7e78435
--- /dev/null
+++ b/gas/config/tc-mcore.c
@@ -0,0 +1,2183 @@
+/* tc-mcore.c -- Assemble code for M*Core
+
+ Copyright (C) 1993,1994, 1999 Free Software Foundation.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "as.h"
+#include "bfd.h"
+#include "subsegs.h"
+#define DEFINE_TABLE
+#include "../opcodes/mcore-opc.h"
+#include <ctype.h>
+#include <string.h>
+
+#ifdef OBJ_ELF
+#include "elf/mcore.h"
+#endif
+
+#ifndef streq
+#define streq(a,b) (strcmp (a, b) == 0)
+#endif
+
+/* Forward declarations for dumb compilers. */
+static void mcore_s_literals PARAMS ((int));
+static void mcore_cons PARAMS ((int));
+static void mcore_float_cons PARAMS ((int));
+static void mcore_stringer PARAMS ((int));
+static int log2 PARAMS ((unsigned int));
+static char * parse_reg PARAMS ((char *, unsigned *));
+static char * parse_creg PARAMS ((char *, unsigned *));
+static char * parse_exp PARAMS ((char *, expressionS *));
+static void make_name PARAMS ((char *, char *, int));
+static int enter_literal PARAMS ((expressionS *, int));
+static char * parse_rt PARAMS ((char *, char **, int, expressionS *));
+static char * parse_imm PARAMS ((char *, unsigned *, unsigned, unsigned));
+static char * parse_mem PARAMS ((char *, unsigned *, unsigned *, unsigned));
+static void dump_literals PARAMS ((int));
+static void check_literals PARAMS ((int, int));
+static void mcore_s_text PARAMS ((int));
+static void mcore_s_data PARAMS ((int));
+#ifdef OBJ_ELF
+static void mcore_s_section PARAMS ((int));
+#endif
+
+/* Several places in this file insert raw instructions into the
+ object. They should use MCORE_INST_XXX macros to get the opcodes
+ and then use these two macros to crack the MCORE_INST value into
+ the appropriate byte values. */
+#define INST_BYTE0(x) (((x) >> 8) & 0xFF)
+#define INST_BYTE1(x) ((x) & 0xFF)
+
+const char comment_chars[] = "#/";
+const char line_separator_chars[] = ";";
+const char line_comment_chars[] = "#/";
+
+const int md_reloc_size = 8;
+
+static int relax; /* set if -relax seen */
+static int do_jsri2bsr = 0; /* change here from 1 by Cruess 19 August 97 */
+static int sifilter_mode = 0;
+
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+#define C(what,length) (((what) << 2) + (length))
+#define GET_WHAT(x) ((x >> 2))
+
+/* These are the two types of relaxable instruction */
+#define COND_JUMP 1
+#define UNCD_JUMP 2
+
+#define UNDEF_DISP 0
+#define COND12 1
+#define COND32 2
+#define UNCD12 1
+#define UNCD32 2
+#define UNDEF_WORD_DISP 4
+#define END 5
+
+#define C12_LEN 2
+#define C32_LEN 10 /* allow for align */
+#define U12_LEN 2
+#define U32_LEN 8 /* allow for align */
+
+
+/* Initialize the relax table */
+const relax_typeS md_relax_table[] =
+{
+{ 1, 1, 0, 0 }, /* 0: unused */
+{ 1, 1, 0, 0 }, /* 1: unused */
+{ 1, 1, 0, 0 }, /* 2: unused */
+{ 1, 1, 0, 0 }, /* 3: unused */
+{ 1, 1, 0, 0 }, /* 4: unused */
+{ 2048, -2046, C12_LEN, C(COND_JUMP, COND32) }, /* 5: C(COND_JUMP, COND12) */
+{ 0, 0, C32_LEN, 0 }, /* 6: C(COND_JUMP, COND32) */
+{ 1, 1, 0, 0 }, /* 7: unused */
+{ 1, 1, 0, 0 }, /* 8: unused */
+{ 2048, -2046, U12_LEN, C(UNCD_JUMP, UNCD32) }, /* 9: C(UNCD_JUMP, UNCD12) */
+{ 0, 0, U32_LEN, 0 }, /*10: C(UNCD_JUMP, UNCD32) */
+{ 1, 1, 0, 0 }, /*11: unused */
+{ 0, 0, 0, 0 } /*12: unused */
+};
+
+/* LITERAL POOL DATA STRUCTURES */
+struct literal
+{
+ unsigned short refcnt;
+ unsigned char ispcrel;
+ unsigned char unused;
+ expressionS e;
+};
+
+#define MAX_POOL_SIZE (1024/4)
+static struct literal litpool [MAX_POOL_SIZE];
+static unsigned poolsize;
+static unsigned poolnumber;
+static unsigned long poolspan;
+
+/* SPANPANIC: the point at which we get too scared and force a dump
+ of the literal pool, and perhaps put a branch in place.
+ Calculated as:
+ 1024 span of lrw/jmpi/jsri insn (actually span+1)
+ -2 possible alignment at the insn.
+ -2 possible alignment to get the table aligned.
+ -2 an inserted branch around the table.
+ == 1018
+ at 1018, we might be in trouble.
+ -- so we have to be smaller than 1018 and since we deal with 2-byte
+ instructions, the next good choice is 1016.
+ -- Note we have a test case that fails when we've got 1018 here. */
+#define SPANPANIC (1016) /* 1024 - 1 entry - 2 byte rounding */
+#define SPANCLOSE (900)
+#define SPANEXIT (600)
+static symbolS * poolsym; /* label for current pool */
+static char poolname[8];
+static struct hash_control * opcode_hash_control; /* Opcode mnemonics */
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ Pseudo-op name without dot
+ Function to call to execute this pseudo-op
+ Integer arg to pass to the function */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "export", s_globl, 0 },
+ { "import", s_ignore, 0 },
+ { "literals", mcore_s_literals, 0 },
+ { "page", listing_eject, 0 },
+ { "bss", s_lcomm_bytes, 1 },
+
+ /* The following are to intercept the placement of data into the text
+ section (eg addresses for a switch table), so that the space they
+ occupy can be taken into account when deciding whether or not to
+ dump the current literal pool.
+ XXX - currently we do not cope with the .space and .dcb.d directives. */
+ { "ascii", mcore_stringer, 0 },
+ { "asciz", mcore_stringer, 1 },
+ { "byte", mcore_cons, 1 },
+ { "dc", mcore_cons, 2 },
+ { "dc.b", mcore_cons, 1 },
+ { "dc.d", mcore_float_cons, 'd' },
+ { "dc.l", mcore_cons, 4 },
+ { "dc.s", mcore_float_cons, 'f' },
+ { "dc.w", mcore_cons, 2 },
+ { "dc.x", mcore_float_cons, 'x' },
+ { "double", mcore_float_cons, 'd'},
+ { "float", mcore_float_cons, 'f'},
+ { "hword", mcore_cons, 2 },
+ { "int", mcore_cons, 4 },
+ { "long", mcore_cons, 4 },
+ { "octa", mcore_cons, 16 },
+ { "quad", mcore_cons, 8 },
+ { "short", mcore_cons, 2 },
+ { "single", mcore_float_cons, 'f'},
+ { "string", mcore_stringer, 1 },
+ { "word", mcore_cons, 2 },
+
+ /* Allow for the effect of section changes. */
+ { "text", mcore_s_text, 0 },
+ { "data", mcore_s_data, 0 },
+
+#ifdef OBJ_ELF
+ { "section", mcore_s_section, 0 },
+ { "section.s", mcore_s_section, 0 },
+ { "sect", mcore_s_section, 0 },
+ { "sect.s", mcore_s_section, 0 },
+#endif
+ { 0, 0, 0 }
+};
+
+static void
+mcore_s_literals (ignore)
+ int ignore;
+{
+ dump_literals (0);
+ demand_empty_rest_of_line ();
+}
+
+
+static void
+mcore_cons (nbytes)
+ int nbytes;
+{
+ if (now_seg == text_section)
+ {
+ char * ptr = input_line_pointer;
+ int commas = 1;
+
+ /* Count the number of commas on the line. */
+ while (! is_end_of_line [* ptr])
+ commas += * ptr ++ == ',';
+
+ poolspan += nbytes * commas;
+ }
+
+ cons (nbytes);
+
+ /* In theory we ought to call check_literals (2,0) here in case
+ we need to dump the literal table. We cannot do this however,
+ as the directives that we are intercepting may be being used
+ to build a switch table, and we must not interfere with its
+ contents. Instead we cross our fingers and pray... */
+}
+
+static void
+mcore_float_cons (float_type)
+ int float_type;
+{
+ if (now_seg == text_section)
+ {
+ char * ptr = input_line_pointer;
+ int commas = 1;
+
+#ifdef REPEAT_CONS_EXPRESSIONS
+#error REPEAT_CONS_EXPRESSIONS not handled
+#endif
+
+ /* Count the number of commas on the line. */
+ while (! is_end_of_line [* ptr])
+ commas += * ptr ++ == ',';
+
+ /* We would like to compute "hex_float (float_type) * commas"
+ but hex_float is not exported from read.c */
+ float_type == 'f' ? 4 : (float_type == 'd' ? 8 : 12);
+ poolspan += float_type * commas;
+ }
+
+ float_cons (float_type);
+
+ /* See the comment in mcore_cons () about calling check_literals.
+ It is unlikely that a switch table will be constructed using
+ floating point values, but it is still likely that an indexed
+ table of floating point constants is being created by these
+ directives, so again we must not interfere with their placement. */
+}
+
+static void
+mcore_stringer (append_zero)
+ int append_zero;
+{
+ if (now_seg == text_section)
+ {
+ char * ptr = input_line_pointer;
+
+ /* In theory we should compute how many bytes are going to
+ be occupied by the string(s) and add this to the poolspan.
+ To keep things simple however, we just add the number of
+ bytes left on the current line. This will be an over-
+ estimate, which is OK, and automatically allows for the
+ appending a zero byte, since the real string(s) is/are
+ required to be enclosed in double quotes. */
+ while (! is_end_of_line [* ptr])
+ ptr ++;
+
+ poolspan += ptr - input_line_pointer;
+ }
+
+ stringer (append_zero);
+
+ /* We call check_literals here in case a large number of strings are
+ being placed into the text section with a sequence of stringer
+ directives. In theory we could be upsetting something if these
+ strings are actually in an indexed table instead of referenced by
+ individual labels. Let us hope that that never happens. */
+ check_literals (2, 0);
+}
+
+static void
+mcore_s_text (ignore)
+ int ignore;
+{
+ dump_literals (0);
+
+ s_text (ignore);
+}
+
+static void
+mcore_s_data (ignore)
+ int ignore;
+{
+ dump_literals (0);
+
+ s_data (ignore);
+}
+
+/* This function is called once, at assembler startup time. This should
+ set up all the tables, etc that the MD part of the assembler needs. */
+void
+md_begin ()
+{
+ mcore_opcode_info * opcode;
+ char * prev_name = "";
+
+ opcode_hash_control = hash_new ();
+
+ /* Insert unique names into hash table */
+ for (opcode = mcore_table; opcode->name; opcode ++)
+ {
+ if (streq (prev_name, opcode->name))
+ {
+ /* Make all the opcodes with the same name point to the same
+ string. */
+ opcode->name = prev_name;
+ }
+ else
+ {
+ prev_name = opcode->name;
+ hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
+ }
+ }
+}
+
+static int reg_m;
+static int reg_n;
+static expressionS immediate; /* absolute expression */
+
+/* Get a log2(val). */
+static int
+log2 (val)
+ unsigned int val;
+{
+ int log = -1;
+ while (val != 0)
+ {
+ log ++;
+ val >>= 1;
+ }
+
+ return log;
+}
+
+/* Try to parse a reg name. */
+static char *
+parse_reg (s, reg)
+ char * s;
+ unsigned * reg;
+{
+ /* Strip leading whitespace. */
+ while (isspace (* s))
+ ++ s;
+
+ if (tolower (s[0]) == 'r')
+ {
+ if (s[1] == '1' && s[2] >= '0' && s[2] <= '5')
+ {
+ *reg = 10 + s[2] - '0';
+ return s + 3;
+ }
+
+ if (s[1] >= '0' && s[1] <= '9')
+ {
+ *reg = s[1] - '0';
+ return s + 2;
+ }
+ }
+ else if ( tolower (s[0]) == 's'
+ && tolower (s[1]) == 'p'
+ && (isspace (s[2]) || s[2] == ','))
+ {
+ * reg = 0;
+ return s + 2;
+ }
+
+ as_bad (_("register expected, but saw '%.6s'"), s);
+ return s;
+}
+
+static struct Cregs
+{
+ char * name;
+ unsigned int crnum;
+}
+cregs[] =
+{
+ { "psr", 0},
+ { "vbr", 1},
+ { "epsr", 2},
+ { "fpsr", 3},
+ { "epc", 4},
+ { "fpc", 5},
+ { "ss0", 6},
+ { "ss1", 7},
+ { "ss2", 8},
+ { "ss3", 9},
+ { "ss4", 10},
+ { "gcr", 11},
+ { "gsr", 12},
+ { "", 0}
+};
+
+static char *
+parse_creg (s, reg)
+ char * s;
+ unsigned * reg;
+{
+ int i;
+
+ /* Strip leading whitespace. */
+ while (isspace (* s))
+ ++s;
+
+ if ((tolower (s[0]) == 'c' && tolower (s[1]) == 'r'))
+ {
+ if (s[2] == '3' && s[3] >= '0' && s[3] <= '1')
+ {
+ *reg = 30 + s[3] - '0';
+ return s + 4;
+ }
+
+ if (s[2] == '2' && s[3] >= '0' && s[3] <= '9')
+ {
+ *reg = 20 + s[3] - '0';
+ return s + 4;
+ }
+
+ if (s[2] == '1' && s[3] >= '0' && s[3] <= '9')
+ {
+ *reg = 10 + s[3] - '0';
+ return s + 4;
+ }
+
+ if (s[2] >= '0' && s[2] <= '9')
+ {
+ *reg = s[2] - '0';
+ return s + 3;
+ }
+ }
+
+ /* Look at alternate creg names before giving error. */
+ for (i = 0; cregs[i].name[0] != '\0'; i++)
+ {
+ char buf [10];
+ int length;
+ int j;
+
+ length = strlen (cregs[i].name);
+
+ for (j = 0; j < length; j++)
+ buf[j] = tolower (s[j]);
+
+ if (strncmp (cregs[i].name, buf, length) == 0)
+ {
+ *reg = cregs[i].crnum;
+ return s + length;
+ }
+ }
+
+ as_bad (_("control register expected, but saw '%.6s'"), s);
+
+ return s;
+}
+
+static char *
+parse_exp (s, e)
+ char * s;
+ expressionS * e;
+{
+ char * save;
+ char * new;
+
+ /* Skip whitespace. */
+ while (isspace (* s))
+ ++ s;
+
+ save = input_line_pointer;
+ input_line_pointer = s;
+
+ expression (e);
+
+ if (e->X_op == O_absent)
+ as_bad (_("missing operand"));
+
+ new = input_line_pointer;
+ input_line_pointer = save;
+
+ return new;
+}
+
+static void
+make_name (s, p, n)
+ char * s;
+ char * p;
+ int n;
+{
+ static const char hex[] = "0123456789ABCDEF";
+
+ s[0] = p[0];
+ s[1] = p[1];
+ s[2] = p[2];
+ s[3] = hex[(n >> 12) & 0xF];
+ s[4] = hex[(n >> 8) & 0xF];
+ s[5] = hex[(n >> 4) & 0xF];
+ s[6] = hex[(n) & 0xF];
+ s[7] = 0;
+}
+
+static void
+dump_literals (isforce)
+ int isforce;
+{
+ int i;
+ struct literal * p;
+ struct symbol * brarsym;
+
+ if (poolsize == 0)
+ return;
+
+ /* Must we branch around the literal table? */
+ if (isforce)
+ {
+ char * output;
+ char brarname[8];
+
+ make_name (brarname, ".YP.", poolnumber);
+
+ brarsym = symbol_make (brarname);
+
+ symbol_table_insert (brarsym);
+
+ output = frag_var (rs_machine_dependent,
+ md_relax_table[C (UNCD_JUMP, UNCD32)].rlx_length,
+ md_relax_table[C (UNCD_JUMP, UNCD12)].rlx_length,
+ C (UNCD_JUMP, 0), brarsym, 0, 0);
+ output[0] = INST_BYTE0 (MCORE_INST_BR); /* br .+xxx */
+ output[1] = INST_BYTE1 (MCORE_INST_BR);
+ }
+
+ /* Make sure that the section is sufficiently aligned and that
+ the literal table is aligned within it. */
+ record_alignment (now_seg, 2);
+ frag_align (2, 0, 0);
+
+ colon (S_GET_NAME (poolsym));
+
+ for (i = 0, p = litpool; i < poolsize; i++, p++)
+ emit_expr (& p->e, 4);
+
+ if (isforce)
+ colon (S_GET_NAME (brarsym));
+
+ poolsize = 0;
+}
+
+static void
+check_literals (kind, offset)
+ int kind;
+ int offset;
+{
+ poolspan += offset;
+
+ /* SPANCLOSE and SPANEXIT are smaller numbers than SPANPANIC.
+ SPANPANIC means that we must dump now.
+ kind == 0 is any old instruction.
+ kind > 0 means we just had a control transfer instruction.
+ kind == 1 means within a function
+ kind == 2 means we just left a function
+
+ The dump_literals (1) call inserts a branch around the table, so
+ we first look to see if its a situation where we won't have to
+ insert a branch (e.g., the previous instruction was an unconditional
+ branch).
+
+ SPANPANIC is the point where we must dump a single-entry pool.
+ it accounts for alignments and an inserted branch.
+ the 'poolsize*2' accounts for the scenario where we do:
+ lrw r1,lit1; lrw r2,lit2; lrw r3,lit3
+ Note that the 'lit2' reference is 2 bytes further along
+ but the literal it references will be 4 bytes further along,
+ so we must consider the poolsize into this equation.
+ This is slightly over-cautious, but guarantees that we won't
+ panic because a relocation is too distant. */
+
+ if (poolspan > SPANCLOSE && kind > 0)
+ dump_literals (0);
+ else if (poolspan > SPANEXIT && kind > 1)
+ dump_literals (0);
+ else if (poolspan >= (SPANPANIC - poolsize * 2))
+ dump_literals (1);
+}
+
+static int
+enter_literal (e, ispcrel)
+ expressionS * e;
+ int ispcrel;
+{
+ int i;
+ struct literal * p;
+
+ if (poolsize >= MAX_POOL_SIZE - 2)
+ {
+ /* The literal pool is as full as we can handle. We have
+ to be 2 entries shy of the 1024/4=256 entries because we
+ have to allow for the branch (2 bytes) and the alignment
+ (2 bytes before the first insn referencing the pool and
+ 2 bytes before the pool itself) == 6 bytes, rounds up
+ to 2 entries. */
+ dump_literals (1);
+ }
+
+ if (poolsize == 0)
+ {
+ /* Create new literal pool. */
+ if (++ poolnumber > 0xFFFF)
+ as_fatal (_("more than 65K literal pools"));
+
+ make_name (poolname, ".XP.", poolnumber);
+ poolsym = symbol_make (poolname);
+ symbol_table_insert (poolsym);
+ poolspan = 0;
+ }
+
+ /* Search pool for value so we don't have duplicates. */
+ for (p = litpool, i = 0; i < poolsize; i++, p++)
+ {
+ if (e->X_op == p->e.X_op
+ && e->X_add_symbol == p->e.X_add_symbol
+ && e->X_add_number == p->e.X_add_number
+ && ispcrel == p->ispcrel)
+ {
+ p->refcnt ++;
+ return i;
+ }
+ }
+
+ p->refcnt = 1;
+ p->ispcrel = ispcrel;
+ p->e = * e;
+
+ poolsize ++;
+
+ return i;
+}
+
+/* Parse a literal specification. -- either new or old syntax.
+ old syntax: the user supplies the label and places the literal.
+ new syntax: we put it into the literal pool. */
+static char *
+parse_rt (s, outputp, ispcrel, ep)
+ char * s;
+ char ** outputp;
+ int ispcrel;
+ expressionS * ep;
+{
+ expressionS e;
+ int n;
+
+ if (ep)
+ /* Indicate nothing there. */
+ ep->X_op = O_absent;
+
+ if (*s == '[')
+ {
+ s = parse_exp (s + 1, & e);
+
+ if (*s == ']')
+ s++;
+ else
+ as_bad (_("missing ']'"));
+ }
+ else
+ {
+ s = parse_exp (s, & e);
+
+ n = enter_literal (& e, ispcrel);
+
+ if (ep)
+ *ep = e;
+
+ /* Create a reference to pool entry. */
+ e.X_op = O_symbol;
+ e.X_add_symbol = poolsym;
+ e.X_add_number = n << 2;
+ }
+
+ * outputp = frag_more (2);
+
+ fix_new_exp (frag_now, (*outputp) - frag_now->fr_literal, 2, & e, 1,
+ BFD_RELOC_MCORE_PCREL_IMM8BY4);
+
+ return s;
+}
+
+static char *
+parse_imm (s, val, min, max)
+ char * s;
+ unsigned * val;
+ unsigned min;
+ unsigned max;
+{
+ char * new;
+ expressionS e;
+
+ new = parse_exp (s, & e);
+
+ if (e.X_op == O_absent)
+ ; /* An error message has already been emitted. */
+ else if (e.X_op != O_constant)
+ as_bad (_("operand must be a constant"));
+ else if (e.X_add_number < min || e.X_add_number > max)
+ as_bad (_("operand must be absolute in range %d..%d, not %d"),
+ min, max, e.X_add_number);
+
+ * val = e.X_add_number;
+
+ return new;
+}
+
+static char *
+parse_mem (s, reg, off, siz)
+ char * s;
+ unsigned * reg;
+ unsigned * off;
+ unsigned siz;
+{
+ char * new;
+
+ * off = 0;
+
+ while (isspace (* s))
+ ++ s;
+
+ if (* s == '(')
+ {
+ s = parse_reg (s + 1, reg);
+
+ while (isspace (* s))
+ ++ s;
+
+ if (* s == ',')
+ {
+ s = parse_imm (s + 1, off, 0, 63);
+
+ if (siz > 1)
+ {
+ if (siz > 2)
+ {
+ if (* off & 0x3)
+ as_bad (_("operand must be a multiple of 4"));
+
+ * off >>= 2;
+ }
+ else
+ {
+ if (* off & 0x1)
+ as_bad (_("operand must be a multiple of 2"));
+
+ * off >>= 1;
+ }
+ }
+ }
+
+ while (isspace (* s))
+ ++ s;
+
+ if (* s == ')')
+ s ++;
+ }
+ else
+ as_bad (_("base register expected"));
+
+ return s;
+}
+
+/* This is the guts of the machine-dependent assembler. STR points to a
+ machine dependent instruction. This function is supposed to emit
+ the frags/bytes it assembles to. */
+
+void
+md_assemble (str)
+ char * str;
+{
+ char * op_start;
+ char * op_end;
+ mcore_opcode_info * opcode;
+ char * output;
+ int nlen = 0;
+ unsigned short inst;
+ unsigned reg;
+ unsigned off;
+ unsigned isize;
+ expressionS e;
+ char name[20];
+
+ /* Drop leading whitespace. */
+ while (isspace (* str))
+ str ++;
+
+ /* Find the op code end. */
+ for (op_start = op_end = str;
+ * op_end && nlen < 20 && !is_end_of_line [*op_end] && *op_end != ' ';
+ op_end++)
+ {
+ name[nlen] = op_start[nlen];
+ nlen++;
+ }
+
+ name [nlen] = 0;
+
+ if (nlen == 0)
+ {
+ as_bad (_("can't find opcode "));
+ return;
+ }
+
+ opcode = (mcore_opcode_info *) hash_find (opcode_hash_control, name);
+ if (opcode == NULL)
+ {
+ as_bad (_("unknown opcode \"%s\""), name);
+ return;
+ }
+
+ inst = opcode->inst;
+ isize = 2;
+
+ switch (opcode->opclass)
+ {
+ case O0:
+ output = frag_more (2);
+ break;
+
+ case OT:
+ op_end = parse_imm (op_end + 1, & reg, 0, 3);
+ inst |= reg;
+ output = frag_more (2);
+ break;
+
+ case O1:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+ output = frag_more (2);
+ break;
+
+ case JMP:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+ output = frag_more (2);
+ /* In a sifilter mode, we emit this insn 2 times,
+ fixes problem of an interrupt during a jmp.. */
+ if (sifilter_mode)
+ {
+ output[0] = (inst >> 8);
+ output[1] = (inst);
+ output = frag_more (2);
+ }
+ break;
+
+ case JSR:
+ op_end = parse_reg (op_end + 1, & reg);
+
+ if (reg == 15)
+ as_bad (_("invalid register: r15 illegal"));
+
+ inst |= reg;
+ output = frag_more (2);
+
+ if (sifilter_mode)
+ {
+ /* Replace with: bsr .+2 ; addi r15,6; jmp rx ; jmp rx */
+ inst = MCORE_INST_BSR; /* with 0 displacement */
+ output[0] = (inst >> 8);
+ output[1] = (inst);
+
+ output = frag_more (2);
+ inst = MCORE_INST_ADDI;
+ inst |= 15; /* addi r15,6 */
+ inst |= (6 - 1) << 4; /* over the jmp's */
+ output[0] = (inst >> 8);
+ output[1] = (inst);
+
+ output = frag_more (2);
+ inst = MCORE_INST_JMP | reg;
+ output[0] = (inst >> 8);
+ output[1] = (inst);
+
+ output = frag_more (2); /* 2nd emitted in fallthru */
+ }
+ break;
+
+ case OC:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (*op_end == ',')
+ {
+ op_end = parse_creg (op_end + 1, & reg);
+ inst |= reg << 4;
+ }
+
+ output = frag_more (2);
+ break;
+
+ case O2:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg << 4;
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case X1: /* Handle both syntax-> xtrb- r1,rx OR xtrb- rx */
+ op_end = parse_reg (op_end + 1, & reg);
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',') /* xtrb- r1,rx */
+ {
+ if (reg != 1)
+ as_bad (_("destination register must be r1"));
+
+ op_end = parse_reg (op_end + 1, & reg);
+ }
+
+ inst |= reg;
+ output = frag_more (2);
+ break;
+
+ case O1R1: /* div- rx,r1 */
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_reg (op_end + 1, & reg);
+ if (reg != 1)
+ as_bad (_("source register must be r1"));
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case OI:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 1, 32);
+ inst |= (reg - 1) << 4;
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case OB:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 0, 31);
+ inst |= reg << 4;
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case OB2: /* like OB, but arg is 2^n instead of n */
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 1, 1 << 31);
+ /* Further restrict the immediate to a power of two. */
+ if ((reg & (reg - 1)) == 0)
+ reg = log2 (reg);
+ else
+ {
+ reg = 0;
+ as_bad (_("immediate is not a power of two"));
+ }
+ inst |= (reg) << 4;
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case OBRa: /* Specific for bgeni: imm of 0->6 translate to movi. */
+ case OBRb:
+ case OBRc:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 0, 31);
+ /* immediate values of 0 -> 6 translate to movi */
+ if (reg <= 6)
+ {
+ inst = (inst & 0xF) | MCORE_INST_BGENI_ALT;
+ reg = 0x1 << reg;
+ as_warn (_("translating bgeni to movi"));
+ }
+ inst &= ~ 0x01f0;
+ inst |= reg << 4;
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case OBR2: /* like OBR, but arg is 2^n instead of n */
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 1, 1 << 31);
+
+ /* Further restrict the immediate to a power of two. */
+ if ((reg & (reg - 1)) == 0)
+ reg = log2 (reg);
+ else
+ {
+ reg = 0;
+ as_bad (_("immediate is not a power of two"));
+ }
+
+ /* Immediate values of 0 -> 6 translate to movi. */
+ if (reg <= 6)
+ {
+ inst = (inst & 0xF) | MCORE_INST_BGENI_ALT;
+ reg = 0x1 << reg;
+ as_warn (_("translating mgeni to movi"));
+ }
+
+ inst |= reg << 4;
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case OMa: /* Specific for bmaski: imm 1->7 translate to movi. */
+ case OMb:
+ case OMc:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 1, 32);
+
+ /* Immediate values of 1 -> 7 translate to movi. */
+ if (reg <= 7)
+ {
+ inst = (inst & 0xF) | MCORE_INST_BMASKI_ALT;
+ reg = (0x1 << reg) - 1;
+ inst |= reg << 4;
+
+ as_warn (_("translating bmaski to movi"));
+ }
+ else
+ {
+ inst &= ~ 0x01F0;
+ inst |= (reg & 0x1F) << 4;
+ }
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case SI:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 1, 31);
+ inst |= reg << 4;
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case I7:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 0, 0x7F);
+ inst |= reg << 4;
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case LS:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg << 8;
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ int size;
+
+ if ((inst & 0x6000) == 0)
+ size = 4;
+ else if ((inst & 0x6000) == 0x4000)
+ size = 2;
+ else if ((inst & 0x6000) == 0x2000)
+ size = 1;
+
+ op_end = parse_mem (op_end + 1, & reg, & off, size);
+
+ if (off > 16)
+ as_bad (_("displacement too large (%d)"), off);
+ else
+ inst |= (reg) | (off << 4);
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case LR:
+ op_end = parse_reg (op_end + 1, & reg);
+
+ if (reg == 0 || reg == 15)
+ as_bad (_("Invalid register: r0 and r15 illegal"));
+
+ inst |= (reg << 8);
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ /* parse_rt calls frag_more() for us. */
+ input_line_pointer = parse_rt (op_end + 1, & output, 0, 0);
+ else
+ {
+ as_bad (_("second operand missing"));
+ output = frag_more (2); /* save its space */
+ }
+ break;
+
+ case LJ:
+ input_line_pointer = parse_rt (op_end + 1, & output, 1, 0);
+ /* parse_rt() calls frag_more() for us. */
+ break;
+
+ case RM:
+ op_end = parse_reg (op_end + 1, & reg);
+
+ if (reg == 0 || reg == 15)
+ as_bad (_("bad starting register: r0 and r15 invalid"));
+
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == '-')
+ {
+ op_end = parse_reg (op_end + 1, & reg);
+
+ if (reg != 15)
+ as_bad (_("ending register must be r15"));
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+ }
+
+ if (* op_end == ',')
+ {
+ op_end ++;
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == '(')
+ {
+ op_end = parse_reg (op_end + 1, & reg);
+
+ if (reg != 0)
+ as_bad (_("bad base register: must be r0"));
+
+ if (* op_end == ')')
+ op_end ++;
+ }
+ else
+ as_bad (_("base register expected"));
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case RQ:
+ op_end = parse_reg (op_end + 1, & reg);
+
+ if (reg != 4)
+ as_fatal (_("first register must be r4"));
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == '-')
+ {
+ op_end = parse_reg (op_end + 1, & reg);
+
+ if (reg != 7)
+ as_fatal (_("last register must be r7"));
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end ++;
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == '(')
+ {
+ op_end = parse_reg (op_end + 1, & reg);
+
+ if (reg >= 4 && reg <= 7)
+ as_fatal ("base register cannot be r4, r5, r6, or r7");
+
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == ')')
+ op_end ++;
+ }
+ else
+ as_bad (_("base register expected"));
+ }
+ else
+ as_bad (_("second operand missing"));
+ }
+ else
+ as_bad (_("reg-reg expected"));
+
+ output = frag_more (2);
+ break;
+
+ case BR:
+ input_line_pointer = parse_exp (op_end + 1, & e);
+
+ output = frag_more (2);
+
+ fix_new_exp (frag_now, output-frag_now->fr_literal,
+ 2, & e, 1, BFD_RELOC_MCORE_PCREL_IMM11BY2);
+ break;
+
+ case BL:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg << 4;
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_exp (op_end + 1, & e);
+ output = frag_more (2);
+
+ fix_new_exp (frag_now, output-frag_now->fr_literal,
+ 2, & e, 1, BFD_RELOC_MCORE_PCREL_IMM4BY2);
+ }
+ else
+ {
+ as_bad (_("second operand missing"));
+ output = frag_more (2);
+ }
+ break;
+
+ case JC:
+ input_line_pointer = parse_exp (op_end + 1, & e);
+
+ output = frag_var (rs_machine_dependent,
+ md_relax_table[C (COND_JUMP, COND32)].rlx_length,
+ md_relax_table[C (COND_JUMP, COND12)].rlx_length,
+ C (COND_JUMP, 0), e.X_add_symbol, e.X_add_number, 0);
+ isize = C32_LEN;
+ break;
+
+ case JU:
+ input_line_pointer = parse_exp (op_end + 1, & e);
+ output = frag_var (rs_machine_dependent,
+ md_relax_table[C (UNCD_JUMP, UNCD32)].rlx_length,
+ md_relax_table[C (UNCD_JUMP, UNCD12)].rlx_length,
+ C (UNCD_JUMP, 0), e.X_add_symbol, e.X_add_number, 0);
+ isize = U32_LEN;
+ break;
+
+ case JL:
+ inst = MCORE_INST_JSRI; /* jsri */
+ input_line_pointer = parse_rt (op_end + 1, & output, 1, & e);
+ /* parse_rt() calls frag_more for us */
+
+ /* Only do this if we know how to do it ... */
+ if (e.X_op != O_absent && do_jsri2bsr)
+ {
+ /* Look at adding the R_PCREL_JSRIMM11BY2. */
+ fix_new_exp (frag_now, output-frag_now->fr_literal,
+ 2, & e, 1, BFD_RELOC_MCORE_PCREL_JSR_IMM11BY2);
+ }
+ break;
+
+ case RSI: /* SI, but imm becomes 32-imm */
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 1, 31);
+
+ reg = 32 - reg;
+ inst |= reg << 4;
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case DO21: /* O2, dup rd, lit must be 1 */
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+ inst |= reg << 4;
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 1, 31);
+
+ if (reg != 1)
+ as_bad (_("second operand must be 1"));
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case SIa:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (isspace (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 1, 31);
+
+ if (reg == 0)
+ as_bad (_("zero used as immediate value"));
+
+ inst |= reg << 4;
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ default:
+ as_bad (_("unimplemented opcode \"%s\""), name);
+ }
+
+ output[0] = inst >> 8;
+ output[1] = inst;
+
+ check_literals (opcode->transfer, isize);
+}
+
+symbolS *
+md_undefined_symbol (name)
+ char * name;
+{
+ return 0;
+}
+
+void
+md_mcore_end ()
+{
+ dump_literals (0);
+ subseg_set (text_section, 0);
+}
+
+/* Various routines to kill one day. */
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+/* Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP. An error message is returned, or NULL on OK.*/
+char *
+md_atof (type, litP, sizeP)
+ int type;
+ char * litP;
+ int * sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE * wordP;
+ char * t;
+ char * atof_ieee ();
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("Bad call to MD_NTOF()");
+ }
+
+ t = atof_ieee (input_line_pointer, type, words);
+
+ if (t)
+ input_line_pointer = t;
+
+ *sizeP = prec * sizeof (LITTLENUM_TYPE);
+
+ for (wordP = words; prec--;)
+ {
+ md_number_to_chars (litP, (long) (*wordP++), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+
+ return 0;
+}
+
+CONST char * md_shortopts = "";
+
+#define OPTION_RELAX (OPTION_MD_BASE)
+#define OPTION_JSRI2BSR_ON (OPTION_MD_BASE + 1)
+#define OPTION_JSRI2BSR_OFF (OPTION_MD_BASE + 2)
+#define OPTION_SIFILTER_ON (OPTION_MD_BASE + 3)
+#define OPTION_SIFILTER_OFF (OPTION_MD_BASE + 4)
+
+struct option md_longopts[] =
+{
+ { "relax", no_argument, NULL, OPTION_RELAX},
+ { "no-jsri2bsr", no_argument, NULL, OPTION_JSRI2BSR_OFF},
+ { "jsri2bsr", no_argument, NULL, OPTION_JSRI2BSR_ON},
+ { "sifilter", no_argument, NULL, OPTION_SIFILTER_ON},
+ { "no-sifilter", no_argument, NULL, OPTION_SIFILTER_OFF},
+ { NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (c, arg)
+ int c;
+ char * arg;
+{
+ int i;
+ char * p;
+
+ switch (c)
+ {
+
+ case OPTION_RELAX: relax = 1; break;
+ case OPTION_JSRI2BSR_ON: do_jsri2bsr = 1; break;
+ case OPTION_JSRI2BSR_OFF: do_jsri2bsr = 0; break;
+ case OPTION_SIFILTER_ON: sifilter_mode = 1; break;
+ case OPTION_SIFILTER_OFF: sifilter_mode = 0; break;
+ default: return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (stream)
+ FILE * stream;
+{
+ fprintf (stream, _("\
+MCORE specific options:\n\
+ -{no-}jsri2bsr {dis}able jsri to bsr transformation (def: off)\n\
+ -{no-}sifilter {dis}able silicon filter behavior (def: off)\n\
+ -relax alter jump instructions for long displacements\n"));
+}
+
+int md_short_jump_size;
+
+void
+md_create_short_jump (ptr, from_Nddr, to_Nddr, frag, to_symbol)
+ char * ptr;
+ addressT from_Nddr;
+ addressT to_Nddr;
+ fragS * frag;
+ symbolS * to_symbol;
+{
+ as_fatal (_("failed sanity check: short_jump"));
+}
+
+void
+md_create_long_jump (ptr, from_Nddr, to_Nddr, frag, to_symbol)
+ char * ptr;
+ addressT from_Nddr;
+ addressT to_Nddr;
+ fragS * frag;
+ symbolS * to_symbol;
+{
+ as_fatal (_("failed sanity check: long_jump"));
+}
+
+/* Called after relaxing, change the frags so they know how big they are. */
+void
+md_convert_frag (abfd, sec, fragP)
+ bfd * abfd;
+ segT sec;
+ register fragS * fragP;
+{
+ unsigned char * buffer;
+ int targ_addr = S_GET_VALUE (fragP->fr_symbol) + fragP->fr_offset;
+
+ buffer = (unsigned char *) (fragP->fr_fix + fragP->fr_literal);
+ targ_addr += fragP->fr_symbol->sy_frag->fr_address;
+
+ switch (fragP->fr_subtype)
+ {
+ case C (COND_JUMP, COND12):
+ case C (UNCD_JUMP, UNCD12):
+ {
+ /* Get the address of the end of the instruction */
+ int next_inst = fragP->fr_fix + fragP->fr_address + 2;
+ unsigned char t0;
+ int disp = targ_addr - next_inst;
+
+ if (disp & 1)
+ as_bad (_("odd displacement at %x"), next_inst - 2);
+
+ disp >>= 1;
+ t0 = buffer[0] & 0xF8;
+
+ md_number_to_chars (buffer, disp, 2);
+
+ buffer[0] = (buffer[0] & 0x07) | t0;
+ fragP->fr_fix += 2;
+ fragP->fr_var = 0;
+ }
+ break;
+
+ case C (COND_JUMP, COND32):
+ case C (COND_JUMP, UNDEF_WORD_DISP):
+ {
+ /* A conditional branch wont fit into 12 bits so:
+ * b!cond 1f
+ * jmpi 0f
+ * .align 2
+ * 0: .long disp
+ * 1:
+ *
+ * if the b!cond is 4 byte aligned, the literal which would
+ * go at x+4 will also be aligned.
+ */
+ int first_inst = fragP->fr_fix + fragP->fr_address;
+ int needpad = (first_inst & 3);
+
+ buffer[0] ^= 0x08; /* Toggle T/F bit */
+
+ buffer[2] = INST_BYTE0 (MCORE_INST_JMPI); /* Build jmpi */
+ buffer[3] = INST_BYTE1 (MCORE_INST_JMPI);
+
+ if (needpad)
+ {
+ buffer[1] = 4; /* branch over jmpi, pad, and ptr */
+ buffer[3] = 1; /* jmpi offset of 1 gets the pointer */
+ buffer[4] = 0; /* alignment/pad */
+ buffer[5] = 0;
+ buffer[6] = 0; /* space for 32 bit address */
+ buffer[7] = 0;
+ buffer[8] = 0;
+ buffer[9] = 0;
+
+ /* Make reloc for the long disp */
+ fix_new (fragP, fragP->fr_fix + 6, 4,
+ fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_32);
+
+ fragP->fr_fix += C32_LEN;
+ }
+ else
+ {
+ /* See comment below about this given gas' limitations for
+ shrinking the fragment. '3' is the amount of code that
+ we inserted here, but '4' is right for the space we reserved
+ for this fragment. */
+ buffer[1] = 3; /* branch over jmpi, and ptr */
+ buffer[3] = 0; /* jmpi offset of 0 gets the pointer */
+ buffer[4] = 0; /* space for 32 bit address */
+ buffer[5] = 0;
+ buffer[6] = 0;
+ buffer[7] = 0;
+
+ /* Make reloc for the long disp. */
+ fix_new (fragP, fragP->fr_fix + 4, 4,
+ fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_32);
+ fragP->fr_fix += C32_LEN;
+
+ /* frag is actually shorter (see the other side of this ifdef)
+ but gas isn't prepared for that. We have to re-adjust
+ the branch displacement so that it goes beyond the
+ full length of the fragment, not just what we actually
+ filled in. */
+ buffer[1] = 4; /* jmpi, ptr, and the 'tail pad' */
+ }
+
+ fragP->fr_var = 0;
+ }
+ break;
+
+ case C (UNCD_JUMP, UNCD32):
+ case C (UNCD_JUMP, UNDEF_WORD_DISP):
+ {
+ /* An unconditional branch will not fit in 12 bits, make code which
+ looks like:
+ jmpi 0f
+ .align 2
+ 0: .long disp
+ we need a pad if "first_inst" is 4 byte aligned.
+ [because the natural literal place is x + 2] */
+ int first_inst = fragP->fr_fix + fragP->fr_address;
+ int needpad = !(first_inst & 3);
+
+ buffer[0] = INST_BYTE0 (MCORE_INST_JMPI); /* Build jmpi */
+ buffer[1] = INST_BYTE1 (MCORE_INST_JMPI);
+
+ if (needpad)
+ {
+ buffer[1] = 1; /* jmpi offset of 1 since padded */
+ buffer[2] = 0; /* alignment */
+ buffer[3] = 0;
+ buffer[4] = 0; /* space for 32 bit address */
+ buffer[5] = 0;
+ buffer[6] = 0;
+ buffer[7] = 0;
+
+ /* Make reloc for the long disp */
+ fix_new (fragP, fragP->fr_fix + 4, 4,
+ fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_32);
+
+ fragP->fr_fix += U32_LEN;
+ }
+ else
+ {
+ buffer[1] = 0; /* jmpi offset of 0 if no pad */
+ buffer[2] = 0; /* space for 32 bit address */
+ buffer[3] = 0;
+ buffer[4] = 0;
+ buffer[5] = 0;
+
+ /* Make reloc for the long disp */
+ fix_new (fragP, fragP->fr_fix + 2, 4,
+ fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_32);
+ fragP->fr_fix += U32_LEN;
+ }
+
+ fragP->fr_var = 0;
+ }
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+/* Applies the desired value to the specified location.
+ Also sets up addends for 'rela' type relocations. */
+int
+md_apply_fix3 (fixP, valp, segment)
+ fixS * fixP;
+ valueT * valp;
+ segT segment;
+{
+ char * buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ char * file = fixP->fx_file ? fixP->fx_file : _("unknown");
+ const char * symname;
+ /* Note: use offsetT because it is signed, valueT is unsigned. */
+ offsetT val = (offsetT) * valp;
+
+ symname = fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : _("<unknown>");
+ /* Save this for the addend in the relocation record. */
+ fixP->fx_addnumber = val;
+
+ /* If the fix is relative to a symbol which is not defined, or not
+ in the same segment as the fix, we cannot resolve it here. */
+ if (fixP->fx_addsy != NULL
+ && ( ! S_IS_DEFINED (fixP->fx_addsy)
+ || (S_GET_SEGMENT (fixP->fx_addsy) != segment)))
+ {
+ fixP->fx_done = 0;
+#ifdef OBJ_ELF
+ /* For ELF we can just return and let the reloc that will be generated
+ take care of everything. For COFF we still have to insert 'val'
+ into the insn since the addend field will be ignored. */
+ return 0;
+#endif
+ }
+ else
+ fixP->fx_done = 1;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_MCORE_PCREL_IMM11BY2: /* second byte of 2 byte opcode */
+ if ((val & 1) != 0)
+ as_bad_where (file, fixP->fx_line,
+ _("odd distance branch (0x%x bytes)"), val);
+ val /= 2;
+ if (((val & ~0x3ff) != 0) && ((val | 0x3ff) != -1))
+ as_bad_where (file, fixP->fx_line,
+ _("pcrel for branch to %s too far (0x%x)"),
+ symname, val);
+ buf[0] |= ((val >> 8) & 0x7);
+ buf[1] |= (val & 0xff);
+ break;
+
+ case BFD_RELOC_MCORE_PCREL_IMM8BY4: /* lower 8 bits of 2 byte opcode */
+ val += 3;
+ val /= 4;
+ if (val & ~0xff)
+ as_bad_where (file, fixP->fx_line,
+ _("pcrel for lrw/jmpi/jsri to %s too far (0x%x)"),
+ symname, val);
+ else
+ buf[1] |= (val & 0xff);
+ break;
+
+ case BFD_RELOC_MCORE_PCREL_IMM4BY2: /* loopt instruction */
+ if ((val < -32) || (val > -2))
+ as_bad_where (file, fixP->fx_line,
+ _("pcrel for loopt too far (0x%x)"), val);
+ val /= 2;
+ buf[1] |= (val & 0xf);
+ break;
+
+ case BFD_RELOC_MCORE_PCREL_JSR_IMM11BY2:
+ /* Conditional linker map jsri to bsr. */
+ /* If its a local target and close enough, fix it.
+ NB: >= -2k for backwards bsr; < 2k for forwards... */
+ if (fixP->fx_addsy == 0 && val >= -2048 && val < 2048)
+ {
+ long nval = (val / 2) & 0x7ff;
+ nval |= MCORE_INST_BSR;
+
+ /* REPLACE the instruction, don't just modify it. */
+ buf[0] = ((nval >> 8) & 0xff);
+ buf[1] = (nval & 0xff);
+ }
+ else
+ fixP->fx_done = 0;
+ break;
+
+ case BFD_RELOC_MCORE_PCREL_32:
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ break;
+
+ default:
+ if (fixP->fx_addsy != NULL)
+ {
+ /* If the fix is an absolute reloc based on a symbol's
+ address, then it cannot be resolved until the final link. */
+ fixP->fx_done = 0;
+ }
+#ifdef OBJ_ELF
+ else
+#endif
+ {
+ if (fixP->fx_size == 4)
+ {
+ *buf++ = val >> 24;
+ *buf++ = val >> 16;
+ *buf++ = val >> 8;
+ *buf = val;
+ }
+ else if (fixP->fx_size == 2 && val >= -32768 && val <= 32767)
+ {
+ *buf++ = val >> 8;
+ *buf = val;
+ }
+ else if (fixP->fx_size == 1 && val >= -256 && val <= 255)
+ *buf = val;
+ else
+ abort ();
+ }
+ break;
+ }
+
+ return 0; /* Return value is ignored. */
+}
+
+void
+md_operand (expressionP)
+ expressionS * expressionP;
+{
+ /* Ignore leading hash symbol, if poresent. */
+ if (* input_line_pointer == '#')
+ {
+ input_line_pointer ++;
+ expression (expressionP);
+ }
+}
+
+int md_long_jump_size;
+
+/* Called just before address relaxation, return the length
+ by which a fragment must grow to reach it's destination. */
+int
+md_estimate_size_before_relax (fragP, segment_type)
+ register fragS * fragP;
+ register segT segment_type;
+{
+ switch (fragP->fr_subtype)
+ {
+ case C (UNCD_JUMP, UNDEF_DISP):
+ /* Used to be a branch to somewhere which was unknown. */
+ if (!fragP->fr_symbol)
+ {
+ fragP->fr_subtype = C (UNCD_JUMP, UNCD12);
+ fragP->fr_var = md_relax_table[C (UNCD_JUMP, UNCD12)].rlx_length;
+ }
+ else if (S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
+ {
+ fragP->fr_subtype = C (UNCD_JUMP, UNCD12);
+ fragP->fr_var = md_relax_table[C (UNCD_JUMP, UNCD12)].rlx_length;
+ }
+ else
+ {
+ fragP->fr_subtype = C (UNCD_JUMP, UNDEF_WORD_DISP);
+ fragP->fr_var = md_relax_table[C (UNCD_JUMP, UNCD32)].rlx_length;
+ return md_relax_table[C (UNCD_JUMP, UNCD32)].rlx_length;
+ }
+ break;
+
+ default:
+ abort ();
+
+ case C (COND_JUMP, UNDEF_DISP):
+ /* Used to be a branch to somewhere which was unknown. */
+ if (fragP->fr_symbol
+ && S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
+ {
+ /* Got a symbol and it's defined in this segment, become byte
+ sized - maybe it will fix up */
+ fragP->fr_subtype = C (COND_JUMP, COND12);
+ fragP->fr_var = md_relax_table[C (COND_JUMP, COND12)].rlx_length;
+ }
+ else if (fragP->fr_symbol)
+ {
+ /* Its got a segment, but its not ours, so it will always be long. */
+ fragP->fr_subtype = C (COND_JUMP, UNDEF_WORD_DISP);
+ fragP->fr_var = md_relax_table[C (COND_JUMP, COND32)].rlx_length;
+ return md_relax_table[C (COND_JUMP, COND32)].rlx_length;
+ }
+ else
+ {
+ /* We know the abs value. */
+ fragP->fr_subtype = C (COND_JUMP, COND12);
+ fragP->fr_var = md_relax_table[C (COND_JUMP, COND12)].rlx_length;
+ }
+
+ break;
+ }
+
+ return fragP->fr_var;
+}
+
+/* Put number into target byte order */
+
+void
+md_number_to_chars (ptr, use, nbytes)
+ char * ptr;
+ valueT use;
+ int nbytes;
+{
+ switch (nbytes)
+ {
+ case 4: *ptr++ = (use >> 24) & 0xff; /* fall through */
+ case 3: *ptr++ = (use >> 16) & 0xff; /* fall through */
+ case 2: *ptr++ = (use >> 8) & 0xff; /* fall through */
+ case 1: *ptr++ = (use >> 0) & 0xff; break;
+ default: abort ();
+ }
+}
+
+/* Round up a section size to the appropriate boundary. */
+valueT
+md_section_align (segment, size)
+ segT segment;
+ valueT size;
+{
+ return size; /* Byte alignment is fine */
+}
+
+
+/* The location from which a PC relative jump should be calculated,
+ given a PC relative reloc. */
+long
+md_pcrel_from_section (fixp, sec)
+ fixS * fixp;
+ segT sec;
+{
+#ifdef OBJ_ELF
+ /* If the symbol is undefined or defined in another section
+ we leave the add number alone for the linker to fix it later.
+ Only account for the PC pre-bump (which is 2 bytes on the MCore). */
+ if (fixp->fx_addsy != (symbolS *) NULL
+ && (! S_IS_DEFINED (fixp->fx_addsy)
+ || (S_GET_SEGMENT (fixp->fx_addsy) != sec)))
+
+ {
+ assert (fixp->fx_size == 2); /* must be an insn */
+ return fixp->fx_size;
+ }
+#endif
+
+ /* The case where we are going to resolve things... */
+ return fixp->fx_size + fixp->fx_where + fixp->fx_frag->fr_address;
+}
+
+#define F(SZ,PCREL) (((SZ) << 1) + (PCREL))
+#define MAP(SZ,PCREL,TYPE) case F (SZ, PCREL): code = (TYPE); break
+
+arelent *
+tc_gen_reloc (section, fixp)
+ asection * section;
+ fixS * fixp;
+{
+ arelent * rel;
+ bfd_reloc_code_real_type code;
+ int handled = 0;
+
+ switch (fixp->fx_r_type)
+ {
+ /* These confuse the size/pcrel macro approach. */
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ case BFD_RELOC_MCORE_PCREL_IMM4BY2:
+ case BFD_RELOC_MCORE_PCREL_IMM8BY4:
+ case BFD_RELOC_MCORE_PCREL_IMM11BY2:
+ case BFD_RELOC_MCORE_PCREL_JSR_IMM11BY2:
+ code = fixp->fx_r_type;
+ break;
+
+ default:
+ switch (F (fixp->fx_size, fixp->fx_pcrel))
+ {
+ MAP (1, 0, BFD_RELOC_8);
+ MAP (2, 0, BFD_RELOC_16);
+ MAP (4, 0, BFD_RELOC_32);
+ MAP (1, 1, BFD_RELOC_8_PCREL);
+ MAP (2, 1, BFD_RELOC_16_PCREL);
+ MAP (4, 1, BFD_RELOC_32_PCREL);
+ default:
+ code = fixp->fx_r_type;
+ as_bad (_("Can not do %d byte %srelocation"),
+ fixp->fx_size,
+ fixp->fx_pcrel ? _("pc-relative") : "");
+ }
+ break;
+ }
+
+ rel = (arelent *) xmalloc (sizeof (arelent));
+ rel->sym_ptr_ptr = & fixp->fx_addsy->bsym;
+ rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ /* Always pass the addend along! */
+ rel->addend = fixp->fx_addnumber;
+
+ rel->howto = bfd_reloc_type_lookup (stdoutput, code);
+
+ if (rel->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Cannot represent relocation type %s"),
+ bfd_get_reloc_code_name (code));
+
+ /* Set howto to a garbage value so that we can keep going. */
+ rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
+ assert (rel->howto != NULL);
+ }
+
+ return rel;
+}
+
+#ifdef OBJ_ELF
+/* See whether we need to force a relocation into the output file.
+ This is used to force out switch and PC relative relocations when
+ relaxing. */
+int
+mcore_force_relocation (fix)
+ fixS * fix;
+{
+ if ( fix->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fix->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 1;
+
+ return 0;
+}
+
+/* Return true if the fix can be handled by GAS, false if it must
+ be passed through to the linker. */
+boolean
+mcore_fix_adjustable (fixP)
+ fixS * fixP;
+{
+ if (fixP->fx_addsy == NULL)
+ return 1;
+
+ /* We need the symbol name for the VTABLE entries. */
+ if ( fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ return 1;
+}
+
+/* Handle the .section pseudo-op. This is like the usual one, but it
+ dumps the literal pool before changing the section. */
+static void
+mcore_s_section (ignore)
+ int ignore;
+{
+ dump_literals (0);
+
+ obj_elf_section (ignore);
+}
+#endif /* OBJ_ELF */
diff --git a/gas/config/tc-mcore.h b/gas/config/tc-mcore.h
new file mode 100644
index 0000000..9e487c9
--- /dev/null
+++ b/gas/config/tc-mcore.h
@@ -0,0 +1,119 @@
+/* This file is tc-mcore.h
+
+ Copyright (C) 1999 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef TC_MCORE
+#define TC_MCORE 1
+
+#ifndef BFD_ASSEMBLER
+ #error MCORE support requires BFD_ASSEMBLER
+#endif
+
+#define TARGET_ARCH bfd_arch_mcore
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+/* Don't write out relocs for pcrel stuff. */
+#define TC_COUNT_RELOC(x) (((x)->fx_addsy || (x)->fx_subsy) && \
+ (x)->fx_r_type < BFD_RELOC_MCORE_PCREL_IMM8BY4)
+
+#define IGNORE_NONSTANDARD_ESCAPES
+
+#define TC_RELOC_MANGLE(a,b,c) tc_reloc_mangle (a, b, c)
+
+/* Some pseudo-op semantic extensions. */
+#define PSEUDO_LCOMM_OPTIONAL_ALIGN
+
+#define LISTING_HEADER "M.CORE GAS Version 2.9.4"
+#define LISTING_LHS_CONT_LINES 4
+
+#define NEED_FX_R_TYPE 1
+#define COFF_FLAGS 1
+
+/* We want local label support. */
+#define LOCAL_LABELS_FB 1
+
+#define TC_COFF_SIZEMACHDEP(frag) tc_coff_sizemachdep (frag)
+
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+#define md_end md_mcore_end
+
+/* Want the section information too... */
+#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section (FIXP, SEC)
+
+#define MD_APPLY_FIX3 /* We want the segment as well. */
+
+
+
+#ifdef OBJ_COFF
+
+#define TARGET_FORMAT (target_big_endian ? "pe-mcore-big" : "pe-mcore-little")
+
+#define TARGET_SYMBOL_FIELDS int sy_flags ;
+
+#endif /* OBJ_COFF */
+
+
+#ifdef OBJ_ELF
+
+#define TARGET_FORMAT (target_big_endian ? "elf32-mcore-big" : "elf32-mcore-little")
+
+#define ELF_TC_SPECIAL_SECTIONS \
+ { ".ctors", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE }, \
+ { ".dtors", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE }, \
+/* Other special sections not generated by the assembler: .reginfo,
+ .liblist, .conflict, .gptab, .got, .dynamic, .rel.dyn. */
+
+/* When relaxing, we need to emit various relocs we otherwise wouldn't. */
+#define TC_FORCE_RELOCATION(fix) mcore_force_relocation (fix)
+extern int mcore_force_relocation PARAMS ((struct fix *));
+
+#define obj_fix_adjustable(fixP) mcore_fix_adjustable (fixP)
+extern boolean mcore_fix_adjustable PARAMS ((struct fix *));
+
+#endif /* OBJ_ELF */
+
+#ifndef TARGET_FORMAT
+# error No target format specified.
+#endif
+
+#include "struc-symbol.h" /* For definition of symbolS */
+#include "write.h" /* For definition of fixS */
+
+extern void md_begin PARAMS ((void));
+extern void md_assemble PARAMS ((char *));
+extern symbolS * md_undefined_symbol PARAMS ((char *));
+extern void md_mcore_end PARAMS ((void));
+extern char * md_atof PARAMS ((int, char *, int *));
+extern int md_parse_option PARAMS ((int, char *));
+extern void md_show_usage PARAMS ((FILE *));
+extern void md_create_short_jump
+ PARAMS ((char *, addressT, addressT, fragS *, symbolS *));
+extern void md_create_long_jump
+ PARAMS ((char *, addressT, addressT, fragS *, symbolS *));
+extern void md_convert_frag PARAMS ((bfd *, segT, fragS *));
+extern int md_apply_fix3 PARAMS ((fixS *, valueT *, segT));
+extern void md_operand PARAMS ((expressionS *));
+extern int md_estimate_size_before_relax PARAMS ((fragS *, segT));
+extern void md_number_to_chars PARAMS ((char *, valueT, int));
+extern valueT md_section_align PARAMS ((segT, valueT));
+extern long md_pcrel_from_section PARAMS ((fixS *, segT));
+extern arelent * tc_gen_reloc PARAMS ((asection *, fixS *));
+
+#endif /* TC_MCORE */
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
new file mode 100644
index 0000000..3d865fd
--- /dev/null
+++ b/gas/config/tc-mips.c
@@ -0,0 +1,11783 @@
+/* tc-mips.c -- assemble code for a MIPS chip.
+ Copyright (C) 1993, 94, 95, 96, 97, 1998 Free Software Foundation, Inc.
+ Contributed by the OSF and Ralph Campbell.
+ Written by Keith Knowles and Ralph Campbell, working independently.
+ Modified for ECOFF and R4000 support by Ian Lance Taylor of Cygnus
+ Support.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#include "as.h"
+#include "config.h"
+#include "subsegs.h"
+
+#include <ctype.h>
+
+#ifdef USE_STDARG
+#include <stdarg.h>
+#endif
+#ifdef USE_VARARGS
+#include <varargs.h>
+#endif
+
+#include "opcode/mips.h"
+#include "itbl-ops.h"
+
+#ifdef DEBUG
+#define DBG(x) printf x
+#else
+#define DBG(x)
+#endif
+
+#ifdef OBJ_MAYBE_ELF
+/* Clean up namespace so we can include obj-elf.h too. */
+static int mips_output_flavor PARAMS ((void));
+static int mips_output_flavor () { return OUTPUT_FLAVOR; }
+#undef OBJ_PROCESS_STAB
+#undef OUTPUT_FLAVOR
+#undef S_GET_ALIGN
+#undef S_GET_SIZE
+#undef S_SET_ALIGN
+#undef S_SET_SIZE
+#undef TARGET_SYMBOL_FIELDS
+#undef obj_frob_file
+#undef obj_frob_file_after_relocs
+#undef obj_frob_symbol
+#undef obj_pop_insert
+#undef obj_sec_sym_ok_for_reloc
+#undef OBJ_COPY_SYMBOL_ATTRIBUTES
+
+#include "obj-elf.h"
+/* Fix any of them that we actually care about. */
+#undef OUTPUT_FLAVOR
+#define OUTPUT_FLAVOR mips_output_flavor()
+#endif
+
+#if defined (OBJ_ELF)
+#include "elf/mips.h"
+#endif
+
+#ifndef ECOFF_DEBUGGING
+#define NO_ECOFF_DEBUGGING
+#define ECOFF_DEBUGGING 0
+#endif
+
+#include "ecoff.h"
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+static char *mips_regmask_frag;
+#endif
+
+#define AT 1
+#define TREG 24
+#define PIC_CALL_REG 25
+#define KT0 26
+#define KT1 27
+#define GP 28
+#define SP 29
+#define FP 30
+#define RA 31
+
+#define ILLEGAL_REG (32)
+
+/* Allow override of standard little-endian ECOFF format. */
+
+#ifndef ECOFF_LITTLE_FORMAT
+#define ECOFF_LITTLE_FORMAT "ecoff-littlemips"
+#endif
+
+extern int target_big_endian;
+
+/* 1 is we should use the 64 bit MIPS ELF ABI, 0 if we should use the
+ 32 bit ABI. This has no meaning for ECOFF.
+ Note that the default is always 32 bit, even if "configured" for
+ 64 bit [e.g. --target=mips64-elf]. */
+static int mips_64;
+
+/* The default target format to use. */
+const char *
+mips_target_format ()
+{
+ switch (OUTPUT_FLAVOR)
+ {
+ case bfd_target_aout_flavour:
+ return target_big_endian ? "a.out-mips-big" : "a.out-mips-little";
+ case bfd_target_ecoff_flavour:
+ return target_big_endian ? "ecoff-bigmips" : ECOFF_LITTLE_FORMAT;
+ case bfd_target_elf_flavour:
+ return (target_big_endian
+ ? (mips_64 ? "elf64-bigmips" : "elf32-bigmips")
+ : (mips_64 ? "elf64-littlemips" : "elf32-littlemips"));
+ default:
+ abort ();
+ return NULL;
+ }
+}
+
+/* The name of the readonly data section. */
+#define RDATA_SECTION_NAME (OUTPUT_FLAVOR == bfd_target_aout_flavour \
+ ? ".data" \
+ : OUTPUT_FLAVOR == bfd_target_ecoff_flavour \
+ ? ".rdata" \
+ : OUTPUT_FLAVOR == bfd_target_elf_flavour \
+ ? ".rodata" \
+ : (abort (), ""))
+
+/* This is the set of options which may be modified by the .set
+ pseudo-op. We use a struct so that .set push and .set pop are more
+ reliable. */
+
+struct mips_set_options
+{
+ /* MIPS ISA (Instruction Set Architecture) level. This is set to -1
+ if it has not been initialized. Changed by `.set mipsN', and the
+ -mipsN command line option, and the default CPU. */
+ int isa;
+ /* Whether we are assembling for the mips16 processor. 0 if we are
+ not, 1 if we are, and -1 if the value has not been initialized.
+ Changed by `.set mips16' and `.set nomips16', and the -mips16 and
+ -nomips16 command line options, and the default CPU. */
+ int mips16;
+ /* Non-zero if we should not reorder instructions. Changed by `.set
+ reorder' and `.set noreorder'. */
+ int noreorder;
+ /* Non-zero if we should not permit the $at ($1) register to be used
+ in instructions. Changed by `.set at' and `.set noat'. */
+ int noat;
+ /* Non-zero if we should warn when a macro instruction expands into
+ more than one machine instruction. Changed by `.set nomacro' and
+ `.set macro'. */
+ int warn_about_macros;
+ /* Non-zero if we should not move instructions. Changed by `.set
+ move', `.set volatile', `.set nomove', and `.set novolatile'. */
+ int nomove;
+ /* Non-zero if we should not optimize branches by moving the target
+ of the branch into the delay slot. Actually, we don't perform
+ this optimization anyhow. Changed by `.set bopt' and `.set
+ nobopt'. */
+ int nobopt;
+ /* Non-zero if we should not autoextend mips16 instructions.
+ Changed by `.set autoextend' and `.set noautoextend'. */
+ int noautoextend;
+};
+
+/* This is the struct we use to hold the current set of options. Note
+ that we must set the isa and mips16 fields to -1 to indicate that
+ they have not been initialized. */
+
+static struct mips_set_options mips_opts = { -1, -1 };
+
+/* These variables are filled in with the masks of registers used.
+ The object format code reads them and puts them in the appropriate
+ place. */
+unsigned long mips_gprmask;
+unsigned long mips_cprmask[4];
+
+/* MIPS ISA we are using for this output file. */
+static int file_mips_isa;
+
+/* The CPU type as a number: 2000, 3000, 4000, 4400, etc. */
+static int mips_cpu = -1;
+
+/* The argument of the -mabi= flag. */
+static char* mips_abi_string = 0;
+
+/* Wether we should mark the file EABI64 or EABI32. */
+static int mips_eabi64 = 0;
+
+/* If they asked for mips1 or mips2 and a cpu that is
+ mips3 or greater, then mark the object file 32BITMODE. */
+static int mips_32bitmode = 0;
+
+/* Whether the processor uses hardware interlocks to protect
+ reads from the HI and LO registers, and thus does not
+ require nops to be inserted.
+
+ FIXME: GCC makes a distinction between -mcpu=FOO and -mFOO:
+ -mcpu=FOO schedules for FOO, but still produces code that meets the
+ requirements of MIPS ISA I. For example, it won't generate any
+ FOO-specific instructions, and it will still assume that any
+ scheduling hazards described in MIPS ISA I are there, even if FOO
+ has interlocks. -mFOO gives GCC permission to generate code that
+ will only run on a FOO; it will generate FOO-specific instructions,
+ and assume interlocks provided by a FOO.
+
+ However, GAS currently doesn't make this distinction; before Jan 28
+ 1999, GAS's -mcpu=FOO implied -mFOO, which violates GCC's
+ assumptions. The GCC driver passes these flags through to GAS, so
+ if GAS actually does anything that doesn't meet MIPS ISA I with
+ -mFOO, then GCC's -mcpu=FOO flag isn't going to work.
+
+ And furthermore, it did not assume that -mFOO implied -mcpu=FOO,
+ which seems senseless --- why generate code which will only run on
+ a FOO, but schedule for something else?
+
+ So now, at least, -mcpu=FOO and -mFOO are exactly equivalent.
+
+ -- Jim Blandy <jimb@cygnus.com> */
+
+#define hilo_interlocks (mips_cpu == 4010 \
+ )
+
+/* Whether the processor uses hardware interlocks to protect reads
+ from the GPRs, and thus does not require nops to be inserted. */
+#define gpr_interlocks \
+ (mips_opts.isa >= 2 \
+ || mips_cpu == 3900)
+
+/* As with other "interlocks" this is used by hardware that has FP
+ (co-processor) interlocks. */
+/* Itbl support may require additional care here. */
+#define cop_interlocks (mips_cpu == 4300 \
+ )
+
+/* MIPS PIC level. */
+
+enum mips_pic_level
+{
+ /* Do not generate PIC code. */
+ NO_PIC,
+
+ /* Generate PIC code as in Irix 4. This is not implemented, and I'm
+ not sure what it is supposed to do. */
+ IRIX4_PIC,
+
+ /* Generate PIC code as in the SVR4 MIPS ABI. */
+ SVR4_PIC,
+
+ /* Generate PIC code without using a global offset table: the data
+ segment has a maximum size of 64K, all data references are off
+ the $gp register, and all text references are PC relative. This
+ is used on some embedded systems. */
+ EMBEDDED_PIC
+};
+
+static enum mips_pic_level mips_pic;
+
+/* 1 if we should generate 32 bit offsets from the GP register in
+ SVR4_PIC mode. Currently has no meaning in other modes. */
+static int mips_big_got;
+
+/* 1 if trap instructions should used for overflow rather than break
+ instructions. */
+static int mips_trap;
+
+/* Non-zero if any .set noreorder directives were used. */
+
+static int mips_any_noreorder;
+
+/* The size of the small data section. */
+static int g_switch_value = 8;
+/* Whether the -G option was used. */
+static int g_switch_seen = 0;
+
+#define N_RMASK 0xc4
+#define N_VFP 0xd4
+
+/* If we can determine in advance that GP optimization won't be
+ possible, we can skip the relaxation stuff that tries to produce
+ GP-relative references. This makes delay slot optimization work
+ better.
+
+ This function can only provide a guess, but it seems to work for
+ gcc output. If it guesses wrong, the only loss should be in
+ efficiency; it shouldn't introduce any bugs.
+
+ I don't know if a fix is needed for the SVR4_PIC mode. I've only
+ fixed it for the non-PIC mode. KR 95/04/07 */
+static int nopic_need_relax PARAMS ((symbolS *, int));
+
+/* handle of the OPCODE hash table */
+static struct hash_control *op_hash = NULL;
+
+/* The opcode hash table we use for the mips16. */
+static struct hash_control *mips16_op_hash = NULL;
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful */
+const char comment_chars[] = "#";
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that C style comments are always supported. */
+const char line_comment_chars[] = "#";
+
+/* This array holds machine specific line separator characters. */
+const char line_separator_chars[] = "";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c . Ideally it shouldn't have to know about it at all,
+ but nothing is ideal around here.
+ */
+
+static char *insn_error;
+
+static int auto_align = 1;
+
+/* When outputting SVR4 PIC code, the assembler needs to know the
+ offset in the stack frame from which to restore the $gp register.
+ This is set by the .cprestore pseudo-op, and saved in this
+ variable. */
+static offsetT mips_cprestore_offset = -1;
+
+/* This is the register which holds the stack frame, as set by the
+ .frame pseudo-op. This is needed to implement .cprestore. */
+static int mips_frame_reg = SP;
+
+/* To output NOP instructions correctly, we need to keep information
+ about the previous two instructions. */
+
+/* Whether we are optimizing. The default value of 2 means to remove
+ unneeded NOPs and swap branch instructions when possible. A value
+ of 1 means to not swap branches. A value of 0 means to always
+ insert NOPs. */
+static int mips_optimize = 2;
+
+/* Debugging level. -g sets this to 2. -gN sets this to N. -g0 is
+ equivalent to seeing no -g option at all. */
+static int mips_debug = 0;
+
+/* The previous instruction. */
+static struct mips_cl_insn prev_insn;
+
+/* The instruction before prev_insn. */
+static struct mips_cl_insn prev_prev_insn;
+
+/* If we don't want information for prev_insn or prev_prev_insn, we
+ point the insn_mo field at this dummy integer. */
+static const struct mips_opcode dummy_opcode = { 0 };
+
+/* Non-zero if prev_insn is valid. */
+static int prev_insn_valid;
+
+/* The frag for the previous instruction. */
+static struct frag *prev_insn_frag;
+
+/* The offset into prev_insn_frag for the previous instruction. */
+static long prev_insn_where;
+
+/* The reloc type for the previous instruction, if any. */
+static bfd_reloc_code_real_type prev_insn_reloc_type;
+
+/* The reloc for the previous instruction, if any. */
+static fixS *prev_insn_fixp;
+
+/* Non-zero if the previous instruction was in a delay slot. */
+static int prev_insn_is_delay_slot;
+
+/* Non-zero if the previous instruction was in a .set noreorder. */
+static int prev_insn_unreordered;
+
+/* Non-zero if the previous instruction uses an extend opcode (if
+ mips16). */
+static int prev_insn_extended;
+
+/* Non-zero if the previous previous instruction was in a .set
+ noreorder. */
+static int prev_prev_insn_unreordered;
+
+/* If this is set, it points to a frag holding nop instructions which
+ were inserted before the start of a noreorder section. If those
+ nops turn out to be unnecessary, the size of the frag can be
+ decreased. */
+static fragS *prev_nop_frag;
+
+/* The number of nop instructions we created in prev_nop_frag. */
+static int prev_nop_frag_holds;
+
+/* The number of nop instructions that we know we need in
+ prev_nop_frag. */
+static int prev_nop_frag_required;
+
+/* The number of instructions we've seen since prev_nop_frag. */
+static int prev_nop_frag_since;
+
+/* For ECOFF and ELF, relocations against symbols are done in two
+ parts, with a HI relocation and a LO relocation. Each relocation
+ has only 16 bits of space to store an addend. This means that in
+ order for the linker to handle carries correctly, it must be able
+ to locate both the HI and the LO relocation. This means that the
+ relocations must appear in order in the relocation table.
+
+ In order to implement this, we keep track of each unmatched HI
+ relocation. We then sort them so that they immediately precede the
+ corresponding LO relocation. */
+
+struct mips_hi_fixup
+{
+ /* Next HI fixup. */
+ struct mips_hi_fixup *next;
+ /* This fixup. */
+ fixS *fixp;
+ /* The section this fixup is in. */
+ segT seg;
+};
+
+/* The list of unmatched HI relocs. */
+
+static struct mips_hi_fixup *mips_hi_fixup_list;
+
+/* Map normal MIPS register numbers to mips16 register numbers. */
+
+#define X ILLEGAL_REG
+static const int mips32_to_16_reg_map[] =
+{
+ X, X, 2, 3, 4, 5, 6, 7,
+ X, X, X, X, X, X, X, X,
+ 0, 1, X, X, X, X, X, X,
+ X, X, X, X, X, X, X, X
+};
+#undef X
+
+/* Map mips16 register numbers to normal MIPS register numbers. */
+
+static const int mips16_to_32_reg_map[] =
+{
+ 16, 17, 2, 3, 4, 5, 6, 7
+};
+
+/* Since the MIPS does not have multiple forms of PC relative
+ instructions, we do not have to do relaxing as is done on other
+ platforms. However, we do have to handle GP relative addressing
+ correctly, which turns out to be a similar problem.
+
+ Every macro that refers to a symbol can occur in (at least) two
+ forms, one with GP relative addressing and one without. For
+ example, loading a global variable into a register generally uses
+ a macro instruction like this:
+ lw $4,i
+ If i can be addressed off the GP register (this is true if it is in
+ the .sbss or .sdata section, or if it is known to be smaller than
+ the -G argument) this will generate the following instruction:
+ lw $4,i($gp)
+ This instruction will use a GPREL reloc. If i can not be addressed
+ off the GP register, the following instruction sequence will be used:
+ lui $at,i
+ lw $4,i($at)
+ In this case the first instruction will have a HI16 reloc, and the
+ second reloc will have a LO16 reloc. Both relocs will be against
+ the symbol i.
+
+ The issue here is that we may not know whether i is GP addressable
+ until after we see the instruction that uses it. Therefore, we
+ want to be able to choose the final instruction sequence only at
+ the end of the assembly. This is similar to the way other
+ platforms choose the size of a PC relative instruction only at the
+ end of assembly.
+
+ When generating position independent code we do not use GP
+ addressing in quite the same way, but the issue still arises as
+ external symbols and local symbols must be handled differently.
+
+ We handle these issues by actually generating both possible
+ instruction sequences. The longer one is put in a frag_var with
+ type rs_machine_dependent. We encode what to do with the frag in
+ the subtype field. We encode (1) the number of existing bytes to
+ replace, (2) the number of new bytes to use, (3) the offset from
+ the start of the existing bytes to the first reloc we must generate
+ (that is, the offset is applied from the start of the existing
+ bytes after they are replaced by the new bytes, if any), (4) the
+ offset from the start of the existing bytes to the second reloc,
+ (5) whether a third reloc is needed (the third reloc is always four
+ bytes after the second reloc), and (6) whether to warn if this
+ variant is used (this is sometimes needed if .set nomacro or .set
+ noat is in effect). All these numbers are reasonably small.
+
+ Generating two instruction sequences must be handled carefully to
+ ensure that delay slots are handled correctly. Fortunately, there
+ are a limited number of cases. When the second instruction
+ sequence is generated, append_insn is directed to maintain the
+ existing delay slot information, so it continues to apply to any
+ code after the second instruction sequence. This means that the
+ second instruction sequence must not impose any requirements not
+ required by the first instruction sequence.
+
+ These variant frags are then handled in functions called by the
+ machine independent code. md_estimate_size_before_relax returns
+ the final size of the frag. md_convert_frag sets up the final form
+ of the frag. tc_gen_reloc adjust the first reloc and adds a second
+ one if needed. */
+#define RELAX_ENCODE(old, new, reloc1, reloc2, reloc3, warn) \
+ ((relax_substateT) \
+ (((old) << 23) \
+ | ((new) << 16) \
+ | (((reloc1) + 64) << 9) \
+ | (((reloc2) + 64) << 2) \
+ | ((reloc3) ? (1 << 1) : 0) \
+ | ((warn) ? 1 : 0)))
+#define RELAX_OLD(i) (((i) >> 23) & 0x7f)
+#define RELAX_NEW(i) (((i) >> 16) & 0x7f)
+#define RELAX_RELOC1(i) ((bfd_vma)(((i) >> 9) & 0x7f) - 64)
+#define RELAX_RELOC2(i) ((bfd_vma)(((i) >> 2) & 0x7f) - 64)
+#define RELAX_RELOC3(i) (((i) >> 1) & 1)
+#define RELAX_WARN(i) ((i) & 1)
+
+/* For mips16 code, we use an entirely different form of relaxation.
+ mips16 supports two versions of most instructions which take
+ immediate values: a small one which takes some small value, and a
+ larger one which takes a 16 bit value. Since branches also follow
+ this pattern, relaxing these values is required.
+
+ We can assemble both mips16 and normal MIPS code in a single
+ object. Therefore, we need to support this type of relaxation at
+ the same time that we support the relaxation described above. We
+ use the high bit of the subtype field to distinguish these cases.
+
+ The information we store for this type of relaxation is the
+ argument code found in the opcode file for this relocation, whether
+ the user explicitly requested a small or extended form, and whether
+ the relocation is in a jump or jal delay slot. That tells us the
+ size of the value, and how it should be stored. We also store
+ whether the fragment is considered to be extended or not. We also
+ store whether this is known to be a branch to a different section,
+ whether we have tried to relax this frag yet, and whether we have
+ ever extended a PC relative fragment because of a shift count. */
+#define RELAX_MIPS16_ENCODE(type, small, ext, dslot, jal_dslot) \
+ (0x80000000 \
+ | ((type) & 0xff) \
+ | ((small) ? 0x100 : 0) \
+ | ((ext) ? 0x200 : 0) \
+ | ((dslot) ? 0x400 : 0) \
+ | ((jal_dslot) ? 0x800 : 0))
+#define RELAX_MIPS16_P(i) (((i) & 0x80000000) != 0)
+#define RELAX_MIPS16_TYPE(i) ((i) & 0xff)
+#define RELAX_MIPS16_USER_SMALL(i) (((i) & 0x100) != 0)
+#define RELAX_MIPS16_USER_EXT(i) (((i) & 0x200) != 0)
+#define RELAX_MIPS16_DSLOT(i) (((i) & 0x400) != 0)
+#define RELAX_MIPS16_JAL_DSLOT(i) (((i) & 0x800) != 0)
+#define RELAX_MIPS16_EXTENDED(i) (((i) & 0x1000) != 0)
+#define RELAX_MIPS16_MARK_EXTENDED(i) ((i) | 0x1000)
+#define RELAX_MIPS16_CLEAR_EXTENDED(i) ((i) &~ 0x1000)
+#define RELAX_MIPS16_LONG_BRANCH(i) (((i) & 0x2000) != 0)
+#define RELAX_MIPS16_MARK_LONG_BRANCH(i) ((i) | 0x2000)
+#define RELAX_MIPS16_CLEAR_LONG_BRANCH(i) ((i) &~ 0x2000)
+
+/* Prototypes for static functions. */
+
+#ifdef __STDC__
+#define internalError() \
+ as_fatal (_("internal Error, line %d, %s"), __LINE__, __FILE__)
+#else
+#define internalError() as_fatal (_("MIPS internal Error"));
+#endif
+
+enum mips_regclass { MIPS_GR_REG, MIPS_FP_REG, MIPS16_REG };
+
+static int insn_uses_reg PARAMS ((struct mips_cl_insn *ip,
+ unsigned int reg, enum mips_regclass class));
+static int reg_needs_delay PARAMS ((int));
+static void mips16_mark_labels PARAMS ((void));
+static void append_insn PARAMS ((char *place,
+ struct mips_cl_insn * ip,
+ expressionS * p,
+ bfd_reloc_code_real_type r,
+ boolean));
+static void mips_no_prev_insn PARAMS ((int));
+static void mips_emit_delays PARAMS ((boolean));
+#ifdef USE_STDARG
+static void macro_build PARAMS ((char *place, int *counter, expressionS * ep,
+ const char *name, const char *fmt,
+ ...));
+#else
+static void macro_build ();
+#endif
+static void mips16_macro_build PARAMS ((char *, int *, expressionS *,
+ const char *, const char *,
+ va_list));
+static void macro_build_lui PARAMS ((char *place, int *counter,
+ expressionS * ep, int regnum));
+static void set_at PARAMS ((int *counter, int reg, int unsignedp));
+static void check_absolute_expr PARAMS ((struct mips_cl_insn * ip,
+ expressionS *));
+static void load_register PARAMS ((int *, int, expressionS *, int));
+static void load_address PARAMS ((int *counter, int reg, expressionS *ep));
+static void macro PARAMS ((struct mips_cl_insn * ip));
+static void mips16_macro PARAMS ((struct mips_cl_insn * ip));
+#ifdef LOSING_COMPILER
+static void macro2 PARAMS ((struct mips_cl_insn * ip));
+#endif
+static void mips_ip PARAMS ((char *str, struct mips_cl_insn * ip));
+static void mips16_ip PARAMS ((char *str, struct mips_cl_insn * ip));
+static void mips16_immed PARAMS ((char *, unsigned int, int, offsetT, boolean,
+ boolean, boolean, unsigned long *,
+ boolean *, unsigned short *));
+static int my_getSmallExpression PARAMS ((expressionS * ep, char *str));
+static void my_getExpression PARAMS ((expressionS * ep, char *str));
+static symbolS *get_symbol PARAMS ((void));
+static void mips_align PARAMS ((int to, int fill, symbolS *label));
+static void s_align PARAMS ((int));
+static void s_change_sec PARAMS ((int));
+static void s_cons PARAMS ((int));
+static void s_float_cons PARAMS ((int));
+static void s_mips_globl PARAMS ((int));
+static void s_option PARAMS ((int));
+static void s_mipsset PARAMS ((int));
+static void s_abicalls PARAMS ((int));
+static void s_cpload PARAMS ((int));
+static void s_cprestore PARAMS ((int));
+static void s_gpword PARAMS ((int));
+static void s_cpadd PARAMS ((int));
+static void s_insn PARAMS ((int));
+static void md_obj_begin PARAMS ((void));
+static void md_obj_end PARAMS ((void));
+static long get_number PARAMS ((void));
+static void s_mips_ent PARAMS ((int));
+static void s_mips_end PARAMS ((int));
+static void s_mips_frame PARAMS ((int));
+static void s_mips_mask PARAMS ((int));
+static void s_mips_stab PARAMS ((int));
+static void s_mips_weakext PARAMS ((int));
+static void s_file PARAMS ((int));
+static int mips16_extended_frag PARAMS ((fragS *, asection *, long));
+
+
+static int validate_mips_insn PARAMS ((const struct mips_opcode *));
+
+/* Pseudo-op table.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book
+ should be defined here, but are currently unsupported: .alias,
+ .galive, .gjaldef, .gjrlive, .livereg, .noalias.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book are
+ specific to the type of debugging information being generated, and
+ should be defined by the object format: .aent, .begin, .bend,
+ .bgnb, .end, .endb, .ent, .fmask, .frame, .loc, .mask, .verstamp,
+ .vreg.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book are
+ not MIPS CPU specific, but are also not specific to the object file
+ format. This file is probably the best place to define them, but
+ they are not currently supported: .asm0, .endr, .lab, .repeat,
+ .struct. */
+
+static const pseudo_typeS mips_pseudo_table[] =
+{
+ /* MIPS specific pseudo-ops. */
+ {"option", s_option, 0},
+ {"set", s_mipsset, 0},
+ {"rdata", s_change_sec, 'r'},
+ {"sdata", s_change_sec, 's'},
+ {"livereg", s_ignore, 0},
+ {"abicalls", s_abicalls, 0},
+ {"cpload", s_cpload, 0},
+ {"cprestore", s_cprestore, 0},
+ {"gpword", s_gpword, 0},
+ {"cpadd", s_cpadd, 0},
+ {"insn", s_insn, 0},
+
+ /* Relatively generic pseudo-ops that happen to be used on MIPS
+ chips. */
+ {"asciiz", stringer, 1},
+ {"bss", s_change_sec, 'b'},
+ {"err", s_err, 0},
+ {"half", s_cons, 1},
+ {"dword", s_cons, 3},
+ {"weakext", s_mips_weakext, 0},
+
+ /* These pseudo-ops are defined in read.c, but must be overridden
+ here for one reason or another. */
+ {"align", s_align, 0},
+ {"byte", s_cons, 0},
+ {"data", s_change_sec, 'd'},
+ {"double", s_float_cons, 'd'},
+ {"float", s_float_cons, 'f'},
+ {"globl", s_mips_globl, 0},
+ {"global", s_mips_globl, 0},
+ {"hword", s_cons, 1},
+ {"int", s_cons, 2},
+ {"long", s_cons, 2},
+ {"octa", s_cons, 4},
+ {"quad", s_cons, 3},
+ {"short", s_cons, 1},
+ {"single", s_float_cons, 'f'},
+ {"stabn", s_mips_stab, 'n'},
+ {"text", s_change_sec, 't'},
+ {"word", s_cons, 2},
+ { 0 },
+};
+
+static const pseudo_typeS mips_nonecoff_pseudo_table[] = {
+ /* These pseudo-ops should be defined by the object file format.
+ However, a.out doesn't support them, so we have versions here. */
+ {"aent", s_mips_ent, 1},
+ {"bgnb", s_ignore, 0},
+ {"end", s_mips_end, 0},
+ {"endb", s_ignore, 0},
+ {"ent", s_mips_ent, 0},
+ {"file", s_file, 0},
+ {"fmask", s_mips_mask, 'F'},
+ {"frame", s_mips_frame, 0},
+ {"loc", s_ignore, 0},
+ {"mask", s_mips_mask, 'R'},
+ {"verstamp", s_ignore, 0},
+ { 0 },
+};
+
+extern void pop_insert PARAMS ((const pseudo_typeS *));
+
+void
+mips_pop_insert ()
+{
+ pop_insert (mips_pseudo_table);
+ if (! ECOFF_DEBUGGING)
+ pop_insert (mips_nonecoff_pseudo_table);
+}
+
+/* Symbols labelling the current insn. */
+
+struct insn_label_list
+{
+ struct insn_label_list *next;
+ symbolS *label;
+};
+
+static struct insn_label_list *insn_labels;
+static struct insn_label_list *free_insn_labels;
+
+static void mips_clear_insn_labels PARAMS ((void));
+
+static inline void
+mips_clear_insn_labels ()
+{
+ register struct insn_label_list **pl;
+
+ for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next)
+ ;
+ *pl = insn_labels;
+ insn_labels = NULL;
+}
+
+static char *expr_end;
+
+/* Expressions which appear in instructions. These are set by
+ mips_ip. */
+
+static expressionS imm_expr;
+static expressionS offset_expr;
+
+/* Relocs associated with imm_expr and offset_expr. */
+
+static bfd_reloc_code_real_type imm_reloc;
+static bfd_reloc_code_real_type offset_reloc;
+
+/* This is set by mips_ip if imm_reloc is an unmatched HI16_S reloc. */
+
+static boolean imm_unmatched_hi;
+
+/* These are set by mips16_ip if an explicit extension is used. */
+
+static boolean mips16_small, mips16_ext;
+
+#ifdef MIPS_STABS_ELF
+/* The pdr segment for per procedure frame/regmask info */
+
+static segT pdr_seg;
+#endif
+
+/*
+ * This function is called once, at assembler startup time. It should
+ * set up all the tables, etc. that the MD part of the assembler will need.
+ */
+void
+md_begin ()
+{
+ boolean ok = false;
+ register const char *retval = NULL;
+ register unsigned int i = 0;
+ const char *cpu;
+ char *a = NULL;
+ int broken = 0;
+ int mips_isa_from_cpu;
+
+ cpu = TARGET_CPU;
+ if (strcmp (cpu + (sizeof TARGET_CPU) - 3, "el") == 0)
+ {
+ a = xmalloc (sizeof TARGET_CPU);
+ strcpy (a, TARGET_CPU);
+ a[(sizeof TARGET_CPU) - 3] = '\0';
+ cpu = a;
+ }
+
+ if (mips_cpu < 0)
+ {
+ /* Set mips_cpu based on TARGET_CPU, unless TARGET_CPU is
+ just the generic 'mips', in which case set mips_cpu based
+ on the given ISA, if any. */
+
+ if (strcmp (cpu, "mips") == 0)
+ {
+ if (mips_opts.isa < 0)
+ mips_cpu = 3000;
+
+ else if (mips_opts.isa == 2)
+ mips_cpu = 6000;
+
+ else if (mips_opts.isa == 3)
+ mips_cpu = 4000;
+
+ else if (mips_opts.isa == 4)
+ mips_cpu = 8000;
+
+ else
+ mips_cpu = 3000;
+ }
+
+ else if (strcmp (cpu, "r3900") == 0
+ || strcmp (cpu, "mipstx39") == 0
+ )
+ mips_cpu = 3900;
+
+ else if (strcmp (cpu, "r6000") == 0
+ || strcmp (cpu, "mips2") == 0)
+ mips_cpu = 6000;
+
+ else if (strcmp (cpu, "mips64") == 0
+ || strcmp (cpu, "r4000") == 0
+ || strcmp (cpu, "mips3") == 0)
+ mips_cpu = 4000;
+
+ else if (strcmp (cpu, "r4400") == 0)
+ mips_cpu = 4400;
+
+ else if (strcmp (cpu, "mips64orion") == 0
+ || strcmp (cpu, "r4600") == 0)
+ mips_cpu = 4600;
+
+ else if (strcmp (cpu, "r4650") == 0)
+ mips_cpu = 4650;
+
+ else if (strcmp (cpu, "mips64vr4300") == 0)
+ mips_cpu = 4300;
+
+ else if (strcmp (cpu, "mips64vr4111") == 0)
+ mips_cpu = 4111;
+
+ else if (strcmp (cpu, "mips64vr4100") == 0)
+ mips_cpu = 4100;
+
+ else if (strcmp (cpu, "r4010") == 0)
+ mips_cpu = 4010;
+
+
+ else if (strcmp (cpu, "r5000") == 0
+ || strcmp (cpu, "mips64vr5000") == 0)
+ mips_cpu = 5000;
+
+
+
+ else if (strcmp (cpu, "r8000") == 0
+ || strcmp (cpu, "mips4") == 0)
+ mips_cpu = 8000;
+
+ else if (strcmp (cpu, "r10000") == 0)
+ mips_cpu = 10000;
+
+ else if (strcmp (cpu, "mips16") == 0)
+ mips_cpu = 0; /* FIXME */
+
+ else
+ mips_cpu = 3000;
+ }
+
+ if (mips_cpu == 3000
+ || mips_cpu == 3900)
+ mips_isa_from_cpu = 1;
+
+ else if (mips_cpu == 6000
+ || mips_cpu == 4010)
+ mips_isa_from_cpu = 2;
+
+ else if (mips_cpu == 4000
+ || mips_cpu == 4100
+ || mips_cpu == 4111
+ || mips_cpu == 4400
+ || mips_cpu == 4300
+ || mips_cpu == 4600
+ || mips_cpu == 4650)
+ mips_isa_from_cpu = 3;
+
+ else if (mips_cpu == 5000
+ || mips_cpu == 8000
+ || mips_cpu == 10000)
+ mips_isa_from_cpu = 4;
+
+ else
+ mips_isa_from_cpu = -1;
+
+ if (mips_opts.isa == -1)
+ {
+ if (mips_isa_from_cpu != -1)
+ mips_opts.isa = mips_isa_from_cpu;
+ else
+ mips_opts.isa = 1;
+ }
+
+ if (mips_opts.mips16 < 0)
+ {
+ if (strncmp (TARGET_CPU, "mips16", sizeof "mips16" - 1) == 0)
+ mips_opts.mips16 = 1;
+ else
+ mips_opts.mips16 = 0;
+ }
+
+ /* End of TARGET_CPU processing, get rid of malloced memory
+ if necessary. */
+ cpu = NULL;
+ if (a != NULL)
+ {
+ free (a);
+ a = NULL;
+ }
+
+ if (mips_opts.isa < 2 && mips_trap)
+ as_bad (_("trap exception not supported at ISA 1"));
+
+ /* Set the EABI kind based on the ISA before the user gets
+ to change the ISA with directives. This isn't really
+ the best, but then neither is basing the abi on the isa. */
+ if (mips_opts.isa > 2
+ && mips_abi_string
+ && 0 == strcmp (mips_abi_string,"eabi"))
+ mips_eabi64 = 1;
+
+ if (mips_cpu != 0 && mips_cpu != -1)
+ {
+ ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, mips_cpu);
+
+ /* If they asked for mips1 or mips2 and a cpu that is
+ mips3 or greater, then mark the object file 32BITMODE. */
+ if (mips_isa_from_cpu != -1
+ && mips_opts.isa <= 2 && mips_isa_from_cpu > 2)
+ mips_32bitmode = 1;
+ }
+ else
+ {
+ switch (mips_opts.isa)
+ {
+ case 1:
+ ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, 3000);
+ break;
+ case 2:
+ ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, 6000);
+ break;
+ case 3:
+ ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, 4000);
+ break;
+ case 4:
+ ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, 8000);
+ break;
+ }
+ }
+
+ if (! ok)
+ as_warn (_("Could not set architecture and machine"));
+
+ file_mips_isa = mips_opts.isa;
+
+ op_hash = hash_new ();
+
+ for (i = 0; i < NUMOPCODES;)
+ {
+ const char *name = mips_opcodes[i].name;
+
+ retval = hash_insert (op_hash, name, (PTR) &mips_opcodes[i]);
+ if (retval != NULL)
+ {
+ fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
+ mips_opcodes[i].name, retval);
+ /* Probably a memory allocation problem? Give up now. */
+ as_fatal (_("Broken assembler. No assembly attempted."));
+ }
+ do
+ {
+ if (mips_opcodes[i].pinfo != INSN_MACRO)
+ {
+ if (!validate_mips_insn (&mips_opcodes[i]))
+ broken = 1;
+ }
+ ++i;
+ }
+ while ((i < NUMOPCODES) && !strcmp (mips_opcodes[i].name, name));
+ }
+
+ mips16_op_hash = hash_new ();
+
+ i = 0;
+ while (i < bfd_mips16_num_opcodes)
+ {
+ const char *name = mips16_opcodes[i].name;
+
+ retval = hash_insert (mips16_op_hash, name, (PTR) &mips16_opcodes[i]);
+ if (retval != NULL)
+ as_fatal (_("internal: can't hash `%s': %s"),
+ mips16_opcodes[i].name, retval);
+ do
+ {
+ if (mips16_opcodes[i].pinfo != INSN_MACRO
+ && ((mips16_opcodes[i].match & mips16_opcodes[i].mask)
+ != mips16_opcodes[i].match))
+ {
+ fprintf (stderr, _("internal error: bad mips16 opcode: %s %s\n"),
+ mips16_opcodes[i].name, mips16_opcodes[i].args);
+ broken = 1;
+ }
+ ++i;
+ }
+ while (i < bfd_mips16_num_opcodes
+ && strcmp (mips16_opcodes[i].name, name) == 0);
+ }
+
+ if (broken)
+ as_fatal (_("Broken assembler. No assembly attempted."));
+
+ /* We add all the general register names to the symbol table. This
+ helps us detect invalid uses of them. */
+ for (i = 0; i < 32; i++)
+ {
+ char buf[5];
+
+ sprintf (buf, "$%d", i);
+ symbol_table_insert (symbol_new (buf, reg_section, i,
+ &zero_address_frag));
+ }
+ symbol_table_insert (symbol_new ("$fp", reg_section, FP,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("$sp", reg_section, SP,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("$gp", reg_section, GP,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("$at", reg_section, AT,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("$kt0", reg_section, KT0,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("$kt1", reg_section, KT1,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("$pc", reg_section, -1,
+ &zero_address_frag));
+
+ mips_no_prev_insn (false);
+
+ mips_gprmask = 0;
+ mips_cprmask[0] = 0;
+ mips_cprmask[1] = 0;
+ mips_cprmask[2] = 0;
+ mips_cprmask[3] = 0;
+
+ /* set the default alignment for the text section (2**2) */
+ record_alignment (text_section, 2);
+
+ if (USE_GLOBAL_POINTER_OPT)
+ bfd_set_gp_size (stdoutput, g_switch_value);
+
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ {
+ /* On a native system, sections must be aligned to 16 byte
+ boundaries. When configured for an embedded ELF target, we
+ don't bother. */
+ if (strcmp (TARGET_OS, "elf") != 0)
+ {
+ (void) bfd_set_section_alignment (stdoutput, text_section, 4);
+ (void) bfd_set_section_alignment (stdoutput, data_section, 4);
+ (void) bfd_set_section_alignment (stdoutput, bss_section, 4);
+ }
+
+ /* Create a .reginfo section for register masks and a .mdebug
+ section for debugging information. */
+ {
+ segT seg;
+ subsegT subseg;
+ flagword flags;
+ segT sec;
+
+ seg = now_seg;
+ subseg = now_subseg;
+
+ /* The ABI says this section should be loaded so that the
+ running program can access it. However, we don't load it
+ if we are configured for an embedded target */
+ flags = SEC_READONLY | SEC_DATA;
+ if (strcmp (TARGET_OS, "elf") != 0)
+ flags |= SEC_ALLOC | SEC_LOAD;
+
+ if (! mips_64)
+ {
+ sec = subseg_new (".reginfo", (subsegT) 0);
+
+
+ (void) bfd_set_section_flags (stdoutput, sec, flags);
+ (void) bfd_set_section_alignment (stdoutput, sec, 2);
+
+#ifdef OBJ_ELF
+ mips_regmask_frag = frag_more (sizeof (Elf32_External_RegInfo));
+#endif
+ }
+ else
+ {
+ /* The 64-bit ABI uses a .MIPS.options section rather than
+ .reginfo section. */
+ sec = subseg_new (".MIPS.options", (subsegT) 0);
+ (void) bfd_set_section_flags (stdoutput, sec, flags);
+ (void) bfd_set_section_alignment (stdoutput, sec, 3);
+
+#ifdef OBJ_ELF
+ /* Set up the option header. */
+ {
+ Elf_Internal_Options opthdr;
+ char *f;
+
+ opthdr.kind = ODK_REGINFO;
+ opthdr.size = (sizeof (Elf_External_Options)
+ + sizeof (Elf64_External_RegInfo));
+ opthdr.section = 0;
+ opthdr.info = 0;
+ f = frag_more (sizeof (Elf_External_Options));
+ bfd_mips_elf_swap_options_out (stdoutput, &opthdr,
+ (Elf_External_Options *) f);
+
+ mips_regmask_frag = frag_more (sizeof (Elf64_External_RegInfo));
+ }
+#endif
+ }
+
+ if (ECOFF_DEBUGGING)
+ {
+ sec = subseg_new (".mdebug", (subsegT) 0);
+ (void) bfd_set_section_flags (stdoutput, sec,
+ SEC_HAS_CONTENTS | SEC_READONLY);
+ (void) bfd_set_section_alignment (stdoutput, sec, 2);
+ }
+
+#ifdef MIPS_STABS_ELF
+ pdr_seg = subseg_new (".pdr", (subsegT) 0);
+ (void) bfd_set_section_flags (stdoutput, pdr_seg,
+ SEC_READONLY | SEC_RELOC | SEC_DEBUGGING);
+ (void) bfd_set_section_alignment (stdoutput, pdr_seg, 2);
+#endif
+
+ subseg_set (seg, subseg);
+ }
+ }
+
+ if (! ECOFF_DEBUGGING)
+ md_obj_begin ();
+}
+
+void
+md_mips_end ()
+{
+ if (! ECOFF_DEBUGGING)
+ md_obj_end ();
+}
+
+void
+md_assemble (str)
+ char *str;
+{
+ struct mips_cl_insn insn;
+
+ imm_expr.X_op = O_absent;
+ imm_reloc = BFD_RELOC_UNUSED;
+ imm_unmatched_hi = false;
+ offset_expr.X_op = O_absent;
+ offset_reloc = BFD_RELOC_UNUSED;
+
+ if (mips_opts.mips16)
+ mips16_ip (str, &insn);
+ else
+ {
+ mips_ip (str, &insn);
+ DBG((_("returned from mips_ip(%s) insn_opcode = 0x%x\n"),
+ str, insn.insn_opcode));
+ }
+
+ if (insn_error)
+ {
+ as_bad ("%s `%s'", insn_error, str);
+ return;
+ }
+
+ if (insn.insn_mo->pinfo == INSN_MACRO)
+ {
+ if (mips_opts.mips16)
+ mips16_macro (&insn);
+ else
+ macro (&insn);
+ }
+ else
+ {
+ if (imm_expr.X_op != O_absent)
+ append_insn ((char *) NULL, &insn, &imm_expr, imm_reloc,
+ imm_unmatched_hi);
+ else if (offset_expr.X_op != O_absent)
+ append_insn ((char *) NULL, &insn, &offset_expr, offset_reloc, false);
+ else
+ append_insn ((char *) NULL, &insn, NULL, BFD_RELOC_UNUSED, false);
+ }
+}
+
+/* See whether instruction IP reads register REG. CLASS is the type
+ of register. */
+
+static int
+insn_uses_reg (ip, reg, class)
+ struct mips_cl_insn *ip;
+ unsigned int reg;
+ enum mips_regclass class;
+{
+ if (class == MIPS16_REG)
+ {
+ assert (mips_opts.mips16);
+ reg = mips16_to_32_reg_map[reg];
+ class = MIPS_GR_REG;
+ }
+
+ /* Don't report on general register 0, since it never changes. */
+ if (class == MIPS_GR_REG && reg == 0)
+ return 0;
+
+ if (class == MIPS_FP_REG)
+ {
+ assert (! mips_opts.mips16);
+ /* If we are called with either $f0 or $f1, we must check $f0.
+ This is not optimal, because it will introduce an unnecessary
+ NOP between "lwc1 $f0" and "swc1 $f1". To fix this we would
+ need to distinguish reading both $f0 and $f1 or just one of
+ them. Note that we don't have to check the other way,
+ because there is no instruction that sets both $f0 and $f1
+ and requires a delay. */
+ if ((ip->insn_mo->pinfo & INSN_READ_FPR_S)
+ && ((((ip->insn_opcode >> OP_SH_FS) & OP_MASK_FS) &~(unsigned)1)
+ == (reg &~ (unsigned) 1)))
+ return 1;
+ if ((ip->insn_mo->pinfo & INSN_READ_FPR_T)
+ && ((((ip->insn_opcode >> OP_SH_FT) & OP_MASK_FT) &~(unsigned)1)
+ == (reg &~ (unsigned) 1)))
+ return 1;
+ }
+ else if (! mips_opts.mips16)
+ {
+ if ((ip->insn_mo->pinfo & INSN_READ_GPR_S)
+ && ((ip->insn_opcode >> OP_SH_RS) & OP_MASK_RS) == reg)
+ return 1;
+ if ((ip->insn_mo->pinfo & INSN_READ_GPR_T)
+ && ((ip->insn_opcode >> OP_SH_RT) & OP_MASK_RT) == reg)
+ return 1;
+ }
+ else
+ {
+ if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_X)
+ && (mips16_to_32_reg_map[((ip->insn_opcode >> MIPS16OP_SH_RX)
+ & MIPS16OP_MASK_RX)]
+ == reg))
+ return 1;
+ if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Y)
+ && (mips16_to_32_reg_map[((ip->insn_opcode >> MIPS16OP_SH_RY)
+ & MIPS16OP_MASK_RY)]
+ == reg))
+ return 1;
+ if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Z)
+ && (mips16_to_32_reg_map[((ip->insn_opcode >> MIPS16OP_SH_MOVE32Z)
+ & MIPS16OP_MASK_MOVE32Z)]
+ == reg))
+ return 1;
+ if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_T) && reg == TREG)
+ return 1;
+ if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_SP) && reg == SP)
+ return 1;
+ if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_31) && reg == RA)
+ return 1;
+ if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_GPR_X)
+ && ((ip->insn_opcode >> MIPS16OP_SH_REGR32)
+ & MIPS16OP_MASK_REGR32) == reg)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* This function returns true if modifying a register requires a
+ delay. */
+
+static int
+reg_needs_delay (reg)
+ int reg;
+{
+ unsigned long prev_pinfo;
+
+ prev_pinfo = prev_insn.insn_mo->pinfo;
+ if (! mips_opts.noreorder
+ && mips_opts.isa < 4
+ && ((prev_pinfo & INSN_LOAD_COPROC_DELAY)
+ || (! gpr_interlocks
+ && (prev_pinfo & INSN_LOAD_MEMORY_DELAY))))
+ {
+ /* A load from a coprocessor or from memory. All load
+ delays delay the use of general register rt for one
+ instruction on the r3000. The r6000 and r4000 use
+ interlocks. */
+ /* Itbl support may require additional care here. */
+ know (prev_pinfo & INSN_WRITE_GPR_T);
+ if (reg == ((prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Mark instruction labels in mips16 mode. This permits the linker to
+ handle them specially, such as generating jalx instructions when
+ needed. We also make them odd for the duration of the assembly, in
+ order to generate the right sort of code. We will make them even
+ in the adjust_symtab routine, while leaving them marked. This is
+ convenient for the debugger and the disassembler. The linker knows
+ to make them odd again. */
+
+static void
+mips16_mark_labels ()
+{
+ if (mips_opts.mips16)
+ {
+ struct insn_label_list *l;
+
+ for (l = insn_labels; l != NULL; l = l->next)
+ {
+#ifdef OBJ_ELF
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ S_SET_OTHER (l->label, STO_MIPS16);
+#endif
+ if ((l->label->sy_value.X_add_number & 1) == 0)
+ ++l->label->sy_value.X_add_number;
+ }
+ }
+}
+
+/* Output an instruction. PLACE is where to put the instruction; if
+ it is NULL, this uses frag_more to get room. IP is the instruction
+ information. ADDRESS_EXPR is an operand of the instruction to be
+ used with RELOC_TYPE. */
+
+static void
+append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
+ char *place;
+ struct mips_cl_insn *ip;
+ expressionS *address_expr;
+ bfd_reloc_code_real_type reloc_type;
+ boolean unmatched_hi;
+{
+ register unsigned long prev_pinfo, pinfo;
+ char *f;
+ fixS *fixp;
+ int nops = 0;
+
+ /* Mark instruction labels in mips16 mode. */
+ if (mips_opts.mips16)
+ mips16_mark_labels ();
+
+ prev_pinfo = prev_insn.insn_mo->pinfo;
+ pinfo = ip->insn_mo->pinfo;
+
+ if (place == NULL && (! mips_opts.noreorder || prev_nop_frag != NULL))
+ {
+ int prev_prev_nop;
+
+ /* If the previous insn required any delay slots, see if we need
+ to insert a NOP or two. There are eight kinds of possible
+ hazards, of which an instruction can have at most one type.
+ (1) a load from memory delay
+ (2) a load from a coprocessor delay
+ (3) an unconditional branch delay
+ (4) a conditional branch delay
+ (5) a move to coprocessor register delay
+ (6) a load coprocessor register from memory delay
+ (7) a coprocessor condition code delay
+ (8) a HI/LO special register delay
+
+ There are a lot of optimizations we could do that we don't.
+ In particular, we do not, in general, reorder instructions.
+ If you use gcc with optimization, it will reorder
+ instructions and generally do much more optimization then we
+ do here; repeating all that work in the assembler would only
+ benefit hand written assembly code, and does not seem worth
+ it. */
+
+ /* This is how a NOP is emitted. */
+#define emit_nop() \
+ (mips_opts.mips16 \
+ ? md_number_to_chars (frag_more (2), 0x6500, 2) \
+ : md_number_to_chars (frag_more (4), 0, 4))
+
+ /* The previous insn might require a delay slot, depending upon
+ the contents of the current insn. */
+ if (! mips_opts.mips16
+ && mips_opts.isa < 4
+ && (((prev_pinfo & INSN_LOAD_COPROC_DELAY)
+ && ! cop_interlocks)
+ || (! gpr_interlocks
+ && (prev_pinfo & INSN_LOAD_MEMORY_DELAY))))
+ {
+ /* A load from a coprocessor or from memory. All load
+ delays delay the use of general register rt for one
+ instruction on the r3000. The r6000 and r4000 use
+ interlocks. */
+ /* Itbl support may require additional care here. */
+ know (prev_pinfo & INSN_WRITE_GPR_T);
+ if (mips_optimize == 0
+ || insn_uses_reg (ip,
+ ((prev_insn.insn_opcode >> OP_SH_RT)
+ & OP_MASK_RT),
+ MIPS_GR_REG))
+ ++nops;
+ }
+ else if (! mips_opts.mips16
+ && mips_opts.isa < 4
+ && (((prev_pinfo & INSN_COPROC_MOVE_DELAY)
+ && ! cop_interlocks)
+ || (mips_opts.isa < 2
+ && (prev_pinfo & INSN_COPROC_MEMORY_DELAY))))
+ {
+ /* A generic coprocessor delay. The previous instruction
+ modified a coprocessor general or control register. If
+ it modified a control register, we need to avoid any
+ coprocessor instruction (this is probably not always
+ required, but it sometimes is). If it modified a general
+ register, we avoid using that register.
+
+ On the r6000 and r4000 loading a coprocessor register
+ from memory is interlocked, and does not require a delay.
+
+ This case is not handled very well. There is no special
+ knowledge of CP0 handling, and the coprocessors other
+ than the floating point unit are not distinguished at
+ all. */
+ /* Itbl support may require additional care here. FIXME!
+ Need to modify this to include knowledge about
+ user specified delays! */
+ if (prev_pinfo & INSN_WRITE_FPR_T)
+ {
+ if (mips_optimize == 0
+ || insn_uses_reg (ip,
+ ((prev_insn.insn_opcode >> OP_SH_FT)
+ & OP_MASK_FT),
+ MIPS_FP_REG))
+ ++nops;
+ }
+ else if (prev_pinfo & INSN_WRITE_FPR_S)
+ {
+ if (mips_optimize == 0
+ || insn_uses_reg (ip,
+ ((prev_insn.insn_opcode >> OP_SH_FS)
+ & OP_MASK_FS),
+ MIPS_FP_REG))
+ ++nops;
+ }
+ else
+ {
+ /* We don't know exactly what the previous instruction
+ does. If the current instruction uses a coprocessor
+ register, we must insert a NOP. If previous
+ instruction may set the condition codes, and the
+ current instruction uses them, we must insert two
+ NOPS. */
+ /* Itbl support may require additional care here. */
+ if (mips_optimize == 0
+ || ((prev_pinfo & INSN_WRITE_COND_CODE)
+ && (pinfo & INSN_READ_COND_CODE)))
+ nops += 2;
+ else if (pinfo & INSN_COP)
+ ++nops;
+ }
+ }
+ else if (! mips_opts.mips16
+ && mips_opts.isa < 4
+ && (prev_pinfo & INSN_WRITE_COND_CODE)
+ && ! cop_interlocks)
+ {
+ /* The previous instruction sets the coprocessor condition
+ codes, but does not require a general coprocessor delay
+ (this means it is a floating point comparison
+ instruction). If this instruction uses the condition
+ codes, we need to insert a single NOP. */
+ /* Itbl support may require additional care here. */
+ if (mips_optimize == 0
+ || (pinfo & INSN_READ_COND_CODE))
+ ++nops;
+ }
+ else if (prev_pinfo & INSN_READ_LO)
+ {
+ /* The previous instruction reads the LO register; if the
+ current instruction writes to the LO register, we must
+ insert two NOPS. Some newer processors have interlocks.
+ Also the tx39's multiply instructions can be exectuted
+ immediatly after a read from HI/LO (without the delay),
+ though the tx39's divide insns still do require the
+ delay. */
+ if (! (hilo_interlocks
+ || (mips_cpu == 3900 && (pinfo & INSN_MULT)))
+ && (mips_optimize == 0
+ || (pinfo & INSN_WRITE_LO)))
+ nops += 2;
+ /* Most mips16 branch insns don't have a delay slot.
+ If a read from LO is immediately followed by a branch
+ to a write to LO we have a read followed by a write
+ less than 2 insns away. We assume the target of
+ a branch might be a write to LO, and insert a nop
+ between a read and an immediately following branch. */
+ else if (mips_opts.mips16
+ && (mips_optimize == 0
+ || (pinfo & MIPS16_INSN_BRANCH)))
+ nops += 1;
+ }
+ else if (prev_insn.insn_mo->pinfo & INSN_READ_HI)
+ {
+ /* The previous instruction reads the HI register; if the
+ current instruction writes to the HI register, we must
+ insert a NOP. Some newer processors have interlocks.
+ Also the note tx39's multiply above. */
+ if (! (hilo_interlocks
+ || (mips_cpu == 3900 && (pinfo & INSN_MULT)))
+ && (mips_optimize == 0
+ || (pinfo & INSN_WRITE_HI)))
+ nops += 2;
+ /* Most mips16 branch insns don't have a delay slot.
+ If a read from HI is immediately followed by a branch
+ to a write to HI we have a read followed by a write
+ less than 2 insns away. We assume the target of
+ a branch might be a write to HI, and insert a nop
+ between a read and an immediately following branch. */
+ else if (mips_opts.mips16
+ && (mips_optimize == 0
+ || (pinfo & MIPS16_INSN_BRANCH)))
+ nops += 1;
+ }
+
+ /* If the previous instruction was in a noreorder section, then
+ we don't want to insert the nop after all. */
+ /* Itbl support may require additional care here. */
+ if (prev_insn_unreordered)
+ nops = 0;
+
+ /* There are two cases which require two intervening
+ instructions: 1) setting the condition codes using a move to
+ coprocessor instruction which requires a general coprocessor
+ delay and then reading the condition codes 2) reading the HI
+ or LO register and then writing to it (except on processors
+ which have interlocks). If we are not already emitting a NOP
+ instruction, we must check for these cases compared to the
+ instruction previous to the previous instruction. */
+ if ((! mips_opts.mips16
+ && mips_opts.isa < 4
+ && (prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY)
+ && (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
+ && (pinfo & INSN_READ_COND_CODE)
+ && ! cop_interlocks)
+ || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)
+ && (pinfo & INSN_WRITE_LO)
+ && ! (hilo_interlocks
+ || (mips_cpu == 3900 && (pinfo & INSN_MULT))))
+ || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
+ && (pinfo & INSN_WRITE_HI)
+ && ! (hilo_interlocks
+ || (mips_cpu == 3900 && (pinfo & INSN_MULT)))))
+ prev_prev_nop = 1;
+ else
+ prev_prev_nop = 0;
+
+ if (prev_prev_insn_unreordered)
+ prev_prev_nop = 0;
+
+ if (prev_prev_nop && nops == 0)
+ ++nops;
+
+ /* If we are being given a nop instruction, don't bother with
+ one of the nops we would otherwise output. This will only
+ happen when a nop instruction is used with mips_optimize set
+ to 0. */
+ if (nops > 0
+ && ! mips_opts.noreorder
+ && ip->insn_opcode == (mips_opts.mips16 ? 0x6500 : 0))
+ --nops;
+
+ /* Now emit the right number of NOP instructions. */
+ if (nops > 0 && ! mips_opts.noreorder)
+ {
+ fragS *old_frag;
+ unsigned long old_frag_offset;
+ int i;
+ struct insn_label_list *l;
+
+ old_frag = frag_now;
+ old_frag_offset = frag_now_fix ();
+
+ for (i = 0; i < nops; i++)
+ emit_nop ();
+
+ if (listing)
+ {
+ listing_prev_line ();
+ /* We may be at the start of a variant frag. In case we
+ are, make sure there is enough space for the frag
+ after the frags created by listing_prev_line. The
+ argument to frag_grow here must be at least as large
+ as the argument to all other calls to frag_grow in
+ this file. We don't have to worry about being in the
+ middle of a variant frag, because the variants insert
+ all needed nop instructions themselves. */
+ frag_grow (40);
+ }
+
+ for (l = insn_labels; l != NULL; l = l->next)
+ {
+ assert (S_GET_SEGMENT (l->label) == now_seg);
+ l->label->sy_frag = frag_now;
+ S_SET_VALUE (l->label, (valueT) frag_now_fix ());
+ /* mips16 text labels are stored as odd. */
+ if (mips_opts.mips16)
+ ++l->label->sy_value.X_add_number;
+ }
+
+#ifndef NO_ECOFF_DEBUGGING
+ if (ECOFF_DEBUGGING)
+ ecoff_fix_loc (old_frag, old_frag_offset);
+#endif
+ }
+ else if (prev_nop_frag != NULL)
+ {
+ /* We have a frag holding nops we may be able to remove. If
+ we don't need any nops, we can decrease the size of
+ prev_nop_frag by the size of one instruction. If we do
+ need some nops, we count them in prev_nops_required. */
+ if (prev_nop_frag_since == 0)
+ {
+ if (nops == 0)
+ {
+ prev_nop_frag->fr_fix -= mips_opts.mips16 ? 2 : 4;
+ --prev_nop_frag_holds;
+ }
+ else
+ prev_nop_frag_required += nops;
+ }
+ else
+ {
+ if (prev_prev_nop == 0)
+ {
+ prev_nop_frag->fr_fix -= mips_opts.mips16 ? 2 : 4;
+ --prev_nop_frag_holds;
+ }
+ else
+ ++prev_nop_frag_required;
+ }
+
+ if (prev_nop_frag_holds <= prev_nop_frag_required)
+ prev_nop_frag = NULL;
+
+ ++prev_nop_frag_since;
+
+ /* Sanity check: by the time we reach the second instruction
+ after prev_nop_frag, we should have used up all the nops
+ one way or another. */
+ assert (prev_nop_frag_since <= 1 || prev_nop_frag == NULL);
+ }
+ }
+
+ if (reloc_type > BFD_RELOC_UNUSED)
+ {
+ /* We need to set up a variant frag. */
+ assert (mips_opts.mips16 && address_expr != NULL);
+ f = frag_var (rs_machine_dependent, 4, 0,
+ RELAX_MIPS16_ENCODE (reloc_type - BFD_RELOC_UNUSED,
+ mips16_small, mips16_ext,
+ (prev_pinfo
+ & INSN_UNCOND_BRANCH_DELAY),
+ (prev_insn_reloc_type
+ == BFD_RELOC_MIPS16_JMP)),
+ make_expr_symbol (address_expr), (offsetT) 0,
+ (char *) NULL);
+ }
+ else if (place != NULL)
+ f = place;
+ else if (mips_opts.mips16
+ && ! ip->use_extend
+ && reloc_type != BFD_RELOC_MIPS16_JMP)
+ {
+ /* Make sure there is enough room to swap this instruction with
+ a following jump instruction. */
+ frag_grow (6);
+ f = frag_more (2);
+ }
+ else
+ {
+ if (mips_opts.mips16
+ && mips_opts.noreorder
+ && (prev_pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
+ as_warn (_("extended instruction in delay slot"));
+
+ f = frag_more (4);
+ }
+
+ fixp = NULL;
+ if (address_expr != NULL && reloc_type < BFD_RELOC_UNUSED)
+ {
+ if (address_expr->X_op == O_constant)
+ {
+ switch (reloc_type)
+ {
+ case BFD_RELOC_32:
+ ip->insn_opcode |= address_expr->X_add_number;
+ break;
+
+ case BFD_RELOC_LO16:
+ ip->insn_opcode |= address_expr->X_add_number & 0xffff;
+ break;
+
+ case BFD_RELOC_MIPS_JMP:
+ if ((address_expr->X_add_number & 3) != 0)
+ as_bad (_("jump to misaligned address (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ ip->insn_opcode |= (address_expr->X_add_number >> 2) & 0x3ffffff;
+ break;
+
+ case BFD_RELOC_MIPS16_JMP:
+ if ((address_expr->X_add_number & 3) != 0)
+ as_bad (_("jump to misaligned address (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ ip->insn_opcode |=
+ (((address_expr->X_add_number & 0x7c0000) << 3)
+ | ((address_expr->X_add_number & 0xf800000) >> 7)
+ | ((address_expr->X_add_number & 0x3fffc) >> 2));
+ break;
+
+
+ case BFD_RELOC_16_PCREL_S2:
+ goto need_reloc;
+
+ default:
+ internalError ();
+ }
+ }
+ else
+ {
+ need_reloc:
+ /* Don't generate a reloc if we are writing into a variant
+ frag. */
+ if (place == NULL)
+ {
+ fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 4,
+ address_expr,
+ reloc_type == BFD_RELOC_16_PCREL_S2,
+ reloc_type);
+ if (unmatched_hi)
+ {
+ struct mips_hi_fixup *hi_fixup;
+
+ assert (reloc_type == BFD_RELOC_HI16_S);
+ hi_fixup = ((struct mips_hi_fixup *)
+ xmalloc (sizeof (struct mips_hi_fixup)));
+ hi_fixup->fixp = fixp;
+ hi_fixup->seg = now_seg;
+ hi_fixup->next = mips_hi_fixup_list;
+ mips_hi_fixup_list = hi_fixup;
+ }
+ }
+ }
+ }
+
+ if (! mips_opts.mips16)
+ md_number_to_chars (f, ip->insn_opcode, 4);
+ else if (reloc_type == BFD_RELOC_MIPS16_JMP)
+ {
+ md_number_to_chars (f, ip->insn_opcode >> 16, 2);
+ md_number_to_chars (f + 2, ip->insn_opcode & 0xffff, 2);
+ }
+ else
+ {
+ if (ip->use_extend)
+ {
+ md_number_to_chars (f, 0xf000 | ip->extend, 2);
+ f += 2;
+ }
+ md_number_to_chars (f, ip->insn_opcode, 2);
+ }
+
+ /* Update the register mask information. */
+ if (! mips_opts.mips16)
+ {
+ if (pinfo & INSN_WRITE_GPR_D)
+ mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD);
+ if ((pinfo & (INSN_WRITE_GPR_T | INSN_READ_GPR_T)) != 0)
+ mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RT) & OP_MASK_RT);
+ if (pinfo & INSN_READ_GPR_S)
+ mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RS) & OP_MASK_RS);
+ if (pinfo & INSN_WRITE_GPR_31)
+ mips_gprmask |= 1 << 31;
+ if (pinfo & INSN_WRITE_FPR_D)
+ mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FD) & OP_MASK_FD);
+ if ((pinfo & (INSN_WRITE_FPR_S | INSN_READ_FPR_S)) != 0)
+ mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FS) & OP_MASK_FS);
+ if ((pinfo & (INSN_WRITE_FPR_T | INSN_READ_FPR_T)) != 0)
+ mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FT) & OP_MASK_FT);
+ if ((pinfo & INSN_READ_FPR_R) != 0)
+ mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FR) & OP_MASK_FR);
+ if (pinfo & INSN_COP)
+ {
+ /* We don't keep enough information to sort these cases out.
+ The itbl support does keep this information however, although
+ we currently don't support itbl fprmats as part of the cop
+ instruction. May want to add this support in the future. */
+ }
+ /* Never set the bit for $0, which is always zero. */
+ mips_gprmask &=~ 1 << 0;
+ }
+ else
+ {
+ if (pinfo & (MIPS16_INSN_WRITE_X | MIPS16_INSN_READ_X))
+ mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_RX)
+ & MIPS16OP_MASK_RX);
+ if (pinfo & (MIPS16_INSN_WRITE_Y | MIPS16_INSN_READ_Y))
+ mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_RY)
+ & MIPS16OP_MASK_RY);
+ if (pinfo & MIPS16_INSN_WRITE_Z)
+ mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_RZ)
+ & MIPS16OP_MASK_RZ);
+ if (pinfo & (MIPS16_INSN_WRITE_T | MIPS16_INSN_READ_T))
+ mips_gprmask |= 1 << TREG;
+ if (pinfo & (MIPS16_INSN_WRITE_SP | MIPS16_INSN_READ_SP))
+ mips_gprmask |= 1 << SP;
+ if (pinfo & (MIPS16_INSN_WRITE_31 | MIPS16_INSN_READ_31))
+ mips_gprmask |= 1 << RA;
+ if (pinfo & MIPS16_INSN_WRITE_GPR_Y)
+ mips_gprmask |= 1 << MIPS16OP_EXTRACT_REG32R (ip->insn_opcode);
+ if (pinfo & MIPS16_INSN_READ_Z)
+ mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_MOVE32Z)
+ & MIPS16OP_MASK_MOVE32Z);
+ if (pinfo & MIPS16_INSN_READ_GPR_X)
+ mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_REGR32)
+ & MIPS16OP_MASK_REGR32);
+ }
+
+ if (place == NULL && ! mips_opts.noreorder)
+ {
+ /* Filling the branch delay slot is more complex. We try to
+ switch the branch with the previous instruction, which we can
+ do if the previous instruction does not set up a condition
+ that the branch tests and if the branch is not itself the
+ target of any branch. */
+ if ((pinfo & INSN_UNCOND_BRANCH_DELAY)
+ || (pinfo & INSN_COND_BRANCH_DELAY))
+ {
+ if (mips_optimize < 2
+ /* If we have seen .set volatile or .set nomove, don't
+ optimize. */
+ || mips_opts.nomove != 0
+ /* If we had to emit any NOP instructions, then we
+ already know we can not swap. */
+ || nops != 0
+ /* If we don't even know the previous insn, we can not
+ swap. */
+ || ! prev_insn_valid
+ /* If the previous insn is already in a branch delay
+ slot, then we can not swap. */
+ || prev_insn_is_delay_slot
+ /* If the previous previous insn was in a .set
+ noreorder, we can't swap. Actually, the MIPS
+ assembler will swap in this situation. However, gcc
+ configured -with-gnu-as will generate code like
+ .set noreorder
+ lw $4,XXX
+ .set reorder
+ INSN
+ bne $4,$0,foo
+ in which we can not swap the bne and INSN. If gcc is
+ not configured -with-gnu-as, it does not output the
+ .set pseudo-ops. We don't have to check
+ prev_insn_unreordered, because prev_insn_valid will
+ be 0 in that case. We don't want to use
+ prev_prev_insn_valid, because we do want to be able
+ to swap at the start of a function. */
+ || prev_prev_insn_unreordered
+ /* If the branch is itself the target of a branch, we
+ can not swap. We cheat on this; all we check for is
+ whether there is a label on this instruction. If
+ there are any branches to anything other than a
+ label, users must use .set noreorder. */
+ || insn_labels != NULL
+ /* If the previous instruction is in a variant frag, we
+ can not do the swap. This does not apply to the
+ mips16, which uses variant frags for different
+ purposes. */
+ || (! mips_opts.mips16
+ && prev_insn_frag->fr_type == rs_machine_dependent)
+ /* If the branch reads the condition codes, we don't
+ even try to swap, because in the sequence
+ ctc1 $X,$31
+ INSN
+ INSN
+ bc1t LABEL
+ we can not swap, and I don't feel like handling that
+ case. */
+ || (! mips_opts.mips16
+ && mips_opts.isa < 4
+ && (pinfo & INSN_READ_COND_CODE))
+ /* We can not swap with an instruction that requires a
+ delay slot, becase the target of the branch might
+ interfere with that instruction. */
+ || (! mips_opts.mips16
+ && mips_opts.isa < 4
+ && (prev_pinfo
+ /* Itbl support may require additional care here. */
+ & (INSN_LOAD_COPROC_DELAY
+ | INSN_COPROC_MOVE_DELAY
+ | INSN_WRITE_COND_CODE)))
+ || (! (hilo_interlocks
+ || (mips_cpu == 3900 && (pinfo & INSN_MULT)))
+ && (prev_pinfo
+ & (INSN_READ_LO
+ | INSN_READ_HI)))
+ || (! mips_opts.mips16
+ && ! gpr_interlocks
+ && (prev_pinfo & INSN_LOAD_MEMORY_DELAY))
+ || (! mips_opts.mips16
+ && mips_opts.isa < 2
+ /* Itbl support may require additional care here. */
+ && (prev_pinfo & INSN_COPROC_MEMORY_DELAY))
+ /* We can not swap with a branch instruction. */
+ || (prev_pinfo
+ & (INSN_UNCOND_BRANCH_DELAY
+ | INSN_COND_BRANCH_DELAY
+ | INSN_COND_BRANCH_LIKELY))
+ /* We do not swap with a trap instruction, since it
+ complicates trap handlers to have the trap
+ instruction be in a delay slot. */
+ || (prev_pinfo & INSN_TRAP)
+ /* If the branch reads a register that the previous
+ instruction sets, we can not swap. */
+ || (! mips_opts.mips16
+ && (prev_pinfo & INSN_WRITE_GPR_T)
+ && insn_uses_reg (ip,
+ ((prev_insn.insn_opcode >> OP_SH_RT)
+ & OP_MASK_RT),
+ MIPS_GR_REG))
+ || (! mips_opts.mips16
+ && (prev_pinfo & INSN_WRITE_GPR_D)
+ && insn_uses_reg (ip,
+ ((prev_insn.insn_opcode >> OP_SH_RD)
+ & OP_MASK_RD),
+ MIPS_GR_REG))
+ || (mips_opts.mips16
+ && (((prev_pinfo & MIPS16_INSN_WRITE_X)
+ && insn_uses_reg (ip,
+ ((prev_insn.insn_opcode
+ >> MIPS16OP_SH_RX)
+ & MIPS16OP_MASK_RX),
+ MIPS16_REG))
+ || ((prev_pinfo & MIPS16_INSN_WRITE_Y)
+ && insn_uses_reg (ip,
+ ((prev_insn.insn_opcode
+ >> MIPS16OP_SH_RY)
+ & MIPS16OP_MASK_RY),
+ MIPS16_REG))
+ || ((prev_pinfo & MIPS16_INSN_WRITE_Z)
+ && insn_uses_reg (ip,
+ ((prev_insn.insn_opcode
+ >> MIPS16OP_SH_RZ)
+ & MIPS16OP_MASK_RZ),
+ MIPS16_REG))
+ || ((prev_pinfo & MIPS16_INSN_WRITE_T)
+ && insn_uses_reg (ip, TREG, MIPS_GR_REG))
+ || ((prev_pinfo & MIPS16_INSN_WRITE_31)
+ && insn_uses_reg (ip, RA, MIPS_GR_REG))
+ || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y)
+ && insn_uses_reg (ip,
+ MIPS16OP_EXTRACT_REG32R (prev_insn.
+ insn_opcode),
+ MIPS_GR_REG))))
+ /* If the branch writes a register that the previous
+ instruction sets, we can not swap (we know that
+ branches write only to RD or to $31). */
+ || (! mips_opts.mips16
+ && (prev_pinfo & INSN_WRITE_GPR_T)
+ && (((pinfo & INSN_WRITE_GPR_D)
+ && (((prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT)
+ == ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD)))
+ || ((pinfo & INSN_WRITE_GPR_31)
+ && (((prev_insn.insn_opcode >> OP_SH_RT)
+ & OP_MASK_RT)
+ == 31))))
+ || (! mips_opts.mips16
+ && (prev_pinfo & INSN_WRITE_GPR_D)
+ && (((pinfo & INSN_WRITE_GPR_D)
+ && (((prev_insn.insn_opcode >> OP_SH_RD) & OP_MASK_RD)
+ == ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD)))
+ || ((pinfo & INSN_WRITE_GPR_31)
+ && (((prev_insn.insn_opcode >> OP_SH_RD)
+ & OP_MASK_RD)
+ == 31))))
+ || (mips_opts.mips16
+ && (pinfo & MIPS16_INSN_WRITE_31)
+ && ((prev_pinfo & MIPS16_INSN_WRITE_31)
+ || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y)
+ && (MIPS16OP_EXTRACT_REG32R (prev_insn.insn_opcode)
+ == RA))))
+ /* If the branch writes a register that the previous
+ instruction reads, we can not swap (we know that
+ branches only write to RD or to $31). */
+ || (! mips_opts.mips16
+ && (pinfo & INSN_WRITE_GPR_D)
+ && insn_uses_reg (&prev_insn,
+ ((ip->insn_opcode >> OP_SH_RD)
+ & OP_MASK_RD),
+ MIPS_GR_REG))
+ || (! mips_opts.mips16
+ && (pinfo & INSN_WRITE_GPR_31)
+ && insn_uses_reg (&prev_insn, 31, MIPS_GR_REG))
+ || (mips_opts.mips16
+ && (pinfo & MIPS16_INSN_WRITE_31)
+ && insn_uses_reg (&prev_insn, RA, MIPS_GR_REG))
+ /* If we are generating embedded PIC code, the branch
+ might be expanded into a sequence which uses $at, so
+ we can't swap with an instruction which reads it. */
+ || (mips_pic == EMBEDDED_PIC
+ && insn_uses_reg (&prev_insn, AT, MIPS_GR_REG))
+ /* If the previous previous instruction has a load
+ delay, and sets a register that the branch reads, we
+ can not swap. */
+ || (! mips_opts.mips16
+ && mips_opts.isa < 4
+ /* Itbl support may require additional care here. */
+ && ((prev_prev_insn.insn_mo->pinfo & INSN_LOAD_COPROC_DELAY)
+ || (! gpr_interlocks
+ && (prev_prev_insn.insn_mo->pinfo
+ & INSN_LOAD_MEMORY_DELAY)))
+ && insn_uses_reg (ip,
+ ((prev_prev_insn.insn_opcode >> OP_SH_RT)
+ & OP_MASK_RT),
+ MIPS_GR_REG))
+ /* If one instruction sets a condition code and the
+ other one uses a condition code, we can not swap. */
+ || ((pinfo & INSN_READ_COND_CODE)
+ && (prev_pinfo & INSN_WRITE_COND_CODE))
+ || ((pinfo & INSN_WRITE_COND_CODE)
+ && (prev_pinfo & INSN_READ_COND_CODE))
+ /* If the previous instruction uses the PC, we can not
+ swap. */
+ || (mips_opts.mips16
+ && (prev_pinfo & MIPS16_INSN_READ_PC))
+ /* If the previous instruction was extended, we can not
+ swap. */
+ || (mips_opts.mips16 && prev_insn_extended)
+ /* If the previous instruction had a fixup in mips16
+ mode, we can not swap. This normally means that the
+ previous instruction was a 4 byte branch anyhow. */
+ || (mips_opts.mips16 && prev_insn_fixp)
+ /* If the previous instruction is a sync, sync.l, or
+ sync.p, we can not swap. */
+ || (prev_pinfo && INSN_SYNC))
+ {
+ /* We could do even better for unconditional branches to
+ portions of this object file; we could pick up the
+ instruction at the destination, put it in the delay
+ slot, and bump the destination address. */
+ emit_nop ();
+ /* Update the previous insn information. */
+ prev_prev_insn = *ip;
+ prev_insn.insn_mo = &dummy_opcode;
+ }
+ else
+ {
+ /* It looks like we can actually do the swap. */
+ if (! mips_opts.mips16)
+ {
+ char *prev_f;
+ char temp[4];
+
+ prev_f = prev_insn_frag->fr_literal + prev_insn_where;
+ memcpy (temp, prev_f, 4);
+ memcpy (prev_f, f, 4);
+ memcpy (f, temp, 4);
+ if (prev_insn_fixp)
+ {
+ prev_insn_fixp->fx_frag = frag_now;
+ prev_insn_fixp->fx_where = f - frag_now->fr_literal;
+ }
+ if (fixp)
+ {
+ fixp->fx_frag = prev_insn_frag;
+ fixp->fx_where = prev_insn_where;
+ }
+ }
+ else
+ {
+ char *prev_f;
+ char temp[2];
+
+ assert (prev_insn_fixp == NULL);
+ prev_f = prev_insn_frag->fr_literal + prev_insn_where;
+ memcpy (temp, prev_f, 2);
+ memcpy (prev_f, f, 2);
+ if (reloc_type != BFD_RELOC_MIPS16_JMP)
+ {
+ assert (reloc_type == BFD_RELOC_UNUSED);
+ memcpy (f, temp, 2);
+ }
+ else
+ {
+ memcpy (f, f + 2, 2);
+ memcpy (f + 2, temp, 2);
+ }
+ if (fixp)
+ {
+ fixp->fx_frag = prev_insn_frag;
+ fixp->fx_where = prev_insn_where;
+ }
+ }
+
+ /* Update the previous insn information; leave prev_insn
+ unchanged. */
+ prev_prev_insn = *ip;
+ }
+ prev_insn_is_delay_slot = 1;
+
+ /* If that was an unconditional branch, forget the previous
+ insn information. */
+ if (pinfo & INSN_UNCOND_BRANCH_DELAY)
+ {
+ prev_prev_insn.insn_mo = &dummy_opcode;
+ prev_insn.insn_mo = &dummy_opcode;
+ }
+
+ prev_insn_fixp = NULL;
+ prev_insn_reloc_type = BFD_RELOC_UNUSED;
+ prev_insn_extended = 0;
+ }
+ else if (pinfo & INSN_COND_BRANCH_LIKELY)
+ {
+ /* We don't yet optimize a branch likely. What we should do
+ is look at the target, copy the instruction found there
+ into the delay slot, and increment the branch to jump to
+ the next instruction. */
+ emit_nop ();
+ /* Update the previous insn information. */
+ prev_prev_insn = *ip;
+ prev_insn.insn_mo = &dummy_opcode;
+ prev_insn_fixp = NULL;
+ prev_insn_reloc_type = BFD_RELOC_UNUSED;
+ prev_insn_extended = 0;
+ }
+ else
+ {
+ /* Update the previous insn information. */
+ if (nops > 0)
+ prev_prev_insn.insn_mo = &dummy_opcode;
+ else
+ prev_prev_insn = prev_insn;
+ prev_insn = *ip;
+
+ /* Any time we see a branch, we always fill the delay slot
+ immediately; since this insn is not a branch, we know it
+ is not in a delay slot. */
+ prev_insn_is_delay_slot = 0;
+
+ prev_insn_fixp = fixp;
+ prev_insn_reloc_type = reloc_type;
+ if (mips_opts.mips16)
+ prev_insn_extended = (ip->use_extend
+ || reloc_type > BFD_RELOC_UNUSED);
+ }
+
+ prev_prev_insn_unreordered = prev_insn_unreordered;
+ prev_insn_unreordered = 0;
+ prev_insn_frag = frag_now;
+ prev_insn_where = f - frag_now->fr_literal;
+ prev_insn_valid = 1;
+ }
+ else if (place == NULL)
+ {
+ /* We need to record a bit of information even when we are not
+ reordering, in order to determine the base address for mips16
+ PC relative relocs. */
+ prev_prev_insn = prev_insn;
+ prev_insn = *ip;
+ prev_insn_reloc_type = reloc_type;
+ prev_prev_insn_unreordered = prev_insn_unreordered;
+ prev_insn_unreordered = 1;
+ }
+
+ /* We just output an insn, so the next one doesn't have a label. */
+ mips_clear_insn_labels ();
+
+ /* We must ensure that a fixup associated with an unmatched %hi
+ reloc does not become a variant frag. Otherwise, the
+ rearrangement of %hi relocs in frob_file may confuse
+ tc_gen_reloc. */
+ if (unmatched_hi)
+ {
+ frag_wane (frag_now);
+ frag_new (0);
+ }
+}
+
+/* This function forgets that there was any previous instruction or
+ label. If PRESERVE is non-zero, it remembers enough information to
+ know whether nops are needed before a noreorder section. */
+
+static void
+mips_no_prev_insn (preserve)
+ int preserve;
+{
+ if (! preserve)
+ {
+ prev_insn.insn_mo = &dummy_opcode;
+ prev_prev_insn.insn_mo = &dummy_opcode;
+ prev_nop_frag = NULL;
+ prev_nop_frag_holds = 0;
+ prev_nop_frag_required = 0;
+ prev_nop_frag_since = 0;
+ }
+ prev_insn_valid = 0;
+ prev_insn_is_delay_slot = 0;
+ prev_insn_unreordered = 0;
+ prev_insn_extended = 0;
+ prev_insn_reloc_type = BFD_RELOC_UNUSED;
+ prev_prev_insn_unreordered = 0;
+ mips_clear_insn_labels ();
+}
+
+/* This function must be called whenever we turn on noreorder or emit
+ something other than instructions. It inserts any NOPS which might
+ be needed by the previous instruction, and clears the information
+ kept for the previous instructions. The INSNS parameter is true if
+ instructions are to follow. */
+
+static void
+mips_emit_delays (insns)
+ boolean insns;
+{
+ if (! mips_opts.noreorder)
+ {
+ int nops;
+
+ nops = 0;
+ if ((! mips_opts.mips16
+ && mips_opts.isa < 4
+ && (! cop_interlocks
+ && (prev_insn.insn_mo->pinfo
+ & (INSN_LOAD_COPROC_DELAY
+ | INSN_COPROC_MOVE_DELAY
+ | INSN_WRITE_COND_CODE))))
+ || (! hilo_interlocks
+ && (prev_insn.insn_mo->pinfo
+ & (INSN_READ_LO
+ | INSN_READ_HI)))
+ || (! mips_opts.mips16
+ && ! gpr_interlocks
+ && (prev_insn.insn_mo->pinfo
+ & INSN_LOAD_MEMORY_DELAY))
+ || (! mips_opts.mips16
+ && mips_opts.isa < 2
+ && (prev_insn.insn_mo->pinfo
+ & INSN_COPROC_MEMORY_DELAY)))
+ {
+ /* Itbl support may require additional care here. */
+ ++nops;
+ if ((! mips_opts.mips16
+ && mips_opts.isa < 4
+ && (! cop_interlocks
+ && prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE))
+ || (! hilo_interlocks
+ && ((prev_insn.insn_mo->pinfo & INSN_READ_HI)
+ || (prev_insn.insn_mo->pinfo & INSN_READ_LO))))
+ ++nops;
+
+ if (prev_insn_unreordered)
+ nops = 0;
+ }
+ else if ((! mips_opts.mips16
+ && mips_opts.isa < 4
+ && (! cop_interlocks
+ && prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE))
+ || (! hilo_interlocks
+ && ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
+ || (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO))))
+ {
+ /* Itbl support may require additional care here. */
+ if (! prev_prev_insn_unreordered)
+ ++nops;
+ }
+
+ if (nops > 0)
+ {
+ struct insn_label_list *l;
+
+ if (insns)
+ {
+ /* Record the frag which holds the nop instructions, so
+ that we can remove them if we don't need them. */
+ frag_grow (mips_opts.mips16 ? nops * 2 : nops * 4);
+ prev_nop_frag = frag_now;
+ prev_nop_frag_holds = nops;
+ prev_nop_frag_required = 0;
+ prev_nop_frag_since = 0;
+ }
+
+ for (; nops > 0; --nops)
+ emit_nop ();
+
+ if (insns)
+ {
+ /* Move on to a new frag, so that it is safe to simply
+ decrease the size of prev_nop_frag. */
+ frag_wane (frag_now);
+ frag_new (0);
+ }
+
+ for (l = insn_labels; l != NULL; l = l->next)
+ {
+ assert (S_GET_SEGMENT (l->label) == now_seg);
+ l->label->sy_frag = frag_now;
+ S_SET_VALUE (l->label, (valueT) frag_now_fix ());
+ /* mips16 text labels are stored as odd. */
+ if (mips_opts.mips16)
+ ++l->label->sy_value.X_add_number;
+ }
+ }
+ }
+
+ /* Mark instruction labels in mips16 mode. */
+ if (mips_opts.mips16 && insns)
+ mips16_mark_labels ();
+
+ mips_no_prev_insn (insns);
+}
+
+/* Build an instruction created by a macro expansion. This is passed
+ a pointer to the count of instructions created so far, an
+ expression, the name of the instruction to build, an operand format
+ string, and corresponding arguments. */
+
+#ifdef USE_STDARG
+static void
+macro_build (char *place,
+ int *counter,
+ expressionS * ep,
+ const char *name,
+ const char *fmt,
+ ...)
+#else
+static void
+macro_build (place, counter, ep, name, fmt, va_alist)
+ char *place;
+ int *counter;
+ expressionS *ep;
+ const char *name;
+ const char *fmt;
+ va_dcl
+#endif
+{
+ struct mips_cl_insn insn;
+ bfd_reloc_code_real_type r;
+ va_list args;
+ int insn_isa;
+
+#ifdef USE_STDARG
+ va_start (args, fmt);
+#else
+ va_start (args);
+#endif
+
+ /*
+ * If the macro is about to expand into a second instruction,
+ * print a warning if needed. We need to pass ip as a parameter
+ * to generate a better warning message here...
+ */
+ if (mips_opts.warn_about_macros && place == NULL && *counter == 1)
+ as_warn (_("Macro instruction expanded into multiple instructions"));
+
+ if (place == NULL)
+ *counter += 1; /* bump instruction counter */
+
+ if (mips_opts.mips16)
+ {
+ mips16_macro_build (place, counter, ep, name, fmt, args);
+ va_end (args);
+ return;
+ }
+
+ r = BFD_RELOC_UNUSED;
+ insn.insn_mo = (struct mips_opcode *) hash_find (op_hash, name);
+ assert (insn.insn_mo);
+ assert (strcmp (name, insn.insn_mo->name) == 0);
+
+ /* Search until we get a match for NAME. */
+ while (1)
+ {
+ if ((insn.insn_mo->membership & INSN_ISA) == INSN_ISA1)
+ insn_isa = 1;
+ else if ((insn.insn_mo->membership & INSN_ISA) == INSN_ISA2)
+ insn_isa = 2;
+ else if ((insn.insn_mo->membership & INSN_ISA) == INSN_ISA3)
+ insn_isa = 3;
+ else if ((insn.insn_mo->membership & INSN_ISA) == INSN_ISA4)
+ insn_isa = 4;
+ else
+ insn_isa = 15;
+
+ if (strcmp (fmt, insn.insn_mo->args) == 0
+ && insn.insn_mo->pinfo != INSN_MACRO
+ && (insn_isa <= mips_opts.isa
+ || (mips_cpu == 4650
+ && (insn.insn_mo->membership & INSN_4650) != 0)
+ || (mips_cpu == 4010
+ && (insn.insn_mo->membership & INSN_4010) != 0)
+ || ((mips_cpu == 4100
+ || mips_cpu == 4111
+ )
+ && (insn.insn_mo->membership & INSN_4100) != 0)
+ || (mips_cpu == 3900
+ && (insn.insn_mo->membership & INSN_3900) != 0))
+ && (mips_cpu != 4650 || (insn.insn_mo->pinfo & FP_D) == 0))
+ break;
+
+ ++insn.insn_mo;
+ assert (insn.insn_mo->name);
+ assert (strcmp (name, insn.insn_mo->name) == 0);
+ }
+
+ insn.insn_opcode = insn.insn_mo->match;
+ for (;;)
+ {
+ switch (*fmt++)
+ {
+ case '\0':
+ break;
+
+ case ',':
+ case '(':
+ case ')':
+ continue;
+
+ case 't':
+ case 'w':
+ case 'E':
+ insn.insn_opcode |= va_arg (args, int) << 16;
+ continue;
+
+ case 'c':
+ case 'T':
+ case 'W':
+ insn.insn_opcode |= va_arg (args, int) << 16;
+ continue;
+
+ case 'd':
+ case 'G':
+ insn.insn_opcode |= va_arg (args, int) << 11;
+ continue;
+
+ case 'V':
+ case 'S':
+ insn.insn_opcode |= va_arg (args, int) << 11;
+ continue;
+
+ case 'z':
+ continue;
+
+ case '<':
+ insn.insn_opcode |= va_arg (args, int) << 6;
+ continue;
+
+ case 'D':
+ insn.insn_opcode |= va_arg (args, int) << 6;
+ continue;
+
+ case 'B':
+ insn.insn_opcode |= va_arg (args, int) << 6;
+ continue;
+
+ case 'q':
+ insn.insn_opcode |= va_arg (args, int) << 6;
+ continue;
+
+ case 'b':
+ case 's':
+ case 'r':
+ case 'v':
+ insn.insn_opcode |= va_arg (args, int) << 21;
+ continue;
+
+ case 'i':
+ case 'j':
+ case 'o':
+ r = (bfd_reloc_code_real_type) va_arg (args, int);
+ assert (r == BFD_RELOC_MIPS_GPREL
+ || r == BFD_RELOC_MIPS_LITERAL
+ || r == BFD_RELOC_LO16
+ || r == BFD_RELOC_MIPS_GOT16
+ || r == BFD_RELOC_MIPS_CALL16
+ || r == BFD_RELOC_MIPS_GOT_LO16
+ || r == BFD_RELOC_MIPS_CALL_LO16
+ || (ep->X_op == O_subtract
+ && now_seg == text_section
+ && r == BFD_RELOC_PCREL_LO16));
+ continue;
+
+ case 'u':
+ r = (bfd_reloc_code_real_type) va_arg (args, int);
+ assert (ep != NULL
+ && (ep->X_op == O_constant
+ || (ep->X_op == O_symbol
+ && (r == BFD_RELOC_HI16_S
+ || r == BFD_RELOC_HI16
+ || r == BFD_RELOC_MIPS_GOT_HI16
+ || r == BFD_RELOC_MIPS_CALL_HI16))
+ || (ep->X_op == O_subtract
+ && now_seg == text_section
+ && r == BFD_RELOC_PCREL_HI16_S)));
+ if (ep->X_op == O_constant)
+ {
+ insn.insn_opcode |= (ep->X_add_number >> 16) & 0xffff;
+ ep = NULL;
+ r = BFD_RELOC_UNUSED;
+ }
+ continue;
+
+ case 'p':
+ assert (ep != NULL);
+ /*
+ * This allows macro() to pass an immediate expression for
+ * creating short branches without creating a symbol.
+ * Note that the expression still might come from the assembly
+ * input, in which case the value is not checked for range nor
+ * is a relocation entry generated (yuck).
+ */
+ if (ep->X_op == O_constant)
+ {
+ insn.insn_opcode |= (ep->X_add_number >> 2) & 0xffff;
+ ep = NULL;
+ }
+ else
+ r = BFD_RELOC_16_PCREL_S2;
+ continue;
+
+ case 'a':
+ assert (ep != NULL);
+ r = BFD_RELOC_MIPS_JMP;
+ continue;
+
+ case 'C':
+ insn.insn_opcode |= va_arg (args, unsigned long);
+ continue;
+
+ default:
+ internalError ();
+ }
+ break;
+ }
+ va_end (args);
+ assert (r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
+
+ append_insn (place, &insn, ep, r, false);
+}
+
+static void
+mips16_macro_build (place, counter, ep, name, fmt, args)
+ char *place;
+ int *counter;
+ expressionS *ep;
+ const char *name;
+ const char *fmt;
+ va_list args;
+{
+ struct mips_cl_insn insn;
+ bfd_reloc_code_real_type r;
+
+ r = BFD_RELOC_UNUSED;
+ insn.insn_mo = (struct mips_opcode *) hash_find (mips16_op_hash, name);
+ assert (insn.insn_mo);
+ assert (strcmp (name, insn.insn_mo->name) == 0);
+
+ while (strcmp (fmt, insn.insn_mo->args) != 0
+ || insn.insn_mo->pinfo == INSN_MACRO)
+ {
+ ++insn.insn_mo;
+ assert (insn.insn_mo->name);
+ assert (strcmp (name, insn.insn_mo->name) == 0);
+ }
+
+ insn.insn_opcode = insn.insn_mo->match;
+ insn.use_extend = false;
+
+ for (;;)
+ {
+ int c;
+
+ c = *fmt++;
+ switch (c)
+ {
+ case '\0':
+ break;
+
+ case ',':
+ case '(':
+ case ')':
+ continue;
+
+ case 'y':
+ case 'w':
+ insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_RY;
+ continue;
+
+ case 'x':
+ case 'v':
+ insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_RX;
+ continue;
+
+ case 'z':
+ insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_RZ;
+ continue;
+
+ case 'Z':
+ insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_MOVE32Z;
+ continue;
+
+ case '0':
+ case 'S':
+ case 'P':
+ case 'R':
+ continue;
+
+ case 'X':
+ insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_REGR32;
+ continue;
+
+ case 'Y':
+ {
+ int regno;
+
+ regno = va_arg (args, int);
+ regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3);
+ insn.insn_opcode |= regno << MIPS16OP_SH_REG32R;
+ }
+ continue;
+
+ case '<':
+ case '>':
+ case '4':
+ case '5':
+ case 'H':
+ case 'W':
+ case 'D':
+ case 'j':
+ case '8':
+ case 'V':
+ case 'C':
+ case 'U':
+ case 'k':
+ case 'K':
+ case 'p':
+ case 'q':
+ {
+ assert (ep != NULL);
+
+ if (ep->X_op != O_constant)
+ r = BFD_RELOC_UNUSED + c;
+ else
+ {
+ mips16_immed ((char *) NULL, 0, c, ep->X_add_number, false,
+ false, false, &insn.insn_opcode,
+ &insn.use_extend, &insn.extend);
+ ep = NULL;
+ r = BFD_RELOC_UNUSED;
+ }
+ }
+ continue;
+
+ case '6':
+ insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_IMM6;
+ continue;
+ }
+
+ break;
+ }
+
+ assert (r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
+
+ append_insn (place, &insn, ep, r, false);
+}
+
+/*
+ * Generate a "lui" instruction.
+ */
+static void
+macro_build_lui (place, counter, ep, regnum)
+ char *place;
+ int *counter;
+ expressionS *ep;
+ int regnum;
+{
+ expressionS high_expr;
+ struct mips_cl_insn insn;
+ bfd_reloc_code_real_type r;
+ CONST char *name = "lui";
+ CONST char *fmt = "t,u";
+
+ assert (! mips_opts.mips16);
+
+ if (place == NULL)
+ high_expr = *ep;
+ else
+ {
+ high_expr.X_op = O_constant;
+ high_expr.X_add_number = ep->X_add_number;
+ }
+
+ if (high_expr.X_op == O_constant)
+ {
+ /* we can compute the instruction now without a relocation entry */
+ if (high_expr.X_add_number & 0x8000)
+ high_expr.X_add_number += 0x10000;
+ high_expr.X_add_number =
+ ((unsigned long) high_expr.X_add_number >> 16) & 0xffff;
+ r = BFD_RELOC_UNUSED;
+ }
+ else
+ {
+ assert (ep->X_op == O_symbol);
+ /* _gp_disp is a special case, used from s_cpload. */
+ assert (mips_pic == NO_PIC
+ || strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0);
+ r = BFD_RELOC_HI16_S;
+ }
+
+ /*
+ * If the macro is about to expand into a second instruction,
+ * print a warning if needed. We need to pass ip as a parameter
+ * to generate a better warning message here...
+ */
+ if (mips_opts.warn_about_macros && place == NULL && *counter == 1)
+ as_warn (_("Macro instruction expanded into multiple instructions"));
+
+ if (place == NULL)
+ *counter += 1; /* bump instruction counter */
+
+ insn.insn_mo = (struct mips_opcode *) hash_find (op_hash, name);
+ assert (insn.insn_mo);
+ assert (strcmp (name, insn.insn_mo->name) == 0);
+ assert (strcmp (fmt, insn.insn_mo->args) == 0);
+
+ insn.insn_opcode = insn.insn_mo->match | (regnum << OP_SH_RT);
+ if (r == BFD_RELOC_UNUSED)
+ {
+ insn.insn_opcode |= high_expr.X_add_number;
+ append_insn (place, &insn, NULL, r, false);
+ }
+ else
+ append_insn (place, &insn, &high_expr, r, false);
+}
+
+/* set_at()
+ * Generates code to set the $at register to true (one)
+ * if reg is less than the immediate expression.
+ */
+static void
+set_at (counter, reg, unsignedp)
+ int *counter;
+ int reg;
+ int unsignedp;
+{
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ macro_build ((char *) NULL, counter, &imm_expr,
+ unsignedp ? "sltiu" : "slti",
+ "t,r,j", AT, reg, (int) BFD_RELOC_LO16);
+ else
+ {
+ load_register (counter, AT, &imm_expr, 0);
+ macro_build ((char *) NULL, counter, NULL,
+ unsignedp ? "sltu" : "slt",
+ "d,v,t", AT, reg, AT);
+ }
+}
+
+/* Warn if an expression is not a constant. */
+
+static void
+check_absolute_expr (ip, ex)
+ struct mips_cl_insn *ip;
+ expressionS *ex;
+{
+ if (ex->X_op == O_big)
+ as_bad (_("unsupported large constant"));
+ else if (ex->X_op != O_constant)
+ as_bad (_("Instruction %s requires absolute expression"), ip->insn_mo->name);
+}
+
+/* Count the leading zeroes by performing a binary chop. This is a
+ bulky bit of source, but performance is a LOT better for the
+ majority of values than a simple loop to count the bits:
+ for (lcnt = 0; (lcnt < 32); lcnt++)
+ if ((v) & (1 << (31 - lcnt)))
+ break;
+ However it is not code size friendly, and the gain will drop a bit
+ on certain cached systems.
+*/
+#define COUNT_TOP_ZEROES(v) \
+ (((v) & ~0xffff) == 0 \
+ ? ((v) & ~0xff) == 0 \
+ ? ((v) & ~0xf) == 0 \
+ ? ((v) & ~0x3) == 0 \
+ ? ((v) & ~0x1) == 0 \
+ ? !(v) \
+ ? 32 \
+ : 31 \
+ : 30 \
+ : ((v) & ~0x7) == 0 \
+ ? 29 \
+ : 28 \
+ : ((v) & ~0x3f) == 0 \
+ ? ((v) & ~0x1f) == 0 \
+ ? 27 \
+ : 26 \
+ : ((v) & ~0x7f) == 0 \
+ ? 25 \
+ : 24 \
+ : ((v) & ~0xfff) == 0 \
+ ? ((v) & ~0x3ff) == 0 \
+ ? ((v) & ~0x1ff) == 0 \
+ ? 23 \
+ : 22 \
+ : ((v) & ~0x7ff) == 0 \
+ ? 21 \
+ : 20 \
+ : ((v) & ~0x3fff) == 0 \
+ ? ((v) & ~0x1fff) == 0 \
+ ? 19 \
+ : 18 \
+ : ((v) & ~0x7fff) == 0 \
+ ? 17 \
+ : 16 \
+ : ((v) & ~0xffffff) == 0 \
+ ? ((v) & ~0xfffff) == 0 \
+ ? ((v) & ~0x3ffff) == 0 \
+ ? ((v) & ~0x1ffff) == 0 \
+ ? 15 \
+ : 14 \
+ : ((v) & ~0x7ffff) == 0 \
+ ? 13 \
+ : 12 \
+ : ((v) & ~0x3fffff) == 0 \
+ ? ((v) & ~0x1fffff) == 0 \
+ ? 11 \
+ : 10 \
+ : ((v) & ~0x7fffff) == 0 \
+ ? 9 \
+ : 8 \
+ : ((v) & ~0xfffffff) == 0 \
+ ? ((v) & ~0x3ffffff) == 0 \
+ ? ((v) & ~0x1ffffff) == 0 \
+ ? 7 \
+ : 6 \
+ : ((v) & ~0x7ffffff) == 0 \
+ ? 5 \
+ : 4 \
+ : ((v) & ~0x3fffffff) == 0 \
+ ? ((v) & ~0x1fffffff) == 0 \
+ ? 3 \
+ : 2 \
+ : ((v) & ~0x7fffffff) == 0 \
+ ? 1 \
+ : 0)
+
+/* load_register()
+ * This routine generates the least number of instructions neccessary to load
+ * an absolute expression value into a register.
+ */
+static void
+load_register (counter, reg, ep, dbl)
+ int *counter;
+ int reg;
+ expressionS *ep;
+ int dbl;
+{
+ int freg;
+ expressionS hi32, lo32;
+
+ if (ep->X_op != O_big)
+ {
+ assert (ep->X_op == O_constant);
+ if (ep->X_add_number < 0x8000
+ && (ep->X_add_number >= 0
+ || (ep->X_add_number >= -0x8000
+ && (! dbl
+ || ! ep->X_unsigned
+ || sizeof (ep->X_add_number) > 4))))
+ {
+ /* We can handle 16 bit signed values with an addiu to
+ $zero. No need to ever use daddiu here, since $zero and
+ the result are always correct in 32 bit mode. */
+ macro_build ((char *) NULL, counter, ep, "addiu", "t,r,j", reg, 0,
+ (int) BFD_RELOC_LO16);
+ return;
+ }
+ else if (ep->X_add_number >= 0 && ep->X_add_number < 0x10000)
+ {
+ /* We can handle 16 bit unsigned values with an ori to
+ $zero. */
+ macro_build ((char *) NULL, counter, ep, "ori", "t,r,i", reg, 0,
+ (int) BFD_RELOC_LO16);
+ return;
+ }
+ else if ((((ep->X_add_number &~ (offsetT) 0x7fffffff) == 0
+ || ((ep->X_add_number &~ (offsetT) 0x7fffffff)
+ == ~ (offsetT) 0x7fffffff))
+ && (! dbl
+ || ! ep->X_unsigned
+ || sizeof (ep->X_add_number) > 4
+ || (ep->X_add_number & 0x80000000) == 0))
+ || ((mips_opts.isa < 3 || ! dbl)
+ && (ep->X_add_number &~ (offsetT) 0xffffffff) == 0)
+ || (mips_opts.isa < 3
+ && ! dbl
+ && ((ep->X_add_number &~ (offsetT) 0xffffffff)
+ == ~ (offsetT) 0xffffffff)))
+ {
+ /* 32 bit values require an lui. */
+ macro_build ((char *) NULL, counter, ep, "lui", "t,u", reg,
+ (int) BFD_RELOC_HI16);
+ if ((ep->X_add_number & 0xffff) != 0)
+ macro_build ((char *) NULL, counter, ep, "ori", "t,r,i", reg, reg,
+ (int) BFD_RELOC_LO16);
+ return;
+ }
+ }
+
+ /* The value is larger than 32 bits. */
+
+ if (mips_opts.isa < 3)
+ {
+ as_bad (_("Number larger than 32 bits"));
+ macro_build ((char *) NULL, counter, ep, "addiu", "t,r,j", reg, 0,
+ (int) BFD_RELOC_LO16);
+ return;
+ }
+
+ if (ep->X_op != O_big)
+ {
+ hi32 = *ep;
+ hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
+ hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
+ hi32.X_add_number &= 0xffffffff;
+ lo32 = *ep;
+ lo32.X_add_number &= 0xffffffff;
+ }
+ else
+ {
+ assert (ep->X_add_number > 2);
+ if (ep->X_add_number == 3)
+ generic_bignum[3] = 0;
+ else if (ep->X_add_number > 4)
+ as_bad (_("Number larger than 64 bits"));
+ lo32.X_op = O_constant;
+ lo32.X_add_number = generic_bignum[0] + (generic_bignum[1] << 16);
+ hi32.X_op = O_constant;
+ hi32.X_add_number = generic_bignum[2] + (generic_bignum[3] << 16);
+ }
+
+ if (hi32.X_add_number == 0)
+ freg = 0;
+ else
+ {
+ int shift, bit;
+ unsigned long hi, lo;
+
+ if (hi32.X_add_number == 0xffffffff)
+ {
+ if ((lo32.X_add_number & 0xffff8000) == 0xffff8000)
+ {
+ macro_build ((char *) NULL, counter, &lo32, "addiu", "t,r,j",
+ reg, 0, (int) BFD_RELOC_LO16);
+ return;
+ }
+ if (lo32.X_add_number & 0x80000000)
+ {
+ macro_build ((char *) NULL, counter, &lo32, "lui", "t,u", reg,
+ (int) BFD_RELOC_HI16);
+ if (lo32.X_add_number & 0xffff)
+ macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i",
+ reg, reg, (int) BFD_RELOC_LO16);
+ return;
+ }
+ }
+
+ /* Check for 16bit shifted constant. We know that hi32 is
+ non-zero, so start the mask on the first bit of the hi32
+ value. */
+ shift = 17;
+ do
+ {
+ unsigned long himask, lomask;
+
+ if (shift < 32)
+ {
+ himask = 0xffff >> (32 - shift);
+ lomask = (0xffff << shift) & 0xffffffff;
+ }
+ else
+ {
+ himask = 0xffff << (shift - 32);
+ lomask = 0;
+ }
+ if ((hi32.X_add_number & ~ (offsetT) himask) == 0
+ && (lo32.X_add_number & ~ (offsetT) lomask) == 0)
+ {
+ expressionS tmp;
+
+ tmp.X_op = O_constant;
+ if (shift < 32)
+ tmp.X_add_number = ((hi32.X_add_number << (32 - shift))
+ | (lo32.X_add_number >> shift));
+ else
+ tmp.X_add_number = hi32.X_add_number >> (shift - 32);
+ macro_build ((char *) NULL, counter, &tmp, "ori", "t,r,i", reg, 0,
+ (int) BFD_RELOC_LO16);
+ macro_build ((char *) NULL, counter, NULL,
+ (shift >= 32) ? "dsll32" : "dsll",
+ "d,w,<", reg, reg,
+ (shift >= 32) ? shift - 32 : shift);
+ return;
+ }
+ shift++;
+ } while (shift <= (64 - 16));
+
+ /* Find the bit number of the lowest one bit, and store the
+ shifted value in hi/lo. */
+ hi = (unsigned long) (hi32.X_add_number & 0xffffffff);
+ lo = (unsigned long) (lo32.X_add_number & 0xffffffff);
+ if (lo != 0)
+ {
+ bit = 0;
+ while ((lo & 1) == 0)
+ {
+ lo >>= 1;
+ ++bit;
+ }
+ lo |= (hi & (((unsigned long) 1 << bit) - 1)) << (32 - bit);
+ hi >>= bit;
+ }
+ else
+ {
+ bit = 32;
+ while ((hi & 1) == 0)
+ {
+ hi >>= 1;
+ ++bit;
+ }
+ lo = hi;
+ hi = 0;
+ }
+
+ /* Optimize if the shifted value is a (power of 2) - 1. */
+ if ((hi == 0 && ((lo + 1) & lo) == 0)
+ || (lo == 0xffffffff && ((hi + 1) & hi) == 0))
+ {
+ shift = COUNT_TOP_ZEROES ((unsigned int) hi32.X_add_number);
+ if (shift != 0)
+ {
+ expressionS tmp;
+
+ /* This instruction will set the register to be all
+ ones. */
+ tmp.X_op = O_constant;
+ tmp.X_add_number = (offsetT) -1;
+ macro_build ((char *) NULL, counter, &tmp, "addiu", "t,r,j",
+ reg, 0, (int) BFD_RELOC_LO16);
+ if (bit != 0)
+ {
+ bit += shift;
+ macro_build ((char *) NULL, counter, NULL,
+ (bit >= 32) ? "dsll32" : "dsll",
+ "d,w,<", reg, reg,
+ (bit >= 32) ? bit - 32 : bit);
+ }
+ macro_build ((char *) NULL, counter, NULL,
+ (shift >= 32) ? "dsrl32" : "dsrl",
+ "d,w,<", reg, reg,
+ (shift >= 32) ? shift - 32 : shift);
+ return;
+ }
+ }
+
+ /* Sign extend hi32 before calling load_register, because we can
+ generally get better code when we load a sign extended value. */
+ if ((hi32.X_add_number & 0x80000000) != 0)
+ hi32.X_add_number |= ~ (offsetT) 0xffffffff;
+ load_register (counter, reg, &hi32, 0);
+ freg = reg;
+ }
+ if ((lo32.X_add_number & 0xffff0000) == 0)
+ {
+ if (freg != 0)
+ {
+ macro_build ((char *) NULL, counter, NULL, "dsll32", "d,w,<", reg,
+ freg, 0);
+ freg = reg;
+ }
+ }
+ else
+ {
+ expressionS mid16;
+
+ if ((freg == 0) && (lo32.X_add_number == 0xffffffff))
+ {
+ macro_build ((char *) NULL, counter, &lo32, "lui", "t,u", reg,
+ (int) BFD_RELOC_HI16);
+ macro_build ((char *) NULL, counter, NULL, "dsrl32", "d,w,<", reg,
+ reg, 0);
+ return;
+ }
+
+ if (freg != 0)
+ {
+ macro_build ((char *) NULL, counter, NULL, "dsll", "d,w,<", reg,
+ freg, 16);
+ freg = reg;
+ }
+ mid16 = lo32;
+ mid16.X_add_number >>= 16;
+ macro_build ((char *) NULL, counter, &mid16, "ori", "t,r,i", reg,
+ freg, (int) BFD_RELOC_LO16);
+ macro_build ((char *) NULL, counter, NULL, "dsll", "d,w,<", reg,
+ reg, 16);
+ freg = reg;
+ }
+ if ((lo32.X_add_number & 0xffff) != 0)
+ macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i", reg, freg,
+ (int) BFD_RELOC_LO16);
+}
+
+/* Load an address into a register. */
+
+static void
+load_address (counter, reg, ep)
+ int *counter;
+ int reg;
+ expressionS *ep;
+{
+ char *p;
+
+ if (ep->X_op != O_constant
+ && ep->X_op != O_symbol)
+ {
+ as_bad (_("expression too complex"));
+ ep->X_op = O_constant;
+ }
+
+ if (ep->X_op == O_constant)
+ {
+ load_register (counter, reg, ep, 0);
+ return;
+ }
+
+ if (mips_pic == NO_PIC)
+ {
+ /* If this is a reference to a GP relative symbol, we want
+ addiu $reg,$gp,<sym> (BFD_RELOC_MIPS_GPREL)
+ Otherwise we want
+ lui $reg,<sym> (BFD_RELOC_HI16_S)
+ addiu $reg,$reg,<sym> (BFD_RELOC_LO16)
+ If we have an addend, we always use the latter form. */
+ if ((valueT) ep->X_add_number >= MAX_GPREL_OFFSET
+ || nopic_need_relax (ep->X_add_symbol, 1))
+ p = NULL;
+ else
+ {
+ frag_grow (20);
+ macro_build ((char *) NULL, counter, ep,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", reg, GP, (int) BFD_RELOC_MIPS_GPREL);
+ p = frag_var (rs_machine_dependent, 8, 0,
+ RELAX_ENCODE (4, 8, 0, 4, 0,
+ mips_opts.warn_about_macros),
+ ep->X_add_symbol, (offsetT) 0, (char *) NULL);
+ }
+ macro_build_lui (p, counter, ep, reg);
+ if (p != NULL)
+ p += 4;
+ macro_build (p, counter, ep,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+ }
+ else if (mips_pic == SVR4_PIC && ! mips_big_got)
+ {
+ expressionS ex;
+
+ /* If this is a reference to an external symbol, we want
+ lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ Otherwise we want
+ lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $reg,$reg,<sym> (BFD_RELOC_LO16)
+ If there is a constant, it must be added in after. */
+ ex.X_add_number = ep->X_add_number;
+ ep->X_add_number = 0;
+ frag_grow (20);
+ macro_build ((char *) NULL, counter, ep,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "lw" : "ld"),
+ "t,o(b)", reg, (int) BFD_RELOC_MIPS_GOT16, GP);
+ macro_build ((char *) NULL, counter, (expressionS *) NULL, "nop", "");
+ p = frag_var (rs_machine_dependent, 4, 0,
+ RELAX_ENCODE (0, 4, -8, 0, 0, mips_opts.warn_about_macros),
+ ep->X_add_symbol, (offsetT) 0, (char *) NULL);
+ macro_build (p, counter, ep,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+ if (ex.X_add_number != 0)
+ {
+ if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ ex.X_op = O_constant;
+ macro_build ((char *) NULL, counter, &ex,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+ }
+ }
+ else if (mips_pic == SVR4_PIC)
+ {
+ expressionS ex;
+ int off;
+
+ /* This is the large GOT case. If this is a reference to an
+ external symbol, we want
+ lui $reg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $reg,$reg,$gp
+ lw $reg,<sym>($reg) (BFD_RELOC_MIPS_GOT_LO16)
+ Otherwise, for a reference to a local symbol, we want
+ lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $reg,$reg,<sym> (BFD_RELOC_LO16)
+ If there is a constant, it must be added in after. */
+ ex.X_add_number = ep->X_add_number;
+ ep->X_add_number = 0;
+ if (reg_needs_delay (GP))
+ off = 4;
+ else
+ off = 0;
+ frag_grow (32);
+ macro_build ((char *) NULL, counter, ep, "lui", "t,u", reg,
+ (int) BFD_RELOC_MIPS_GOT_HI16);
+ macro_build ((char *) NULL, counter, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", reg, reg, GP);
+ macro_build ((char *) NULL, counter, ep,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "lw" : "ld"),
+ "t,o(b)", reg, (int) BFD_RELOC_MIPS_GOT_LO16, reg);
+ p = frag_var (rs_machine_dependent, 12 + off, 0,
+ RELAX_ENCODE (12, 12 + off, off, 8 + off, 0,
+ mips_opts.warn_about_macros),
+ ep->X_add_symbol, (offsetT) 0, (char *) NULL);
+ if (off > 0)
+ {
+ /* We need a nop before loading from $gp. This special
+ check is required because the lui which starts the main
+ instruction stream does not refer to $gp, and so will not
+ insert the nop which may be required. */
+ macro_build (p, counter, (expressionS *) NULL, "nop", "");
+ p += 4;
+ }
+ macro_build (p, counter, ep,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "lw" : "ld"),
+ "t,o(b)", reg, (int) BFD_RELOC_MIPS_GOT16, GP);
+ p += 4;
+ macro_build (p, counter, (expressionS *) NULL, "nop", "");
+ p += 4;
+ macro_build (p, counter, ep,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+ if (ex.X_add_number != 0)
+ {
+ if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ ex.X_op = O_constant;
+ macro_build ((char *) NULL, counter, &ex,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+ }
+ }
+ else if (mips_pic == EMBEDDED_PIC)
+ {
+ /* We always do
+ addiu $reg,$gp,<sym> (BFD_RELOC_MIPS_GPREL)
+ */
+ macro_build ((char *) NULL, counter, ep,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", reg, GP, (int) BFD_RELOC_MIPS_GPREL);
+ }
+ else
+ abort ();
+}
+
+/*
+ * Build macros
+ * This routine implements the seemingly endless macro or synthesized
+ * instructions and addressing modes in the mips assembly language. Many
+ * of these macros are simple and are similar to each other. These could
+ * probably be handled by some kind of table or grammer aproach instead of
+ * this verbose method. Others are not simple macros but are more like
+ * optimizing code generation.
+ * One interesting optimization is when several store macros appear
+ * consecutivly that would load AT with the upper half of the same address.
+ * The ensuing load upper instructions are ommited. This implies some kind
+ * of global optimization. We currently only optimize within a single macro.
+ * For many of the load and store macros if the address is specified as a
+ * constant expression in the first 64k of memory (ie ld $2,0x4000c) we
+ * first load register 'at' with zero and use it as the base register. The
+ * mips assembler simply uses register $zero. Just one tiny optimization
+ * we're missing.
+ */
+static void
+macro (ip)
+ struct mips_cl_insn *ip;
+{
+ register int treg, sreg, dreg, breg;
+ int tempreg;
+ int mask;
+ int icnt = 0;
+ int used_at;
+ expressionS expr1;
+ const char *s;
+ const char *s2;
+ const char *fmt;
+ int likely = 0;
+ int dbl = 0;
+ int coproc = 0;
+ int lr = 0;
+ int imm = 0;
+ offsetT maxnum;
+ int off;
+ bfd_reloc_code_real_type r;
+ char *p;
+ int hold_mips_optimize;
+
+ assert (! mips_opts.mips16);
+
+ treg = (ip->insn_opcode >> 16) & 0x1f;
+ dreg = (ip->insn_opcode >> 11) & 0x1f;
+ sreg = breg = (ip->insn_opcode >> 21) & 0x1f;
+ mask = ip->insn_mo->mask;
+
+ expr1.X_op = O_constant;
+ expr1.X_op_symbol = NULL;
+ expr1.X_add_symbol = NULL;
+ expr1.X_add_number = 1;
+
+ switch (mask)
+ {
+ case M_DABS:
+ dbl = 1;
+ case M_ABS:
+ /* bgez $a0,.+12
+ move v0,$a0
+ sub v0,$zero,$a0
+ */
+
+ mips_emit_delays (true);
+ ++mips_opts.noreorder;
+ mips_any_noreorder = 1;
+
+ expr1.X_add_number = 8;
+ macro_build ((char *) NULL, &icnt, &expr1, "bgez", "s,p", sreg);
+ if (dreg == sreg)
+ macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
+ else
+ macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", dreg, sreg, 0);
+ macro_build ((char *) NULL, &icnt, NULL,
+ dbl ? "dsub" : "sub",
+ "d,v,t", dreg, 0, sreg);
+
+ --mips_opts.noreorder;
+ return;
+
+ case M_ADD_I:
+ s = "addi";
+ s2 = "add";
+ goto do_addi;
+ case M_ADDU_I:
+ s = "addiu";
+ s2 = "addu";
+ goto do_addi;
+ case M_DADD_I:
+ dbl = 1;
+ s = "daddi";
+ s2 = "dadd";
+ goto do_addi;
+ case M_DADDU_I:
+ dbl = 1;
+ s = "daddiu";
+ s2 = "daddu";
+ do_addi:
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ {
+ macro_build ((char *) NULL, &icnt, &imm_expr, s, "t,r,j", treg, sreg,
+ (int) BFD_RELOC_LO16);
+ return;
+ }
+ load_register (&icnt, AT, &imm_expr, dbl);
+ macro_build ((char *) NULL, &icnt, NULL, s2, "d,v,t", treg, sreg, AT);
+ break;
+
+ case M_AND_I:
+ s = "andi";
+ s2 = "and";
+ goto do_bit;
+ case M_OR_I:
+ s = "ori";
+ s2 = "or";
+ goto do_bit;
+ case M_NOR_I:
+ s = "";
+ s2 = "nor";
+ goto do_bit;
+ case M_XOR_I:
+ s = "xori";
+ s2 = "xor";
+ do_bit:
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= 0
+ && imm_expr.X_add_number < 0x10000)
+ {
+ if (mask != M_NOR_I)
+ macro_build ((char *) NULL, &icnt, &imm_expr, s, "t,r,i", treg,
+ sreg, (int) BFD_RELOC_LO16);
+ else
+ {
+ macro_build ((char *) NULL, &icnt, &imm_expr, "ori", "t,r,i",
+ treg, sreg, (int) BFD_RELOC_LO16);
+ macro_build ((char *) NULL, &icnt, NULL, "nor", "d,v,t",
+ treg, treg, 0);
+ }
+ return;
+ }
+
+ load_register (&icnt, AT, &imm_expr, 0);
+ macro_build ((char *) NULL, &icnt, NULL, s2, "d,v,t", treg, sreg, AT);
+ break;
+
+ case M_BEQ_I:
+ s = "beq";
+ goto beq_i;
+ case M_BEQL_I:
+ s = "beql";
+ likely = 1;
+ goto beq_i;
+ case M_BNE_I:
+ s = "bne";
+ goto beq_i;
+ case M_BNEL_I:
+ s = "bnel";
+ likely = 1;
+ beq_i:
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr, s, "s,t,p", sreg,
+ 0);
+ return;
+ }
+ load_register (&icnt, AT, &imm_expr, 0);
+ macro_build ((char *) NULL, &icnt, &offset_expr, s, "s,t,p", sreg, AT);
+ break;
+
+ case M_BGEL:
+ likely = 1;
+ case M_BGE:
+ if (treg == 0)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "bgezl" : "bgez",
+ "s,p", sreg);
+ return;
+ }
+ if (sreg == 0)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "blezl" : "blez",
+ "s,p", treg);
+ return;
+ }
+ macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", AT, sreg, treg);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "beql" : "beq",
+ "s,t,p", AT, 0);
+ break;
+
+ case M_BGTL_I:
+ likely = 1;
+ case M_BGT_I:
+ /* check for > max integer */
+ maxnum = 0x7fffffff;
+ if (mips_opts.isa >= 3 && sizeof (maxnum) > 4)
+ {
+ maxnum <<= 16;
+ maxnum |= 0xffff;
+ maxnum <<= 16;
+ maxnum |= 0xffff;
+ }
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= maxnum
+ && (mips_opts.isa < 3 || sizeof (maxnum) > 4))
+ {
+ do_false:
+ /* result is always false */
+ if (! likely)
+ {
+ as_warn (_("Branch %s is always false (nop)"), ip->insn_mo->name);
+ macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
+ }
+ else
+ {
+ as_warn (_("Branch likely %s is always false"), ip->insn_mo->name);
+ macro_build ((char *) NULL, &icnt, &offset_expr, "bnel",
+ "s,t,p", 0, 0);
+ }
+ return;
+ }
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ imm_expr.X_add_number++;
+ /* FALLTHROUGH */
+ case M_BGE_I:
+ case M_BGEL_I:
+ if (mask == M_BGEL_I)
+ likely = 1;
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "bgezl" : "bgez",
+ "s,p", sreg);
+ return;
+ }
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "bgtzl" : "bgtz",
+ "s,p", sreg);
+ return;
+ }
+ maxnum = 0x7fffffff;
+ if (mips_opts.isa >= 3 && sizeof (maxnum) > 4)
+ {
+ maxnum <<= 16;
+ maxnum |= 0xffff;
+ maxnum <<= 16;
+ maxnum |= 0xffff;
+ }
+ maxnum = - maxnum - 1;
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number <= maxnum
+ && (mips_opts.isa < 3 || sizeof (maxnum) > 4))
+ {
+ do_true:
+ /* result is always true */
+ as_warn (_("Branch %s is always true"), ip->insn_mo->name);
+ macro_build ((char *) NULL, &icnt, &offset_expr, "b", "p");
+ return;
+ }
+ set_at (&icnt, sreg, 0);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "beql" : "beq",
+ "s,t,p", AT, 0);
+ break;
+
+ case M_BGEUL:
+ likely = 1;
+ case M_BGEU:
+ if (treg == 0)
+ goto do_true;
+ if (sreg == 0)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "beql" : "beq",
+ "s,t,p", 0, treg);
+ return;
+ }
+ macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", AT, sreg,
+ treg);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "beql" : "beq",
+ "s,t,p", AT, 0);
+ break;
+
+ case M_BGTUL_I:
+ likely = 1;
+ case M_BGTU_I:
+ if (sreg == 0
+ || (mips_opts.isa < 3
+ && imm_expr.X_op == O_constant
+ && imm_expr.X_add_number == 0xffffffff))
+ goto do_false;
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ imm_expr.X_add_number++;
+ /* FALLTHROUGH */
+ case M_BGEU_I:
+ case M_BGEUL_I:
+ if (mask == M_BGEUL_I)
+ likely = 1;
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ goto do_true;
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "bnel" : "bne",
+ "s,t,p", sreg, 0);
+ return;
+ }
+ set_at (&icnt, sreg, 1);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "beql" : "beq",
+ "s,t,p", AT, 0);
+ break;
+
+ case M_BGTL:
+ likely = 1;
+ case M_BGT:
+ if (treg == 0)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "bgtzl" : "bgtz",
+ "s,p", sreg);
+ return;
+ }
+ if (sreg == 0)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "bltzl" : "bltz",
+ "s,p", treg);
+ return;
+ }
+ macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", AT, treg, sreg);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "bnel" : "bne",
+ "s,t,p", AT, 0);
+ break;
+
+ case M_BGTUL:
+ likely = 1;
+ case M_BGTU:
+ if (treg == 0)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "bnel" : "bne",
+ "s,t,p", sreg, 0);
+ return;
+ }
+ if (sreg == 0)
+ goto do_false;
+ macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", AT, treg,
+ sreg);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "bnel" : "bne",
+ "s,t,p", AT, 0);
+ break;
+
+ case M_BLEL:
+ likely = 1;
+ case M_BLE:
+ if (treg == 0)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "blezl" : "blez",
+ "s,p", sreg);
+ return;
+ }
+ if (sreg == 0)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "bgezl" : "bgez",
+ "s,p", treg);
+ return;
+ }
+ macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", AT, treg, sreg);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "beql" : "beq",
+ "s,t,p", AT, 0);
+ break;
+
+ case M_BLEL_I:
+ likely = 1;
+ case M_BLE_I:
+ maxnum = 0x7fffffff;
+ if (mips_opts.isa >= 3 && sizeof (maxnum) > 4)
+ {
+ maxnum <<= 16;
+ maxnum |= 0xffff;
+ maxnum <<= 16;
+ maxnum |= 0xffff;
+ }
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= maxnum
+ && (mips_opts.isa < 3 || sizeof (maxnum) > 4))
+ goto do_true;
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ imm_expr.X_add_number++;
+ /* FALLTHROUGH */
+ case M_BLT_I:
+ case M_BLTL_I:
+ if (mask == M_BLTL_I)
+ likely = 1;
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "bltzl" : "bltz",
+ "s,p", sreg);
+ return;
+ }
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "blezl" : "blez",
+ "s,p", sreg);
+ return;
+ }
+ set_at (&icnt, sreg, 0);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "bnel" : "bne",
+ "s,t,p", AT, 0);
+ break;
+
+ case M_BLEUL:
+ likely = 1;
+ case M_BLEU:
+ if (treg == 0)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "beql" : "beq",
+ "s,t,p", sreg, 0);
+ return;
+ }
+ if (sreg == 0)
+ goto do_true;
+ macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", AT, treg,
+ sreg);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "beql" : "beq",
+ "s,t,p", AT, 0);
+ break;
+
+ case M_BLEUL_I:
+ likely = 1;
+ case M_BLEU_I:
+ if (sreg == 0
+ || (mips_opts.isa < 3
+ && imm_expr.X_op == O_constant
+ && imm_expr.X_add_number == 0xffffffff))
+ goto do_true;
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ imm_expr.X_add_number++;
+ /* FALLTHROUGH */
+ case M_BLTU_I:
+ case M_BLTUL_I:
+ if (mask == M_BLTUL_I)
+ likely = 1;
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ goto do_false;
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "beql" : "beq",
+ "s,t,p", sreg, 0);
+ return;
+ }
+ set_at (&icnt, sreg, 1);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "bnel" : "bne",
+ "s,t,p", AT, 0);
+ break;
+
+ case M_BLTL:
+ likely = 1;
+ case M_BLT:
+ if (treg == 0)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "bltzl" : "bltz",
+ "s,p", sreg);
+ return;
+ }
+ if (sreg == 0)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "bgtzl" : "bgtz",
+ "s,p", treg);
+ return;
+ }
+ macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", AT, sreg, treg);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "bnel" : "bne",
+ "s,t,p", AT, 0);
+ break;
+
+ case M_BLTUL:
+ likely = 1;
+ case M_BLTU:
+ if (treg == 0)
+ goto do_false;
+ if (sreg == 0)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "bnel" : "bne",
+ "s,t,p", 0, treg);
+ return;
+ }
+ macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", AT, sreg,
+ treg);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ likely ? "bnel" : "bne",
+ "s,t,p", AT, 0);
+ break;
+
+ case M_DDIV_3:
+ dbl = 1;
+ case M_DIV_3:
+ s = "mflo";
+ goto do_div3;
+ case M_DREM_3:
+ dbl = 1;
+ case M_REM_3:
+ s = "mfhi";
+ do_div3:
+ if (treg == 0)
+ {
+ as_warn (_("Divide by zero."));
+ if (mips_trap)
+ macro_build ((char *) NULL, &icnt, NULL, "teq", "s,t", 0, 0);
+ else
+ macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7);
+ return;
+ }
+
+ mips_emit_delays (true);
+ ++mips_opts.noreorder;
+ mips_any_noreorder = 1;
+ if (mips_trap)
+ {
+ macro_build ((char *) NULL, &icnt, NULL, "teq", "s,t", treg, 0);
+ macro_build ((char *) NULL, &icnt, NULL,
+ dbl ? "ddiv" : "div",
+ "z,s,t", sreg, treg);
+ }
+ else
+ {
+ expr1.X_add_number = 8;
+ macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", treg, 0);
+ macro_build ((char *) NULL, &icnt, NULL,
+ dbl ? "ddiv" : "div",
+ "z,s,t", sreg, treg);
+ macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7);
+ }
+ expr1.X_add_number = -1;
+ macro_build ((char *) NULL, &icnt, &expr1,
+ dbl ? "daddiu" : "addiu",
+ "t,r,j", AT, 0, (int) BFD_RELOC_LO16);
+ expr1.X_add_number = mips_trap ? (dbl ? 12 : 8) : (dbl ? 20 : 16);
+ macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", treg, AT);
+ if (dbl)
+ {
+ expr1.X_add_number = 1;
+ macro_build ((char *) NULL, &icnt, &expr1, "daddiu", "t,r,j", AT, 0,
+ (int) BFD_RELOC_LO16);
+ macro_build ((char *) NULL, &icnt, NULL, "dsll32", "d,w,<", AT, AT,
+ 31);
+ }
+ else
+ {
+ expr1.X_add_number = 0x80000000;
+ macro_build ((char *) NULL, &icnt, &expr1, "lui", "t,u", AT,
+ (int) BFD_RELOC_HI16);
+ }
+ if (mips_trap)
+ {
+ macro_build ((char *) NULL, &icnt, NULL, "teq", "s,t", sreg, AT);
+ /* We want to close the noreorder block as soon as possible, so
+ that later insns are available for delay slot filling. */
+ --mips_opts.noreorder;
+ }
+ else
+ {
+ expr1.X_add_number = 8;
+ macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", sreg, AT);
+ macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
+
+ /* We want to close the noreorder block as soon as possible, so
+ that later insns are available for delay slot filling. */
+ --mips_opts.noreorder;
+
+ macro_build ((char *) NULL, &icnt, NULL, "break", "c", 6);
+ }
+ macro_build ((char *) NULL, &icnt, NULL, s, "d", dreg);
+ break;
+
+ case M_DIV_3I:
+ s = "div";
+ s2 = "mflo";
+ goto do_divi;
+ case M_DIVU_3I:
+ s = "divu";
+ s2 = "mflo";
+ goto do_divi;
+ case M_REM_3I:
+ s = "div";
+ s2 = "mfhi";
+ goto do_divi;
+ case M_REMU_3I:
+ s = "divu";
+ s2 = "mfhi";
+ goto do_divi;
+ case M_DDIV_3I:
+ dbl = 1;
+ s = "ddiv";
+ s2 = "mflo";
+ goto do_divi;
+ case M_DDIVU_3I:
+ dbl = 1;
+ s = "ddivu";
+ s2 = "mflo";
+ goto do_divi;
+ case M_DREM_3I:
+ dbl = 1;
+ s = "ddiv";
+ s2 = "mfhi";
+ goto do_divi;
+ case M_DREMU_3I:
+ dbl = 1;
+ s = "ddivu";
+ s2 = "mfhi";
+ do_divi:
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ {
+ as_warn (_("Divide by zero."));
+ if (mips_trap)
+ macro_build ((char *) NULL, &icnt, NULL, "teq", "s,t", 0, 0);
+ else
+ macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7);
+ return;
+ }
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+ {
+ if (strcmp (s2, "mflo") == 0)
+ macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", dreg,
+ sreg);
+ else
+ macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", dreg, 0);
+ return;
+ }
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number == -1
+ && s[strlen (s) - 1] != 'u')
+ {
+ if (strcmp (s2, "mflo") == 0)
+ {
+ if (dbl)
+ macro_build ((char *) NULL, &icnt, NULL, "dneg", "d,w", dreg,
+ sreg);
+ else
+ macro_build ((char *) NULL, &icnt, NULL, "neg", "d,w", dreg,
+ sreg);
+ }
+ else
+ macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", dreg, 0);
+ return;
+ }
+
+ load_register (&icnt, AT, &imm_expr, dbl);
+ macro_build ((char *) NULL, &icnt, NULL, s, "z,s,t", sreg, AT);
+ macro_build ((char *) NULL, &icnt, NULL, s2, "d", dreg);
+ break;
+
+ case M_DIVU_3:
+ s = "divu";
+ s2 = "mflo";
+ goto do_divu3;
+ case M_REMU_3:
+ s = "divu";
+ s2 = "mfhi";
+ goto do_divu3;
+ case M_DDIVU_3:
+ s = "ddivu";
+ s2 = "mflo";
+ goto do_divu3;
+ case M_DREMU_3:
+ s = "ddivu";
+ s2 = "mfhi";
+ do_divu3:
+ mips_emit_delays (true);
+ ++mips_opts.noreorder;
+ mips_any_noreorder = 1;
+ if (mips_trap)
+ {
+ macro_build ((char *) NULL, &icnt, NULL, "teq", "s,t", treg, 0);
+ macro_build ((char *) NULL, &icnt, NULL, s, "z,s,t", sreg, treg);
+ /* We want to close the noreorder block as soon as possible, so
+ that later insns are available for delay slot filling. */
+ --mips_opts.noreorder;
+ }
+ else
+ {
+ expr1.X_add_number = 8;
+ macro_build ((char *) NULL, &icnt, &expr1, "bne", "s,t,p", treg, 0);
+ macro_build ((char *) NULL, &icnt, NULL, s, "z,s,t", sreg, treg);
+
+ /* We want to close the noreorder block as soon as possible, so
+ that later insns are available for delay slot filling. */
+ --mips_opts.noreorder;
+ macro_build ((char *) NULL, &icnt, NULL, "break", "c", 7);
+ }
+ macro_build ((char *) NULL, &icnt, NULL, s2, "d", dreg);
+ return;
+
+ case M_DLA_AB:
+ dbl = 1;
+ case M_LA_AB:
+ /* Load the address of a symbol into a register. If breg is not
+ zero, we then add a base register to it. */
+
+ /* When generating embedded PIC code, we permit expressions of
+ the form
+ la $4,foo-bar
+ where bar is an address in the .text section. These are used
+ when getting the addresses of functions. We don't permit
+ X_add_number to be non-zero, because if the symbol is
+ external the relaxing code needs to know that any addend is
+ purely the offset to X_op_symbol. */
+ if (mips_pic == EMBEDDED_PIC
+ && offset_expr.X_op == O_subtract
+ && now_seg == text_section
+ && (offset_expr.X_op_symbol->sy_value.X_op == O_constant
+ ? S_GET_SEGMENT (offset_expr.X_op_symbol) == text_section
+ : (offset_expr.X_op_symbol->sy_value.X_op == O_symbol
+ && (S_GET_SEGMENT (offset_expr.X_op_symbol
+ ->sy_value.X_add_symbol)
+ == text_section)))
+ && breg == 0
+ && offset_expr.X_add_number == 0)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
+ treg, (int) BFD_RELOC_PCREL_HI16_S);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", treg, treg, (int) BFD_RELOC_PCREL_LO16);
+ return;
+ }
+
+ if (offset_expr.X_op != O_symbol
+ && offset_expr.X_op != O_constant)
+ {
+ as_bad (_("expression too complex"));
+ offset_expr.X_op = O_constant;
+ }
+
+ if (treg == breg)
+ {
+ tempreg = AT;
+ used_at = 1;
+ }
+ else
+ {
+ tempreg = treg;
+ used_at = 0;
+ }
+
+ if (offset_expr.X_op == O_constant)
+ load_register (&icnt, tempreg, &offset_expr, dbl);
+ else if (mips_pic == NO_PIC)
+ {
+ /* If this is a reference to an GP relative symbol, we want
+ addiu $tempreg,$gp,<sym> (BFD_RELOC_MIPS_GPREL)
+ Otherwise we want
+ lui $tempreg,<sym> (BFD_RELOC_HI16_S)
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+ If we have a constant, we need two instructions anyhow,
+ so we may as well always use the latter form. */
+ if ((valueT) offset_expr.X_add_number >= MAX_GPREL_OFFSET
+ || nopic_need_relax (offset_expr.X_add_symbol, 1))
+ p = NULL;
+ else
+ {
+ frag_grow (20);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", tempreg, GP, (int) BFD_RELOC_MIPS_GPREL);
+ p = frag_var (rs_machine_dependent, 8, 0,
+ RELAX_ENCODE (4, 8, 0, 4, 0,
+ mips_opts.warn_about_macros),
+ offset_expr.X_add_symbol, (offsetT) 0,
+ (char *) NULL);
+ }
+ macro_build_lui (p, &icnt, &offset_expr, tempreg);
+ if (p != NULL)
+ p += 4;
+ macro_build (p, &icnt, &offset_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
+ }
+ else if (mips_pic == SVR4_PIC && ! mips_big_got)
+ {
+ /* If this is a reference to an external symbol, and there
+ is no constant, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ For a local symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+
+ If we have a small constant, and this is a reference to
+ an external symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<constant>
+ For a local symbol, we want the same instruction
+ sequence, but we output a BFD_RELOC_LO16 reloc on the
+ addiu instruction.
+
+ If we have a large constant, and this is a reference to
+ an external symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ lui $at,<hiconstant>
+ addiu $at,$at,<loconstant>
+ addu $tempreg,$tempreg,$at
+ For a local symbol, we want the same instruction
+ sequence, but we output a BFD_RELOC_LO16 reloc on the
+ addiu instruction. */
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ frag_grow (32);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ dbl ? "ld" : "lw",
+ "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP);
+ if (expr1.X_add_number == 0)
+ {
+ int off;
+
+ if (breg == 0)
+ off = 0;
+ else
+ {
+ /* We're going to put in an addu instruction using
+ tempreg, so we may as well insert the nop right
+ now. */
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ "nop", "");
+ off = 4;
+ }
+ p = frag_var (rs_machine_dependent, 8 - off, 0,
+ RELAX_ENCODE (0, 8 - off, -4 - off, 4 - off, 0,
+ (breg == 0
+ ? mips_opts.warn_about_macros
+ : 0)),
+ offset_expr.X_add_symbol, (offsetT) 0,
+ (char *) NULL);
+ if (breg == 0)
+ {
+ macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+ p += 4;
+ }
+ macro_build (p, &icnt, &expr1,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
+ /* FIXME: If breg == 0, and the next instruction uses
+ $tempreg, then if this variant case is used an extra
+ nop will be generated. */
+ }
+ else if (expr1.X_add_number >= -0x8000
+ && expr1.X_add_number < 0x8000)
+ {
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ "nop", "");
+ macro_build ((char *) NULL, &icnt, &expr1,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
+ (void) frag_var (rs_machine_dependent, 0, 0,
+ RELAX_ENCODE (0, 0, -12, -4, 0, 0),
+ offset_expr.X_add_symbol, (offsetT) 0,
+ (char *) NULL);
+ }
+ else
+ {
+ int off1;
+
+ /* If we are going to add in a base register, and the
+ target register and the base register are the same,
+ then we are using AT as a temporary register. Since
+ we want to load the constant into AT, we add our
+ current AT (from the global offset table) and the
+ register into the register now, and pretend we were
+ not using a base register. */
+ if (breg != treg)
+ off1 = 0;
+ else
+ {
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ "nop", "");
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", treg, AT, breg);
+ breg = 0;
+ tempreg = treg;
+ off1 = -8;
+ }
+
+ /* Set mips_optimize around the lui instruction to avoid
+ inserting an unnecessary nop after the lw. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
+ macro_build_lui ((char *) NULL, &icnt, &expr1, AT);
+ mips_optimize = hold_mips_optimize;
+
+ macro_build ((char *) NULL, &icnt, &expr1,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", AT, AT, (int) BFD_RELOC_LO16);
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", tempreg, tempreg, AT);
+ (void) frag_var (rs_machine_dependent, 0, 0,
+ RELAX_ENCODE (0, 0, -16 + off1, -8, 0, 0),
+ offset_expr.X_add_symbol, (offsetT) 0,
+ (char *) NULL);
+ used_at = 1;
+ }
+ }
+ else if (mips_pic == SVR4_PIC)
+ {
+ int gpdel;
+
+ /* This is the large GOT case. If this is a reference to an
+ external symbol, and there is no constant, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ For a local symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+
+ If we have a small constant, and this is a reference to
+ an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ nop
+ addiu $tempreg,$tempreg,<constant>
+ For a local symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<constant> (BFD_RELOC_LO16)
+
+ If we have a large constant, and this is a reference to
+ an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ lui $at,<hiconstant>
+ addiu $at,$at,<loconstant>
+ addu $tempreg,$tempreg,$at
+ For a local symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ lui $at,<hiconstant>
+ addiu $at,$at,<loconstant> (BFD_RELOC_LO16)
+ addu $tempreg,$tempreg,$at
+ */
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ frag_grow (52);
+ if (reg_needs_delay (GP))
+ gpdel = 4;
+ else
+ gpdel = 0;
+ macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
+ tempreg, (int) BFD_RELOC_MIPS_GOT_HI16);
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", tempreg, tempreg, GP);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ dbl ? "ld" : "lw",
+ "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT_LO16,
+ tempreg);
+ if (expr1.X_add_number == 0)
+ {
+ int off;
+
+ if (breg == 0)
+ off = 0;
+ else
+ {
+ /* We're going to put in an addu instruction using
+ tempreg, so we may as well insert the nop right
+ now. */
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ "nop", "");
+ off = 4;
+ }
+
+ p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
+ RELAX_ENCODE (12 + off, 12 + gpdel, gpdel,
+ 8 + gpdel, 0,
+ (breg == 0
+ ? mips_opts.warn_about_macros
+ : 0)),
+ offset_expr.X_add_symbol, (offsetT) 0,
+ (char *) NULL);
+ }
+ else if (expr1.X_add_number >= -0x8000
+ && expr1.X_add_number < 0x8000)
+ {
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ "nop", "");
+ macro_build ((char *) NULL, &icnt, &expr1,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
+
+ p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
+ RELAX_ENCODE (20, 12 + gpdel, gpdel, 8 + gpdel, 0,
+ (breg == 0
+ ? mips_opts.warn_about_macros
+ : 0)),
+ offset_expr.X_add_symbol, (offsetT) 0,
+ (char *) NULL);
+ }
+ else
+ {
+ int adj, dreg;
+
+ /* If we are going to add in a base register, and the
+ target register and the base register are the same,
+ then we are using AT as a temporary register. Since
+ we want to load the constant into AT, we add our
+ current AT (from the global offset table) and the
+ register into the register now, and pretend we were
+ not using a base register. */
+ if (breg != treg)
+ {
+ adj = 0;
+ dreg = tempreg;
+ }
+ else
+ {
+ assert (tempreg == AT);
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ "nop", "");
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", treg, AT, breg);
+ dreg = treg;
+ adj = 8;
+ }
+
+ /* Set mips_optimize around the lui instruction to avoid
+ inserting an unnecessary nop after the lw. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
+ macro_build_lui ((char *) NULL, &icnt, &expr1, AT);
+ mips_optimize = hold_mips_optimize;
+
+ macro_build ((char *) NULL, &icnt, &expr1,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", AT, AT, (int) BFD_RELOC_LO16);
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", dreg, dreg, AT);
+
+ p = frag_var (rs_machine_dependent, 16 + gpdel + adj, 0,
+ RELAX_ENCODE (24 + adj, 16 + gpdel + adj, gpdel,
+ 8 + gpdel, 0,
+ (breg == 0
+ ? mips_opts.warn_about_macros
+ : 0)),
+ offset_expr.X_add_symbol, (offsetT) 0,
+ (char *) NULL);
+
+ used_at = 1;
+ }
+
+ if (gpdel > 0)
+ {
+ /* This is needed because this instruction uses $gp, but
+ the first instruction on the main stream does not. */
+ macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+ p += 4;
+ }
+ macro_build (p, &icnt, &offset_expr,
+ dbl ? "ld" : "lw",
+ "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP);
+ p += 4;
+ if (expr1.X_add_number >= -0x8000
+ && expr1.X_add_number < 0x8000)
+ {
+ macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+ p += 4;
+ macro_build (p, &icnt, &expr1,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
+ /* FIXME: If add_number is 0, and there was no base
+ register, the external symbol case ended with a load,
+ so if the symbol turns out to not be external, and
+ the next instruction uses tempreg, an unnecessary nop
+ will be inserted. */
+ }
+ else
+ {
+ if (breg == treg)
+ {
+ /* We must add in the base register now, as in the
+ external symbol case. */
+ assert (tempreg == AT);
+ macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+ p += 4;
+ macro_build (p, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", treg, AT, breg);
+ p += 4;
+ tempreg = treg;
+ /* We set breg to 0 because we have arranged to add
+ it in in both cases. */
+ breg = 0;
+ }
+
+ macro_build_lui (p, &icnt, &expr1, AT);
+ p += 4;
+ macro_build (p, &icnt, &expr1,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", AT, AT, (int) BFD_RELOC_LO16);
+ p += 4;
+ macro_build (p, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", tempreg, tempreg, AT);
+ p += 4;
+ }
+ }
+ else if (mips_pic == EMBEDDED_PIC)
+ {
+ /* We use
+ addiu $tempreg,$gp,<sym> (BFD_RELOC_MIPS_GPREL)
+ */
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", tempreg, GP, (int) BFD_RELOC_MIPS_GPREL);
+ }
+ else
+ abort ();
+
+ if (breg != 0)
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", treg, tempreg, breg);
+
+ if (! used_at)
+ return;
+
+ break;
+
+ case M_J_A:
+ /* The j instruction may not be used in PIC code, since it
+ requires an absolute address. We convert it to a b
+ instruction. */
+ if (mips_pic == NO_PIC)
+ macro_build ((char *) NULL, &icnt, &offset_expr, "j", "a");
+ else
+ macro_build ((char *) NULL, &icnt, &offset_expr, "b", "p");
+ return;
+
+ /* The jal instructions must be handled as macros because when
+ generating PIC code they expand to multi-instruction
+ sequences. Normally they are simple instructions. */
+ case M_JAL_1:
+ dreg = RA;
+ /* Fall through. */
+ case M_JAL_2:
+ if (mips_pic == NO_PIC
+ || mips_pic == EMBEDDED_PIC)
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "jalr",
+ "d,s", dreg, sreg);
+ else if (mips_pic == SVR4_PIC)
+ {
+ if (sreg != PIC_CALL_REG)
+ as_warn (_("MIPS PIC call to register other than $25"));
+
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "jalr",
+ "d,s", dreg, sreg);
+ if (mips_cprestore_offset < 0)
+ as_warn (_("No .cprestore pseudo-op used in PIC code"));
+ else
+ {
+ expr1.X_add_number = mips_cprestore_offset;
+ macro_build ((char *) NULL, &icnt, &expr1,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "lw" : "ld"),
+ "t,o(b)", GP, (int) BFD_RELOC_LO16, mips_frame_reg);
+ }
+ }
+ else
+ abort ();
+
+ return;
+
+ case M_JAL_A:
+ if (mips_pic == NO_PIC)
+ macro_build ((char *) NULL, &icnt, &offset_expr, "jal", "a");
+ else if (mips_pic == SVR4_PIC)
+ {
+ /* If this is a reference to an external symbol, and we are
+ using a small GOT, we want
+ lw $25,<sym>($gp) (BFD_RELOC_MIPS_CALL16)
+ nop
+ jalr $25
+ nop
+ lw $gp,cprestore($sp)
+ The cprestore value is set using the .cprestore
+ pseudo-op. If we are using a big GOT, we want
+ lui $25,<sym> (BFD_RELOC_MIPS_CALL_HI16)
+ addu $25,$25,$gp
+ lw $25,<sym>($25) (BFD_RELOC_MIPS_CALL_LO16)
+ nop
+ jalr $25
+ nop
+ lw $gp,cprestore($sp)
+ If the symbol is not external, we want
+ lw $25,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $25,$25,<sym> (BFD_RELOC_LO16)
+ jalr $25
+ nop
+ lw $gp,cprestore($sp) */
+ frag_grow (40);
+ if (! mips_big_got)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "lw" : "ld"),
+ "t,o(b)", PIC_CALL_REG,
+ (int) BFD_RELOC_MIPS_CALL16, GP);
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ "nop", "");
+ p = frag_var (rs_machine_dependent, 4, 0,
+ RELAX_ENCODE (0, 4, -8, 0, 0, 0),
+ offset_expr.X_add_symbol, (offsetT) 0,
+ (char *) NULL);
+ }
+ else
+ {
+ int gpdel;
+
+ if (reg_needs_delay (GP))
+ gpdel = 4;
+ else
+ gpdel = 0;
+ macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
+ PIC_CALL_REG, (int) BFD_RELOC_MIPS_CALL_HI16);
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", PIC_CALL_REG, PIC_CALL_REG, GP);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "lw" : "ld"),
+ "t,o(b)", PIC_CALL_REG,
+ (int) BFD_RELOC_MIPS_CALL_LO16, PIC_CALL_REG);
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ "nop", "");
+ p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
+ RELAX_ENCODE (16, 12 + gpdel, gpdel, 8 + gpdel,
+ 0, 0),
+ offset_expr.X_add_symbol, (offsetT) 0,
+ (char *) NULL);
+ if (gpdel > 0)
+ {
+ macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+ p += 4;
+ }
+ macro_build (p, &icnt, &offset_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "lw" : "ld"),
+ "t,o(b)", PIC_CALL_REG,
+ (int) BFD_RELOC_MIPS_GOT16, GP);
+ p += 4;
+ macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+ p += 4;
+ }
+ macro_build (p, &icnt, &offset_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", PIC_CALL_REG, PIC_CALL_REG,
+ (int) BFD_RELOC_LO16);
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ "jalr", "s", PIC_CALL_REG);
+ if (mips_cprestore_offset < 0)
+ as_warn (_("No .cprestore pseudo-op used in PIC code"));
+ else
+ {
+ if (mips_opts.noreorder)
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ "nop", "");
+ expr1.X_add_number = mips_cprestore_offset;
+ macro_build ((char *) NULL, &icnt, &expr1,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "lw" : "ld"),
+ "t,o(b)", GP, (int) BFD_RELOC_LO16,
+ mips_frame_reg);
+ }
+ }
+ else if (mips_pic == EMBEDDED_PIC)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr, "bal", "p");
+ /* The linker may expand the call to a longer sequence which
+ uses $at, so we must break rather than return. */
+ break;
+ }
+ else
+ abort ();
+
+ return;
+
+ case M_LB_AB:
+ s = "lb";
+ goto ld;
+ case M_LBU_AB:
+ s = "lbu";
+ goto ld;
+ case M_LH_AB:
+ s = "lh";
+ goto ld;
+ case M_LHU_AB:
+ s = "lhu";
+ goto ld;
+ case M_LW_AB:
+ s = "lw";
+ goto ld;
+ case M_LWC0_AB:
+ s = "lwc0";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld;
+ case M_LWC1_AB:
+ s = "lwc1";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld;
+ case M_LWC2_AB:
+ s = "lwc2";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld;
+ case M_LWC3_AB:
+ s = "lwc3";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld;
+ case M_LWL_AB:
+ s = "lwl";
+ lr = 1;
+ goto ld;
+ case M_LWR_AB:
+ s = "lwr";
+ lr = 1;
+ goto ld;
+ case M_LDC1_AB:
+ if (mips_cpu == 4650)
+ {
+ as_bad (_("opcode not supported on this processor"));
+ return;
+ }
+ s = "ldc1";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld;
+ case M_LDC2_AB:
+ s = "ldc2";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld;
+ case M_LDC3_AB:
+ s = "ldc3";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld;
+ case M_LDL_AB:
+ s = "ldl";
+ lr = 1;
+ goto ld;
+ case M_LDR_AB:
+ s = "ldr";
+ lr = 1;
+ goto ld;
+ case M_LL_AB:
+ s = "ll";
+ goto ld;
+ case M_LLD_AB:
+ s = "lld";
+ goto ld;
+ case M_LWU_AB:
+ s = "lwu";
+ ld:
+ if (breg == treg || coproc || lr)
+ {
+ tempreg = AT;
+ used_at = 1;
+ }
+ else
+ {
+ tempreg = treg;
+ used_at = 0;
+ }
+ goto ld_st;
+ case M_SB_AB:
+ s = "sb";
+ goto st;
+ case M_SH_AB:
+ s = "sh";
+ goto st;
+ case M_SW_AB:
+ s = "sw";
+ goto st;
+ case M_SWC0_AB:
+ s = "swc0";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto st;
+ case M_SWC1_AB:
+ s = "swc1";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto st;
+ case M_SWC2_AB:
+ s = "swc2";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto st;
+ case M_SWC3_AB:
+ s = "swc3";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto st;
+ case M_SWL_AB:
+ s = "swl";
+ goto st;
+ case M_SWR_AB:
+ s = "swr";
+ goto st;
+ case M_SC_AB:
+ s = "sc";
+ goto st;
+ case M_SCD_AB:
+ s = "scd";
+ goto st;
+ case M_SDC1_AB:
+ if (mips_cpu == 4650)
+ {
+ as_bad (_("opcode not supported on this processor"));
+ return;
+ }
+ s = "sdc1";
+ coproc = 1;
+ /* Itbl support may require additional care here. */
+ goto st;
+ case M_SDC2_AB:
+ s = "sdc2";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto st;
+ case M_SDC3_AB:
+ s = "sdc3";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto st;
+ case M_SDL_AB:
+ s = "sdl";
+ goto st;
+ case M_SDR_AB:
+ s = "sdr";
+ st:
+ tempreg = AT;
+ used_at = 1;
+ ld_st:
+ /* Itbl support may require additional care here. */
+ if (mask == M_LWC1_AB
+ || mask == M_SWC1_AB
+ || mask == M_LDC1_AB
+ || mask == M_SDC1_AB
+ || mask == M_L_DAB
+ || mask == M_S_DAB)
+ fmt = "T,o(b)";
+ else if (coproc)
+ fmt = "E,o(b)";
+ else
+ fmt = "t,o(b)";
+
+ if (offset_expr.X_op != O_constant
+ && offset_expr.X_op != O_symbol)
+ {
+ as_bad (_("expression too complex"));
+ offset_expr.X_op = O_constant;
+ }
+
+ /* A constant expression in PIC code can be handled just as it
+ is in non PIC code. */
+ if (mips_pic == NO_PIC
+ || offset_expr.X_op == O_constant)
+ {
+ /* If this is a reference to a GP relative symbol, and there
+ is no base register, we want
+ <op> $treg,<sym>($gp) (BFD_RELOC_MIPS_GPREL)
+ Otherwise, if there is no base register, we want
+ lui $tempreg,<sym> (BFD_RELOC_HI16_S)
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16)
+ If we have a constant, we need two instructions anyhow,
+ so we always use the latter form.
+
+ If we have a base register, and this is a reference to a
+ GP relative symbol, we want
+ addu $tempreg,$breg,$gp
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_MIPS_GPREL)
+ Otherwise we want
+ lui $tempreg,<sym> (BFD_RELOC_HI16_S)
+ addu $tempreg,$tempreg,$breg
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16)
+ With a constant we always use the latter case. */
+ if (breg == 0)
+ {
+ if ((valueT) offset_expr.X_add_number >= MAX_GPREL_OFFSET
+ || nopic_need_relax (offset_expr.X_add_symbol, 1))
+ p = NULL;
+ else
+ {
+ frag_grow (20);
+ macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
+ treg, (int) BFD_RELOC_MIPS_GPREL, GP);
+ p = frag_var (rs_machine_dependent, 8, 0,
+ RELAX_ENCODE (4, 8, 0, 4, 0,
+ (mips_opts.warn_about_macros
+ || (used_at
+ && mips_opts.noat))),
+ offset_expr.X_add_symbol, (offsetT) 0,
+ (char *) NULL);
+ used_at = 0;
+ }
+ macro_build_lui (p, &icnt, &offset_expr, tempreg);
+ if (p != NULL)
+ p += 4;
+ macro_build (p, &icnt, &offset_expr, s, fmt, treg,
+ (int) BFD_RELOC_LO16, tempreg);
+ }
+ else
+ {
+ if ((valueT) offset_expr.X_add_number >= MAX_GPREL_OFFSET
+ || nopic_need_relax (offset_expr.X_add_symbol, 1))
+ p = NULL;
+ else
+ {
+ frag_grow (28);
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", tempreg, breg, GP);
+ macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
+ treg, (int) BFD_RELOC_MIPS_GPREL, tempreg);
+ p = frag_var (rs_machine_dependent, 12, 0,
+ RELAX_ENCODE (8, 12, 0, 8, 0, 0),
+ offset_expr.X_add_symbol, (offsetT) 0,
+ (char *) NULL);
+ }
+ macro_build_lui (p, &icnt, &offset_expr, tempreg);
+ if (p != NULL)
+ p += 4;
+ macro_build (p, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", tempreg, tempreg, breg);
+ if (p != NULL)
+ p += 4;
+ macro_build (p, &icnt, &offset_expr, s, fmt, treg,
+ (int) BFD_RELOC_LO16, tempreg);
+ }
+ }
+ else if (mips_pic == SVR4_PIC && ! mips_big_got)
+ {
+ /* If this is a reference to an external symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ <op> $treg,0($tempreg)
+ Otherwise we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+ <op> $treg,0($tempreg)
+ If there is a base register, we add it to $tempreg before
+ the <op>. If there is a constant, we stick it in the
+ <op> instruction. We don't handle constants larger than
+ 16 bits, because we have no way to load the upper 16 bits
+ (actually, we could handle them for the subset of cases
+ in which we are not using $at). */
+ assert (offset_expr.X_op == O_symbol);
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ if (expr1.X_add_number < -0x8000
+ || expr1.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ frag_grow (20);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "lw" : "ld"),
+ "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP);
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
+ p = frag_var (rs_machine_dependent, 4, 0,
+ RELAX_ENCODE (0, 4, -8, 0, 0, 0),
+ offset_expr.X_add_symbol, (offsetT) 0,
+ (char *) NULL);
+ macro_build (p, &icnt, &offset_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
+ if (breg != 0)
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", tempreg, tempreg, breg);
+ macro_build ((char *) NULL, &icnt, &expr1, s, fmt, treg,
+ (int) BFD_RELOC_LO16, tempreg);
+ }
+ else if (mips_pic == SVR4_PIC)
+ {
+ int gpdel;
+
+ /* If this is a reference to an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ <op> $treg,0($tempreg)
+ Otherwise we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+ <op> $treg,0($tempreg)
+ If there is a base register, we add it to $tempreg before
+ the <op>. If there is a constant, we stick it in the
+ <op> instruction. We don't handle constants larger than
+ 16 bits, because we have no way to load the upper 16 bits
+ (actually, we could handle them for the subset of cases
+ in which we are not using $at). */
+ assert (offset_expr.X_op == O_symbol);
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ if (expr1.X_add_number < -0x8000
+ || expr1.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ if (reg_needs_delay (GP))
+ gpdel = 4;
+ else
+ gpdel = 0;
+ frag_grow (36);
+ macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
+ tempreg, (int) BFD_RELOC_MIPS_GOT_HI16);
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", tempreg, tempreg, GP);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "lw" : "ld"),
+ "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT_LO16,
+ tempreg);
+ p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
+ RELAX_ENCODE (12, 12 + gpdel, gpdel, 8 + gpdel, 0, 0),
+ offset_expr.X_add_symbol, (offsetT) 0, (char *) NULL);
+ if (gpdel > 0)
+ {
+ macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+ p += 4;
+ }
+ macro_build (p, &icnt, &offset_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "lw" : "ld"),
+ "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP);
+ p += 4;
+ macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+ p += 4;
+ macro_build (p, &icnt, &offset_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
+ if (breg != 0)
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", tempreg, tempreg, breg);
+ macro_build ((char *) NULL, &icnt, &expr1, s, fmt, treg,
+ (int) BFD_RELOC_LO16, tempreg);
+ }
+ else if (mips_pic == EMBEDDED_PIC)
+ {
+ /* If there is no base register, we want
+ <op> $treg,<sym>($gp) (BFD_RELOC_MIPS_GPREL)
+ If there is a base register, we want
+ addu $tempreg,$breg,$gp
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_MIPS_GPREL)
+ */
+ assert (offset_expr.X_op == O_symbol);
+ if (breg == 0)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
+ treg, (int) BFD_RELOC_MIPS_GPREL, GP);
+ used_at = 0;
+ }
+ else
+ {
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", tempreg, breg, GP);
+ macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
+ treg, (int) BFD_RELOC_MIPS_GPREL, tempreg);
+ }
+ }
+ else
+ abort ();
+
+ if (! used_at)
+ return;
+
+ break;
+
+ case M_LI:
+ case M_LI_S:
+ load_register (&icnt, treg, &imm_expr, 0);
+ return;
+
+ case M_DLI:
+ load_register (&icnt, treg, &imm_expr, 1);
+ return;
+
+ case M_LI_SS:
+ if (imm_expr.X_op == O_constant)
+ {
+ load_register (&icnt, AT, &imm_expr, 0);
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ "mtc1", "t,G", AT, treg);
+ break;
+ }
+ else
+ {
+ assert (offset_expr.X_op == O_symbol
+ && strcmp (segment_name (S_GET_SEGMENT
+ (offset_expr.X_add_symbol)),
+ ".lit4") == 0
+ && offset_expr.X_add_number == 0);
+ macro_build ((char *) NULL, &icnt, &offset_expr, "lwc1", "T,o(b)",
+ treg, (int) BFD_RELOC_MIPS_LITERAL, GP);
+ return;
+ }
+
+ case M_LI_D:
+ /* If we have a constant in IMM_EXPR, then in mips3 mode it is
+ the entire value, and in mips1 mode it is the high order 32
+ bits of the value and the low order 32 bits are either zero
+ or in offset_expr. */
+ if (imm_expr.X_op == O_constant || imm_expr.X_op == O_big)
+ {
+ if (mips_opts.isa >= 3)
+ load_register (&icnt, treg, &imm_expr, 1);
+ else
+ {
+ int hreg, lreg;
+
+ if (target_big_endian)
+ {
+ hreg = treg;
+ lreg = treg + 1;
+ }
+ else
+ {
+ hreg = treg + 1;
+ lreg = treg;
+ }
+
+ if (hreg <= 31)
+ load_register (&icnt, hreg, &imm_expr, 0);
+ if (lreg <= 31)
+ {
+ if (offset_expr.X_op == O_absent)
+ macro_build ((char *) NULL, &icnt, NULL, "move", "d,s",
+ lreg, 0);
+ else
+ {
+ assert (offset_expr.X_op == O_constant);
+ load_register (&icnt, lreg, &offset_expr, 0);
+ }
+ }
+ }
+ return;
+ }
+
+ /* We know that sym is in the .rdata section. First we get the
+ upper 16 bits of the address. */
+ if (mips_pic == NO_PIC)
+ {
+ /* FIXME: This won't work for a 64 bit address. */
+ macro_build_lui ((char *) NULL, &icnt, &offset_expr, AT);
+ }
+ else if (mips_pic == SVR4_PIC)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "lw" : "ld"),
+ "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16, GP);
+ }
+ else if (mips_pic == EMBEDDED_PIC)
+ {
+ /* For embedded PIC we pick up the entire address off $gp in
+ a single instruction. */
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", AT, GP, (int) BFD_RELOC_MIPS_GPREL);
+ offset_expr.X_op = O_constant;
+ offset_expr.X_add_number = 0;
+ }
+ else
+ abort ();
+
+ /* Now we load the register(s). */
+ if (mips_opts.isa >= 3)
+ macro_build ((char *) NULL, &icnt, &offset_expr, "ld", "t,o(b)",
+ treg, (int) BFD_RELOC_LO16, AT);
+ else
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr, "lw", "t,o(b)",
+ treg, (int) BFD_RELOC_LO16, AT);
+ if (treg != 31)
+ {
+ /* FIXME: How in the world do we deal with the possible
+ overflow here? */
+ offset_expr.X_add_number += 4;
+ macro_build ((char *) NULL, &icnt, &offset_expr, "lw", "t,o(b)",
+ treg + 1, (int) BFD_RELOC_LO16, AT);
+ }
+ }
+
+ /* To avoid confusion in tc_gen_reloc, we must ensure that this
+ does not become a variant frag. */
+ frag_wane (frag_now);
+ frag_new (0);
+
+ break;
+
+ case M_LI_DD:
+ /* If we have a constant in IMM_EXPR, then in mips3 mode it is
+ the entire value, and in mips1 mode it is the high order 32
+ bits of the value and the low order 32 bits are either zero
+ or in offset_expr. */
+ if (imm_expr.X_op == O_constant || imm_expr.X_op == O_big)
+ {
+ load_register (&icnt, AT, &imm_expr, mips_opts.isa >= 3);
+ if (mips_opts.isa >= 3)
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ "dmtc1", "t,S", AT, treg);
+ else
+ {
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ "mtc1", "t,G", AT, treg + 1);
+ if (offset_expr.X_op == O_absent)
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ "mtc1", "t,G", 0, treg);
+ else
+ {
+ assert (offset_expr.X_op == O_constant);
+ load_register (&icnt, AT, &offset_expr, 0);
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ "mtc1", "t,G", AT, treg);
+ }
+ }
+ break;
+ }
+
+ assert (offset_expr.X_op == O_symbol
+ && offset_expr.X_add_number == 0);
+ s = segment_name (S_GET_SEGMENT (offset_expr.X_add_symbol));
+ if (strcmp (s, ".lit8") == 0)
+ {
+ if (mips_opts.isa >= 2)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr, "ldc1",
+ "T,o(b)", treg, (int) BFD_RELOC_MIPS_LITERAL, GP);
+ return;
+ }
+ breg = GP;
+ r = BFD_RELOC_MIPS_LITERAL;
+ goto dob;
+ }
+ else
+ {
+ assert (strcmp (s, RDATA_SECTION_NAME) == 0);
+ if (mips_pic == SVR4_PIC)
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "lw" : "ld"),
+ "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16, GP);
+ else
+ {
+ /* FIXME: This won't work for a 64 bit address. */
+ macro_build_lui ((char *) NULL, &icnt, &offset_expr, AT);
+ }
+
+ if (mips_opts.isa >= 2)
+ {
+ macro_build ((char *) NULL, &icnt, &offset_expr, "ldc1",
+ "T,o(b)", treg, (int) BFD_RELOC_LO16, AT);
+
+ /* To avoid confusion in tc_gen_reloc, we must ensure
+ that this does not become a variant frag. */
+ frag_wane (frag_now);
+ frag_new (0);
+
+ break;
+ }
+ breg = AT;
+ r = BFD_RELOC_LO16;
+ goto dob;
+ }
+
+ case M_L_DOB:
+ if (mips_cpu == 4650)
+ {
+ as_bad (_("opcode not supported on this processor"));
+ return;
+ }
+ /* Even on a big endian machine $fn comes before $fn+1. We have
+ to adjust when loading from memory. */
+ r = BFD_RELOC_LO16;
+ dob:
+ assert (mips_opts.isa < 2);
+ macro_build ((char *) NULL, &icnt, &offset_expr, "lwc1", "T,o(b)",
+ target_big_endian ? treg + 1 : treg,
+ (int) r, breg);
+ /* FIXME: A possible overflow which I don't know how to deal
+ with. */
+ offset_expr.X_add_number += 4;
+ macro_build ((char *) NULL, &icnt, &offset_expr, "lwc1", "T,o(b)",
+ target_big_endian ? treg : treg + 1,
+ (int) r, breg);
+
+ /* To avoid confusion in tc_gen_reloc, we must ensure that this
+ does not become a variant frag. */
+ frag_wane (frag_now);
+ frag_new (0);
+
+ if (breg != AT)
+ return;
+ break;
+
+ case M_L_DAB:
+ /*
+ * The MIPS assembler seems to check for X_add_number not
+ * being double aligned and generating:
+ * lui at,%hi(foo+1)
+ * addu at,at,v1
+ * addiu at,at,%lo(foo+1)
+ * lwc1 f2,0(at)
+ * lwc1 f3,4(at)
+ * But, the resulting address is the same after relocation so why
+ * generate the extra instruction?
+ */
+ if (mips_cpu == 4650)
+ {
+ as_bad (_("opcode not supported on this processor"));
+ return;
+ }
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ if (mips_opts.isa >= 2)
+ {
+ s = "ldc1";
+ goto ld;
+ }
+
+ s = "lwc1";
+ fmt = "T,o(b)";
+ goto ldd_std;
+
+ case M_S_DAB:
+ if (mips_cpu == 4650)
+ {
+ as_bad (_("opcode not supported on this processor"));
+ return;
+ }
+
+ if (mips_opts.isa >= 2)
+ {
+ s = "sdc1";
+ goto st;
+ }
+
+ s = "swc1";
+ fmt = "T,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ldd_std;
+
+ case M_LD_AB:
+ if (mips_opts.isa >= 3)
+ {
+ s = "ld";
+ goto ld;
+ }
+
+ s = "lw";
+ fmt = "t,o(b)";
+ goto ldd_std;
+
+ case M_SD_AB:
+ if (mips_opts.isa >= 3)
+ {
+ s = "sd";
+ goto st;
+ }
+
+ s = "sw";
+ fmt = "t,o(b)";
+
+ ldd_std:
+ if (offset_expr.X_op != O_symbol
+ && offset_expr.X_op != O_constant)
+ {
+ as_bad (_("expression too complex"));
+ offset_expr.X_op = O_constant;
+ }
+
+ /* Even on a big endian machine $fn comes before $fn+1. We have
+ to adjust when loading from memory. We set coproc if we must
+ load $fn+1 first. */
+ /* Itbl support may require additional care here. */
+ if (! target_big_endian)
+ coproc = 0;
+
+ if (mips_pic == NO_PIC
+ || offset_expr.X_op == O_constant)
+ {
+ /* If this is a reference to a GP relative symbol, we want
+ <op> $treg,<sym>($gp) (BFD_RELOC_MIPS_GPREL)
+ <op> $treg+1,<sym>+4($gp) (BFD_RELOC_MIPS_GPREL)
+ If we have a base register, we use this
+ addu $at,$breg,$gp
+ <op> $treg,<sym>($at) (BFD_RELOC_MIPS_GPREL)
+ <op> $treg+1,<sym>+4($at) (BFD_RELOC_MIPS_GPREL)
+ If this is not a GP relative symbol, we want
+ lui $at,<sym> (BFD_RELOC_HI16_S)
+ <op> $treg,<sym>($at) (BFD_RELOC_LO16)
+ <op> $treg+1,<sym>+4($at) (BFD_RELOC_LO16)
+ If there is a base register, we add it to $at after the
+ lui instruction. If there is a constant, we always use
+ the last case. */
+ if ((valueT) offset_expr.X_add_number >= MAX_GPREL_OFFSET
+ || nopic_need_relax (offset_expr.X_add_symbol, 1))
+ {
+ p = NULL;
+ used_at = 1;
+ }
+ else
+ {
+ int off;
+
+ if (breg == 0)
+ {
+ frag_grow (28);
+ tempreg = GP;
+ off = 0;
+ used_at = 0;
+ }
+ else
+ {
+ frag_grow (36);
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", AT, breg, GP);
+ tempreg = AT;
+ off = 4;
+ used_at = 1;
+ }
+
+ /* Itbl support may require additional care here. */
+ macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
+ coproc ? treg + 1 : treg,
+ (int) BFD_RELOC_MIPS_GPREL, tempreg);
+ offset_expr.X_add_number += 4;
+
+ /* Set mips_optimize to 2 to avoid inserting an
+ undesired nop. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
+ /* Itbl support may require additional care here. */
+ macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
+ coproc ? treg : treg + 1,
+ (int) BFD_RELOC_MIPS_GPREL, tempreg);
+ mips_optimize = hold_mips_optimize;
+
+ p = frag_var (rs_machine_dependent, 12 + off, 0,
+ RELAX_ENCODE (8 + off, 12 + off, 0, 4 + off, 1,
+ used_at && mips_opts.noat),
+ offset_expr.X_add_symbol, (offsetT) 0,
+ (char *) NULL);
+
+ /* We just generated two relocs. When tc_gen_reloc
+ handles this case, it will skip the first reloc and
+ handle the second. The second reloc already has an
+ extra addend of 4, which we added above. We must
+ subtract it out, and then subtract another 4 to make
+ the first reloc come out right. The second reloc
+ will come out right because we are going to add 4 to
+ offset_expr when we build its instruction below.
+
+ If we have a symbol, then we don't want to include
+ the offset, because it will wind up being included
+ when we generate the reloc. */
+
+ if (offset_expr.X_op == O_constant)
+ offset_expr.X_add_number -= 8;
+ else
+ {
+ offset_expr.X_add_number = -4;
+ offset_expr.X_op = O_constant;
+ }
+ }
+ macro_build_lui (p, &icnt, &offset_expr, AT);
+ if (p != NULL)
+ p += 4;
+ if (breg != 0)
+ {
+ macro_build (p, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", AT, breg, AT);
+ if (p != NULL)
+ p += 4;
+ }
+ /* Itbl support may require additional care here. */
+ macro_build (p, &icnt, &offset_expr, s, fmt,
+ coproc ? treg + 1 : treg,
+ (int) BFD_RELOC_LO16, AT);
+ if (p != NULL)
+ p += 4;
+ /* FIXME: How do we handle overflow here? */
+ offset_expr.X_add_number += 4;
+ /* Itbl support may require additional care here. */
+ macro_build (p, &icnt, &offset_expr, s, fmt,
+ coproc ? treg : treg + 1,
+ (int) BFD_RELOC_LO16, AT);
+ }
+ else if (mips_pic == SVR4_PIC && ! mips_big_got)
+ {
+ int off;
+
+ /* If this is a reference to an external symbol, we want
+ lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ <op> $treg,0($at)
+ <op> $treg+1,4($at)
+ Otherwise we want
+ lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ <op> $treg,<sym>($at) (BFD_RELOC_LO16)
+ <op> $treg+1,<sym>+4($at) (BFD_RELOC_LO16)
+ If there is a base register we add it to $at before the
+ lwc1 instructions. If there is a constant we include it
+ in the lwc1 instructions. */
+ used_at = 1;
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ if (expr1.X_add_number < -0x8000
+ || expr1.X_add_number >= 0x8000 - 4)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ if (breg == 0)
+ off = 0;
+ else
+ off = 4;
+ frag_grow (24 + off);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "lw" : "ld"),
+ "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16, GP);
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
+ if (breg != 0)
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", AT, breg, AT);
+ /* Itbl support may require additional care here. */
+ macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
+ coproc ? treg + 1 : treg,
+ (int) BFD_RELOC_LO16, AT);
+ expr1.X_add_number += 4;
+
+ /* Set mips_optimize to 2 to avoid inserting an undesired
+ nop. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
+ /* Itbl support may require additional care here. */
+ macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
+ coproc ? treg : treg + 1,
+ (int) BFD_RELOC_LO16, AT);
+ mips_optimize = hold_mips_optimize;
+
+ (void) frag_var (rs_machine_dependent, 0, 0,
+ RELAX_ENCODE (0, 0, -16 - off, -8, 1, 0),
+ offset_expr.X_add_symbol, (offsetT) 0,
+ (char *) NULL);
+ }
+ else if (mips_pic == SVR4_PIC)
+ {
+ int gpdel, off;
+
+ /* If this is a reference to an external symbol, we want
+ lui $at,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $at,$at,$gp
+ lw $at,<sym>($at) (BFD_RELOC_MIPS_GOT_LO16)
+ nop
+ <op> $treg,0($at)
+ <op> $treg+1,4($at)
+ Otherwise we want
+ lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ <op> $treg,<sym>($at) (BFD_RELOC_LO16)
+ <op> $treg+1,<sym>+4($at) (BFD_RELOC_LO16)
+ If there is a base register we add it to $at before the
+ lwc1 instructions. If there is a constant we include it
+ in the lwc1 instructions. */
+ used_at = 1;
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ if (expr1.X_add_number < -0x8000
+ || expr1.X_add_number >= 0x8000 - 4)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ if (reg_needs_delay (GP))
+ gpdel = 4;
+ else
+ gpdel = 0;
+ if (breg == 0)
+ off = 0;
+ else
+ off = 4;
+ frag_grow (56);
+ macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
+ AT, (int) BFD_RELOC_MIPS_GOT_HI16);
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", AT, AT, GP);
+ macro_build ((char *) NULL, &icnt, &offset_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "lw" : "ld"),
+ "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT_LO16, AT);
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
+ if (breg != 0)
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", AT, breg, AT);
+ /* Itbl support may require additional care here. */
+ macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
+ coproc ? treg + 1 : treg,
+ (int) BFD_RELOC_LO16, AT);
+ expr1.X_add_number += 4;
+
+ /* Set mips_optimize to 2 to avoid inserting an undesired
+ nop. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
+ /* Itbl support may require additional care here. */
+ macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
+ coproc ? treg : treg + 1,
+ (int) BFD_RELOC_LO16, AT);
+ mips_optimize = hold_mips_optimize;
+ expr1.X_add_number -= 4;
+
+ p = frag_var (rs_machine_dependent, 16 + gpdel + off, 0,
+ RELAX_ENCODE (24 + off, 16 + gpdel + off, gpdel,
+ 8 + gpdel + off, 1, 0),
+ offset_expr.X_add_symbol, (offsetT) 0,
+ (char *) NULL);
+ if (gpdel > 0)
+ {
+ macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+ p += 4;
+ }
+ macro_build (p, &icnt, &offset_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "lw" : "ld"),
+ "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16, GP);
+ p += 4;
+ macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+ p += 4;
+ if (breg != 0)
+ {
+ macro_build (p, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", AT, breg, AT);
+ p += 4;
+ }
+ /* Itbl support may require additional care here. */
+ macro_build (p, &icnt, &expr1, s, fmt,
+ coproc ? treg + 1 : treg,
+ (int) BFD_RELOC_LO16, AT);
+ p += 4;
+ expr1.X_add_number += 4;
+
+ /* Set mips_optimize to 2 to avoid inserting an undesired
+ nop. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
+ /* Itbl support may require additional care here. */
+ macro_build (p, &icnt, &expr1, s, fmt,
+ coproc ? treg : treg + 1,
+ (int) BFD_RELOC_LO16, AT);
+ mips_optimize = hold_mips_optimize;
+ }
+ else if (mips_pic == EMBEDDED_PIC)
+ {
+ /* If there is no base register, we use
+ <op> $treg,<sym>($gp) (BFD_RELOC_MIPS_GPREL)
+ <op> $treg+1,<sym>+4($gp) (BFD_RELOC_MIPS_GPREL)
+ If we have a base register, we use
+ addu $at,$breg,$gp
+ <op> $treg,<sym>($at) (BFD_RELOC_MIPS_GPREL)
+ <op> $treg+1,<sym>+4($at) (BFD_RELOC_MIPS_GPREL)
+ */
+ if (breg == 0)
+ {
+ tempreg = GP;
+ used_at = 0;
+ }
+ else
+ {
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", AT, breg, GP);
+ tempreg = AT;
+ used_at = 1;
+ }
+
+ /* Itbl support may require additional care here. */
+ macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
+ coproc ? treg + 1 : treg,
+ (int) BFD_RELOC_MIPS_GPREL, tempreg);
+ offset_expr.X_add_number += 4;
+ /* Itbl support may require additional care here. */
+ macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
+ coproc ? treg : treg + 1,
+ (int) BFD_RELOC_MIPS_GPREL, tempreg);
+ }
+ else
+ abort ();
+
+ if (! used_at)
+ return;
+
+ break;
+
+ case M_LD_OB:
+ s = "lw";
+ goto sd_ob;
+ case M_SD_OB:
+ s = "sw";
+ sd_ob:
+ assert (bfd_arch_bits_per_address (stdoutput) == 32 || mips_opts.isa < 3);
+ macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
+ (int) BFD_RELOC_LO16, breg);
+ offset_expr.X_add_number += 4;
+ macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg + 1,
+ (int) BFD_RELOC_LO16, breg);
+ return;
+
+ /* New code added to support COPZ instructions.
+ This code builds table entries out of the macros in mip_opcodes.
+ R4000 uses interlocks to handle coproc delays.
+ Other chips (like the R3000) require nops to be inserted for delays.
+
+ FIXME: Currently, we require that the user handle delays.
+ In order to fill delay slots for non-interlocked chips,
+ we must have a way to specify delays based on the coprocessor.
+ Eg. 4 cycles if load coproc reg from memory, 1 if in cache, etc.
+ What are the side-effects of the cop instruction?
+ What cache support might we have and what are its effects?
+ Both coprocessor & memory require delays. how long???
+ What registers are read/set/modified?
+
+ If an itbl is provided to interpret cop instructions,
+ this knowledge can be encoded in the itbl spec. */
+
+ case M_COP0:
+ s = "c0";
+ goto copz;
+ case M_COP1:
+ s = "c1";
+ goto copz;
+ case M_COP2:
+ s = "c2";
+ goto copz;
+ case M_COP3:
+ s = "c3";
+ copz:
+ /* For now we just do C (same as Cz). The parameter will be
+ stored in insn_opcode by mips_ip. */
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "C",
+ ip->insn_opcode);
+ return;
+
+#ifdef LOSING_COMPILER
+ default:
+ /* Try and see if this is a new itbl instruction.
+ This code builds table entries out of the macros in mip_opcodes.
+ FIXME: For now we just assemble the expression and pass it's
+ value along as a 32-bit immediate.
+ We may want to have the assembler assemble this value,
+ so that we gain the assembler's knowledge of delay slots,
+ symbols, etc.
+ Would it be more efficient to use mask (id) here? */
+ if (itbl_have_entries
+ && (immed_expr = itbl_assemble (ip->insn_mo->name, "")))
+ {
+ s = ip->insn_mo->name;
+ s2 = "cop3";
+ coproc = ITBL_DECODE_PNUM (immed_expr);;
+ macro_build ((char *) NULL, &icnt, &immed_expr, s, "C");
+ return;
+ }
+ macro2 (ip);
+ return;
+ }
+ if (mips_opts.noat)
+ as_warn (_("Macro used $at after \".set noat\""));
+}
+
+static void
+macro2 (ip)
+ struct mips_cl_insn *ip;
+{
+ register int treg, sreg, dreg, breg;
+ int tempreg;
+ int mask;
+ int icnt = 0;
+ int used_at;
+ expressionS expr1;
+ const char *s;
+ const char *s2;
+ const char *fmt;
+ int likely = 0;
+ int dbl = 0;
+ int coproc = 0;
+ int lr = 0;
+ int imm = 0;
+ int off;
+ offsetT maxnum;
+ bfd_reloc_code_real_type r;
+ char *p;
+
+ treg = (ip->insn_opcode >> 16) & 0x1f;
+ dreg = (ip->insn_opcode >> 11) & 0x1f;
+ sreg = breg = (ip->insn_opcode >> 21) & 0x1f;
+ mask = ip->insn_mo->mask;
+
+ expr1.X_op = O_constant;
+ expr1.X_op_symbol = NULL;
+ expr1.X_add_symbol = NULL;
+ expr1.X_add_number = 1;
+
+ switch (mask)
+ {
+#endif /* LOSING_COMPILER */
+
+ case M_DMUL:
+ dbl = 1;
+ case M_MUL:
+ macro_build ((char *) NULL, &icnt, NULL,
+ dbl ? "dmultu" : "multu",
+ "s,t", sreg, treg);
+ macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg);
+ return;
+
+ case M_DMUL_I:
+ dbl = 1;
+ case M_MUL_I:
+ /* The MIPS assembler some times generates shifts and adds. I'm
+ not trying to be that fancy. GCC should do this for us
+ anyway. */
+ load_register (&icnt, AT, &imm_expr, dbl);
+ macro_build ((char *) NULL, &icnt, NULL,
+ dbl ? "dmult" : "mult",
+ "s,t", sreg, AT);
+ macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg);
+ break;
+
+ case M_DMULO_I:
+ dbl = 1;
+ case M_MULO_I:
+ imm = 1;
+ goto do_mulo;
+
+ case M_DMULO:
+ dbl = 1;
+ case M_MULO:
+ do_mulo:
+ mips_emit_delays (true);
+ ++mips_opts.noreorder;
+ mips_any_noreorder = 1;
+ if (imm)
+ load_register (&icnt, AT, &imm_expr, dbl);
+ macro_build ((char *) NULL, &icnt, NULL,
+ dbl ? "dmult" : "mult",
+ "s,t", sreg, imm ? AT : treg);
+ macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg);
+ macro_build ((char *) NULL, &icnt, NULL,
+ dbl ? "dsra32" : "sra",
+ "d,w,<", dreg, dreg, 31);
+ macro_build ((char *) NULL, &icnt, NULL, "mfhi", "d", AT);
+ if (mips_trap)
+ macro_build ((char *) NULL, &icnt, NULL, "tne", "s,t", dreg, AT);
+ else
+ {
+ expr1.X_add_number = 8;
+ macro_build ((char *) NULL, &icnt, &expr1, "beq", "s,t,p", dreg, AT);
+ macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
+ macro_build ((char *) NULL, &icnt, NULL, "break", "c", 6);
+ }
+ --mips_opts.noreorder;
+ macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg);
+ break;
+
+ case M_DMULOU_I:
+ dbl = 1;
+ case M_MULOU_I:
+ imm = 1;
+ goto do_mulou;
+
+ case M_DMULOU:
+ dbl = 1;
+ case M_MULOU:
+ do_mulou:
+ mips_emit_delays (true);
+ ++mips_opts.noreorder;
+ mips_any_noreorder = 1;
+ if (imm)
+ load_register (&icnt, AT, &imm_expr, dbl);
+ macro_build ((char *) NULL, &icnt, NULL,
+ dbl ? "dmultu" : "multu",
+ "s,t", sreg, imm ? AT : treg);
+ macro_build ((char *) NULL, &icnt, NULL, "mfhi", "d", AT);
+ macro_build ((char *) NULL, &icnt, NULL, "mflo", "d", dreg);
+ if (mips_trap)
+ macro_build ((char *) NULL, &icnt, NULL, "tne", "s,t", AT, 0);
+ else
+ {
+ expr1.X_add_number = 8;
+ macro_build ((char *) NULL, &icnt, &expr1, "beq", "s,t,p", AT, 0);
+ macro_build ((char *) NULL, &icnt, NULL, "nop", "", 0);
+ macro_build ((char *) NULL, &icnt, NULL, "break", "c", 6);
+ }
+ --mips_opts.noreorder;
+ break;
+
+ case M_ROL:
+ macro_build ((char *) NULL, &icnt, NULL, "subu", "d,v,t", AT, 0, treg);
+ macro_build ((char *) NULL, &icnt, NULL, "srlv", "d,t,s", AT, sreg, AT);
+ macro_build ((char *) NULL, &icnt, NULL, "sllv", "d,t,s", dreg, sreg,
+ treg);
+ macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
+ break;
+
+ case M_ROL_I:
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("rotate count too large"));
+ macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", AT, sreg,
+ (int) (imm_expr.X_add_number & 0x1f));
+ macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", dreg, sreg,
+ (int) ((0 - imm_expr.X_add_number) & 0x1f));
+ macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
+ break;
+
+ case M_ROR:
+ macro_build ((char *) NULL, &icnt, NULL, "subu", "d,v,t", AT, 0, treg);
+ macro_build ((char *) NULL, &icnt, NULL, "sllv", "d,t,s", AT, sreg, AT);
+ macro_build ((char *) NULL, &icnt, NULL, "srlv", "d,t,s", dreg, sreg,
+ treg);
+ macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
+ break;
+
+ case M_ROR_I:
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("rotate count too large"));
+ macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", AT, sreg,
+ (int) (imm_expr.X_add_number & 0x1f));
+ macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", dreg, sreg,
+ (int) ((0 - imm_expr.X_add_number) & 0x1f));
+ macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
+ break;
+
+ case M_S_DOB:
+ if (mips_cpu == 4650)
+ {
+ as_bad (_("opcode not supported on this processor"));
+ return;
+ }
+ assert (mips_opts.isa < 2);
+ /* Even on a big endian machine $fn comes before $fn+1. We have
+ to adjust when storing to memory. */
+ macro_build ((char *) NULL, &icnt, &offset_expr, "swc1", "T,o(b)",
+ target_big_endian ? treg + 1 : treg,
+ (int) BFD_RELOC_LO16, breg);
+ offset_expr.X_add_number += 4;
+ macro_build ((char *) NULL, &icnt, &offset_expr, "swc1", "T,o(b)",
+ target_big_endian ? treg : treg + 1,
+ (int) BFD_RELOC_LO16, breg);
+ return;
+
+ case M_SEQ:
+ if (sreg == 0)
+ macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg,
+ treg, (int) BFD_RELOC_LO16);
+ else if (treg == 0)
+ macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg,
+ sreg, (int) BFD_RELOC_LO16);
+ else
+ {
+ macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg,
+ sreg, treg);
+ macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg,
+ dreg, (int) BFD_RELOC_LO16);
+ }
+ return;
+
+ case M_SEQ_I:
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ {
+ macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg,
+ sreg, (int) BFD_RELOC_LO16);
+ return;
+ }
+ if (sreg == 0)
+ {
+ as_warn (_("Instruction %s: result is always false"),
+ ip->insn_mo->name);
+ macro_build ((char *) NULL, &icnt, NULL, "move", "d,s", dreg, 0);
+ return;
+ }
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= 0
+ && imm_expr.X_add_number < 0x10000)
+ {
+ macro_build ((char *) NULL, &icnt, &imm_expr, "xori", "t,r,i", dreg,
+ sreg, (int) BFD_RELOC_LO16);
+ used_at = 0;
+ }
+ else if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number > -0x8000
+ && imm_expr.X_add_number < 0)
+ {
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build ((char *) NULL, &icnt, &imm_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", dreg, sreg,
+ (int) BFD_RELOC_LO16);
+ used_at = 0;
+ }
+ else
+ {
+ load_register (&icnt, AT, &imm_expr, 0);
+ macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg,
+ sreg, AT);
+ used_at = 1;
+ }
+ macro_build ((char *) NULL, &icnt, &expr1, "sltiu", "t,r,j", dreg, dreg,
+ (int) BFD_RELOC_LO16);
+ if (used_at)
+ break;
+ return;
+
+ case M_SGE: /* sreg >= treg <==> not (sreg < treg) */
+ s = "slt";
+ goto sge;
+ case M_SGEU:
+ s = "sltu";
+ sge:
+ macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, sreg, treg);
+ macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg,
+ (int) BFD_RELOC_LO16);
+ return;
+
+ case M_SGE_I: /* sreg >= I <==> not (sreg < I) */
+ case M_SGEU_I:
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ {
+ macro_build ((char *) NULL, &icnt, &imm_expr,
+ mask == M_SGE_I ? "slti" : "sltiu",
+ "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16);
+ used_at = 0;
+ }
+ else
+ {
+ load_register (&icnt, AT, &imm_expr, 0);
+ macro_build ((char *) NULL, &icnt, NULL,
+ mask == M_SGE_I ? "slt" : "sltu",
+ "d,v,t", dreg, sreg, AT);
+ used_at = 1;
+ }
+ macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg,
+ (int) BFD_RELOC_LO16);
+ if (used_at)
+ break;
+ return;
+
+ case M_SGT: /* sreg > treg <==> treg < sreg */
+ s = "slt";
+ goto sgt;
+ case M_SGTU:
+ s = "sltu";
+ sgt:
+ macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, treg, sreg);
+ return;
+
+ case M_SGT_I: /* sreg > I <==> I < sreg */
+ s = "slt";
+ goto sgti;
+ case M_SGTU_I:
+ s = "sltu";
+ sgti:
+ load_register (&icnt, AT, &imm_expr, 0);
+ macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, AT, sreg);
+ break;
+
+ case M_SLE: /* sreg <= treg <==> treg >= sreg <==> not (treg < sreg) */
+ s = "slt";
+ goto sle;
+ case M_SLEU:
+ s = "sltu";
+ sle:
+ macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, treg, sreg);
+ macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg,
+ (int) BFD_RELOC_LO16);
+ return;
+
+ case M_SLE_I: /* sreg <= I <==> I >= sreg <==> not (I < sreg) */
+ s = "slt";
+ goto slei;
+ case M_SLEU_I:
+ s = "sltu";
+ slei:
+ load_register (&icnt, AT, &imm_expr, 0);
+ macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, AT, sreg);
+ macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg,
+ (int) BFD_RELOC_LO16);
+ break;
+
+ case M_SLT_I:
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ {
+ macro_build ((char *) NULL, &icnt, &imm_expr, "slti", "t,r,j",
+ dreg, sreg, (int) BFD_RELOC_LO16);
+ return;
+ }
+ load_register (&icnt, AT, &imm_expr, 0);
+ macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", dreg, sreg, AT);
+ break;
+
+ case M_SLTU_I:
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ {
+ macro_build ((char *) NULL, &icnt, &imm_expr, "sltiu", "t,r,j",
+ dreg, sreg, (int) BFD_RELOC_LO16);
+ return;
+ }
+ load_register (&icnt, AT, &imm_expr, 0);
+ macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, sreg,
+ AT);
+ break;
+
+ case M_SNE:
+ if (sreg == 0)
+ macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, 0,
+ treg);
+ else if (treg == 0)
+ macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, 0,
+ sreg);
+ else
+ {
+ macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg,
+ sreg, treg);
+ macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, 0,
+ dreg);
+ }
+ return;
+
+ case M_SNE_I:
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ {
+ macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, 0,
+ sreg);
+ return;
+ }
+ if (sreg == 0)
+ {
+ as_warn (_("Instruction %s: result is always true"),
+ ip->insn_mo->name);
+ macro_build ((char *) NULL, &icnt, &expr1,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", dreg, 0, (int) BFD_RELOC_LO16);
+ return;
+ }
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= 0
+ && imm_expr.X_add_number < 0x10000)
+ {
+ macro_build ((char *) NULL, &icnt, &imm_expr, "xori", "t,r,i",
+ dreg, sreg, (int) BFD_RELOC_LO16);
+ used_at = 0;
+ }
+ else if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number > -0x8000
+ && imm_expr.X_add_number < 0)
+ {
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build ((char *) NULL, &icnt, &imm_expr,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addiu" : "daddiu"),
+ "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16);
+ used_at = 0;
+ }
+ else
+ {
+ load_register (&icnt, AT, &imm_expr, 0);
+ macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg,
+ sreg, AT);
+ used_at = 1;
+ }
+ macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, 0, dreg);
+ if (used_at)
+ break;
+ return;
+
+ case M_DSUB_I:
+ dbl = 1;
+ case M_SUB_I:
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number > -0x8000
+ && imm_expr.X_add_number <= 0x8000)
+ {
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build ((char *) NULL, &icnt, &imm_expr,
+ dbl ? "daddi" : "addi",
+ "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16);
+ return;
+ }
+ load_register (&icnt, AT, &imm_expr, dbl);
+ macro_build ((char *) NULL, &icnt, NULL,
+ dbl ? "dsub" : "sub",
+ "d,v,t", dreg, sreg, AT);
+ break;
+
+ case M_DSUBU_I:
+ dbl = 1;
+ case M_SUBU_I:
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number > -0x8000
+ && imm_expr.X_add_number <= 0x8000)
+ {
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build ((char *) NULL, &icnt, &imm_expr,
+ dbl ? "daddiu" : "addiu",
+ "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16);
+ return;
+ }
+ load_register (&icnt, AT, &imm_expr, dbl);
+ macro_build ((char *) NULL, &icnt, NULL,
+ dbl ? "dsubu" : "subu",
+ "d,v,t", dreg, sreg, AT);
+ break;
+
+ case M_TEQ_I:
+ s = "teq";
+ goto trap;
+ case M_TGE_I:
+ s = "tge";
+ goto trap;
+ case M_TGEU_I:
+ s = "tgeu";
+ goto trap;
+ case M_TLT_I:
+ s = "tlt";
+ goto trap;
+ case M_TLTU_I:
+ s = "tltu";
+ goto trap;
+ case M_TNE_I:
+ s = "tne";
+ trap:
+ load_register (&icnt, AT, &imm_expr, 0);
+ macro_build ((char *) NULL, &icnt, NULL, s, "s,t", sreg, AT);
+ break;
+
+ case M_TRUNCWD:
+ case M_TRUNCWS:
+ assert (mips_opts.isa < 2);
+ sreg = (ip->insn_opcode >> 11) & 0x1f; /* floating reg */
+ dreg = (ip->insn_opcode >> 06) & 0x1f; /* floating reg */
+
+ /*
+ * Is the double cfc1 instruction a bug in the mips assembler;
+ * or is there a reason for it?
+ */
+ mips_emit_delays (true);
+ ++mips_opts.noreorder;
+ mips_any_noreorder = 1;
+ macro_build ((char *) NULL, &icnt, NULL, "cfc1", "t,G", treg, 31);
+ macro_build ((char *) NULL, &icnt, NULL, "cfc1", "t,G", treg, 31);
+ macro_build ((char *) NULL, &icnt, NULL, "nop", "");
+ expr1.X_add_number = 3;
+ macro_build ((char *) NULL, &icnt, &expr1, "ori", "t,r,i", AT, treg,
+ (int) BFD_RELOC_LO16);
+ expr1.X_add_number = 2;
+ macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", AT, AT,
+ (int) BFD_RELOC_LO16);
+ macro_build ((char *) NULL, &icnt, NULL, "ctc1", "t,G", AT, 31);
+ macro_build ((char *) NULL, &icnt, NULL, "nop", "");
+ macro_build ((char *) NULL, &icnt, NULL,
+ mask == M_TRUNCWD ? "cvt.w.d" : "cvt.w.s", "D,S", dreg, sreg);
+ macro_build ((char *) NULL, &icnt, NULL, "ctc1", "t,G", treg, 31);
+ macro_build ((char *) NULL, &icnt, NULL, "nop", "");
+ --mips_opts.noreorder;
+ break;
+
+ case M_ULH:
+ s = "lb";
+ goto ulh;
+ case M_ULHU:
+ s = "lbu";
+ ulh:
+ if (offset_expr.X_add_number >= 0x7fff)
+ as_bad (_("operand overflow"));
+ /* avoid load delay */
+ if (! target_big_endian)
+ offset_expr.X_add_number += 1;
+ macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
+ (int) BFD_RELOC_LO16, breg);
+ if (! target_big_endian)
+ offset_expr.X_add_number -= 1;
+ else
+ offset_expr.X_add_number += 1;
+ macro_build ((char *) NULL, &icnt, &offset_expr, "lbu", "t,o(b)", AT,
+ (int) BFD_RELOC_LO16, breg);
+ macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", treg, treg, 8);
+ macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", treg, treg, AT);
+ break;
+
+ case M_ULD:
+ s = "ldl";
+ s2 = "ldr";
+ off = 7;
+ goto ulw;
+ case M_ULW:
+ s = "lwl";
+ s2 = "lwr";
+ off = 3;
+ ulw:
+ if (offset_expr.X_add_number >= 0x8000 - off)
+ as_bad (_("operand overflow"));
+ if (! target_big_endian)
+ offset_expr.X_add_number += off;
+ macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
+ (int) BFD_RELOC_LO16, breg);
+ if (! target_big_endian)
+ offset_expr.X_add_number -= off;
+ else
+ offset_expr.X_add_number += off;
+ macro_build ((char *) NULL, &icnt, &offset_expr, s2, "t,o(b)", treg,
+ (int) BFD_RELOC_LO16, breg);
+ return;
+
+ case M_ULD_A:
+ s = "ldl";
+ s2 = "ldr";
+ off = 7;
+ goto ulwa;
+ case M_ULW_A:
+ s = "lwl";
+ s2 = "lwr";
+ off = 3;
+ ulwa:
+ load_address (&icnt, AT, &offset_expr);
+ if (breg != 0)
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", AT, AT, breg);
+ if (! target_big_endian)
+ expr1.X_add_number = off;
+ else
+ expr1.X_add_number = 0;
+ macro_build ((char *) NULL, &icnt, &expr1, s, "t,o(b)", treg,
+ (int) BFD_RELOC_LO16, AT);
+ if (! target_big_endian)
+ expr1.X_add_number = 0;
+ else
+ expr1.X_add_number = off;
+ macro_build ((char *) NULL, &icnt, &expr1, s2, "t,o(b)", treg,
+ (int) BFD_RELOC_LO16, AT);
+ break;
+
+ case M_ULH_A:
+ case M_ULHU_A:
+ load_address (&icnt, AT, &offset_expr);
+ if (breg != 0)
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", AT, AT, breg);
+ if (target_big_endian)
+ expr1.X_add_number = 0;
+ macro_build ((char *) NULL, &icnt, &expr1,
+ mask == M_ULH_A ? "lb" : "lbu", "t,o(b)", treg,
+ (int) BFD_RELOC_LO16, AT);
+ if (target_big_endian)
+ expr1.X_add_number = 1;
+ else
+ expr1.X_add_number = 0;
+ macro_build ((char *) NULL, &icnt, &expr1, "lbu", "t,o(b)", AT,
+ (int) BFD_RELOC_LO16, AT);
+ macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", treg,
+ treg, 8);
+ macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", treg,
+ treg, AT);
+ break;
+
+ case M_USH:
+ if (offset_expr.X_add_number >= 0x7fff)
+ as_bad (_("operand overflow"));
+ if (target_big_endian)
+ offset_expr.X_add_number += 1;
+ macro_build ((char *) NULL, &icnt, &offset_expr, "sb", "t,o(b)", treg,
+ (int) BFD_RELOC_LO16, breg);
+ macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", AT, treg, 8);
+ if (target_big_endian)
+ offset_expr.X_add_number -= 1;
+ else
+ offset_expr.X_add_number += 1;
+ macro_build ((char *) NULL, &icnt, &offset_expr, "sb", "t,o(b)", AT,
+ (int) BFD_RELOC_LO16, breg);
+ break;
+
+ case M_USD:
+ s = "sdl";
+ s2 = "sdr";
+ off = 7;
+ goto usw;
+ case M_USW:
+ s = "swl";
+ s2 = "swr";
+ off = 3;
+ usw:
+ if (offset_expr.X_add_number >= 0x8000 - off)
+ as_bad (_("operand overflow"));
+ if (! target_big_endian)
+ offset_expr.X_add_number += off;
+ macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
+ (int) BFD_RELOC_LO16, breg);
+ if (! target_big_endian)
+ offset_expr.X_add_number -= off;
+ else
+ offset_expr.X_add_number += off;
+ macro_build ((char *) NULL, &icnt, &offset_expr, s2, "t,o(b)", treg,
+ (int) BFD_RELOC_LO16, breg);
+ return;
+
+ case M_USD_A:
+ s = "sdl";
+ s2 = "sdr";
+ off = 7;
+ goto uswa;
+ case M_USW_A:
+ s = "swl";
+ s2 = "swr";
+ off = 3;
+ uswa:
+ load_address (&icnt, AT, &offset_expr);
+ if (breg != 0)
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", AT, AT, breg);
+ if (! target_big_endian)
+ expr1.X_add_number = off;
+ else
+ expr1.X_add_number = 0;
+ macro_build ((char *) NULL, &icnt, &expr1, s, "t,o(b)", treg,
+ (int) BFD_RELOC_LO16, AT);
+ if (! target_big_endian)
+ expr1.X_add_number = 0;
+ else
+ expr1.X_add_number = off;
+ macro_build ((char *) NULL, &icnt, &expr1, s2, "t,o(b)", treg,
+ (int) BFD_RELOC_LO16, AT);
+ break;
+
+ case M_USH_A:
+ load_address (&icnt, AT, &offset_expr);
+ if (breg != 0)
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", AT, AT, breg);
+ if (! target_big_endian)
+ expr1.X_add_number = 0;
+ macro_build ((char *) NULL, &icnt, &expr1, "sb", "t,o(b)", treg,
+ (int) BFD_RELOC_LO16, AT);
+ macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", treg,
+ treg, 8);
+ if (! target_big_endian)
+ expr1.X_add_number = 1;
+ else
+ expr1.X_add_number = 0;
+ macro_build ((char *) NULL, &icnt, &expr1, "sb", "t,o(b)", treg,
+ (int) BFD_RELOC_LO16, AT);
+ if (! target_big_endian)
+ expr1.X_add_number = 0;
+ else
+ expr1.X_add_number = 1;
+ macro_build ((char *) NULL, &icnt, &expr1, "lbu", "t,o(b)", AT,
+ (int) BFD_RELOC_LO16, AT);
+ macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", treg,
+ treg, 8);
+ macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", treg,
+ treg, AT);
+ break;
+
+ default:
+ /* FIXME: Check if this is one of the itbl macros, since they
+ are added dynamically. */
+ as_bad (_("Macro %s not implemented yet"), ip->insn_mo->name);
+ break;
+ }
+ if (mips_opts.noat)
+ as_warn (_("Macro used $at after \".set noat\""));
+}
+
+/* Implement macros in mips16 mode. */
+
+static void
+mips16_macro (ip)
+ struct mips_cl_insn *ip;
+{
+ int mask;
+ int xreg, yreg, zreg, tmp;
+ int icnt;
+ expressionS expr1;
+ int dbl;
+ const char *s, *s2, *s3;
+
+ mask = ip->insn_mo->mask;
+
+ xreg = (ip->insn_opcode >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX;
+ yreg = (ip->insn_opcode >> MIPS16OP_SH_RY) & MIPS16OP_MASK_RY;
+ zreg = (ip->insn_opcode >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ;
+
+ icnt = 0;
+
+ expr1.X_op = O_constant;
+ expr1.X_op_symbol = NULL;
+ expr1.X_add_symbol = NULL;
+ expr1.X_add_number = 1;
+
+ dbl = 0;
+
+ switch (mask)
+ {
+ default:
+ internalError ();
+
+ case M_DDIV_3:
+ dbl = 1;
+ case M_DIV_3:
+ s = "mflo";
+ goto do_div3;
+ case M_DREM_3:
+ dbl = 1;
+ case M_REM_3:
+ s = "mfhi";
+ do_div3:
+ mips_emit_delays (true);
+ ++mips_opts.noreorder;
+ mips_any_noreorder = 1;
+ macro_build ((char *) NULL, &icnt, NULL,
+ dbl ? "ddiv" : "div",
+ "0,x,y", xreg, yreg);
+ expr1.X_add_number = 2;
+ macro_build ((char *) NULL, &icnt, &expr1, "bnez", "x,p", yreg);
+ macro_build ((char *) NULL, &icnt, NULL, "break", "6", 7);
+
+ /* FIXME: The normal code checks for of -1 / -0x80000000 here,
+ since that causes an overflow. We should do that as well,
+ but I don't see how to do the comparisons without a temporary
+ register. */
+ --mips_opts.noreorder;
+ macro_build ((char *) NULL, &icnt, NULL, s, "x", zreg);
+ break;
+
+ case M_DIVU_3:
+ s = "divu";
+ s2 = "mflo";
+ goto do_divu3;
+ case M_REMU_3:
+ s = "divu";
+ s2 = "mfhi";
+ goto do_divu3;
+ case M_DDIVU_3:
+ s = "ddivu";
+ s2 = "mflo";
+ goto do_divu3;
+ case M_DREMU_3:
+ s = "ddivu";
+ s2 = "mfhi";
+ do_divu3:
+ mips_emit_delays (true);
+ ++mips_opts.noreorder;
+ mips_any_noreorder = 1;
+ macro_build ((char *) NULL, &icnt, NULL, s, "0,x,y", xreg, yreg);
+ expr1.X_add_number = 2;
+ macro_build ((char *) NULL, &icnt, &expr1, "bnez", "x,p", yreg);
+ macro_build ((char *) NULL, &icnt, NULL, "break", "6", 7);
+ --mips_opts.noreorder;
+ macro_build ((char *) NULL, &icnt, NULL, s2, "x", zreg);
+ break;
+
+ case M_DMUL:
+ dbl = 1;
+ case M_MUL:
+ macro_build ((char *) NULL, &icnt, NULL,
+ dbl ? "dmultu" : "multu",
+ "x,y", xreg, yreg);
+ macro_build ((char *) NULL, &icnt, NULL, "mflo", "x", zreg);
+ return;
+
+ case M_DSUBU_I:
+ dbl = 1;
+ goto do_subu;
+ case M_SUBU_I:
+ do_subu:
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build ((char *) NULL, &icnt, &imm_expr,
+ dbl ? "daddiu" : "addiu",
+ "y,x,4", yreg, xreg);
+ break;
+
+ case M_SUBU_I_2:
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build ((char *) NULL, &icnt, &imm_expr, "addiu",
+ "x,k", xreg);
+ break;
+
+ case M_DSUBU_I_2:
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build ((char *) NULL, &icnt, &imm_expr, "daddiu",
+ "y,j", yreg);
+ break;
+
+ case M_BEQ:
+ s = "cmp";
+ s2 = "bteqz";
+ goto do_branch;
+ case M_BNE:
+ s = "cmp";
+ s2 = "btnez";
+ goto do_branch;
+ case M_BLT:
+ s = "slt";
+ s2 = "btnez";
+ goto do_branch;
+ case M_BLTU:
+ s = "sltu";
+ s2 = "btnez";
+ goto do_branch;
+ case M_BLE:
+ s = "slt";
+ s2 = "bteqz";
+ goto do_reverse_branch;
+ case M_BLEU:
+ s = "sltu";
+ s2 = "bteqz";
+ goto do_reverse_branch;
+ case M_BGE:
+ s = "slt";
+ s2 = "bteqz";
+ goto do_branch;
+ case M_BGEU:
+ s = "sltu";
+ s2 = "bteqz";
+ goto do_branch;
+ case M_BGT:
+ s = "slt";
+ s2 = "btnez";
+ goto do_reverse_branch;
+ case M_BGTU:
+ s = "sltu";
+ s2 = "btnez";
+
+ do_reverse_branch:
+ tmp = xreg;
+ xreg = yreg;
+ yreg = tmp;
+
+ do_branch:
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "x,y",
+ xreg, yreg);
+ macro_build ((char *) NULL, &icnt, &offset_expr, s2, "p");
+ break;
+
+ case M_BEQ_I:
+ s = "cmpi";
+ s2 = "bteqz";
+ s3 = "x,U";
+ goto do_branch_i;
+ case M_BNE_I:
+ s = "cmpi";
+ s2 = "btnez";
+ s3 = "x,U";
+ goto do_branch_i;
+ case M_BLT_I:
+ s = "slti";
+ s2 = "btnez";
+ s3 = "x,8";
+ goto do_branch_i;
+ case M_BLTU_I:
+ s = "sltiu";
+ s2 = "btnez";
+ s3 = "x,8";
+ goto do_branch_i;
+ case M_BLE_I:
+ s = "slti";
+ s2 = "btnez";
+ s3 = "x,8";
+ goto do_addone_branch_i;
+ case M_BLEU_I:
+ s = "sltiu";
+ s2 = "btnez";
+ s3 = "x,8";
+ goto do_addone_branch_i;
+ case M_BGE_I:
+ s = "slti";
+ s2 = "bteqz";
+ s3 = "x,8";
+ goto do_branch_i;
+ case M_BGEU_I:
+ s = "sltiu";
+ s2 = "bteqz";
+ s3 = "x,8";
+ goto do_branch_i;
+ case M_BGT_I:
+ s = "slti";
+ s2 = "bteqz";
+ s3 = "x,8";
+ goto do_addone_branch_i;
+ case M_BGTU_I:
+ s = "sltiu";
+ s2 = "bteqz";
+ s3 = "x,8";
+
+ do_addone_branch_i:
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ ++imm_expr.X_add_number;
+
+ do_branch_i:
+ macro_build ((char *) NULL, &icnt, &imm_expr, s, s3, xreg);
+ macro_build ((char *) NULL, &icnt, &offset_expr, s2, "p");
+ break;
+
+ case M_ABS:
+ expr1.X_add_number = 0;
+ macro_build ((char *) NULL, &icnt, &expr1, "slti", "x,8", yreg);
+ if (xreg != yreg)
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ "move", "y,X", xreg, yreg);
+ expr1.X_add_number = 2;
+ macro_build ((char *) NULL, &icnt, &expr1, "bteqz", "p");
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ "neg", "x,w", xreg, xreg);
+ }
+}
+
+/* For consistency checking, verify that all bits are specified either
+ by the match/mask part of the instruction definition, or by the
+ operand list. */
+static int
+validate_mips_insn (opc)
+ const struct mips_opcode *opc;
+{
+ const char *p = opc->args;
+ char c;
+ unsigned long used_bits = opc->mask;
+
+ if ((used_bits & opc->match) != opc->match)
+ {
+ as_bad (_("internal: bad mips opcode (mask error): %s %s"),
+ opc->name, opc->args);
+ return 0;
+ }
+#define USE_BITS(mask,shift) (used_bits |= ((mask) << (shift)))
+ while (*p)
+ switch (c = *p++)
+ {
+ case ',': break;
+ case '(': break;
+ case ')': break;
+ case '<': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
+ case '>': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
+ case 'A': break;
+ case 'B': USE_BITS (OP_MASK_SYSCALL, OP_SH_SYSCALL); break;
+ case 'C': USE_BITS (OP_MASK_COPZ, OP_SH_COPZ); break;
+ case 'D': USE_BITS (OP_MASK_FD, OP_SH_FD); break;
+ case 'E': USE_BITS (OP_MASK_RT, OP_SH_RT); break;
+ case 'F': break;
+ case 'G': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
+ case 'I': break;
+ case 'L': break;
+ case 'M': USE_BITS (OP_MASK_CCC, OP_SH_CCC); break;
+ case 'N': USE_BITS (OP_MASK_BCC, OP_SH_BCC); break;
+ case 'R': USE_BITS (OP_MASK_FR, OP_SH_FR); break;
+ case 'S': USE_BITS (OP_MASK_FS, OP_SH_FS); break;
+ case 'T': USE_BITS (OP_MASK_FT, OP_SH_FT); break;
+ case 'V': USE_BITS (OP_MASK_FS, OP_SH_FS); break;
+ case 'W': USE_BITS (OP_MASK_FT, OP_SH_FT); break;
+ case 'a': USE_BITS (OP_MASK_TARGET, OP_SH_TARGET); break;
+ case 'b': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
+ case 'c': USE_BITS (OP_MASK_CODE, OP_SH_CODE); break;
+ case 'd': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
+ case 'f': break;
+ case 'h': USE_BITS (OP_MASK_PREFX, OP_SH_PREFX); break;
+ case 'i': USE_BITS (OP_MASK_IMMEDIATE, OP_SH_IMMEDIATE); break;
+ case 'j': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break;
+ case 'k': USE_BITS (OP_MASK_CACHE, OP_SH_CACHE); break;
+ case 'l': break;
+ case 'o': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break;
+ case 'p': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break;
+ case 'q': USE_BITS (OP_MASK_CODE2, OP_SH_CODE2); break;
+ case 'r': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
+ case 's': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
+ case 't': USE_BITS (OP_MASK_RT, OP_SH_RT); break;
+ case 'u': USE_BITS (OP_MASK_IMMEDIATE, OP_SH_IMMEDIATE); break;
+ case 'v': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
+ case 'w': USE_BITS (OP_MASK_RT, OP_SH_RT); break;
+ case 'x': break;
+ case 'z': break;
+ case 'P': USE_BITS (OP_MASK_PERFREG, OP_SH_PERFREG); break;
+ default:
+ as_bad (_("internal: bad mips opcode (unknown operand type `%c'): %s %s"),
+ c, opc->name, opc->args);
+ return 0;
+ }
+#undef USE_BITS
+ if (used_bits != 0xffffffff)
+ {
+ as_bad (_("internal: bad mips opcode (bits 0x%lx undefined): %s %s"),
+ ~used_bits & 0xffffffff, opc->name, opc->args);
+ return 0;
+ }
+ return 1;
+}
+
+/* This routine assembles an instruction into its binary format. As a
+ side effect, it sets one of the global variables imm_reloc or
+ offset_reloc to the type of relocation to do if one of the operands
+ is an address expression. */
+
+static void
+mips_ip (str, ip)
+ char *str;
+ struct mips_cl_insn *ip;
+{
+ char *s;
+ const char *args;
+ char c;
+ struct mips_opcode *insn;
+ char *argsStart;
+ unsigned int regno;
+ unsigned int lastregno = 0;
+ char *s_reset;
+ char save_c = 0;
+ int full_opcode_match = 1;
+
+ insn_error = NULL;
+
+ /* If the instruction contains a '.', we first try to match an instruction
+ including the '.'. Then we try again without the '.'. */
+ insn = NULL;
+ for (s = str; *s != '\0' && !isspace(*s); ++s)
+ continue;
+
+ /* If we stopped on whitespace, then replace the whitespace with null for
+ the call to hash_find. Save the character we replaced just in case we
+ have to re-parse the instruction. */
+ if (isspace (*s))
+ {
+ save_c = *s;
+ *s++ = '\0';
+ }
+
+ insn = (struct mips_opcode *) hash_find (op_hash, str);
+
+ /* If we didn't find the instruction in the opcode table, try again, but
+ this time with just the instruction up to, but not including the
+ first '.'. */
+ if (insn == NULL)
+ {
+ /* Restore the character we overwrite above (if any). */
+ if (save_c)
+ *(--s) = save_c;
+
+ /* Scan up to the first '.' or whitespace. */
+ for (s = str; *s != '\0' && *s != '.' && !isspace (*s); ++s)
+ continue;
+
+ /* If we did not find a '.', then we can quit now. */
+ if (*s != '.')
+ {
+ insn_error = "unrecognized opcode";
+ return;
+ }
+
+ /* Lookup the instruction in the hash table. */
+ *s++ = '\0';
+ if ((insn = (struct mips_opcode *) hash_find (op_hash, str)) == NULL)
+ {
+ insn_error = "unrecognized opcode";
+ return;
+ }
+
+ full_opcode_match = 0;
+ }
+
+ argsStart = s;
+ for (;;)
+ {
+ int insn_isa;
+ boolean ok;
+
+ assert (strcmp (insn->name, str) == 0);
+
+ if ((insn->membership & INSN_ISA) == INSN_ISA1)
+ insn_isa = 1;
+ else if ((insn->membership & INSN_ISA) == INSN_ISA2)
+ insn_isa = 2;
+ else if ((insn->membership & INSN_ISA) == INSN_ISA3)
+ insn_isa = 3;
+ else if ((insn->membership & INSN_ISA) == INSN_ISA4)
+ insn_isa = 4;
+ else
+ insn_isa = 15;
+
+ if (insn_isa <= mips_opts.isa)
+ ok = true;
+ else if (insn->pinfo == INSN_MACRO)
+ ok = false;
+ else if ((mips_cpu == 4650 && (insn->membership & INSN_4650) != 0)
+ || (mips_cpu == 4010 && (insn->membership & INSN_4010) != 0)
+ || ((mips_cpu == 4100
+ || mips_cpu == 4111
+ )
+ && (insn->membership & INSN_4100) != 0)
+ || (mips_cpu == 3900 && (insn->membership & INSN_3900) != 0))
+ ok = true;
+ else
+ ok = false;
+
+ if (insn->pinfo != INSN_MACRO)
+ {
+ if (mips_cpu == 4650 && (insn->pinfo & FP_D) != 0)
+ ok = false;
+ }
+
+ if (! ok)
+ {
+ if (insn + 1 < &mips_opcodes[NUMOPCODES]
+ && strcmp (insn->name, insn[1].name) == 0)
+ {
+ ++insn;
+ continue;
+ }
+ if (insn_isa == 15
+ || insn_isa <= mips_opts.isa)
+ insn_error = _("opcode not supported on this processor");
+ else
+ {
+ static char buf[100];
+
+ sprintf (buf, _("opcode requires -mips%d or greater"), insn_isa);
+ insn_error = buf;
+ }
+ return;
+ }
+
+ ip->insn_mo = insn;
+ ip->insn_opcode = insn->match;
+ for (args = insn->args;; ++args)
+ {
+ if (*s == ' ')
+ ++s;
+ switch (*args)
+ {
+ case '\0': /* end of args */
+ if (*s == '\0')
+ return;
+ break;
+
+ case ',':
+ if (*s++ == *args)
+ continue;
+ s--;
+ switch (*++args)
+ {
+ case 'r':
+ case 'v':
+ ip->insn_opcode |= lastregno << 21;
+ continue;
+
+ case 'w':
+ case 'W':
+ ip->insn_opcode |= lastregno << 16;
+ continue;
+
+ case 'V':
+ ip->insn_opcode |= lastregno << 11;
+ continue;
+ }
+ break;
+
+ case '(':
+ /* Handle optional base register.
+ Either the base register is omitted or
+ we must have a left paren. */
+ /* This is dependent on the next operand specifier
+ is a base register specification. */
+ assert (args[1] == 'b' || args[1] == '5'
+ || args[1] == '-' || args[1] == '4');
+ if (*s == '\0')
+ return;
+
+ case ')': /* these must match exactly */
+ if (*s++ == *args)
+ continue;
+ break;
+
+ case '<': /* must be at least one digit */
+ /*
+ * According to the manual, if the shift amount is greater
+ * than 31 or less than 0 the the shift amount should be
+ * mod 32. In reality the mips assembler issues an error.
+ * We issue a warning and mask out all but the low 5 bits.
+ */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number > 31)
+ {
+ as_warn (_("Improper shift amount (%ld)"),
+ (long) imm_expr.X_add_number);
+ imm_expr.X_add_number = imm_expr.X_add_number & 0x1f;
+ }
+ ip->insn_opcode |= imm_expr.X_add_number << 6;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case '>': /* shift amount minus 32 */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number < 32
+ || (unsigned long) imm_expr.X_add_number > 63)
+ break;
+ ip->insn_opcode |= (imm_expr.X_add_number - 32) << 6;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+
+ case 'k': /* cache code */
+ case 'h': /* prefx code */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number > 31)
+ {
+ as_warn (_("Invalid value for `%s' (%lu)"),
+ ip->insn_mo->name,
+ (unsigned long) imm_expr.X_add_number);
+ imm_expr.X_add_number &= 0x1f;
+ }
+ if (*args == 'k')
+ ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CACHE;
+ else
+ ip->insn_opcode |= imm_expr.X_add_number << OP_SH_PREFX;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'c': /* break code */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned) imm_expr.X_add_number > 1023)
+ {
+ as_warn (_("Illegal break code (%ld)"),
+ (long) imm_expr.X_add_number);
+ imm_expr.X_add_number &= 0x3ff;
+ }
+ ip->insn_opcode |= imm_expr.X_add_number << 16;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'q': /* lower break code */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned) imm_expr.X_add_number > 1023)
+ {
+ as_warn (_("Illegal lower break code (%ld)"),
+ (long) imm_expr.X_add_number);
+ imm_expr.X_add_number &= 0x3ff;
+ }
+ ip->insn_opcode |= imm_expr.X_add_number << 6;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'B': /* syscall code */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned) imm_expr.X_add_number > 0xfffff)
+ as_warn (_("Illegal syscall code (%ld)"),
+ (long) imm_expr.X_add_number);
+ ip->insn_opcode |= imm_expr.X_add_number << 6;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'C': /* Coprocessor code */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number >= (1<<25))
+ {
+ as_warn (_("Coproccesor code > 25 bits (%ld)"),
+ (long) imm_expr.X_add_number);
+ imm_expr.X_add_number &= ((1<<25) - 1);
+ }
+ ip->insn_opcode |= imm_expr.X_add_number;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'P': /* Performance register */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if (imm_expr.X_add_number != 0 && imm_expr.X_add_number != 1)
+ {
+ as_warn (_("Invalidate performance regster (%ld)"),
+ (long) imm_expr.X_add_number);
+ imm_expr.X_add_number &= 1;
+ }
+ ip->insn_opcode |= (imm_expr.X_add_number << 1);
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'b': /* base register */
+ case 'd': /* destination register */
+ case 's': /* source register */
+ case 't': /* target register */
+ case 'r': /* both target and source */
+ case 'v': /* both dest and source */
+ case 'w': /* both dest and target */
+ case 'E': /* coprocessor target register */
+ case 'G': /* coprocessor destination register */
+ case 'x': /* ignore register name */
+ case 'z': /* must be zero register */
+ s_reset = s;
+ if (s[0] == '$')
+ {
+
+ if (isdigit (s[1]))
+ {
+ ++s;
+ regno = 0;
+ do
+ {
+ regno *= 10;
+ regno += *s - '0';
+ ++s;
+ }
+ while (isdigit (*s));
+ if (regno > 31)
+ as_bad (_("Invalid register number (%d)"), regno);
+ }
+ else if (*args == 'E' || *args == 'G')
+ goto notreg;
+ else
+ {
+ if (s[1] == 'f' && s[2] == 'p')
+ {
+ s += 3;
+ regno = FP;
+ }
+ else if (s[1] == 's' && s[2] == 'p')
+ {
+ s += 3;
+ regno = SP;
+ }
+ else if (s[1] == 'g' && s[2] == 'p')
+ {
+ s += 3;
+ regno = GP;
+ }
+ else if (s[1] == 'a' && s[2] == 't')
+ {
+ s += 3;
+ regno = AT;
+ }
+ else if (s[1] == 'k' && s[2] == 't' && s[3] == '0')
+ {
+ s += 4;
+ regno = KT0;
+ }
+ else if (s[1] == 'k' && s[2] == 't' && s[3] == '1')
+ {
+ s += 4;
+ regno = KT1;
+ }
+ else if (itbl_have_entries)
+ {
+ char *p, *n;
+ int r;
+
+ p = s+1; /* advance past '$' */
+ n = itbl_get_field (&p); /* n is name */
+
+ /* See if this is a register defined in an
+ itbl entry */
+ r = itbl_get_reg_val (n);
+ if (r)
+ {
+ /* Get_field advances to the start of
+ the next field, so we need to back
+ rack to the end of the last field. */
+ if (p)
+ s = p - 1;
+ else
+ s = strchr (s,'\0');
+ regno = r;
+ }
+ else
+ goto notreg;
+ }
+ else
+ goto notreg;
+ }
+ if (regno == AT
+ && ! mips_opts.noat
+ && *args != 'E'
+ && *args != 'G')
+ as_warn (_("Used $at without \".set noat\""));
+ c = *args;
+ if (*s == ' ')
+ s++;
+ if (args[1] != *s)
+ {
+ if (c == 'r' || c == 'v' || c == 'w')
+ {
+ regno = lastregno;
+ s = s_reset;
+ args++;
+ }
+ }
+ /* 'z' only matches $0. */
+ if (c == 'z' && regno != 0)
+ break;
+
+ /* Now that we have assembled one operand, we use the args string
+ * to figure out where it goes in the instruction. */
+ switch (c)
+ {
+ case 'r':
+ case 's':
+ case 'v':
+ case 'b':
+ ip->insn_opcode |= regno << 21;
+ break;
+ case 'd':
+ case 'G':
+ ip->insn_opcode |= regno << 11;
+ break;
+ case 'w':
+ case 't':
+ case 'E':
+ ip->insn_opcode |= regno << 16;
+ break;
+ case 'x':
+ /* This case exists because on the r3000 trunc
+ expands into a macro which requires a gp
+ register. On the r6000 or r4000 it is
+ assembled into a single instruction which
+ ignores the register. Thus the insn version
+ is MIPS_ISA2 and uses 'x', and the macro
+ version is MIPS_ISA1 and uses 't'. */
+ break;
+ case 'z':
+ /* This case is for the div instruction, which
+ acts differently if the destination argument
+ is $0. This only matches $0, and is checked
+ outside the switch. */
+ break;
+ case 'D':
+ /* Itbl operand; not yet implemented. FIXME ?? */
+ break;
+ /* What about all other operands like 'i', which
+ can be specified in the opcode table? */
+ }
+ lastregno = regno;
+ continue;
+ }
+ notreg:
+ switch (*args++)
+ {
+ case 'r':
+ case 'v':
+ ip->insn_opcode |= lastregno << 21;
+ continue;
+ case 'w':
+ ip->insn_opcode |= lastregno << 16;
+ continue;
+ }
+ break;
+
+ case 'D': /* floating point destination register */
+ case 'S': /* floating point source register */
+ case 'T': /* floating point target register */
+ case 'R': /* floating point source register */
+ case 'V':
+ case 'W':
+ s_reset = s;
+ if (s[0] == '$' && s[1] == 'f' && isdigit (s[2]))
+ {
+ s += 2;
+ regno = 0;
+ do
+ {
+ regno *= 10;
+ regno += *s - '0';
+ ++s;
+ }
+ while (isdigit (*s));
+
+ if (regno > 31)
+ as_bad (_("Invalid float register number (%d)"), regno);
+
+ if ((regno & 1) != 0
+ && mips_opts.isa < 3
+ && ! (strcmp (str, "mtc1") == 0
+ || strcmp (str, "mfc1") == 0
+ || strcmp (str, "lwc1") == 0
+ || strcmp (str, "swc1") == 0
+ || strcmp (str, "l.s") == 0
+ || strcmp (str, "s.s") == 0))
+ as_warn (_("Float register should be even, was %d"),
+ regno);
+
+ c = *args;
+ if (*s == ' ')
+ s++;
+ if (args[1] != *s)
+ {
+ if (c == 'V' || c == 'W')
+ {
+ regno = lastregno;
+ s = s_reset;
+ args++;
+ }
+ }
+ switch (c)
+ {
+ case 'D':
+ ip->insn_opcode |= regno << 6;
+ break;
+ case 'V':
+ case 'S':
+ ip->insn_opcode |= regno << 11;
+ break;
+ case 'W':
+ case 'T':
+ ip->insn_opcode |= regno << 16;
+ break;
+ case 'R':
+ ip->insn_opcode |= regno << 21;
+ break;
+ }
+ lastregno = regno;
+ continue;
+ }
+
+
+ switch (*args++)
+ {
+ case 'V':
+ ip->insn_opcode |= lastregno << 11;
+ continue;
+ case 'W':
+ ip->insn_opcode |= lastregno << 16;
+ continue;
+ }
+ break;
+
+ case 'I':
+ my_getExpression (&imm_expr, s);
+ if (imm_expr.X_op != O_big
+ && imm_expr.X_op != O_constant)
+ insn_error = _("absolute expression required");
+ s = expr_end;
+ continue;
+
+ case 'A':
+ my_getExpression (&offset_expr, s);
+ imm_reloc = BFD_RELOC_32;
+ s = expr_end;
+ continue;
+
+ case 'F':
+ case 'L':
+ case 'f':
+ case 'l':
+ {
+ int f64;
+ char *save_in;
+ char *err;
+ unsigned char temp[8];
+ int len;
+ unsigned int length;
+ segT seg;
+ subsegT subseg;
+ char *p;
+
+ /* These only appear as the last operand in an
+ instruction, and every instruction that accepts
+ them in any variant accepts them in all variants.
+ This means we don't have to worry about backing out
+ any changes if the instruction does not match.
+
+ The difference between them is the size of the
+ floating point constant and where it goes. For 'F'
+ and 'L' the constant is 64 bits; for 'f' and 'l' it
+ is 32 bits. Where the constant is placed is based
+ on how the MIPS assembler does things:
+ F -- .rdata
+ L -- .lit8
+ f -- immediate value
+ l -- .lit4
+
+ The .lit4 and .lit8 sections are only used if
+ permitted by the -G argument.
+
+ When generating embedded PIC code, we use the
+ .lit8 section but not the .lit4 section (we can do
+ .lit4 inline easily; we need to put .lit8
+ somewhere in the data segment, and using .lit8
+ permits the linker to eventually combine identical
+ .lit8 entries). */
+
+ f64 = *args == 'F' || *args == 'L';
+
+ save_in = input_line_pointer;
+ input_line_pointer = s;
+ err = md_atof (f64 ? 'd' : 'f', (char *) temp, &len);
+ length = len;
+ s = input_line_pointer;
+ input_line_pointer = save_in;
+ if (err != NULL && *err != '\0')
+ {
+ as_bad (_("Bad floating point constant: %s"), err);
+ memset (temp, '\0', sizeof temp);
+ length = f64 ? 8 : 4;
+ }
+
+ assert (length == (f64 ? 8 : 4));
+
+ if (*args == 'f'
+ || (*args == 'l'
+ && (! USE_GLOBAL_POINTER_OPT
+ || mips_pic == EMBEDDED_PIC
+ || g_switch_value < 4
+ || (temp[0] == 0 && temp[1] == 0)
+ || (temp[2] == 0 && temp[3] == 0))))
+ {
+ imm_expr.X_op = O_constant;
+ if (! target_big_endian)
+ imm_expr.X_add_number = bfd_getl32 (temp);
+ else
+ imm_expr.X_add_number = bfd_getb32 (temp);
+ }
+ else if (length > 4
+ && ((temp[0] == 0 && temp[1] == 0)
+ || (temp[2] == 0 && temp[3] == 0))
+ && ((temp[4] == 0 && temp[5] == 0)
+ || (temp[6] == 0 && temp[7] == 0)))
+ {
+ /* The value is simple enough to load with a
+ couple of instructions. In mips1 mode, set
+ imm_expr to the high order 32 bits and
+ offset_expr to the low order 32 bits.
+ Otherwise, set imm_expr to the entire 64 bit
+ constant. */
+ if (mips_opts.isa < 3)
+ {
+ imm_expr.X_op = O_constant;
+ offset_expr.X_op = O_constant;
+ if (! target_big_endian)
+ {
+ imm_expr.X_add_number = bfd_getl32 (temp + 4);
+ offset_expr.X_add_number = bfd_getl32 (temp);
+ }
+ else
+ {
+ imm_expr.X_add_number = bfd_getb32 (temp);
+ offset_expr.X_add_number = bfd_getb32 (temp + 4);
+ }
+ if (offset_expr.X_add_number == 0)
+ offset_expr.X_op = O_absent;
+ }
+ else if (sizeof (imm_expr.X_add_number) > 4)
+ {
+ imm_expr.X_op = O_constant;
+ if (! target_big_endian)
+ imm_expr.X_add_number = bfd_getl64 (temp);
+ else
+ imm_expr.X_add_number = bfd_getb64 (temp);
+ }
+ else
+ {
+ imm_expr.X_op = O_big;
+ imm_expr.X_add_number = 4;
+ if (! target_big_endian)
+ {
+ generic_bignum[0] = bfd_getl16 (temp);
+ generic_bignum[1] = bfd_getl16 (temp + 2);
+ generic_bignum[2] = bfd_getl16 (temp + 4);
+ generic_bignum[3] = bfd_getl16 (temp + 6);
+ }
+ else
+ {
+ generic_bignum[0] = bfd_getb16 (temp + 6);
+ generic_bignum[1] = bfd_getb16 (temp + 4);
+ generic_bignum[2] = bfd_getb16 (temp + 2);
+ generic_bignum[3] = bfd_getb16 (temp);
+ }
+ }
+ }
+ else
+ {
+ const char *newname;
+ segT new_seg;
+
+ /* Switch to the right section. */
+ seg = now_seg;
+ subseg = now_subseg;
+ switch (*args)
+ {
+ default: /* unused default case avoids warnings. */
+ case 'L':
+ newname = RDATA_SECTION_NAME;
+ if (USE_GLOBAL_POINTER_OPT && g_switch_value >= 8)
+ newname = ".lit8";
+ break;
+ case 'F':
+ newname = RDATA_SECTION_NAME;
+ break;
+ case 'l':
+ assert (!USE_GLOBAL_POINTER_OPT
+ || g_switch_value >= 4);
+ newname = ".lit4";
+ break;
+ }
+ new_seg = subseg_new (newname, (subsegT) 0);
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ bfd_set_section_flags (stdoutput, new_seg,
+ (SEC_ALLOC
+ | SEC_LOAD
+ | SEC_READONLY
+ | SEC_DATA));
+ frag_align (*args == 'l' ? 2 : 3, 0, 0);
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+ && strcmp (TARGET_OS, "elf") != 0)
+ record_alignment (new_seg, 4);
+ else
+ record_alignment (new_seg, *args == 'l' ? 2 : 3);
+ if (seg == now_seg)
+ as_bad (_("Can't use floating point insn in this section"));
+
+ /* Set the argument to the current address in the
+ section. */
+ offset_expr.X_op = O_symbol;
+ offset_expr.X_add_symbol =
+ symbol_new ("L0\001", now_seg,
+ (valueT) frag_now_fix (), frag_now);
+ offset_expr.X_add_number = 0;
+
+ /* Put the floating point number into the section. */
+ p = frag_more ((int) length);
+ memcpy (p, temp, length);
+
+ /* Switch back to the original section. */
+ subseg_set (seg, subseg);
+ }
+ }
+ continue;
+
+ case 'i': /* 16 bit unsigned immediate */
+ case 'j': /* 16 bit signed immediate */
+ imm_reloc = BFD_RELOC_LO16;
+ c = my_getSmallExpression (&imm_expr, s);
+ if (c != '\0')
+ {
+ if (c != 'l')
+ {
+ if (imm_expr.X_op == O_constant)
+ imm_expr.X_add_number =
+ (imm_expr.X_add_number >> 16) & 0xffff;
+ else if (c == 'h')
+ {
+ imm_reloc = BFD_RELOC_HI16_S;
+ imm_unmatched_hi = true;
+ }
+ else
+ imm_reloc = BFD_RELOC_HI16;
+ }
+ else if (imm_expr.X_op == O_constant)
+ imm_expr.X_add_number &= 0xffff;
+ }
+ if (*args == 'i')
+ {
+ if ((c == '\0' && imm_expr.X_op != O_constant)
+ || ((imm_expr.X_add_number < 0
+ || imm_expr.X_add_number >= 0x10000)
+ && imm_expr.X_op == O_constant))
+ {
+ if (insn + 1 < &mips_opcodes[NUMOPCODES] &&
+ !strcmp (insn->name, insn[1].name))
+ break;
+ if (imm_expr.X_op != O_constant
+ && imm_expr.X_op != O_big)
+ insn_error = _("absolute expression required");
+ else
+ as_bad (_("16 bit expression not in range 0..65535"));
+ }
+ }
+ else
+ {
+ int more;
+ offsetT max;
+
+ /* The upper bound should be 0x8000, but
+ unfortunately the MIPS assembler accepts numbers
+ from 0x8000 to 0xffff and sign extends them, and
+ we want to be compatible. We only permit this
+ extended range for an instruction which does not
+ provide any further alternates, since those
+ alternates may handle other cases. People should
+ use the numbers they mean, rather than relying on
+ a mysterious sign extension. */
+ more = (insn + 1 < &mips_opcodes[NUMOPCODES] &&
+ strcmp (insn->name, insn[1].name) == 0);
+ if (more)
+ max = 0x8000;
+ else
+ max = 0x10000;
+ if ((c == '\0' && imm_expr.X_op != O_constant)
+ || ((imm_expr.X_add_number < -0x8000
+ || imm_expr.X_add_number >= max)
+ && imm_expr.X_op == O_constant)
+ || (more
+ && imm_expr.X_add_number < 0
+ && mips_opts.isa >= 3
+ && imm_expr.X_unsigned
+ && sizeof (imm_expr.X_add_number) <= 4))
+ {
+ if (more)
+ break;
+ if (imm_expr.X_op != O_constant
+ && imm_expr.X_op != O_big)
+ insn_error = _("absolute expression required");
+ else
+ as_bad (_("16 bit expression not in range -32768..32767"));
+ }
+ }
+ s = expr_end;
+ continue;
+
+ case 'o': /* 16 bit offset */
+ c = my_getSmallExpression (&offset_expr, s);
+
+ /* If this value won't fit into a 16 bit offset, then go
+ find a macro that will generate the 32 bit offset
+ code pattern. As a special hack, we accept the
+ difference of two local symbols as a constant. This
+ is required to suppose embedded PIC switches, which
+ use an instruction which looks like
+ lw $4,$L12-$LS12($4)
+ The problem with handling this in a more general
+ fashion is that the macro function doesn't expect to
+ see anything which can be handled in a single
+ constant instruction. */
+ if (c == 0
+ && (offset_expr.X_op != O_constant
+ || offset_expr.X_add_number >= 0x8000
+ || offset_expr.X_add_number < -0x8000)
+ && (mips_pic != EMBEDDED_PIC
+ || offset_expr.X_op != O_subtract
+ || now_seg != text_section
+ || (S_GET_SEGMENT (offset_expr.X_op_symbol)
+ != text_section)))
+ break;
+
+ if (c == 'h' || c == 'H')
+ {
+ if (offset_expr.X_op != O_constant)
+ break;
+ offset_expr.X_add_number =
+ (offset_expr.X_add_number >> 16) & 0xffff;
+ }
+ offset_reloc = BFD_RELOC_LO16;
+ s = expr_end;
+ continue;
+
+ case 'p': /* pc relative offset */
+ offset_reloc = BFD_RELOC_16_PCREL_S2;
+ my_getExpression (&offset_expr, s);
+ s = expr_end;
+ continue;
+
+ case 'u': /* upper 16 bits */
+ c = my_getSmallExpression (&imm_expr, s);
+ imm_reloc = BFD_RELOC_LO16;
+ if (c)
+ {
+ if (c != 'l')
+ {
+ if (imm_expr.X_op == O_constant)
+ imm_expr.X_add_number =
+ (imm_expr.X_add_number >> 16) & 0xffff;
+ else if (c == 'h')
+ {
+ imm_reloc = BFD_RELOC_HI16_S;
+ imm_unmatched_hi = true;
+ }
+ else
+ imm_reloc = BFD_RELOC_HI16;
+ }
+ else if (imm_expr.X_op == O_constant)
+ imm_expr.X_add_number &= 0xffff;
+ }
+ if (imm_expr.X_op == O_constant
+ && (imm_expr.X_add_number < 0
+ || imm_expr.X_add_number >= 0x10000))
+ as_bad (_("lui expression not in range 0..65535"));
+ s = expr_end;
+ continue;
+
+ case 'a': /* 26 bit address */
+ my_getExpression (&offset_expr, s);
+ s = expr_end;
+ offset_reloc = BFD_RELOC_MIPS_JMP;
+ continue;
+
+ case 'N': /* 3 bit branch condition code */
+ case 'M': /* 3 bit compare condition code */
+ if (strncmp (s, "$fcc", 4) != 0)
+ break;
+ s += 4;
+ regno = 0;
+ do
+ {
+ regno *= 10;
+ regno += *s - '0';
+ ++s;
+ }
+ while (isdigit (*s));
+ if (regno > 7)
+ as_bad (_("invalid condition code register $fcc%d"), regno);
+ if (*args == 'N')
+ ip->insn_opcode |= regno << OP_SH_BCC;
+ else
+ ip->insn_opcode |= regno << OP_SH_CCC;
+ continue;
+
+ default:
+ as_bad (_("bad char = '%c'\n"), *args);
+ internalError ();
+ }
+ break;
+ }
+ /* Args don't match. */
+ if (insn + 1 < &mips_opcodes[NUMOPCODES] &&
+ !strcmp (insn->name, insn[1].name))
+ {
+ ++insn;
+ s = argsStart;
+ continue;
+ }
+ insn_error = _("illegal operands");
+ return;
+ }
+}
+
+/* This routine assembles an instruction into its binary format when
+ assembling for the mips16. As a side effect, it sets one of the
+ global variables imm_reloc or offset_reloc to the type of
+ relocation to do if one of the operands is an address expression.
+ It also sets mips16_small and mips16_ext if the user explicitly
+ requested a small or extended instruction. */
+
+static void
+mips16_ip (str, ip)
+ char *str;
+ struct mips_cl_insn *ip;
+{
+ char *s;
+ const char *args;
+ struct mips_opcode *insn;
+ char *argsstart;
+ unsigned int regno;
+ unsigned int lastregno = 0;
+ char *s_reset;
+
+ insn_error = NULL;
+
+ mips16_small = false;
+ mips16_ext = false;
+
+ for (s = str; islower (*s); ++s)
+ ;
+ switch (*s)
+ {
+ case '\0':
+ break;
+
+ case ' ':
+ *s++ = '\0';
+ break;
+
+ case '.':
+ if (s[1] == 't' && s[2] == ' ')
+ {
+ *s = '\0';
+ mips16_small = true;
+ s += 3;
+ break;
+ }
+ else if (s[1] == 'e' && s[2] == ' ')
+ {
+ *s = '\0';
+ mips16_ext = true;
+ s += 3;
+ break;
+ }
+ /* Fall through. */
+ default:
+ insn_error = _("unknown opcode");
+ return;
+ }
+
+ if (mips_opts.noautoextend && ! mips16_ext)
+ mips16_small = true;
+
+ if ((insn = (struct mips_opcode *) hash_find (mips16_op_hash, str)) == NULL)
+ {
+ insn_error = _("unrecognized opcode");
+ return;
+ }
+
+ argsstart = s;
+ for (;;)
+ {
+ assert (strcmp (insn->name, str) == 0);
+
+ ip->insn_mo = insn;
+ ip->insn_opcode = insn->match;
+ ip->use_extend = false;
+ imm_expr.X_op = O_absent;
+ imm_reloc = BFD_RELOC_UNUSED;
+ offset_expr.X_op = O_absent;
+ offset_reloc = BFD_RELOC_UNUSED;
+ for (args = insn->args; 1; ++args)
+ {
+ int c;
+
+ if (*s == ' ')
+ ++s;
+
+ /* In this switch statement we call break if we did not find
+ a match, continue if we did find a match, or return if we
+ are done. */
+
+ c = *args;
+ switch (c)
+ {
+ case '\0':
+ if (*s == '\0')
+ {
+ /* Stuff the immediate value in now, if we can. */
+ if (imm_expr.X_op == O_constant
+ && imm_reloc > BFD_RELOC_UNUSED
+ && insn->pinfo != INSN_MACRO)
+ {
+ mips16_immed ((char *) NULL, 0,
+ imm_reloc - BFD_RELOC_UNUSED,
+ imm_expr.X_add_number, true, mips16_small,
+ mips16_ext, &ip->insn_opcode,
+ &ip->use_extend, &ip->extend);
+ imm_expr.X_op = O_absent;
+ imm_reloc = BFD_RELOC_UNUSED;
+ }
+
+ return;
+ }
+ break;
+
+ case ',':
+ if (*s++ == c)
+ continue;
+ s--;
+ switch (*++args)
+ {
+ case 'v':
+ ip->insn_opcode |= lastregno << MIPS16OP_SH_RX;
+ continue;
+ case 'w':
+ ip->insn_opcode |= lastregno << MIPS16OP_SH_RY;
+ continue;
+ }
+ break;
+
+ case '(':
+ case ')':
+ if (*s++ == c)
+ continue;
+ break;
+
+ case 'v':
+ case 'w':
+ if (s[0] != '$')
+ {
+ if (c == 'v')
+ ip->insn_opcode |= lastregno << MIPS16OP_SH_RX;
+ else
+ ip->insn_opcode |= lastregno << MIPS16OP_SH_RY;
+ ++args;
+ continue;
+ }
+ /* Fall through. */
+ case 'x':
+ case 'y':
+ case 'z':
+ case 'Z':
+ case '0':
+ case 'S':
+ case 'R':
+ case 'X':
+ case 'Y':
+ if (s[0] != '$')
+ break;
+ s_reset = s;
+ if (isdigit (s[1]))
+ {
+ ++s;
+ regno = 0;
+ do
+ {
+ regno *= 10;
+ regno += *s - '0';
+ ++s;
+ }
+ while (isdigit (*s));
+ if (regno > 31)
+ {
+ as_bad (_("invalid register number (%d)"), regno);
+ regno = 2;
+ }
+ }
+ else
+ {
+ if (s[1] == 'f' && s[2] == 'p')
+ {
+ s += 3;
+ regno = FP;
+ }
+ else if (s[1] == 's' && s[2] == 'p')
+ {
+ s += 3;
+ regno = SP;
+ }
+ else if (s[1] == 'g' && s[2] == 'p')
+ {
+ s += 3;
+ regno = GP;
+ }
+ else if (s[1] == 'a' && s[2] == 't')
+ {
+ s += 3;
+ regno = AT;
+ }
+ else if (s[1] == 'k' && s[2] == 't' && s[3] == '0')
+ {
+ s += 4;
+ regno = KT0;
+ }
+ else if (s[1] == 'k' && s[2] == 't' && s[3] == '1')
+ {
+ s += 4;
+ regno = KT1;
+ }
+ else
+ break;
+ }
+
+ if (*s == ' ')
+ ++s;
+ if (args[1] != *s)
+ {
+ if (c == 'v' || c == 'w')
+ {
+ regno = mips16_to_32_reg_map[lastregno];
+ s = s_reset;
+ args++;
+ }
+ }
+
+ switch (c)
+ {
+ case 'x':
+ case 'y':
+ case 'z':
+ case 'v':
+ case 'w':
+ case 'Z':
+ regno = mips32_to_16_reg_map[regno];
+ break;
+
+ case '0':
+ if (regno != 0)
+ regno = ILLEGAL_REG;
+ break;
+
+ case 'S':
+ if (regno != SP)
+ regno = ILLEGAL_REG;
+ break;
+
+ case 'R':
+ if (regno != RA)
+ regno = ILLEGAL_REG;
+ break;
+
+ case 'X':
+ case 'Y':
+ if (regno == AT && ! mips_opts.noat)
+ as_warn (_("used $at without \".set noat\""));
+ break;
+
+ default:
+ internalError ();
+ }
+
+ if (regno == ILLEGAL_REG)
+ break;
+
+ switch (c)
+ {
+ case 'x':
+ case 'v':
+ ip->insn_opcode |= regno << MIPS16OP_SH_RX;
+ break;
+ case 'y':
+ case 'w':
+ ip->insn_opcode |= regno << MIPS16OP_SH_RY;
+ break;
+ case 'z':
+ ip->insn_opcode |= regno << MIPS16OP_SH_RZ;
+ break;
+ case 'Z':
+ ip->insn_opcode |= regno << MIPS16OP_SH_MOVE32Z;
+ case '0':
+ case 'S':
+ case 'R':
+ break;
+ case 'X':
+ ip->insn_opcode |= regno << MIPS16OP_SH_REGR32;
+ break;
+ case 'Y':
+ regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3);
+ ip->insn_opcode |= regno << MIPS16OP_SH_REG32R;
+ break;
+ default:
+ internalError ();
+ }
+
+ lastregno = regno;
+ continue;
+
+ case 'P':
+ if (strncmp (s, "$pc", 3) == 0)
+ {
+ s += 3;
+ continue;
+ }
+ break;
+
+ case '<':
+ case '>':
+ case '[':
+ case ']':
+ case '4':
+ case '5':
+ case 'H':
+ case 'W':
+ case 'D':
+ case 'j':
+ case '8':
+ case 'V':
+ case 'C':
+ case 'U':
+ case 'k':
+ case 'K':
+ if (s[0] == '%'
+ && strncmp (s + 1, "gprel(", sizeof "gprel(" - 1) == 0)
+ {
+ /* This is %gprel(SYMBOL). We need to read SYMBOL,
+ and generate the appropriate reloc. If the text
+ inside %gprel is not a symbol name with an
+ optional offset, then we generate a normal reloc
+ and will probably fail later. */
+ my_getExpression (&imm_expr, s + sizeof "%gprel" - 1);
+ if (imm_expr.X_op == O_symbol)
+ {
+ mips16_ext = true;
+ imm_reloc = BFD_RELOC_MIPS16_GPREL;
+ s = expr_end;
+ ip->use_extend = true;
+ ip->extend = 0;
+ continue;
+ }
+ }
+ else
+ {
+ /* Just pick up a normal expression. */
+ my_getExpression (&imm_expr, s);
+ }
+
+ if (imm_expr.X_op == O_register)
+ {
+ /* What we thought was an expression turned out to
+ be a register. */
+
+ if (s[0] == '(' && args[1] == '(')
+ {
+ /* It looks like the expression was omitted
+ before a register indirection, which means
+ that the expression is implicitly zero. We
+ still set up imm_expr, so that we handle
+ explicit extensions correctly. */
+ imm_expr.X_op = O_constant;
+ imm_expr.X_add_number = 0;
+ imm_reloc = (int) BFD_RELOC_UNUSED + c;
+ continue;
+ }
+
+ break;
+ }
+
+ /* We need to relax this instruction. */
+ imm_reloc = (int) BFD_RELOC_UNUSED + c;
+ s = expr_end;
+ continue;
+
+ case 'p':
+ case 'q':
+ case 'A':
+ case 'B':
+ case 'E':
+ /* We use offset_reloc rather than imm_reloc for the PC
+ relative operands. This lets macros with both
+ immediate and address operands work correctly. */
+ my_getExpression (&offset_expr, s);
+
+ if (offset_expr.X_op == O_register)
+ break;
+
+ /* We need to relax this instruction. */
+ offset_reloc = (int) BFD_RELOC_UNUSED + c;
+ s = expr_end;
+ continue;
+
+ case '6': /* break code */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number > 63)
+ {
+ as_warn (_("Invalid value for `%s' (%lu)"),
+ ip->insn_mo->name,
+ (unsigned long) imm_expr.X_add_number);
+ imm_expr.X_add_number &= 0x3f;
+ }
+ ip->insn_opcode |= imm_expr.X_add_number << MIPS16OP_SH_IMM6;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'a': /* 26 bit address */
+ my_getExpression (&offset_expr, s);
+ s = expr_end;
+ offset_reloc = BFD_RELOC_MIPS16_JMP;
+ ip->insn_opcode <<= 16;
+ continue;
+
+ case 'l': /* register list for entry macro */
+ case 'L': /* register list for exit macro */
+ {
+ int mask;
+
+ if (c == 'l')
+ mask = 0;
+ else
+ mask = 7 << 3;
+ while (*s != '\0')
+ {
+ int freg, reg1, reg2;
+
+ while (*s == ' ' || *s == ',')
+ ++s;
+ if (*s != '$')
+ {
+ as_bad (_("can't parse register list"));
+ break;
+ }
+ ++s;
+ if (*s != 'f')
+ freg = 0;
+ else
+ {
+ freg = 1;
+ ++s;
+ }
+ reg1 = 0;
+ while (isdigit (*s))
+ {
+ reg1 *= 10;
+ reg1 += *s - '0';
+ ++s;
+ }
+ if (*s == ' ')
+ ++s;
+ if (*s != '-')
+ reg2 = reg1;
+ else
+ {
+ ++s;
+ if (*s != '$')
+ break;
+ ++s;
+ if (freg)
+ {
+ if (*s == 'f')
+ ++s;
+ else
+ {
+ as_bad (_("invalid register list"));
+ break;
+ }
+ }
+ reg2 = 0;
+ while (isdigit (*s))
+ {
+ reg2 *= 10;
+ reg2 += *s - '0';
+ ++s;
+ }
+ }
+ if (freg && reg1 == 0 && reg2 == 0 && c == 'L')
+ {
+ mask &= ~ (7 << 3);
+ mask |= 5 << 3;
+ }
+ else if (freg && reg1 == 0 && reg2 == 1 && c == 'L')
+ {
+ mask &= ~ (7 << 3);
+ mask |= 6 << 3;
+ }
+ else if (reg1 == 4 && reg2 >= 4 && reg2 <= 7 && c != 'L')
+ mask |= (reg2 - 3) << 3;
+ else if (reg1 == 16 && reg2 >= 16 && reg2 <= 17)
+ mask |= (reg2 - 15) << 1;
+ else if (reg1 == 31 && reg2 == 31)
+ mask |= 1;
+ else
+ {
+ as_bad (_("invalid register list"));
+ break;
+ }
+ }
+ /* The mask is filled in in the opcode table for the
+ benefit of the disassembler. We remove it before
+ applying the actual mask. */
+ ip->insn_opcode &= ~ ((7 << 3) << MIPS16OP_SH_IMM6);
+ ip->insn_opcode |= mask << MIPS16OP_SH_IMM6;
+ }
+ continue;
+
+ case 'e': /* extend code */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number > 0x7ff)
+ {
+ as_warn (_("Invalid value for `%s' (%lu)"),
+ ip->insn_mo->name,
+ (unsigned long) imm_expr.X_add_number);
+ imm_expr.X_add_number &= 0x7ff;
+ }
+ ip->insn_opcode |= imm_expr.X_add_number;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ default:
+ internalError ();
+ }
+ break;
+ }
+
+ /* Args don't match. */
+ if (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes] &&
+ strcmp (insn->name, insn[1].name) == 0)
+ {
+ ++insn;
+ s = argsstart;
+ continue;
+ }
+
+ insn_error = _("illegal operands");
+
+ return;
+ }
+}
+
+/* This structure holds information we know about a mips16 immediate
+ argument type. */
+
+struct mips16_immed_operand
+{
+ /* The type code used in the argument string in the opcode table. */
+ int type;
+ /* The number of bits in the short form of the opcode. */
+ int nbits;
+ /* The number of bits in the extended form of the opcode. */
+ int extbits;
+ /* The amount by which the short form is shifted when it is used;
+ for example, the sw instruction has a shift count of 2. */
+ int shift;
+ /* The amount by which the short form is shifted when it is stored
+ into the instruction code. */
+ int op_shift;
+ /* Non-zero if the short form is unsigned. */
+ int unsp;
+ /* Non-zero if the extended form is unsigned. */
+ int extu;
+ /* Non-zero if the value is PC relative. */
+ int pcrel;
+};
+
+/* The mips16 immediate operand types. */
+
+static const struct mips16_immed_operand mips16_immed_operands[] =
+{
+ { '<', 3, 5, 0, MIPS16OP_SH_RZ, 1, 1, 0 },
+ { '>', 3, 5, 0, MIPS16OP_SH_RX, 1, 1, 0 },
+ { '[', 3, 6, 0, MIPS16OP_SH_RZ, 1, 1, 0 },
+ { ']', 3, 6, 0, MIPS16OP_SH_RX, 1, 1, 0 },
+ { '4', 4, 15, 0, MIPS16OP_SH_IMM4, 0, 0, 0 },
+ { '5', 5, 16, 0, MIPS16OP_SH_IMM5, 1, 0, 0 },
+ { 'H', 5, 16, 1, MIPS16OP_SH_IMM5, 1, 0, 0 },
+ { 'W', 5, 16, 2, MIPS16OP_SH_IMM5, 1, 0, 0 },
+ { 'D', 5, 16, 3, MIPS16OP_SH_IMM5, 1, 0, 0 },
+ { 'j', 5, 16, 0, MIPS16OP_SH_IMM5, 0, 0, 0 },
+ { '8', 8, 16, 0, MIPS16OP_SH_IMM8, 1, 0, 0 },
+ { 'V', 8, 16, 2, MIPS16OP_SH_IMM8, 1, 0, 0 },
+ { 'C', 8, 16, 3, MIPS16OP_SH_IMM8, 1, 0, 0 },
+ { 'U', 8, 16, 0, MIPS16OP_SH_IMM8, 1, 1, 0 },
+ { 'k', 8, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 0 },
+ { 'K', 8, 16, 3, MIPS16OP_SH_IMM8, 0, 0, 0 },
+ { 'p', 8, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 1 },
+ { 'q', 11, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 1 },
+ { 'A', 8, 16, 2, MIPS16OP_SH_IMM8, 1, 0, 1 },
+ { 'B', 5, 16, 3, MIPS16OP_SH_IMM5, 1, 0, 1 },
+ { 'E', 5, 16, 2, MIPS16OP_SH_IMM5, 1, 0, 1 }
+};
+
+#define MIPS16_NUM_IMMED \
+ (sizeof mips16_immed_operands / sizeof mips16_immed_operands[0])
+
+/* Handle a mips16 instruction with an immediate value. This or's the
+ small immediate value into *INSN. It sets *USE_EXTEND to indicate
+ whether an extended value is needed; if one is needed, it sets
+ *EXTEND to the value. The argument type is TYPE. The value is VAL.
+ If SMALL is true, an unextended opcode was explicitly requested.
+ If EXT is true, an extended opcode was explicitly requested. If
+ WARN is true, warn if EXT does not match reality. */
+
+static void
+mips16_immed (file, line, type, val, warn, small, ext, insn, use_extend,
+ extend)
+ char *file;
+ unsigned int line;
+ int type;
+ offsetT val;
+ boolean warn;
+ boolean small;
+ boolean ext;
+ unsigned long *insn;
+ boolean *use_extend;
+ unsigned short *extend;
+{
+ register const struct mips16_immed_operand *op;
+ int mintiny, maxtiny;
+ boolean needext;
+
+ op = mips16_immed_operands;
+ while (op->type != type)
+ {
+ ++op;
+ assert (op < mips16_immed_operands + MIPS16_NUM_IMMED);
+ }
+
+ if (op->unsp)
+ {
+ if (type == '<' || type == '>' || type == '[' || type == ']')
+ {
+ mintiny = 1;
+ maxtiny = 1 << op->nbits;
+ }
+ else
+ {
+ mintiny = 0;
+ maxtiny = (1 << op->nbits) - 1;
+ }
+ }
+ else
+ {
+ mintiny = - (1 << (op->nbits - 1));
+ maxtiny = (1 << (op->nbits - 1)) - 1;
+ }
+
+ /* Branch offsets have an implicit 0 in the lowest bit. */
+ if (type == 'p' || type == 'q')
+ val /= 2;
+
+ if ((val & ((1 << op->shift) - 1)) != 0
+ || val < (mintiny << op->shift)
+ || val > (maxtiny << op->shift))
+ needext = true;
+ else
+ needext = false;
+
+ if (warn && ext && ! needext)
+ as_warn_where (file, line, _("extended operand requested but not required"));
+ if (small && needext)
+ as_bad_where (file, line, _("invalid unextended operand value"));
+
+ if (small || (! ext && ! needext))
+ {
+ int insnval;
+
+ *use_extend = false;
+ insnval = ((val >> op->shift) & ((1 << op->nbits) - 1));
+ insnval <<= op->op_shift;
+ *insn |= insnval;
+ }
+ else
+ {
+ long minext, maxext;
+ int extval;
+
+ if (op->extu)
+ {
+ minext = 0;
+ maxext = (1 << op->extbits) - 1;
+ }
+ else
+ {
+ minext = - (1 << (op->extbits - 1));
+ maxext = (1 << (op->extbits - 1)) - 1;
+ }
+ if (val < minext || val > maxext)
+ as_bad_where (file, line,
+ _("operand value out of range for instruction"));
+
+ *use_extend = true;
+ if (op->extbits == 16)
+ {
+ extval = ((val >> 11) & 0x1f) | (val & 0x7e0);
+ val &= 0x1f;
+ }
+ else if (op->extbits == 15)
+ {
+ extval = ((val >> 11) & 0xf) | (val & 0x7f0);
+ val &= 0xf;
+ }
+ else
+ {
+ extval = ((val & 0x1f) << 6) | (val & 0x20);
+ val = 0;
+ }
+
+ *extend = (unsigned short) extval;
+ *insn |= val;
+ }
+}
+
+#define LP '('
+#define RP ')'
+
+static int
+my_getSmallExpression (ep, str)
+ expressionS *ep;
+ char *str;
+{
+ char *sp;
+ int c = 0;
+
+ if (*str == ' ')
+ str++;
+ if (*str == LP
+ || (*str == '%' &&
+ ((str[1] == 'h' && str[2] == 'i')
+ || (str[1] == 'H' && str[2] == 'I')
+ || (str[1] == 'l' && str[2] == 'o'))
+ && str[3] == LP))
+ {
+ if (*str == LP)
+ c = 0;
+ else
+ {
+ c = str[1];
+ str += 3;
+ }
+
+ /*
+ * A small expression may be followed by a base register.
+ * Scan to the end of this operand, and then back over a possible
+ * base register. Then scan the small expression up to that
+ * point. (Based on code in sparc.c...)
+ */
+ for (sp = str; *sp && *sp != ','; sp++)
+ ;
+ if (sp - 4 >= str && sp[-1] == RP)
+ {
+ if (isdigit (sp[-2]))
+ {
+ for (sp -= 3; sp >= str && isdigit (*sp); sp--)
+ ;
+ if (*sp == '$' && sp > str && sp[-1] == LP)
+ {
+ sp--;
+ goto do_it;
+ }
+ }
+ else if (sp - 5 >= str
+ && sp[-5] == LP
+ && sp[-4] == '$'
+ && ((sp[-3] == 'f' && sp[-2] == 'p')
+ || (sp[-3] == 's' && sp[-2] == 'p')
+ || (sp[-3] == 'g' && sp[-2] == 'p')
+ || (sp[-3] == 'a' && sp[-2] == 't')))
+ {
+ sp -= 5;
+ do_it:
+ if (sp == str)
+ {
+ /* no expression means zero offset */
+ if (c)
+ {
+ /* %xx(reg) is an error */
+ ep->X_op = O_absent;
+ expr_end = str - 3;
+ }
+ else
+ {
+ ep->X_op = O_constant;
+ expr_end = sp;
+ }
+ ep->X_add_symbol = NULL;
+ ep->X_op_symbol = NULL;
+ ep->X_add_number = 0;
+ }
+ else
+ {
+ *sp = '\0';
+ my_getExpression (ep, str);
+ *sp = LP;
+ }
+ return c;
+ }
+ }
+ }
+ my_getExpression (ep, str);
+ return c; /* => %hi or %lo encountered */
+}
+
+static void
+my_getExpression (ep, str)
+ expressionS *ep;
+ char *str;
+{
+ char *save_in;
+
+ save_in = input_line_pointer;
+ input_line_pointer = str;
+ expression (ep);
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+
+ /* If we are in mips16 mode, and this is an expression based on `.',
+ then we bump the value of the symbol by 1 since that is how other
+ text symbols are handled. We don't bother to handle complex
+ expressions, just `.' plus or minus a constant. */
+ if (mips_opts.mips16
+ && ep->X_op == O_symbol
+ && strcmp (S_GET_NAME (ep->X_add_symbol), FAKE_LABEL_NAME) == 0
+ && S_GET_SEGMENT (ep->X_add_symbol) == now_seg
+ && ep->X_add_symbol->sy_frag == frag_now
+ && ep->X_add_symbol->sy_value.X_op == O_constant
+ && ep->X_add_symbol->sy_value.X_add_number == frag_now_fix ())
+ ++ep->X_add_symbol->sy_value.X_add_number;
+}
+
+/* Turn a string in input_line_pointer into a floating point constant
+ of type type, and store the appropriate bytes in *litP. The number
+ of LITTLENUMS emitted is stored in *sizeP . An error message is
+ returned, or NULL on OK. */
+
+char *
+md_atof (type, litP, sizeP)
+ int type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[4];
+ char *t;
+ int i;
+
+ switch (type)
+ {
+ case 'f':
+ prec = 2;
+ break;
+
+ case 'd':
+ prec = 4;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("bad call to md_atof");
+ }
+
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+
+ *sizeP = prec * 2;
+
+ if (! target_big_endian)
+ {
+ for (i = prec - 1; i >= 0; i--)
+ {
+ md_number_to_chars (litP, (valueT) words[i], 2);
+ litP += 2;
+ }
+ }
+ else
+ {
+ for (i = 0; i < prec; i++)
+ {
+ md_number_to_chars (litP, (valueT) words[i], 2);
+ litP += 2;
+ }
+ }
+
+ return NULL;
+}
+
+void
+md_number_to_chars (buf, val, n)
+ char *buf;
+ valueT val;
+ int n;
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+CONST char *md_shortopts = "O::g::G:";
+
+struct option md_longopts[] = {
+#define OPTION_MIPS1 (OPTION_MD_BASE + 1)
+ {"mips0", no_argument, NULL, OPTION_MIPS1},
+ {"mips1", no_argument, NULL, OPTION_MIPS1},
+#define OPTION_MIPS2 (OPTION_MD_BASE + 2)
+ {"mips2", no_argument, NULL, OPTION_MIPS2},
+#define OPTION_MIPS3 (OPTION_MD_BASE + 3)
+ {"mips3", no_argument, NULL, OPTION_MIPS3},
+#define OPTION_MIPS4 (OPTION_MD_BASE + 4)
+ {"mips4", no_argument, NULL, OPTION_MIPS4},
+#define OPTION_MCPU (OPTION_MD_BASE + 5)
+ {"mcpu", required_argument, NULL, OPTION_MCPU},
+#define OPTION_MEMBEDDED_PIC (OPTION_MD_BASE + 6)
+ {"membedded-pic", no_argument, NULL, OPTION_MEMBEDDED_PIC},
+#define OPTION_TRAP (OPTION_MD_BASE + 9)
+ {"trap", no_argument, NULL, OPTION_TRAP},
+ {"no-break", no_argument, NULL, OPTION_TRAP},
+#define OPTION_BREAK (OPTION_MD_BASE + 10)
+ {"break", no_argument, NULL, OPTION_BREAK},
+ {"no-trap", no_argument, NULL, OPTION_BREAK},
+#define OPTION_EB (OPTION_MD_BASE + 11)
+ {"EB", no_argument, NULL, OPTION_EB},
+#define OPTION_EL (OPTION_MD_BASE + 12)
+ {"EL", no_argument, NULL, OPTION_EL},
+#define OPTION_M4650 (OPTION_MD_BASE + 13)
+ {"m4650", no_argument, NULL, OPTION_M4650},
+#define OPTION_NO_M4650 (OPTION_MD_BASE + 14)
+ {"no-m4650", no_argument, NULL, OPTION_NO_M4650},
+#define OPTION_M4010 (OPTION_MD_BASE + 15)
+ {"m4010", no_argument, NULL, OPTION_M4010},
+#define OPTION_NO_M4010 (OPTION_MD_BASE + 16)
+ {"no-m4010", no_argument, NULL, OPTION_NO_M4010},
+#define OPTION_M4100 (OPTION_MD_BASE + 17)
+ {"m4100", no_argument, NULL, OPTION_M4100},
+#define OPTION_NO_M4100 (OPTION_MD_BASE + 18)
+ {"no-m4100", no_argument, NULL, OPTION_NO_M4100},
+#define OPTION_MIPS16 (OPTION_MD_BASE + 22)
+ {"mips16", no_argument, NULL, OPTION_MIPS16},
+#define OPTION_NO_MIPS16 (OPTION_MD_BASE + 23)
+ {"no-mips16", no_argument, NULL, OPTION_NO_MIPS16},
+#define OPTION_M3900 (OPTION_MD_BASE + 26)
+ {"m3900", no_argument, NULL, OPTION_M3900},
+#define OPTION_NO_M3900 (OPTION_MD_BASE + 27)
+ {"no-m3900", no_argument, NULL, OPTION_NO_M3900},
+
+
+#define OPTION_MABI (OPTION_MD_BASE + 38)
+ {"mabi", required_argument, NULL, OPTION_MABI},
+
+#define OPTION_CALL_SHARED (OPTION_MD_BASE + 7)
+#define OPTION_NON_SHARED (OPTION_MD_BASE + 8)
+#define OPTION_XGOT (OPTION_MD_BASE + 19)
+#define OPTION_32 (OPTION_MD_BASE + 20)
+#define OPTION_64 (OPTION_MD_BASE + 21)
+#ifdef OBJ_ELF
+ {"KPIC", no_argument, NULL, OPTION_CALL_SHARED},
+ {"xgot", no_argument, NULL, OPTION_XGOT},
+ {"call_shared", no_argument, NULL, OPTION_CALL_SHARED},
+ {"non_shared", no_argument, NULL, OPTION_NON_SHARED},
+ {"32", no_argument, NULL, OPTION_32},
+ {"64", no_argument, NULL, OPTION_64},
+#endif
+
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ switch (c)
+ {
+ case OPTION_TRAP:
+ mips_trap = 1;
+ break;
+
+ case OPTION_BREAK:
+ mips_trap = 0;
+ break;
+
+ case OPTION_EB:
+ target_big_endian = 1;
+ break;
+
+ case OPTION_EL:
+ target_big_endian = 0;
+ break;
+
+ case 'O':
+ if (arg && arg[1] == '0')
+ mips_optimize = 1;
+ else
+ mips_optimize = 2;
+ break;
+
+ case 'g':
+ if (arg == NULL)
+ mips_debug = 2;
+ else
+ mips_debug = atoi (arg);
+ /* When the MIPS assembler sees -g or -g2, it does not do
+ optimizations which limit full symbolic debugging. We take
+ that to be equivalent to -O0. */
+ if (mips_debug == 2)
+ mips_optimize = 1;
+ break;
+
+ case OPTION_MIPS1:
+ mips_opts.isa = 1;
+ break;
+
+ case OPTION_MIPS2:
+ mips_opts.isa = 2;
+ break;
+
+ case OPTION_MIPS3:
+ mips_opts.isa = 3;
+ break;
+
+ case OPTION_MIPS4:
+ mips_opts.isa = 4;
+ break;
+
+ case OPTION_MCPU:
+ {
+ char *p;
+
+ /* Identify the processor type */
+ p = arg;
+ if (strcmp (p, "default") == 0
+ || strcmp (p, "DEFAULT") == 0)
+ mips_cpu = -1;
+ else
+ {
+ int sv = 0;
+
+ /* We need to cope with the various "vr" prefixes for the 4300
+ processor. */
+ if (*p == 'v' || *p == 'V')
+ {
+ sv = 1;
+ p++;
+ }
+
+ if (*p == 'r' || *p == 'R')
+ p++;
+
+ mips_cpu = -1;
+ switch (*p)
+ {
+ case '1':
+ if (strcmp (p, "10000") == 0
+ || strcmp (p, "10k") == 0
+ || strcmp (p, "10K") == 0)
+ mips_cpu = 10000;
+ break;
+
+ case '2':
+ if (strcmp (p, "2000") == 0
+ || strcmp (p, "2k") == 0
+ || strcmp (p, "2K") == 0)
+ mips_cpu = 2000;
+ break;
+
+ case '3':
+ if (strcmp (p, "3000") == 0
+ || strcmp (p, "3k") == 0
+ || strcmp (p, "3K") == 0)
+ mips_cpu = 3000;
+ else if (strcmp (p, "3900") == 0)
+ mips_cpu = 3900;
+ break;
+
+ case '4':
+ if (strcmp (p, "4000") == 0
+ || strcmp (p, "4k") == 0
+ || strcmp (p, "4K") == 0)
+ mips_cpu = 4000;
+ else if (strcmp (p, "4100") == 0)
+ mips_cpu = 4100;
+ else if (strcmp (p, "4111") == 0)
+ mips_cpu = 4111;
+ else if (strcmp (p, "4300") == 0)
+ mips_cpu = 4300;
+ else if (strcmp (p, "4400") == 0)
+ mips_cpu = 4400;
+ else if (strcmp (p, "4600") == 0)
+ mips_cpu = 4600;
+ else if (strcmp (p, "4650") == 0)
+ mips_cpu = 4650;
+ else if (strcmp (p, "4010") == 0)
+ mips_cpu = 4010;
+ break;
+
+ case '5':
+ if (strcmp (p, "5000") == 0
+ || strcmp (p, "5k") == 0
+ || strcmp (p, "5K") == 0)
+ mips_cpu = 5000;
+ break;
+
+ case '6':
+ if (strcmp (p, "6000") == 0
+ || strcmp (p, "6k") == 0
+ || strcmp (p, "6K") == 0)
+ mips_cpu = 6000;
+ break;
+
+ case '8':
+ if (strcmp (p, "8000") == 0
+ || strcmp (p, "8k") == 0
+ || strcmp (p, "8K") == 0)
+ mips_cpu = 8000;
+ break;
+
+ case 'o':
+ if (strcmp (p, "orion") == 0)
+ mips_cpu = 4600;
+ break;
+ }
+
+ if (sv
+ && (mips_cpu != 4300
+ && mips_cpu != 4100
+ && mips_cpu != 4111
+ && mips_cpu != 5000))
+ {
+ as_bad (_("ignoring invalid leading 'v' in -mcpu=%s switch"), arg);
+ return 0;
+ }
+
+ if (mips_cpu == -1)
+ {
+ as_bad (_("invalid architecture -mcpu=%s"), arg);
+ return 0;
+ }
+ }
+ }
+ break;
+
+ case OPTION_M4650:
+ mips_cpu = 4650;
+ break;
+
+ case OPTION_NO_M4650:
+ break;
+
+ case OPTION_M4010:
+ mips_cpu = 4010;
+ break;
+
+ case OPTION_NO_M4010:
+ break;
+
+ case OPTION_M4100:
+ mips_cpu = 4100;
+ break;
+
+ case OPTION_NO_M4100:
+ break;
+
+
+ case OPTION_M3900:
+ mips_cpu = 3900;
+ break;
+
+ case OPTION_NO_M3900:
+ break;
+
+ case OPTION_MIPS16:
+ mips_opts.mips16 = 1;
+ mips_no_prev_insn (false);
+ break;
+
+ case OPTION_NO_MIPS16:
+ mips_opts.mips16 = 0;
+ mips_no_prev_insn (false);
+ break;
+
+ case OPTION_MEMBEDDED_PIC:
+ mips_pic = EMBEDDED_PIC;
+ if (USE_GLOBAL_POINTER_OPT && g_switch_seen)
+ {
+ as_bad (_("-G may not be used with embedded PIC code"));
+ return 0;
+ }
+ g_switch_value = 0x7fffffff;
+ break;
+
+ /* When generating ELF code, we permit -KPIC and -call_shared to
+ select SVR4_PIC, and -non_shared to select no PIC. This is
+ intended to be compatible with Irix 5. */
+ case OPTION_CALL_SHARED:
+ if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+ {
+ as_bad (_("-call_shared is supported only for ELF format"));
+ return 0;
+ }
+ mips_pic = SVR4_PIC;
+ if (g_switch_seen && g_switch_value != 0)
+ {
+ as_bad (_("-G may not be used with SVR4 PIC code"));
+ return 0;
+ }
+ g_switch_value = 0;
+ break;
+
+ case OPTION_NON_SHARED:
+ if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+ {
+ as_bad (_("-non_shared is supported only for ELF format"));
+ return 0;
+ }
+ mips_pic = NO_PIC;
+ break;
+
+ /* The -xgot option tells the assembler to use 32 offsets when
+ accessing the got in SVR4_PIC mode. It is for Irix
+ compatibility. */
+ case OPTION_XGOT:
+ mips_big_got = 1;
+ break;
+
+ case 'G':
+ if (! USE_GLOBAL_POINTER_OPT)
+ {
+ as_bad (_("-G is not supported for this configuration"));
+ return 0;
+ }
+ else if (mips_pic == SVR4_PIC || mips_pic == EMBEDDED_PIC)
+ {
+ as_bad (_("-G may not be used with SVR4 or embedded PIC code"));
+ return 0;
+ }
+ else
+ g_switch_value = atoi (arg);
+ g_switch_seen = 1;
+ break;
+
+ /* The -32 and -64 options tell the assembler to output the 32
+ bit or the 64 bit MIPS ELF format. */
+ case OPTION_32:
+ mips_64 = 0;
+ break;
+
+ case OPTION_64:
+ {
+ const char **list, **l;
+
+ list = bfd_target_list ();
+ for (l = list; *l != NULL; l++)
+ if (strcmp (*l, "elf64-bigmips") == 0
+ || strcmp (*l, "elf64-littlemips") == 0)
+ break;
+ if (*l == NULL)
+ as_fatal (_("No compiled in support for 64 bit object file format"));
+ free (list);
+ mips_64 = 1;
+ }
+ break;
+
+
+ case OPTION_MABI:
+ if (strcmp (arg,"32") == 0
+ || strcmp (arg,"n32") == 0
+ || strcmp (arg,"64") == 0
+ || strcmp (arg,"o64") == 0
+ || strcmp (arg,"eabi") == 0)
+ mips_abi_string = arg;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static void
+show (stream, string, col_p, first_p)
+ FILE *stream;
+ char *string;
+ int *col_p;
+ int *first_p;
+{
+ if (*first_p)
+ {
+ fprintf (stream, "%24s", "");
+ *col_p = 24;
+ }
+ else
+ {
+ fprintf (stream, ", ");
+ *col_p += 2;
+ }
+
+ if (*col_p + strlen (string) > 72)
+ {
+ fprintf (stream, "\n%24s", "");
+ *col_p = 24;
+ }
+
+ fprintf (stream, "%s", string);
+ *col_p += strlen (string);
+
+ *first_p = 0;
+}
+
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ int column, first;
+
+ fprintf(stream, _("\
+MIPS options:\n\
+-membedded-pic generate embedded position independent code\n\
+-EB generate big endian output\n\
+-EL generate little endian output\n\
+-g, -g2 do not remove uneeded NOPs or swap branches\n\
+-G NUM allow referencing objects up to NUM bytes\n\
+ implicitly with the gp register [default 8]\n"));
+ fprintf(stream, _("\
+-mips1 generate MIPS ISA I instructions\n\
+-mips2 generate MIPS ISA II instructions\n\
+-mips3 generate MIPS ISA III instructions\n\
+-mips4 generate MIPS ISA IV instructions\n\
+-mcpu=CPU generate code for CPU, where CPU is one of:\n"));
+
+ first = 1;
+
+ show (stream, "2000", &column, &first);
+ show (stream, "3000", &column, &first);
+ show (stream, "3900", &column, &first);
+ show (stream, "4000", &column, &first);
+ show (stream, "4010", &column, &first);
+ show (stream, "4100", &column, &first);
+ show (stream, "4111", &column, &first);
+ show (stream, "4300", &column, &first);
+ show (stream, "4400", &column, &first);
+ show (stream, "4600", &column, &first);
+ show (stream, "4650", &column, &first);
+ show (stream, "5000", &column, &first);
+ show (stream, "6000", &column, &first);
+ show (stream, "8000", &column, &first);
+ show (stream, "10000", &column, &first);
+ fputc ('\n', stream);
+
+ fprintf (stream, _("\
+-mCPU equivalent to -mcpu=CPU.\n\
+-no-mCPU don't generate code specific to CPU.\n\
+ For -mCPU and -no-mCPU, CPU must be one of:\n"));
+
+ first = 1;
+
+ show (stream, "3900", &column, &first);
+ show (stream, "4010", &column, &first);
+ show (stream, "4100", &column, &first);
+ show (stream, "4650", &column, &first);
+ fputc ('\n', stream);
+
+ fprintf(stream, _("\
+-mips16 generate mips16 instructions\n\
+-no-mips16 do not generate mips16 instructions\n"));
+ fprintf(stream, _("\
+-O0 remove unneeded NOPs, do not swap branches\n\
+-O remove unneeded NOPs and swap branches\n\
+--trap, --no-break trap exception on div by 0 and mult overflow\n\
+--break, --no-trap break exception on div by 0 and mult overflow\n"));
+#ifdef OBJ_ELF
+ fprintf(stream, _("\
+-KPIC, -call_shared generate SVR4 position independent code\n\
+-non_shared do not generate position independent code\n\
+-xgot assume a 32 bit GOT\n\
+-32 create 32 bit object file (default)\n\
+-64 create 64 bit object file\n"));
+#endif
+}
+
+void
+mips_init_after_args ()
+{
+ /* initialize opcodes */
+ bfd_mips_num_opcodes = bfd_mips_num_builtin_opcodes;
+ mips_opcodes = (struct mips_opcode*) mips_builtin_opcodes;
+}
+
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ if (OUTPUT_FLAVOR != bfd_target_aout_flavour
+ && fixP->fx_addsy != (symbolS *) NULL
+ && ! S_IS_DEFINED (fixP->fx_addsy))
+ {
+ /* This makes a branch to an undefined symbol be a branch to the
+ current location. */
+ return 4;
+ }
+
+ /* return the address of the delay slot */
+ return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+/* This is called by emit_expr via TC_CONS_FIX_NEW when creating a
+ reloc for a cons. We could use the definition there, except that
+ we want to handle 64 bit relocs specially. */
+
+void
+cons_fix_new_mips (frag, where, nbytes, exp)
+ fragS *frag;
+ int where;
+ unsigned int nbytes;
+ expressionS *exp;
+{
+#ifndef OBJ_ELF
+ /* If we are assembling in 32 bit mode, turn an 8 byte reloc into a
+ 4 byte reloc. */
+ if (nbytes == 8 && ! mips_64)
+ {
+ if (target_big_endian)
+ where += 4;
+ nbytes = 4;
+ }
+#endif
+
+ if (nbytes != 2 && nbytes != 4 && nbytes != 8)
+ as_bad (_("Unsupported reloc size %d"), nbytes);
+
+ fix_new_exp (frag_now, where, (int) nbytes, exp, 0,
+ (nbytes == 2
+ ? BFD_RELOC_16
+ : (nbytes == 4 ? BFD_RELOC_32 : BFD_RELOC_64)));
+}
+
+/* This is called before the symbol table is processed. In order to
+ work with gcc when using mips-tfile, we must keep all local labels.
+ However, in other cases, we want to discard them. If we were
+ called with -g, but we didn't see any debugging information, it may
+ mean that gcc is smuggling debugging information through to
+ mips-tfile, in which case we must generate all local labels. */
+
+void
+mips_frob_file_before_adjust ()
+{
+#ifndef NO_ECOFF_DEBUGGING
+ if (ECOFF_DEBUGGING
+ && mips_debug != 0
+ && ! ecoff_debugging_seen)
+ flag_keep_locals = 1;
+#endif
+}
+
+/* Sort any unmatched HI16_S relocs so that they immediately precede
+ the corresponding LO reloc. This is called before md_apply_fix and
+ tc_gen_reloc. Unmatched HI16_S relocs can only be generated by
+ explicit use of the %hi modifier. */
+
+void
+mips_frob_file ()
+{
+ struct mips_hi_fixup *l;
+
+ for (l = mips_hi_fixup_list; l != NULL; l = l->next)
+ {
+ segment_info_type *seginfo;
+ int pass;
+
+ assert (l->fixp->fx_r_type == BFD_RELOC_HI16_S);
+
+ /* Check quickly whether the next fixup happens to be a matching
+ %lo. */
+ if (l->fixp->fx_next != NULL
+ && l->fixp->fx_next->fx_r_type == BFD_RELOC_LO16
+ && l->fixp->fx_addsy == l->fixp->fx_next->fx_addsy
+ && l->fixp->fx_offset == l->fixp->fx_next->fx_offset)
+ continue;
+
+ /* Look through the fixups for this segment for a matching %lo.
+ When we find one, move the %hi just in front of it. We do
+ this in two passes. In the first pass, we try to find a
+ unique %lo. In the second pass, we permit multiple %hi
+ relocs for a single %lo (this is a GNU extension). */
+ seginfo = seg_info (l->seg);
+ for (pass = 0; pass < 2; pass++)
+ {
+ fixS *f, *prev;
+
+ prev = NULL;
+ for (f = seginfo->fix_root; f != NULL; f = f->fx_next)
+ {
+ /* Check whether this is a %lo fixup which matches l->fixp. */
+ if (f->fx_r_type == BFD_RELOC_LO16
+ && f->fx_addsy == l->fixp->fx_addsy
+ && f->fx_offset == l->fixp->fx_offset
+ && (pass == 1
+ || prev == NULL
+ || prev->fx_r_type != BFD_RELOC_HI16_S
+ || prev->fx_addsy != f->fx_addsy
+ || prev->fx_offset != f->fx_offset))
+ {
+ fixS **pf;
+
+ /* Move l->fixp before f. */
+ for (pf = &seginfo->fix_root;
+ *pf != l->fixp;
+ pf = &(*pf)->fx_next)
+ assert (*pf != NULL);
+
+ *pf = l->fixp->fx_next;
+
+ l->fixp->fx_next = f;
+ if (prev == NULL)
+ seginfo->fix_root = l->fixp;
+ else
+ prev->fx_next = l->fixp;
+
+ break;
+ }
+
+ prev = f;
+ }
+
+ if (f != NULL)
+ break;
+
+#if 0 /* GCC code motion plus incomplete dead code elimination
+ can leave a %hi without a %lo. */
+ if (pass == 1)
+ as_warn_where (l->fixp->fx_file, l->fixp->fx_line,
+ _("Unmatched %%hi reloc"));
+#endif
+ }
+ }
+}
+
+/* When generating embedded PIC code we need to use a special
+ relocation to represent the difference of two symbols in the .text
+ section (switch tables use a difference of this sort). See
+ include/coff/mips.h for details. This macro checks whether this
+ fixup requires the special reloc. */
+#define SWITCH_TABLE(fixp) \
+ ((fixp)->fx_r_type == BFD_RELOC_32 \
+ && (fixp)->fx_addsy != NULL \
+ && (fixp)->fx_subsy != NULL \
+ && S_GET_SEGMENT ((fixp)->fx_addsy) == text_section \
+ && S_GET_SEGMENT ((fixp)->fx_subsy) == text_section)
+
+/* When generating embedded PIC code we must keep all PC relative
+ relocations, in case the linker has to relax a call. We also need
+ to keep relocations for switch table entries. */
+
+/*ARGSUSED*/
+int
+mips_force_relocation (fixp)
+ fixS *fixp;
+{
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 1;
+
+ return (mips_pic == EMBEDDED_PIC
+ && (fixp->fx_pcrel
+ || SWITCH_TABLE (fixp)
+ || fixp->fx_r_type == BFD_RELOC_PCREL_HI16_S
+ || fixp->fx_r_type == BFD_RELOC_PCREL_LO16));
+}
+
+/* Apply a fixup to the object file. */
+
+int
+md_apply_fix (fixP, valueP)
+ fixS *fixP;
+ valueT *valueP;
+{
+ unsigned char *buf;
+ long insn, value;
+
+ assert (fixP->fx_size == 4
+ || fixP->fx_r_type == BFD_RELOC_16
+ || fixP->fx_r_type == BFD_RELOC_64
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY);
+
+ value = *valueP;
+
+ /* If we aren't adjusting this fixup to be against the section
+ symbol, we need to adjust the value. */
+#ifdef OBJ_ELF
+ if (fixP->fx_addsy != NULL && OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ if (S_GET_OTHER (fixP->fx_addsy) == STO_MIPS16
+ || S_IS_WEAK (fixP->fx_addsy)
+ || (fixP->fx_addsy->sy_used_in_reloc
+ && (bfd_get_section_flags (stdoutput,
+ S_GET_SEGMENT (fixP->fx_addsy))
+ & SEC_LINK_ONCE != 0)
+ || !strncmp (segment_name (S_GET_SEGMENT (fixP->fx_addsy)),
+ ".gnu.linkonce",
+ sizeof (".gnu.linkonce") - 1)))
+
+ {
+ value -= S_GET_VALUE (fixP->fx_addsy);
+ if (value != 0 && ! fixP->fx_pcrel)
+ {
+ /* In this case, the bfd_install_relocation routine will
+ incorrectly add the symbol value back in. We just want
+ the addend to appear in the object file. */
+ value -= S_GET_VALUE (fixP->fx_addsy);
+ }
+ }
+#endif
+
+
+ fixP->fx_addnumber = value; /* Remember value for tc_gen_reloc */
+
+ if (fixP->fx_addsy == NULL && ! fixP->fx_pcrel)
+ fixP->fx_done = 1;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_MIPS_JMP:
+ case BFD_RELOC_HI16:
+ case BFD_RELOC_HI16_S:
+ case BFD_RELOC_MIPS_GPREL:
+ case BFD_RELOC_MIPS_LITERAL:
+ case BFD_RELOC_MIPS_CALL16:
+ case BFD_RELOC_MIPS_GOT16:
+ case BFD_RELOC_MIPS_GPREL32:
+ case BFD_RELOC_MIPS_GOT_HI16:
+ case BFD_RELOC_MIPS_GOT_LO16:
+ case BFD_RELOC_MIPS_CALL_HI16:
+ case BFD_RELOC_MIPS_CALL_LO16:
+ case BFD_RELOC_MIPS16_GPREL:
+ if (fixP->fx_pcrel)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Invalid PC relative reloc"));
+ /* Nothing needed to do. The value comes from the reloc entry */
+ break;
+
+ case BFD_RELOC_MIPS16_JMP:
+ /* We currently always generate a reloc against a symbol, which
+ means that we don't want an addend even if the symbol is
+ defined. */
+ fixP->fx_addnumber = 0;
+ break;
+
+ case BFD_RELOC_PCREL_HI16_S:
+ /* The addend for this is tricky if it is internal, so we just
+ do everything here rather than in bfd_install_relocation. */
+ if ((fixP->fx_addsy->bsym->flags & BSF_SECTION_SYM) == 0)
+ {
+ /* For an external symbol adjust by the address to make it
+ pcrel_offset. We use the address of the RELLO reloc
+ which follows this one. */
+ value += (fixP->fx_next->fx_frag->fr_address
+ + fixP->fx_next->fx_where);
+ }
+ if (value & 0x8000)
+ value += 0x10000;
+ value >>= 16;
+ buf = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
+ if (target_big_endian)
+ buf += 2;
+ md_number_to_chars (buf, value, 2);
+ break;
+
+ case BFD_RELOC_PCREL_LO16:
+ /* The addend for this is tricky if it is internal, so we just
+ do everything here rather than in bfd_install_relocation. */
+ if ((fixP->fx_addsy->bsym->flags & BSF_SECTION_SYM) == 0)
+ value += fixP->fx_frag->fr_address + fixP->fx_where;
+ buf = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
+ if (target_big_endian)
+ buf += 2;
+ md_number_to_chars (buf, value, 2);
+ break;
+
+ case BFD_RELOC_64:
+ /* This is handled like BFD_RELOC_32, but we output a sign
+ extended value if we are only 32 bits. */
+ if (fixP->fx_done
+ || (mips_pic == EMBEDDED_PIC && SWITCH_TABLE (fixP)))
+ {
+ if (8 <= sizeof (valueT))
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ value, 8);
+ else
+ {
+ long w1, w2;
+ long hiv;
+
+ w1 = w2 = fixP->fx_where;
+ if (target_big_endian)
+ w1 += 4;
+ else
+ w2 += 4;
+ md_number_to_chars (fixP->fx_frag->fr_literal + w1, value, 4);
+ if ((value & 0x80000000) != 0)
+ hiv = 0xffffffff;
+ else
+ hiv = 0;
+ md_number_to_chars (fixP->fx_frag->fr_literal + w2, hiv, 4);
+ }
+ }
+ break;
+
+ case BFD_RELOC_32:
+ /* If we are deleting this reloc entry, we must fill in the
+ value now. This can happen if we have a .word which is not
+ resolved when it appears but is later defined. We also need
+ to fill in the value if this is an embedded PIC switch table
+ entry. */
+ if (fixP->fx_done
+ || (mips_pic == EMBEDDED_PIC && SWITCH_TABLE (fixP)))
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ value, 4);
+ break;
+
+ case BFD_RELOC_16:
+ /* If we are deleting this reloc entry, we must fill in the
+ value now. */
+ assert (fixP->fx_size == 2);
+ if (fixP->fx_done)
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ value, 2);
+ break;
+
+ case BFD_RELOC_LO16:
+ /* When handling an embedded PIC switch statement, we can wind
+ up deleting a LO16 reloc. See the 'o' case in mips_ip. */
+ if (fixP->fx_done)
+ {
+ if (value < -0x8000 || value > 0x7fff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ buf = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
+ if (target_big_endian)
+ buf += 2;
+ md_number_to_chars (buf, value, 2);
+ }
+ break;
+
+ case BFD_RELOC_16_PCREL_S2:
+ /*
+ * We need to save the bits in the instruction since fixup_segment()
+ * might be deleting the relocation entry (i.e., a branch within
+ * the current segment).
+ */
+ if ((value & 0x3) != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Branch to odd address (%lx)"), value);
+ value >>= 2;
+
+ /* update old instruction data */
+ buf = (unsigned char *) (fixP->fx_where + fixP->fx_frag->fr_literal);
+ if (target_big_endian)
+ insn = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ else
+ insn = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+
+ if (value >= -0x8000 && value < 0x8000)
+ insn |= value & 0xffff;
+ else
+ {
+ /* The branch offset is too large. If this is an
+ unconditional branch, and we are not generating PIC code,
+ we can convert it to an absolute jump instruction. */
+ if (mips_pic == NO_PIC
+ && fixP->fx_done
+ && fixP->fx_frag->fr_address >= text_section->vma
+ && (fixP->fx_frag->fr_address
+ < text_section->vma + text_section->_raw_size)
+ && ((insn & 0xffff0000) == 0x10000000 /* beq $0,$0 */
+ || (insn & 0xffff0000) == 0x04010000 /* bgez $0 */
+ || (insn & 0xffff0000) == 0x04110000)) /* bgezal $0 */
+ {
+ if ((insn & 0xffff0000) == 0x04110000) /* bgezal $0 */
+ insn = 0x0c000000; /* jal */
+ else
+ insn = 0x08000000; /* j */
+ fixP->fx_r_type = BFD_RELOC_MIPS_JMP;
+ fixP->fx_done = 0;
+ fixP->fx_addsy = section_symbol (text_section);
+ fixP->fx_addnumber = (value << 2) + md_pcrel_from (fixP);
+ }
+ else
+ {
+ /* FIXME. It would be possible in principle to handle
+ conditional branches which overflow. They could be
+ transformed into a branch around a jump. This would
+ require setting up variant frags for each different
+ branch type. The native MIPS assembler attempts to
+ handle these cases, but it appears to do it
+ incorrectly. */
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Branch out of range"));
+ }
+ }
+
+ md_number_to_chars ((char *) buf, (valueT) insn, 4);
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ fixP->fx_done = 0;
+ if (fixP->fx_addsy
+ && !S_IS_DEFINED (fixP->fx_addsy)
+ && !S_IS_WEAK (fixP->fx_addsy))
+ S_SET_WEAK (fixP->fx_addsy);
+ break;
+
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ break;
+
+ default:
+ internalError ();
+ }
+
+ return 1;
+}
+
+#if 0
+void
+printInsn (oc)
+ unsigned long oc;
+{
+ const struct mips_opcode *p;
+ int treg, sreg, dreg, shamt;
+ short imm;
+ const char *args;
+ int i;
+
+ for (i = 0; i < NUMOPCODES; ++i)
+ {
+ p = &mips_opcodes[i];
+ if (((oc & p->mask) == p->match) && (p->pinfo != INSN_MACRO))
+ {
+ printf ("%08lx %s\t", oc, p->name);
+ treg = (oc >> 16) & 0x1f;
+ sreg = (oc >> 21) & 0x1f;
+ dreg = (oc >> 11) & 0x1f;
+ shamt = (oc >> 6) & 0x1f;
+ imm = oc;
+ for (args = p->args;; ++args)
+ {
+ switch (*args)
+ {
+ case '\0':
+ printf ("\n");
+ break;
+
+ case ',':
+ case '(':
+ case ')':
+ printf ("%c", *args);
+ continue;
+
+ case 'r':
+ assert (treg == sreg);
+ printf ("$%d,$%d", treg, sreg);
+ continue;
+
+ case 'd':
+ case 'G':
+ printf ("$%d", dreg);
+ continue;
+
+ case 't':
+ case 'E':
+ printf ("$%d", treg);
+ continue;
+
+ case 'k':
+ printf ("0x%x", treg);
+ continue;
+
+ case 'b':
+ case 's':
+ printf ("$%d", sreg);
+ continue;
+
+ case 'a':
+ printf ("0x%08lx", oc & 0x1ffffff);
+ continue;
+
+ case 'i':
+ case 'j':
+ case 'o':
+ case 'u':
+ printf ("%d", imm);
+ continue;
+
+ case '<':
+ case '>':
+ printf ("$%d", shamt);
+ continue;
+
+ default:
+ internalError ();
+ }
+ break;
+ }
+ return;
+ }
+ }
+ printf (_("%08lx UNDEFINED\n"), oc);
+}
+#endif
+
+static symbolS *
+get_symbol ()
+{
+ int c;
+ char *name;
+ symbolS *p;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = (symbolS *) symbol_find_or_make (name);
+ *input_line_pointer = c;
+ return p;
+}
+
+/* Align the current frag to a given power of two. The MIPS assembler
+ also automatically adjusts any preceding label. */
+
+static void
+mips_align (to, fill, label)
+ int to;
+ int fill;
+ symbolS *label;
+{
+ mips_emit_delays (false);
+ frag_align (to, fill, 0);
+ record_alignment (now_seg, to);
+ if (label != NULL)
+ {
+ assert (S_GET_SEGMENT (label) == now_seg);
+ label->sy_frag = frag_now;
+ S_SET_VALUE (label, (valueT) frag_now_fix ());
+ }
+}
+
+/* Align to a given power of two. .align 0 turns off the automatic
+ alignment used by the data creating pseudo-ops. */
+
+static void
+s_align (x)
+ int x;
+{
+ register int temp;
+ register long temp_fill;
+ long max_alignment = 15;
+
+ /*
+
+ o Note that the assembler pulls down any immediately preceeding label
+ to the aligned address.
+ o It's not documented but auto alignment is reinstated by
+ a .align pseudo instruction.
+ o Note also that after auto alignment is turned off the mips assembler
+ issues an error on attempt to assemble an improperly aligned data item.
+ We don't.
+
+ */
+
+ temp = get_absolute_expression ();
+ if (temp > max_alignment)
+ as_bad (_("Alignment too large: %d. assumed."), temp = max_alignment);
+ else if (temp < 0)
+ {
+ as_warn (_("Alignment negative: 0 assumed."));
+ temp = 0;
+ }
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ temp_fill = get_absolute_expression ();
+ }
+ else
+ temp_fill = 0;
+ if (temp)
+ {
+ auto_align = 1;
+ mips_align (temp, (int) temp_fill,
+ insn_labels != NULL ? insn_labels->label : NULL);
+ }
+ else
+ {
+ auto_align = 0;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+void
+mips_flush_pending_output ()
+{
+ mips_emit_delays (false);
+ mips_clear_insn_labels ();
+}
+
+static void
+s_change_sec (sec)
+ int sec;
+{
+ segT seg;
+
+ /* When generating embedded PIC code, we only use the .text, .lit8,
+ .sdata and .sbss sections. We change the .data and .rdata
+ pseudo-ops to use .sdata. */
+ if (mips_pic == EMBEDDED_PIC
+ && (sec == 'd' || sec == 'r'))
+ sec = 's';
+
+#ifdef OBJ_ELF
+ /* The ELF backend needs to know that we are changing sections, so
+ that .previous works correctly. We could do something like check
+ for a obj_section_change_hook macro, but that might be confusing
+ as it would not be appropriate to use it in the section changing
+ functions in read.c, since obj-elf.c intercepts those. FIXME:
+ This should be cleaner, somehow. */
+ obj_elf_section_change_hook ();
+#endif
+
+ mips_emit_delays (false);
+ switch (sec)
+ {
+ case 't':
+ s_text (0);
+ break;
+ case 'd':
+ s_data (0);
+ break;
+ case 'b':
+ subseg_set (bss_section, (subsegT) get_absolute_expression ());
+ demand_empty_rest_of_line ();
+ break;
+
+ case 'r':
+ if (USE_GLOBAL_POINTER_OPT)
+ {
+ seg = subseg_new (RDATA_SECTION_NAME,
+ (subsegT) get_absolute_expression ());
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ {
+ bfd_set_section_flags (stdoutput, seg,
+ (SEC_ALLOC
+ | SEC_LOAD
+ | SEC_READONLY
+ | SEC_RELOC
+ | SEC_DATA));
+ if (strcmp (TARGET_OS, "elf") != 0)
+ bfd_set_section_alignment (stdoutput, seg, 4);
+ }
+ demand_empty_rest_of_line ();
+ }
+ else
+ {
+ as_bad (_("No read only data section in this object file format"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ break;
+
+ case 's':
+ if (USE_GLOBAL_POINTER_OPT)
+ {
+ seg = subseg_new (".sdata", (subsegT) get_absolute_expression ());
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ {
+ bfd_set_section_flags (stdoutput, seg,
+ SEC_ALLOC | SEC_LOAD | SEC_RELOC
+ | SEC_DATA);
+ if (strcmp (TARGET_OS, "elf") != 0)
+ bfd_set_section_alignment (stdoutput, seg, 4);
+ }
+ demand_empty_rest_of_line ();
+ break;
+ }
+ else
+ {
+ as_bad (_("Global pointers not supported; recompile -G 0"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ }
+
+ auto_align = 1;
+}
+
+void
+mips_enable_auto_align ()
+{
+ auto_align = 1;
+}
+
+static void
+s_cons (log_size)
+ int log_size;
+{
+ symbolS *label;
+
+ label = insn_labels != NULL ? insn_labels->label : NULL;
+ mips_emit_delays (false);
+ if (log_size > 0 && auto_align)
+ mips_align (log_size, 0, label);
+ mips_clear_insn_labels ();
+ cons (1 << log_size);
+}
+
+static void
+s_float_cons (type)
+ int type;
+{
+ symbolS *label;
+
+ label = insn_labels != NULL ? insn_labels->label : NULL;
+
+ mips_emit_delays (false);
+
+ if (auto_align)
+ if (type == 'd')
+ mips_align (3, 0, label);
+ else
+ mips_align (2, 0, label);
+
+ mips_clear_insn_labels ();
+
+ float_cons (type);
+}
+
+/* Handle .globl. We need to override it because on Irix 5 you are
+ permitted to say
+ .globl foo .text
+ where foo is an undefined symbol, to mean that foo should be
+ considered to be the address of a function. */
+
+static void
+s_mips_globl (x)
+ int x;
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+ flagword flag;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+
+ /* On Irix 5, every global symbol that is not explicitly labelled as
+ being a function is apparently labelled as being an object. */
+ flag = BSF_OBJECT;
+
+ if (! is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ char *secname;
+ asection *sec;
+
+ secname = input_line_pointer;
+ c = get_symbol_end ();
+ sec = bfd_get_section_by_name (stdoutput, secname);
+ if (sec == NULL)
+ as_bad (_("%s: no such section"), secname);
+ *input_line_pointer = c;
+
+ if (sec != NULL && (sec->flags & SEC_CODE) != 0)
+ flag = BSF_FUNCTION;
+ }
+
+ symbolP->bsym->flags |= flag;
+
+ S_SET_EXTERNAL (symbolP);
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_option (x)
+ int x;
+{
+ char *opt;
+ char c;
+
+ opt = input_line_pointer;
+ c = get_symbol_end ();
+
+ if (*opt == 'O')
+ {
+ /* FIXME: What does this mean? */
+ }
+ else if (strncmp (opt, "pic", 3) == 0)
+ {
+ int i;
+
+ i = atoi (opt + 3);
+ if (i == 0)
+ mips_pic = NO_PIC;
+ else if (i == 2)
+ mips_pic = SVR4_PIC;
+ else
+ as_bad (_(".option pic%d not supported"), i);
+
+ if (USE_GLOBAL_POINTER_OPT && mips_pic == SVR4_PIC)
+ {
+ if (g_switch_seen && g_switch_value != 0)
+ as_warn (_("-G may not be used with SVR4 PIC code"));
+ g_switch_value = 0;
+ bfd_set_gp_size (stdoutput, 0);
+ }
+ }
+ else
+ as_warn (_("Unrecognized option \"%s\""), opt);
+
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+}
+
+/* This structure is used to hold a stack of .set values. */
+
+struct mips_option_stack
+{
+ struct mips_option_stack *next;
+ struct mips_set_options options;
+};
+
+static struct mips_option_stack *mips_opts_stack;
+
+/* Handle the .set pseudo-op. */
+
+static void
+s_mipsset (x)
+ int x;
+{
+ char *name = input_line_pointer, ch;
+
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ input_line_pointer++;
+ ch = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ if (strcmp (name, "reorder") == 0)
+ {
+ if (mips_opts.noreorder && prev_nop_frag != NULL)
+ {
+ /* If we still have pending nops, we can discard them. The
+ usual nop handling will insert any that are still
+ needed. */
+ prev_nop_frag->fr_fix -= (prev_nop_frag_holds
+ * (mips_opts.mips16 ? 2 : 4));
+ prev_nop_frag = NULL;
+ }
+ mips_opts.noreorder = 0;
+ }
+ else if (strcmp (name, "noreorder") == 0)
+ {
+ mips_emit_delays (true);
+ mips_opts.noreorder = 1;
+ mips_any_noreorder = 1;
+ }
+ else if (strcmp (name, "at") == 0)
+ {
+ mips_opts.noat = 0;
+ }
+ else if (strcmp (name, "noat") == 0)
+ {
+ mips_opts.noat = 1;
+ }
+ else if (strcmp (name, "macro") == 0)
+ {
+ mips_opts.warn_about_macros = 0;
+ }
+ else if (strcmp (name, "nomacro") == 0)
+ {
+ if (mips_opts.noreorder == 0)
+ as_bad (_("`noreorder' must be set before `nomacro'"));
+ mips_opts.warn_about_macros = 1;
+ }
+ else if (strcmp (name, "move") == 0 || strcmp (name, "novolatile") == 0)
+ {
+ mips_opts.nomove = 0;
+ }
+ else if (strcmp (name, "nomove") == 0 || strcmp (name, "volatile") == 0)
+ {
+ mips_opts.nomove = 1;
+ }
+ else if (strcmp (name, "bopt") == 0)
+ {
+ mips_opts.nobopt = 0;
+ }
+ else if (strcmp (name, "nobopt") == 0)
+ {
+ mips_opts.nobopt = 1;
+ }
+ else if (strcmp (name, "mips16") == 0
+ || strcmp (name, "MIPS-16") == 0)
+ mips_opts.mips16 = 1;
+ else if (strcmp (name, "nomips16") == 0
+ || strcmp (name, "noMIPS-16") == 0)
+ mips_opts.mips16 = 0;
+ else if (strncmp (name, "mips", 4) == 0)
+ {
+ int isa;
+
+ /* Permit the user to change the ISA on the fly. Needless to
+ say, misuse can cause serious problems. */
+ isa = atoi (name + 4);
+ if (isa == 0)
+ mips_opts.isa = file_mips_isa;
+ else if (isa < 1 || isa > 4)
+ as_bad (_("unknown ISA level"));
+ else
+ mips_opts.isa = isa;
+ }
+ else if (strcmp (name, "autoextend") == 0)
+ mips_opts.noautoextend = 0;
+ else if (strcmp (name, "noautoextend") == 0)
+ mips_opts.noautoextend = 1;
+ else if (strcmp (name, "push") == 0)
+ {
+ struct mips_option_stack *s;
+
+ s = (struct mips_option_stack *) xmalloc (sizeof *s);
+ s->next = mips_opts_stack;
+ s->options = mips_opts;
+ mips_opts_stack = s;
+ }
+ else if (strcmp (name, "pop") == 0)
+ {
+ struct mips_option_stack *s;
+
+ s = mips_opts_stack;
+ if (s == NULL)
+ as_bad (_(".set pop with no .set push"));
+ else
+ {
+ /* If we're changing the reorder mode we need to handle
+ delay slots correctly. */
+ if (s->options.noreorder && ! mips_opts.noreorder)
+ mips_emit_delays (true);
+ else if (! s->options.noreorder && mips_opts.noreorder)
+ {
+ if (prev_nop_frag != NULL)
+ {
+ prev_nop_frag->fr_fix -= (prev_nop_frag_holds
+ * (mips_opts.mips16 ? 2 : 4));
+ prev_nop_frag = NULL;
+ }
+ }
+
+ mips_opts = s->options;
+ mips_opts_stack = s->next;
+ free (s);
+ }
+ }
+ else
+ {
+ as_warn (_("Tried to set unrecognized symbol: %s\n"), name);
+ }
+ *input_line_pointer = ch;
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .abicalls pseudo-op. I believe this is equivalent to
+ .option pic2. It means to generate SVR4 PIC calls. */
+
+static void
+s_abicalls (ignore)
+ int ignore;
+{
+ mips_pic = SVR4_PIC;
+ if (USE_GLOBAL_POINTER_OPT)
+ {
+ if (g_switch_seen && g_switch_value != 0)
+ as_warn (_("-G may not be used with SVR4 PIC code"));
+ g_switch_value = 0;
+ }
+ bfd_set_gp_size (stdoutput, 0);
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .cpload pseudo-op. This is used when generating SVR4
+ PIC code. It sets the $gp register for the function based on the
+ function address, which is in the register named in the argument.
+ This uses a relocation against _gp_disp, which is handled specially
+ by the linker. The result is:
+ lui $gp,%hi(_gp_disp)
+ addiu $gp,$gp,%lo(_gp_disp)
+ addu $gp,$gp,.cpload argument
+ The .cpload argument is normally $25 == $t9. */
+
+static void
+s_cpload (ignore)
+ int ignore;
+{
+ expressionS ex;
+ int icnt = 0;
+
+ /* If we are not generating SVR4 PIC code, .cpload is ignored. */
+ if (mips_pic != SVR4_PIC)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ /* .cpload should be a in .set noreorder section. */
+ if (mips_opts.noreorder == 0)
+ as_warn (_(".cpload not in noreorder section"));
+
+ ex.X_op = O_symbol;
+ ex.X_add_symbol = symbol_find_or_make ("_gp_disp");
+ ex.X_op_symbol = NULL;
+ ex.X_add_number = 0;
+
+ /* In ELF, this symbol is implicitly an STT_OBJECT symbol. */
+ ex.X_add_symbol->bsym->flags |= BSF_OBJECT;
+
+ macro_build_lui ((char *) NULL, &icnt, &ex, GP);
+ macro_build ((char *) NULL, &icnt, &ex, "addiu", "t,r,j", GP, GP,
+ (int) BFD_RELOC_LO16);
+
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "addu", "d,v,t",
+ GP, GP, tc_get_register (0));
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .cprestore pseudo-op. This stores $gp into a given
+ offset from $sp. The offset is remembered, and after making a PIC
+ call $gp is restored from that location. */
+
+static void
+s_cprestore (ignore)
+ int ignore;
+{
+ expressionS ex;
+ int icnt = 0;
+
+ /* If we are not generating SVR4 PIC code, .cprestore is ignored. */
+ if (mips_pic != SVR4_PIC)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ mips_cprestore_offset = get_absolute_expression ();
+
+ ex.X_op = O_constant;
+ ex.X_add_symbol = NULL;
+ ex.X_op_symbol = NULL;
+ ex.X_add_number = mips_cprestore_offset;
+
+ macro_build ((char *) NULL, &icnt, &ex,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "sw" : "sd"),
+ "t,o(b)", GP, (int) BFD_RELOC_LO16, SP);
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .gpword pseudo-op. This is used when generating PIC
+ code. It generates a 32 bit GP relative reloc. */
+
+static void
+s_gpword (ignore)
+ int ignore;
+{
+ symbolS *label;
+ expressionS ex;
+ char *p;
+
+ /* When not generating PIC code, this is treated as .word. */
+ if (mips_pic != SVR4_PIC)
+ {
+ s_cons (2);
+ return;
+ }
+
+ label = insn_labels != NULL ? insn_labels->label : NULL;
+ mips_emit_delays (true);
+ if (auto_align)
+ mips_align (2, 0, label);
+ mips_clear_insn_labels ();
+
+ expression (&ex);
+
+ if (ex.X_op != O_symbol || ex.X_add_number != 0)
+ {
+ as_bad (_("Unsupported use of .gpword"));
+ ignore_rest_of_line ();
+ }
+
+ p = frag_more (4);
+ md_number_to_chars (p, (valueT) 0, 4);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, 0,
+ BFD_RELOC_MIPS_GPREL32);
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .cpadd pseudo-op. This is used when dealing with switch
+ tables in SVR4 PIC code. */
+
+static void
+s_cpadd (ignore)
+ int ignore;
+{
+ int icnt = 0;
+ int reg;
+
+ /* This is ignored when not generating SVR4 PIC code. */
+ if (mips_pic != SVR4_PIC)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ /* Add $gp to the register named as an argument. */
+ reg = tc_get_register (0);
+ macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+ ((bfd_arch_bits_per_address (stdoutput) == 32
+ || mips_opts.isa < 3)
+ ? "addu" : "daddu"),
+ "d,v,t", reg, reg, GP);
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .insn pseudo-op. This marks instruction labels in
+ mips16 mode. This permits the linker to handle them specially,
+ such as generating jalx instructions when needed. We also make
+ them odd for the duration of the assembly, in order to generate the
+ right sort of code. We will make them even in the adjust_symtab
+ routine, while leaving them marked. This is convenient for the
+ debugger and the disassembler. The linker knows to make them odd
+ again. */
+
+static void
+s_insn (ignore)
+ int ignore;
+{
+ if (mips_opts.mips16)
+ mips16_mark_labels ();
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .stabn directive. We need these in order to mark a label
+ as being a mips16 text label correctly. Sometimes the compiler
+ will emit a label, followed by a .stabn, and then switch sections.
+ If the label and .stabn are in mips16 mode, then the label is
+ really a mips16 text label. */
+
+static void
+s_mips_stab (type)
+ int type;
+{
+ if (type == 'n' && mips_opts.mips16)
+ mips16_mark_labels ();
+
+ s_stab (type);
+}
+
+/* Handle the .weakext pseudo-op as defined in Kane and Heinrich.
+ */
+
+static void
+s_mips_weakext (ignore)
+ int ignore;
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+ expressionS exp;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+ S_SET_WEAK (symbolP);
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE ();
+
+ if (! is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ if (S_IS_DEFINED (symbolP))
+ {
+ as_bad ("Ignoring attempt to redefine symbol `%s'.",
+ S_GET_NAME (symbolP));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ }
+
+ expression (&exp);
+ if (exp.X_op != O_symbol)
+ {
+ as_bad ("bad .weakext directive");
+ ignore_rest_of_line();
+ return;
+ }
+ symbolP->sy_value = exp;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Parse a register string into a number. Called from the ECOFF code
+ to parse .frame. The argument is non-zero if this is the frame
+ register, so that we can record it in mips_frame_reg. */
+
+int
+tc_get_register (frame)
+ int frame;
+{
+ int reg;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer++ != '$')
+ {
+ as_warn (_("expected `$'"));
+ reg = 0;
+ }
+ else if (isdigit ((unsigned char) *input_line_pointer))
+ {
+ reg = get_absolute_expression ();
+ if (reg < 0 || reg >= 32)
+ {
+ as_warn (_("Bad register number"));
+ reg = 0;
+ }
+ }
+ else
+ {
+ if (strncmp (input_line_pointer, "fp", 2) == 0)
+ reg = FP;
+ else if (strncmp (input_line_pointer, "sp", 2) == 0)
+ reg = SP;
+ else if (strncmp (input_line_pointer, "gp", 2) == 0)
+ reg = GP;
+ else if (strncmp (input_line_pointer, "at", 2) == 0)
+ reg = AT;
+ else
+ {
+ as_warn (_("Unrecognized register name"));
+ reg = 0;
+ }
+ input_line_pointer += 2;
+ }
+ if (frame)
+ mips_frame_reg = reg != 0 ? reg : SP;
+ return reg;
+}
+
+valueT
+md_section_align (seg, addr)
+ asection *seg;
+ valueT addr;
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+
+#ifdef OBJ_ELF
+ /* We don't need to align ELF sections to the full alignment.
+ However, Irix 5 may prefer that we align them at least to a 16
+ byte boundary. We don't bother to align the sections if we are
+ targeted for an embedded system. */
+ if (strcmp (TARGET_OS, "elf") == 0)
+ return addr;
+ if (align > 4)
+ align = 4;
+#endif
+
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+/* Utility routine, called from above as well. If called while the
+ input file is still being read, it's only an approximation. (For
+ example, a symbol may later become defined which appeared to be
+ undefined earlier.) */
+
+static int
+nopic_need_relax (sym, before_relaxing)
+ symbolS *sym;
+ int before_relaxing;
+{
+ if (sym == 0)
+ return 0;
+
+ if (USE_GLOBAL_POINTER_OPT)
+ {
+ const char *symname;
+ int change;
+
+ /* Find out whether this symbol can be referenced off the GP
+ register. It can be if it is smaller than the -G size or if
+ it is in the .sdata or .sbss section. Certain symbols can
+ not be referenced off the GP, although it appears as though
+ they can. */
+ symname = S_GET_NAME (sym);
+ if (symname != (const char *) NULL
+ && (strcmp (symname, "eprol") == 0
+ || strcmp (symname, "etext") == 0
+ || strcmp (symname, "_gp") == 0
+ || strcmp (symname, "edata") == 0
+ || strcmp (symname, "_fbss") == 0
+ || strcmp (symname, "_fdata") == 0
+ || strcmp (symname, "_ftext") == 0
+ || strcmp (symname, "end") == 0
+ || strcmp (symname, "_gp_disp") == 0))
+ change = 1;
+ else if ((! S_IS_DEFINED (sym) || S_IS_COMMON (sym))
+ && (0
+#ifndef NO_ECOFF_DEBUGGING
+ || (sym->ecoff_extern_size != 0
+ && sym->ecoff_extern_size <= g_switch_value)
+#endif
+ /* We must defer this decision until after the whole
+ file has been read, since there might be a .extern
+ after the first use of this symbol. */
+ || (before_relaxing
+#ifndef NO_ECOFF_DEBUGGING
+ && sym->ecoff_extern_size == 0
+#endif
+ && S_GET_VALUE (sym) == 0)
+ || (S_GET_VALUE (sym) != 0
+ && S_GET_VALUE (sym) <= g_switch_value)))
+ change = 0;
+ else
+ {
+ const char *segname;
+
+ segname = segment_name (S_GET_SEGMENT (sym));
+ assert (strcmp (segname, ".lit8") != 0
+ && strcmp (segname, ".lit4") != 0);
+ change = (strcmp (segname, ".sdata") != 0
+ && strcmp (segname, ".sbss") != 0);
+ }
+ return change;
+ }
+ else
+ /* We are not optimizing for the GP register. */
+ return 1;
+}
+
+/* Given a mips16 variant frag FRAGP, return non-zero if it needs an
+ extended opcode. SEC is the section the frag is in. */
+
+static int
+mips16_extended_frag (fragp, sec, stretch)
+ fragS *fragp;
+ asection *sec;
+ long stretch;
+{
+ int type;
+ register const struct mips16_immed_operand *op;
+ offsetT val;
+ int mintiny, maxtiny;
+ segT symsec;
+
+ if (RELAX_MIPS16_USER_SMALL (fragp->fr_subtype))
+ return 0;
+ if (RELAX_MIPS16_USER_EXT (fragp->fr_subtype))
+ return 1;
+
+ type = RELAX_MIPS16_TYPE (fragp->fr_subtype);
+ op = mips16_immed_operands;
+ while (op->type != type)
+ {
+ ++op;
+ assert (op < mips16_immed_operands + MIPS16_NUM_IMMED);
+ }
+
+ if (op->unsp)
+ {
+ if (type == '<' || type == '>' || type == '[' || type == ']')
+ {
+ mintiny = 1;
+ maxtiny = 1 << op->nbits;
+ }
+ else
+ {
+ mintiny = 0;
+ maxtiny = (1 << op->nbits) - 1;
+ }
+ }
+ else
+ {
+ mintiny = - (1 << (op->nbits - 1));
+ maxtiny = (1 << (op->nbits - 1)) - 1;
+ }
+
+ /* We can't call S_GET_VALUE here, because we don't want to lock in
+ a particular frag address. */
+ if (fragp->fr_symbol->sy_value.X_op == O_constant)
+ {
+ val = (fragp->fr_symbol->sy_value.X_add_number
+ + fragp->fr_symbol->sy_frag->fr_address);
+ symsec = S_GET_SEGMENT (fragp->fr_symbol);
+ }
+ else if (fragp->fr_symbol->sy_value.X_op == O_symbol
+ && (fragp->fr_symbol->sy_value.X_add_symbol->sy_value.X_op
+ == O_constant))
+ {
+ val = (fragp->fr_symbol->sy_value.X_add_symbol->sy_value.X_add_number
+ + fragp->fr_symbol->sy_value.X_add_symbol->sy_frag->fr_address
+ + fragp->fr_symbol->sy_value.X_add_number
+ + fragp->fr_symbol->sy_frag->fr_address);
+ symsec = S_GET_SEGMENT (fragp->fr_symbol->sy_value.X_add_symbol);
+ }
+ else
+ return 1;
+
+ if (op->pcrel)
+ {
+ addressT addr;
+
+ /* We won't have the section when we are called from
+ mips_relax_frag. However, we will always have been called
+ from md_estimate_size_before_relax first. If this is a
+ branch to a different section, we mark it as such. If SEC is
+ NULL, and the frag is not marked, then it must be a branch to
+ the same section. */
+ if (sec == NULL)
+ {
+ if (RELAX_MIPS16_LONG_BRANCH (fragp->fr_subtype))
+ return 1;
+ }
+ else
+ {
+ if (symsec != sec)
+ {
+ fragp->fr_subtype =
+ RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
+
+ /* FIXME: We should support this, and let the linker
+ catch branches and loads that are out of range. */
+ as_bad_where (fragp->fr_file, fragp->fr_line,
+ _("unsupported PC relative reference to different section"));
+
+ return 1;
+ }
+ }
+
+ /* In this case, we know for sure that the symbol fragment is in
+ the same section. If the fr_address of the symbol fragment
+ is greater then the address of this fragment we want to add
+ in STRETCH in order to get a better estimate of the address.
+ This particularly matters because of the shift bits. */
+ if (stretch != 0
+ && fragp->fr_symbol->sy_frag->fr_address >= fragp->fr_address)
+ {
+ fragS *f;
+
+ /* Adjust stretch for any alignment frag. Note that if have
+ been expanding the earlier code, the symbol may be
+ defined in what appears to be an earlier frag. FIXME:
+ This doesn't handle the fr_subtype field, which specifies
+ a maximum number of bytes to skip when doing an
+ alignment. */
+ for (f = fragp;
+ f != NULL && f != fragp->fr_symbol->sy_frag;
+ f = f->fr_next)
+ {
+ if (f->fr_type == rs_align || f->fr_type == rs_align_code)
+ {
+ if (stretch < 0)
+ stretch = - ((- stretch)
+ & ~ ((1 << (int) f->fr_offset) - 1));
+ else
+ stretch &= ~ ((1 << (int) f->fr_offset) - 1);
+ if (stretch == 0)
+ break;
+ }
+ }
+ if (f != NULL)
+ val += stretch;
+ }
+
+ addr = fragp->fr_address + fragp->fr_fix;
+
+ /* The base address rules are complicated. The base address of
+ a branch is the following instruction. The base address of a
+ PC relative load or add is the instruction itself, but if it
+ is in a delay slot (in which case it can not be extended) use
+ the address of the instruction whose delay slot it is in. */
+ if (type == 'p' || type == 'q')
+ {
+ addr += 2;
+
+ /* If we are currently assuming that this frag should be
+ extended, then, the current address is two bytes
+ higher. */
+ if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
+ addr += 2;
+
+ /* Ignore the low bit in the target, since it will be set
+ for a text label. */
+ if ((val & 1) != 0)
+ --val;
+ }
+ else if (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype))
+ addr -= 4;
+ else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype))
+ addr -= 2;
+
+ val -= addr & ~ ((1 << op->shift) - 1);
+
+ /* Branch offsets have an implicit 0 in the lowest bit. */
+ if (type == 'p' || type == 'q')
+ val /= 2;
+
+ /* If any of the shifted bits are set, we must use an extended
+ opcode. If the address depends on the size of this
+ instruction, this can lead to a loop, so we arrange to always
+ use an extended opcode. We only check this when we are in
+ the main relaxation loop, when SEC is NULL. */
+ if ((val & ((1 << op->shift) - 1)) != 0 && sec == NULL)
+ {
+ fragp->fr_subtype =
+ RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
+ return 1;
+ }
+
+ /* If we are about to mark a frag as extended because the value
+ is precisely maxtiny + 1, then there is a chance of an
+ infinite loop as in the following code:
+ la $4,foo
+ .skip 1020
+ .align 2
+ foo:
+ In this case when the la is extended, foo is 0x3fc bytes
+ away, so the la can be shrunk, but then foo is 0x400 away, so
+ the la must be extended. To avoid this loop, we mark the
+ frag as extended if it was small, and is about to become
+ extended with a value of maxtiny + 1. */
+ if (val == ((maxtiny + 1) << op->shift)
+ && ! RELAX_MIPS16_EXTENDED (fragp->fr_subtype)
+ && sec == NULL)
+ {
+ fragp->fr_subtype =
+ RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
+ return 1;
+ }
+ }
+ else if (symsec != absolute_section && sec != NULL)
+ as_bad_where (fragp->fr_file, fragp->fr_line, _("unsupported relocation"));
+
+ if ((val & ((1 << op->shift) - 1)) != 0
+ || val < (mintiny << op->shift)
+ || val > (maxtiny << op->shift))
+ return 1;
+ else
+ return 0;
+}
+
+/* Estimate the size of a frag before relaxing. Unless this is the
+ mips16, we are not really relaxing here, and the final size is
+ encoded in the subtype information. For the mips16, we have to
+ decide whether we are using an extended opcode or not. */
+
+/*ARGSUSED*/
+int
+md_estimate_size_before_relax (fragp, segtype)
+ fragS *fragp;
+ asection *segtype;
+{
+ int change;
+
+ if (RELAX_MIPS16_P (fragp->fr_subtype))
+ {
+ if (mips16_extended_frag (fragp, segtype, 0))
+ {
+ fragp->fr_subtype = RELAX_MIPS16_MARK_EXTENDED (fragp->fr_subtype);
+ return 4;
+ }
+ else
+ {
+ fragp->fr_subtype = RELAX_MIPS16_CLEAR_EXTENDED (fragp->fr_subtype);
+ return 2;
+ }
+ }
+
+ if (mips_pic == NO_PIC)
+ {
+ change = nopic_need_relax (fragp->fr_symbol, 0);
+ }
+ else if (mips_pic == SVR4_PIC)
+ {
+ symbolS *sym;
+ asection *symsec;
+
+ sym = fragp->fr_symbol;
+
+ /* Handle the case of a symbol equated to another symbol. */
+ while (sym->sy_value.X_op == O_symbol
+ && (! S_IS_DEFINED (sym) || S_IS_COMMON (sym)))
+ {
+ symbolS *n;
+
+ /* It's possible to get a loop here in a badly written
+ program. */
+ n = sym->sy_value.X_add_symbol;
+ if (n == sym)
+ break;
+ sym = n;
+ }
+
+ symsec = S_GET_SEGMENT (sym);
+
+ /* This must duplicate the test in adjust_reloc_syms. */
+ change = (symsec != &bfd_und_section
+ && symsec != &bfd_abs_section
+ && ! bfd_is_com_section (symsec));
+ }
+ else
+ abort ();
+
+ if (change)
+ {
+ /* Record the offset to the first reloc in the fr_opcode field.
+ This lets md_convert_frag and tc_gen_reloc know that the code
+ must be expanded. */
+ fragp->fr_opcode = (fragp->fr_literal
+ + fragp->fr_fix
+ - RELAX_OLD (fragp->fr_subtype)
+ + RELAX_RELOC1 (fragp->fr_subtype));
+ /* FIXME: This really needs as_warn_where. */
+ if (RELAX_WARN (fragp->fr_subtype))
+ as_warn (_("AT used after \".set noat\" or macro used after \".set nomacro\""));
+ }
+
+ if (! change)
+ return 0;
+ else
+ return RELAX_NEW (fragp->fr_subtype) - RELAX_OLD (fragp->fr_subtype);
+}
+
+/* This is called to see whether a reloc against a defined symbol
+ should be converted into a reloc against a section. Don't adjust
+ MIPS16 jump relocations, so we don't have to worry about the format
+ of the offset in the .o file. Don't adjust relocations against
+ mips16 symbols, so that the linker can find them if it needs to set
+ up a stub. */
+
+int
+mips_fix_adjustable (fixp)
+ fixS *fixp;
+{
+ if (fixp->fx_r_type == BFD_RELOC_MIPS16_JMP)
+ return 0;
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+ if (fixp->fx_addsy == NULL)
+ return 1;
+#ifdef OBJ_ELF
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+ && S_GET_OTHER (fixp->fx_addsy) == STO_MIPS16
+ && fixp->fx_subsy == NULL)
+ return 0;
+#endif
+ return 1;
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format. */
+
+arelent **
+tc_gen_reloc (section, fixp)
+ asection *section;
+ fixS *fixp;
+{
+ static arelent *retval[4];
+ arelent *reloc;
+ bfd_reloc_code_real_type code;
+
+ reloc = retval[0] = (arelent *) xmalloc (sizeof (arelent));
+ retval[1] = NULL;
+
+ reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ if (mips_pic == EMBEDDED_PIC
+ && SWITCH_TABLE (fixp))
+ {
+ /* For a switch table entry we use a special reloc. The addend
+ is actually the difference between the reloc address and the
+ subtrahend. */
+ reloc->addend = reloc->address - S_GET_VALUE (fixp->fx_subsy);
+ if (OUTPUT_FLAVOR != bfd_target_ecoff_flavour)
+ as_fatal (_("Double check fx_r_type in tc-mips.c:tc_gen_reloc"));
+ fixp->fx_r_type = BFD_RELOC_GPREL32;
+ }
+ else if (fixp->fx_r_type == BFD_RELOC_PCREL_LO16)
+ {
+ /* We use a special addend for an internal RELLO reloc. */
+ if (fixp->fx_addsy->bsym->flags & BSF_SECTION_SYM)
+ reloc->addend = reloc->address - S_GET_VALUE (fixp->fx_subsy);
+ else
+ reloc->addend = fixp->fx_addnumber + reloc->address;
+ }
+ else if (fixp->fx_r_type == BFD_RELOC_PCREL_HI16_S)
+ {
+ assert (fixp->fx_next != NULL
+ && fixp->fx_next->fx_r_type == BFD_RELOC_PCREL_LO16);
+ /* We use a special addend for an internal RELHI reloc. The
+ reloc is relative to the RELLO; adjust the addend
+ accordingly. */
+ if (fixp->fx_addsy->bsym->flags & BSF_SECTION_SYM)
+ reloc->addend = (fixp->fx_next->fx_frag->fr_address
+ + fixp->fx_next->fx_where
+ - S_GET_VALUE (fixp->fx_subsy));
+ else
+ reloc->addend = (fixp->fx_addnumber
+ + fixp->fx_next->fx_frag->fr_address
+ + fixp->fx_next->fx_where);
+ }
+ else if (fixp->fx_pcrel == 0)
+ reloc->addend = fixp->fx_addnumber;
+ else
+ {
+ if (OUTPUT_FLAVOR != bfd_target_aout_flavour)
+ /* A gruesome hack which is a result of the gruesome gas reloc
+ handling. */
+ reloc->addend = reloc->address;
+ else
+ reloc->addend = -reloc->address;
+ }
+
+ /* If this is a variant frag, we may need to adjust the existing
+ reloc and generate a new one. */
+ if (fixp->fx_frag->fr_opcode != NULL
+ && (fixp->fx_r_type == BFD_RELOC_MIPS_GPREL
+ || fixp->fx_r_type == BFD_RELOC_MIPS_GOT16
+ || fixp->fx_r_type == BFD_RELOC_MIPS_CALL16
+ || fixp->fx_r_type == BFD_RELOC_MIPS_GOT_HI16
+ || fixp->fx_r_type == BFD_RELOC_MIPS_GOT_LO16
+ || fixp->fx_r_type == BFD_RELOC_MIPS_CALL_HI16
+ || fixp->fx_r_type == BFD_RELOC_MIPS_CALL_LO16))
+ {
+ arelent *reloc2;
+
+ assert (! RELAX_MIPS16_P (fixp->fx_frag->fr_subtype));
+
+ /* If this is not the last reloc in this frag, then we have two
+ GPREL relocs, or a GOT_HI16/GOT_LO16 pair, or a
+ CALL_HI16/CALL_LO16, both of which are being replaced. Let
+ the second one handle all of them. */
+ if (fixp->fx_next != NULL
+ && fixp->fx_frag == fixp->fx_next->fx_frag)
+ {
+ assert ((fixp->fx_r_type == BFD_RELOC_MIPS_GPREL
+ && fixp->fx_next->fx_r_type == BFD_RELOC_MIPS_GPREL)
+ || (fixp->fx_r_type == BFD_RELOC_MIPS_GOT_HI16
+ && (fixp->fx_next->fx_r_type
+ == BFD_RELOC_MIPS_GOT_LO16))
+ || (fixp->fx_r_type == BFD_RELOC_MIPS_CALL_HI16
+ && (fixp->fx_next->fx_r_type
+ == BFD_RELOC_MIPS_CALL_LO16)));
+ retval[0] = NULL;
+ return retval;
+ }
+
+ fixp->fx_where = fixp->fx_frag->fr_opcode - fixp->fx_frag->fr_literal;
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc2 = retval[1] = (arelent *) xmalloc (sizeof (arelent));
+ retval[2] = NULL;
+ reloc2->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+ reloc2->address = (reloc->address
+ + (RELAX_RELOC2 (fixp->fx_frag->fr_subtype)
+ - RELAX_RELOC1 (fixp->fx_frag->fr_subtype)));
+ reloc2->addend = fixp->fx_addnumber;
+ reloc2->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_LO16);
+ assert (reloc2->howto != NULL);
+
+ if (RELAX_RELOC3 (fixp->fx_frag->fr_subtype))
+ {
+ arelent *reloc3;
+
+ reloc3 = retval[2] = (arelent *) xmalloc (sizeof (arelent));
+ retval[3] = NULL;
+ *reloc3 = *reloc2;
+ reloc3->address += 4;
+ }
+
+ if (mips_pic == NO_PIC)
+ {
+ assert (fixp->fx_r_type == BFD_RELOC_MIPS_GPREL);
+ fixp->fx_r_type = BFD_RELOC_HI16_S;
+ }
+ else if (mips_pic == SVR4_PIC)
+ {
+ switch (fixp->fx_r_type)
+ {
+ default:
+ abort ();
+ case BFD_RELOC_MIPS_GOT16:
+ break;
+ case BFD_RELOC_MIPS_CALL16:
+ case BFD_RELOC_MIPS_GOT_LO16:
+ case BFD_RELOC_MIPS_CALL_LO16:
+ fixp->fx_r_type = BFD_RELOC_MIPS_GOT16;
+ break;
+ }
+ }
+ else
+ abort ();
+ }
+
+ /* Since MIPS ELF uses Rel instead of Rela, encode the vtable entry
+ to be used in the relocation's section offset. */
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ {
+ reloc->address = reloc->addend;
+ reloc->addend = 0;
+ }
+
+ /* Since DIFF_EXPR_OK is defined in tc-mips.h, it is possible that
+ fixup_segment converted a non-PC relative reloc into a PC
+ relative reloc. In such a case, we need to convert the reloc
+ code. */
+ code = fixp->fx_r_type;
+ if (fixp->fx_pcrel)
+ {
+ switch (code)
+ {
+ case BFD_RELOC_8:
+ code = BFD_RELOC_8_PCREL;
+ break;
+ case BFD_RELOC_16:
+ code = BFD_RELOC_16_PCREL;
+ break;
+ case BFD_RELOC_32:
+ code = BFD_RELOC_32_PCREL;
+ break;
+ case BFD_RELOC_64:
+ code = BFD_RELOC_64_PCREL;
+ break;
+ case BFD_RELOC_8_PCREL:
+ case BFD_RELOC_16_PCREL:
+ case BFD_RELOC_32_PCREL:
+ case BFD_RELOC_64_PCREL:
+ case BFD_RELOC_16_PCREL_S2:
+ case BFD_RELOC_PCREL_HI16_S:
+ case BFD_RELOC_PCREL_LO16:
+ break;
+ default:
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Cannot make %s relocation PC relative"),
+ bfd_get_reloc_code_name (code));
+ }
+ }
+
+ /* To support a PC relative reloc when generating embedded PIC code
+ for ECOFF, we use a Cygnus extension. We check for that here to
+ make sure that we don't let such a reloc escape normally. */
+ if (OUTPUT_FLAVOR == bfd_target_ecoff_flavour
+ && code == BFD_RELOC_16_PCREL_S2
+ && mips_pic != EMBEDDED_PIC)
+ reloc->howto = NULL;
+ else
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Can not represent %s relocation in this object file format"),
+ bfd_get_reloc_code_name (code));
+ retval[0] = NULL;
+ }
+
+ return retval;
+}
+
+/* Relax a machine dependent frag. This returns the amount by which
+ the current size of the frag should change. */
+
+int
+mips_relax_frag (fragp, stretch)
+ fragS *fragp;
+ long stretch;
+{
+ if (! RELAX_MIPS16_P (fragp->fr_subtype))
+ return 0;
+
+ if (mips16_extended_frag (fragp, (asection *) NULL, stretch))
+ {
+ if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
+ return 0;
+ fragp->fr_subtype = RELAX_MIPS16_MARK_EXTENDED (fragp->fr_subtype);
+ return 2;
+ }
+ else
+ {
+ if (! RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
+ return 0;
+ fragp->fr_subtype = RELAX_MIPS16_CLEAR_EXTENDED (fragp->fr_subtype);
+ return -2;
+ }
+
+ return 0;
+}
+
+/* Convert a machine dependent frag. */
+
+void
+md_convert_frag (abfd, asec, fragp)
+ bfd *abfd;
+ segT asec;
+ fragS *fragp;
+{
+ int old, new;
+ char *fixptr;
+
+ if (RELAX_MIPS16_P (fragp->fr_subtype))
+ {
+ int type;
+ register const struct mips16_immed_operand *op;
+ boolean small, ext;
+ offsetT val;
+ bfd_byte *buf;
+ unsigned long insn;
+ boolean use_extend;
+ unsigned short extend;
+
+ type = RELAX_MIPS16_TYPE (fragp->fr_subtype);
+ op = mips16_immed_operands;
+ while (op->type != type)
+ ++op;
+
+ if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
+ {
+ small = false;
+ ext = true;
+ }
+ else
+ {
+ small = true;
+ ext = false;
+ }
+
+ resolve_symbol_value (fragp->fr_symbol, 1);
+ val = S_GET_VALUE (fragp->fr_symbol);
+ if (op->pcrel)
+ {
+ addressT addr;
+
+ addr = fragp->fr_address + fragp->fr_fix;
+
+ /* The rules for the base address of a PC relative reloc are
+ complicated; see mips16_extended_frag. */
+ if (type == 'p' || type == 'q')
+ {
+ addr += 2;
+ if (ext)
+ addr += 2;
+ /* Ignore the low bit in the target, since it will be
+ set for a text label. */
+ if ((val & 1) != 0)
+ --val;
+ }
+ else if (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype))
+ addr -= 4;
+ else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype))
+ addr -= 2;
+
+ addr &= ~ (addressT) ((1 << op->shift) - 1);
+ val -= addr;
+
+ /* Make sure the section winds up with the alignment we have
+ assumed. */
+ if (op->shift > 0)
+ record_alignment (asec, op->shift);
+ }
+
+ if (ext
+ && (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype)
+ || RELAX_MIPS16_DSLOT (fragp->fr_subtype)))
+ as_warn_where (fragp->fr_file, fragp->fr_line,
+ _("extended instruction in delay slot"));
+
+ buf = (bfd_byte *) (fragp->fr_literal + fragp->fr_fix);
+
+ if (target_big_endian)
+ insn = bfd_getb16 (buf);
+ else
+ insn = bfd_getl16 (buf);
+
+ mips16_immed (fragp->fr_file, fragp->fr_line, type, val,
+ RELAX_MIPS16_USER_EXT (fragp->fr_subtype),
+ small, ext, &insn, &use_extend, &extend);
+
+ if (use_extend)
+ {
+ md_number_to_chars (buf, 0xf000 | extend, 2);
+ fragp->fr_fix += 2;
+ buf += 2;
+ }
+
+ md_number_to_chars (buf, insn, 2);
+ fragp->fr_fix += 2;
+ buf += 2;
+ }
+ else
+ {
+ if (fragp->fr_opcode == NULL)
+ return;
+
+ old = RELAX_OLD (fragp->fr_subtype);
+ new = RELAX_NEW (fragp->fr_subtype);
+ fixptr = fragp->fr_literal + fragp->fr_fix;
+
+ if (new > 0)
+ memcpy (fixptr - old, fixptr, new);
+
+ fragp->fr_fix += new - old;
+ }
+}
+
+#ifdef OBJ_ELF
+
+/* This function is called after the relocs have been generated.
+ We've been storing mips16 text labels as odd. Here we convert them
+ back to even for the convenience of the debugger. */
+
+void
+mips_frob_file_after_relocs ()
+{
+ asymbol **syms;
+ unsigned int count, i;
+
+ if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+ return;
+
+ syms = bfd_get_outsymbols (stdoutput);
+ count = bfd_get_symcount (stdoutput);
+ for (i = 0; i < count; i++, syms++)
+ {
+ if (elf_symbol (*syms)->internal_elf_sym.st_other == STO_MIPS16
+ && ((*syms)->value & 1) != 0)
+ {
+ (*syms)->value &= ~1;
+ /* If the symbol has an odd size, it was probably computed
+ incorrectly, so adjust that as well. */
+ if ((elf_symbol (*syms)->internal_elf_sym.st_size & 1) != 0)
+ ++elf_symbol (*syms)->internal_elf_sym.st_size;
+ }
+ }
+}
+
+#endif
+
+/* This function is called whenever a label is defined. It is used
+ when handling branch delays; if a branch has a label, we assume we
+ can not move it. */
+
+void
+mips_define_label (sym)
+ symbolS *sym;
+{
+ struct insn_label_list *l;
+
+ if (free_insn_labels == NULL)
+ l = (struct insn_label_list *) xmalloc (sizeof *l);
+ else
+ {
+ l = free_insn_labels;
+ free_insn_labels = l->next;
+ }
+
+ l->label = sym;
+ l->next = insn_labels;
+ insn_labels = l;
+}
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+
+/* Some special processing for a MIPS ELF file. */
+
+void
+mips_elf_final_processing ()
+{
+ /* Write out the register information. */
+ if (! mips_64)
+ {
+ Elf32_RegInfo s;
+
+ s.ri_gprmask = mips_gprmask;
+ s.ri_cprmask[0] = mips_cprmask[0];
+ s.ri_cprmask[1] = mips_cprmask[1];
+ s.ri_cprmask[2] = mips_cprmask[2];
+ s.ri_cprmask[3] = mips_cprmask[3];
+ /* The gp_value field is set by the MIPS ELF backend. */
+
+ bfd_mips_elf32_swap_reginfo_out (stdoutput, &s,
+ ((Elf32_External_RegInfo *)
+ mips_regmask_frag));
+ }
+ else
+ {
+ Elf64_Internal_RegInfo s;
+
+ s.ri_gprmask = mips_gprmask;
+ s.ri_pad = 0;
+ s.ri_cprmask[0] = mips_cprmask[0];
+ s.ri_cprmask[1] = mips_cprmask[1];
+ s.ri_cprmask[2] = mips_cprmask[2];
+ s.ri_cprmask[3] = mips_cprmask[3];
+ /* The gp_value field is set by the MIPS ELF backend. */
+
+ bfd_mips_elf64_swap_reginfo_out (stdoutput, &s,
+ ((Elf64_External_RegInfo *)
+ mips_regmask_frag));
+ }
+
+ /* Set the MIPS ELF flag bits. FIXME: There should probably be some
+ sort of BFD interface for this. */
+ if (mips_any_noreorder)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_NOREORDER;
+ if (mips_pic != NO_PIC)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_PIC;
+
+ /* Set the MIPS ELF ABI flags. */
+ if (mips_abi_string == 0)
+ ;
+ else if (strcmp (mips_abi_string,"32") == 0)
+ elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_O32;
+ else if (strcmp (mips_abi_string,"o64") == 0)
+ elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_O64;
+ else if (strcmp (mips_abi_string,"eabi") == 0)
+ {
+ if (mips_eabi64)
+ elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_EABI64;
+ else
+ elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_EABI32;
+ }
+
+ if (mips_32bitmode)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_32BITMODE;
+}
+
+#endif /* OBJ_ELF || OBJ_MAYBE_ELF */
+
+typedef struct proc
+ {
+ struct symbol *isym;
+ unsigned long reg_mask;
+ unsigned long reg_offset;
+ unsigned long fpreg_mask;
+ unsigned long fpreg_offset;
+ unsigned long frame_offset;
+ unsigned long frame_reg;
+ unsigned long pc_reg;
+ }
+procS;
+
+static procS cur_proc;
+static procS *cur_proc_ptr;
+static int numprocs;
+
+static void
+md_obj_begin ()
+{
+}
+
+static void
+md_obj_end ()
+{
+ /* check for premature end, nesting errors, etc */
+ if (cur_proc_ptr)
+ as_warn (_("missing `.end' at end of assembly"));
+}
+
+static long
+get_number ()
+{
+ int negative = 0;
+ long val = 0;
+
+ if (*input_line_pointer == '-')
+ {
+ ++input_line_pointer;
+ negative = 1;
+ }
+ if (!isdigit (*input_line_pointer))
+ as_bad (_("Expected simple number."));
+ if (input_line_pointer[0] == '0')
+ {
+ if (input_line_pointer[1] == 'x')
+ {
+ input_line_pointer += 2;
+ while (isxdigit (*input_line_pointer))
+ {
+ val <<= 4;
+ val |= hex_value (*input_line_pointer++);
+ }
+ return negative ? -val : val;
+ }
+ else
+ {
+ ++input_line_pointer;
+ while (isdigit (*input_line_pointer))
+ {
+ val <<= 3;
+ val |= *input_line_pointer++ - '0';
+ }
+ return negative ? -val : val;
+ }
+ }
+ if (!isdigit (*input_line_pointer))
+ {
+ printf (_(" *input_line_pointer == '%c' 0x%02x\n"),
+ *input_line_pointer, *input_line_pointer);
+ as_warn (_("Invalid number"));
+ return -1;
+ }
+ while (isdigit (*input_line_pointer))
+ {
+ val *= 10;
+ val += *input_line_pointer++ - '0';
+ }
+ return negative ? -val : val;
+}
+
+/* The .file directive; just like the usual .file directive, but there
+ is an initial number which is the ECOFF file index. */
+
+static void
+s_file (x)
+ int x;
+{
+ int line;
+
+ line = get_number ();
+ s_app_file (0);
+}
+
+
+/* The .end directive. */
+
+static void
+s_mips_end (x)
+ int x;
+{
+ symbolS *p;
+ int maybe_text;
+
+ if (!is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ p = get_symbol ();
+ demand_empty_rest_of_line ();
+ }
+ else
+ p = NULL;
+
+#ifdef BFD_ASSEMBLER
+ if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
+ maybe_text = 1;
+ else
+ maybe_text = 0;
+#else
+ if (now_seg != data_section && now_seg != bss_section)
+ maybe_text = 1;
+ else
+ maybe_text = 0;
+#endif
+
+ if (!maybe_text)
+ as_warn (_(".end not in text section"));
+
+ if (!cur_proc_ptr)
+ {
+ as_warn (_(".end directive without a preceding .ent directive."));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ if (p != NULL)
+ {
+ assert (S_GET_NAME (p));
+ if (strcmp (S_GET_NAME (p), S_GET_NAME (cur_proc_ptr->isym)))
+ as_warn (_(".end symbol does not match .ent symbol."));
+ }
+ else
+ as_warn (_(".end directive missing or unknown symbol"));
+
+#ifdef MIPS_STABS_ELF
+ {
+ segT saved_seg = now_seg;
+ subsegT saved_subseg = now_subseg;
+ fragS *saved_frag = frag_now;
+ valueT dot;
+ segT seg;
+ expressionS exp;
+ char *fragp;
+
+ dot = frag_now_fix ();
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ assert (pdr_seg);
+ subseg_set (pdr_seg, 0);
+
+ /* Write the symbol */
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = p;
+ exp.X_add_number = 0;
+ emit_expr (&exp, 4);
+
+ fragp = frag_more (7*4);
+
+ md_number_to_chars (fragp, (valueT) cur_proc_ptr->reg_mask, 4);
+ md_number_to_chars (fragp + 4, (valueT) cur_proc_ptr->reg_offset, 4);
+ md_number_to_chars (fragp + 8, (valueT) cur_proc_ptr->fpreg_mask, 4);
+ md_number_to_chars (fragp +12, (valueT) cur_proc_ptr->fpreg_offset, 4);
+ md_number_to_chars (fragp +16, (valueT) cur_proc_ptr->frame_offset, 4);
+ md_number_to_chars (fragp +20, (valueT) cur_proc_ptr->frame_reg, 4);
+ md_number_to_chars (fragp +24, (valueT) cur_proc_ptr->pc_reg, 4);
+
+ subseg_set (saved_seg, saved_subseg);
+ }
+#endif
+
+ cur_proc_ptr = NULL;
+}
+
+/* The .aent and .ent directives. */
+
+static void
+s_mips_ent (aent)
+ int aent;
+{
+ int number = 0;
+ symbolS *symbolP;
+ int maybe_text;
+
+ symbolP = get_symbol ();
+ if (*input_line_pointer == ',')
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (isdigit (*input_line_pointer) || *input_line_pointer == '-')
+ number = get_number ();
+
+#ifdef BFD_ASSEMBLER
+ if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
+ maybe_text = 1;
+ else
+ maybe_text = 0;
+#else
+ if (now_seg != data_section && now_seg != bss_section)
+ maybe_text = 1;
+ else
+ maybe_text = 0;
+#endif
+
+ if (!maybe_text)
+ as_warn (_(".ent or .aent not in text section."));
+
+ if (!aent && cur_proc_ptr)
+ as_warn (_("missing `.end'"));
+
+ if (!aent)
+ {
+ cur_proc_ptr = &cur_proc;
+ memset (cur_proc_ptr, '\0', sizeof (procS));
+
+ cur_proc_ptr->isym = symbolP;
+
+ symbolP->bsym->flags |= BSF_FUNCTION;
+
+ numprocs++;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .frame directive. If the mdebug section is present (IRIX 5 native)
+ then ecoff.c (ecoff_directive_frame) is used. For embedded targets,
+ s_mips_frame is used so that we can set the PDR information correctly.
+ We can't use the ecoff routines because they make reference to the ecoff
+ symbol table (in the mdebug section). */
+
+static void
+s_mips_frame (ignore)
+ int ignore;
+{
+#ifdef MIPS_STABS_ELF
+
+ long val;
+
+ if (cur_proc_ptr == (procS *) NULL)
+ {
+ as_warn (_(".frame outside of .ent"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ cur_proc_ptr->frame_reg = tc_get_register (1);
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer++ != ','
+ || get_absolute_expression_and_terminator (&val) != ',')
+ {
+ as_warn (_("Bad .frame directive"));
+ --input_line_pointer;
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ cur_proc_ptr->frame_offset = val;
+ cur_proc_ptr->pc_reg = tc_get_register (0);
+
+ demand_empty_rest_of_line ();
+#else
+ s_ignore (ignore);
+#endif /* MIPS_STABS_ELF */
+}
+
+/* The .fmask and .mask directives. If the mdebug section is present
+ (IRIX 5 native) then ecoff.c (ecoff_directive_mask) is used. For
+ embedded targets, s_mips_mask is used so that we can set the PDR
+ information correctly. We can't use the ecoff routines because they
+ make reference to the ecoff symbol table (in the mdebug section). */
+
+static void
+s_mips_mask (reg_type)
+ char reg_type;
+{
+#ifdef MIPS_STABS_ELF
+ long mask, off;
+
+ if (cur_proc_ptr == (procS *) NULL)
+ {
+ as_warn (_(".mask/.fmask outside of .ent"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ if (get_absolute_expression_and_terminator (&mask) != ',')
+ {
+ as_warn (_("Bad .mask/.fmask directive"));
+ --input_line_pointer;
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ off = get_absolute_expression ();
+
+ if (reg_type == 'F')
+ {
+ cur_proc_ptr->fpreg_mask = mask;
+ cur_proc_ptr->fpreg_offset = off;
+ }
+ else
+ {
+ cur_proc_ptr->reg_mask = mask;
+ cur_proc_ptr->reg_offset = off;
+ }
+
+ demand_empty_rest_of_line ();
+#else
+ s_ignore (reg_type);
+#endif /* MIPS_STABS_ELF */
+}
+
+/* The .loc directive. */
+
+#if 0
+static void
+s_loc (x)
+ int x;
+{
+ symbolS *symbolP;
+ int lineno;
+ int addroff;
+
+ assert (now_seg == text_section);
+
+ lineno = get_number ();
+ addroff = frag_now_fix ();
+
+ symbolP = symbol_new ("", N_SLINE, addroff, frag_now);
+ S_SET_TYPE (symbolP, N_SLINE);
+ S_SET_OTHER (symbolP, 0);
+ S_SET_DESC (symbolP, lineno);
+ symbolP->sy_segment = now_seg;
+}
+#endif
+
+
+
diff --git a/gas/config/tc-mips.h b/gas/config/tc-mips.h
new file mode 100644
index 0000000..868aede
--- /dev/null
+++ b/gas/config/tc-mips.h
@@ -0,0 +1,153 @@
+/* tc-mips.h -- header file for tc-mips.c.
+ Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
+ Contributed by the OSF and Ralph Campbell.
+ Written by Keith Knowles and Ralph Campbell, working independently.
+ Modified for ECOFF support by Ian Lance Taylor of Cygnus Support.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifndef TC_MIPS
+
+#define TC_MIPS
+
+#ifdef ANSI_PROTOTYPES
+struct frag;
+struct expressionS;
+#endif
+
+/* Default to big endian. */
+#ifndef TARGET_BYTES_BIG_ENDIAN
+#define TARGET_BYTES_BIG_ENDIAN 1
+#endif
+
+#define TARGET_ARCH bfd_arch_mips
+
+#define ONLY_STANDARD_ESCAPES
+#define WORKING_DOT_WORD 1
+#define OLD_FLOAT_READS
+#define REPEAT_CONS_EXPRESSIONS
+#define RELOC_EXPANSION_POSSIBLE
+#define MAX_RELOC_EXPANSION 3
+#define LOCAL_LABELS_FB 1
+
+/* Maximum symbol offset that can be encoded in a BFD_RELOC_MIPS_GPREL
+ relocation: */
+#define MAX_GPREL_OFFSET (0x7FF4)
+
+#define md_relax_frag(fragp, stretch) mips_relax_frag(fragp, stretch)
+extern int mips_relax_frag PARAMS ((struct frag *, long));
+
+#define md_undefined_symbol(name) (0)
+#define md_operand(x)
+
+/* We permit PC relative difference expressions when generating
+ embedded PIC code. */
+#define DIFF_EXPR_OK
+
+/* Tell assembler that we have an itbl_mips.h header file to include. */
+#define HAVE_ITBL_CPU
+
+/* The endianness of the target format may change based on command
+ line arguments. */
+#define TARGET_FORMAT mips_target_format()
+extern const char *mips_target_format PARAMS ((void));
+
+struct mips_cl_insn
+{
+ unsigned long insn_opcode;
+ const struct mips_opcode *insn_mo;
+ /* The next two fields are used when generating mips16 code. */
+ boolean use_extend;
+ unsigned short extend;
+};
+
+extern int tc_get_register PARAMS ((int frame));
+
+#define tc_init_after_args() mips_init_after_args()
+extern void mips_init_after_args PARAMS ((void));
+
+#define md_parse_long_option(arg) mips_parse_long_option (arg)
+extern int mips_parse_long_option PARAMS ((const char *));
+
+#define tc_frob_label(sym) mips_define_label (sym)
+extern void mips_define_label PARAMS ((struct symbol *));
+
+#define tc_frob_file_before_adjust() mips_frob_file_before_adjust ()
+extern void mips_frob_file_before_adjust PARAMS ((void));
+
+#define tc_frob_file() mips_frob_file ()
+extern void mips_frob_file PARAMS ((void));
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+#define tc_frob_file_after_relocs mips_frob_file_after_relocs
+extern void mips_frob_file_after_relocs PARAMS ((void));
+#endif
+
+#define TC_CONS_FIX_NEW cons_fix_new_mips
+extern void cons_fix_new_mips
+ PARAMS ((struct frag *, int, unsigned int, struct expressionS *));
+
+#define tc_fix_adjustable(fixp) mips_fix_adjustable (fixp)
+extern int mips_fix_adjustable PARAMS ((struct fix *));
+
+/* When generating embedded PIC code we must keep PC relative
+ relocations. */
+#define TC_FORCE_RELOCATION(fixp) mips_force_relocation (fixp)
+extern int mips_force_relocation PARAMS ((struct fix *));
+
+/* md_apply_fix sets fx_done correctly. */
+#define TC_HANDLE_FX_DONE 1
+
+/* Register mask variables. These are set by the MIPS assembly code
+ and used by ECOFF and possibly other object file formats. */
+extern unsigned long mips_gprmask;
+extern unsigned long mips_cprmask[4];
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+
+#define elf_tc_final_processing mips_elf_final_processing
+extern void mips_elf_final_processing PARAMS ((void));
+
+#define ELF_TC_SPECIAL_SECTIONS \
+ { ".sdata", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL }, \
+ { ".sbss", SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL }, \
+ { ".lit4", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL }, \
+ { ".lit8", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_MIPS_GPREL }, \
+ { ".ucode", SHT_MIPS_UCODE, 0 }, \
+ { ".mdebug", SHT_MIPS_DEBUG, 0 },
+/* Other special sections not generated by the assembler: .reginfo,
+ .liblist, .conflict, .gptab, .got, .dynamic, .rel.dyn. */
+
+#endif
+
+extern void md_mips_end PARAMS ((void));
+#define md_end() md_mips_end()
+
+#define USE_GLOBAL_POINTER_OPT (OUTPUT_FLAVOR == bfd_target_ecoff_flavour \
+ || OUTPUT_FLAVOR == bfd_target_elf_flavour)
+
+extern void mips_pop_insert PARAMS ((void));
+#define md_pop_insert() mips_pop_insert()
+
+extern void mips_flush_pending_output PARAMS ((void));
+#define md_flush_pending_output mips_flush_pending_output
+
+extern void mips_enable_auto_align PARAMS ((void));
+#define md_elf_section_change_hook() mips_enable_auto_align()
+
+#endif /* TC_MIPS */
diff --git a/gas/config/tc-mn10200.c b/gas/config/tc-mn10200.c
new file mode 100644
index 0000000..2380915
--- /dev/null
+++ b/gas/config/tc-mn10200.c
@@ -0,0 +1,1414 @@
+/* tc-mn10200.c -- Assembler code for the Matsushita 10200
+
+ Copyright (C) 1996, 1997, 1998 Free Software Foundation.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "as.h"
+#include "subsegs.h"
+#include "opcode/mn10200.h"
+
+/* Structure to hold information about predefined registers. */
+struct reg_name
+{
+ const char *name;
+ int value;
+};
+
+/* Generic assembler global variables which must be defined by all targets. */
+
+/* Characters which always start a comment. */
+const char comment_chars[] = "#";
+
+/* Characters which start a comment at the beginning of a line. */
+const char line_comment_chars[] = ";#";
+
+/* Characters which may be used to separate multiple commands on a
+ single line. */
+const char line_separator_chars[] = ";";
+
+/* Characters which are used to indicate an exponent in a floating
+ point number. */
+const char EXP_CHARS[] = "eE";
+
+/* Characters which mean that a number is a floating point constant,
+ as in 0d1.0. */
+const char FLT_CHARS[] = "dD";
+
+
+const relax_typeS md_relax_table[] = {
+ /* bCC relaxing */
+ {0x81, -0x7e, 2, 1},
+ {0x8004, -0x7ffb, 5, 2},
+ {0x800006, -0x7ffff9, 7, 0},
+ /* bCCx relaxing */
+ {0x81, -0x7e, 3, 4},
+ {0x8004, -0x7ffb, 6, 5},
+ {0x800006, -0x7ffff9, 8, 0},
+ /* jsr relaxing */
+ {0x8004, -0x7ffb, 3, 7},
+ {0x800006, -0x7ffff9, 5, 0},
+ /* jmp relaxing */
+ {0x81, -0x7e, 2, 9},
+ {0x8004, -0x7ffb, 3, 10},
+ {0x800006, -0x7ffff9, 5, 0},
+
+};
+/* local functions */
+static void mn10200_insert_operand PARAMS ((unsigned long *, unsigned long *,
+ const struct mn10200_operand *,
+ offsetT, char *, unsigned,
+ unsigned));
+static unsigned long check_operand PARAMS ((unsigned long,
+ const struct mn10200_operand *,
+ offsetT));
+static int reg_name_search PARAMS ((const struct reg_name *, int, const char *));
+static boolean data_register_name PARAMS ((expressionS *expressionP));
+static boolean address_register_name PARAMS ((expressionS *expressionP));
+static boolean other_register_name PARAMS ((expressionS *expressionP));
+
+
+/* fixups */
+#define MAX_INSN_FIXUPS (5)
+struct mn10200_fixup
+{
+ expressionS exp;
+ int opindex;
+ bfd_reloc_code_real_type reloc;
+};
+struct mn10200_fixup fixups[MAX_INSN_FIXUPS];
+static int fc;
+
+const char *md_shortopts = "";
+struct option md_longopts[] = {
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { NULL, NULL, 0 }
+};
+
+/* Opcode hash table. */
+static struct hash_control *mn10200_hash;
+
+/* This table is sorted. Suitable for searching by a binary search. */
+static const struct reg_name data_registers[] =
+{
+ { "d0", 0 },
+ { "d1", 1 },
+ { "d2", 2 },
+ { "d3", 3 },
+};
+#define DATA_REG_NAME_CNT (sizeof(data_registers) / sizeof(struct reg_name))
+
+static const struct reg_name address_registers[] =
+{
+ { "a0", 0 },
+ { "a1", 1 },
+ { "a2", 2 },
+ { "a3", 3 },
+};
+#define ADDRESS_REG_NAME_CNT (sizeof(address_registers) / sizeof(struct reg_name))
+
+static const struct reg_name other_registers[] =
+{
+ { "mdr", 0 },
+ { "psw", 0 },
+};
+#define OTHER_REG_NAME_CNT (sizeof(other_registers) / sizeof(struct reg_name))
+
+/* reg_name_search does a binary search of the given register table
+ to see if "name" is a valid regiter name. Returns the register
+ number from the array on success, or -1 on failure. */
+
+static int
+reg_name_search (regs, regcount, name)
+ const struct reg_name *regs;
+ int regcount;
+ const char *name;
+{
+ int middle, low, high;
+ int cmp;
+
+ low = 0;
+ high = regcount - 1;
+
+ do
+ {
+ middle = (low + high) / 2;
+ cmp = strcasecmp (name, regs[middle].name);
+ if (cmp < 0)
+ high = middle - 1;
+ else if (cmp > 0)
+ low = middle + 1;
+ else
+ return regs[middle].value;
+ }
+ while (low <= high);
+ return -1;
+}
+
+
+/* Summary of register_name().
+ *
+ * in: Input_line_pointer points to 1st char of operand.
+ *
+ * out: A expressionS.
+ * The operand may have been a register: in this case, X_op == O_register,
+ * X_add_number is set to the register number, and truth is returned.
+ * Input_line_pointer->(next non-blank) char after operand, or is in
+ * its original state.
+ */
+static boolean
+data_register_name (expressionP)
+ expressionS *expressionP;
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (data_registers, DATA_REG_NAME_CNT, name);
+
+ /* look to see if it's in the register table */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ /* make the rest nice */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+ *input_line_pointer = c; /* put back the delimiting char */
+ return true;
+ }
+ else
+ {
+ /* reset the line as if we had not done anything */
+ *input_line_pointer = c; /* put back the delimiting char */
+ input_line_pointer = start; /* reset input_line pointer */
+ return false;
+ }
+}
+
+/* Summary of register_name().
+ *
+ * in: Input_line_pointer points to 1st char of operand.
+ *
+ * out: A expressionS.
+ * The operand may have been a register: in this case, X_op == O_register,
+ * X_add_number is set to the register number, and truth is returned.
+ * Input_line_pointer->(next non-blank) char after operand, or is in
+ * its original state.
+ */
+static boolean
+address_register_name (expressionP)
+ expressionS *expressionP;
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (address_registers, ADDRESS_REG_NAME_CNT, name);
+
+ /* look to see if it's in the register table */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ /* make the rest nice */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+ *input_line_pointer = c; /* put back the delimiting char */
+ return true;
+ }
+ else
+ {
+ /* reset the line as if we had not done anything */
+ *input_line_pointer = c; /* put back the delimiting char */
+ input_line_pointer = start; /* reset input_line pointer */
+ return false;
+ }
+}
+
+/* Summary of register_name().
+ *
+ * in: Input_line_pointer points to 1st char of operand.
+ *
+ * out: A expressionS.
+ * The operand may have been a register: in this case, X_op == O_register,
+ * X_add_number is set to the register number, and truth is returned.
+ * Input_line_pointer->(next non-blank) char after operand, or is in
+ * its original state.
+ */
+static boolean
+other_register_name (expressionP)
+ expressionS *expressionP;
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (other_registers, OTHER_REG_NAME_CNT, name);
+
+ /* look to see if it's in the register table */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ /* make the rest nice */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+ *input_line_pointer = c; /* put back the delimiting char */
+ return true;
+ }
+ else
+ {
+ /* reset the line as if we had not done anything */
+ *input_line_pointer = c; /* put back the delimiting char */
+ input_line_pointer = start; /* reset input_line pointer */
+ return false;
+ }
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ fprintf(stream, _("MN10200 options:\n\
+none yet\n"));
+}
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ return 0;
+}
+
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ return 0;
+}
+
+char *
+md_atof (type, litp, sizep)
+ int type;
+ char *litp;
+ int *sizep;
+{
+ int prec;
+ LITTLENUM_TYPE words[4];
+ char *t;
+ int i;
+
+ switch (type)
+ {
+ case 'f':
+ prec = 2;
+ break;
+
+ case 'd':
+ prec = 4;
+ break;
+
+ default:
+ *sizep = 0;
+ return _("bad call to md_atof");
+ }
+
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+
+ *sizep = prec * 2;
+
+ for (i = prec - 1; i >= 0; i--)
+ {
+ md_number_to_chars (litp, (valueT) words[i], 2);
+ litp += 2;
+ }
+
+ return NULL;
+}
+
+
+void
+md_convert_frag (abfd, sec, fragP)
+ bfd *abfd;
+ asection *sec;
+ fragS *fragP;
+{
+ static unsigned long label_count = 0;
+ char buf[40];
+
+ subseg_change (sec, 0);
+ if (fragP->fr_subtype == 0)
+ {
+ fix_new (fragP, fragP->fr_fix + 1, 1, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_8_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 2;
+ }
+ else if (fragP->fr_subtype == 1)
+ {
+ /* Reverse the condition of the first branch. */
+ int offset = fragP->fr_fix;
+ int opcode = fragP->fr_literal[offset] & 0xff;
+
+ switch (opcode)
+ {
+ case 0xe8:
+ opcode = 0xe9;
+ break;
+ case 0xe9:
+ opcode = 0xe8;
+ break;
+ case 0xe0:
+ opcode = 0xe2;
+ break;
+ case 0xe2:
+ opcode = 0xe0;
+ break;
+ case 0xe3:
+ opcode = 0xe1;
+ break;
+ case 0xe1:
+ opcode = 0xe3;
+ break;
+ case 0xe4:
+ opcode = 0xe6;
+ break;
+ case 0xe6:
+ opcode = 0xe4;
+ break;
+ case 0xe7:
+ opcode = 0xe5;
+ break;
+ case 0xe5:
+ opcode = 0xe7;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_literal[offset] = opcode;
+
+ /* Create a fixup for the reversed conditional branch. */
+ sprintf (buf, ".%s_%d", FAKE_LABEL_NAME, label_count++);
+ fix_new (fragP, fragP->fr_fix + 1, 1,
+ symbol_new (buf, sec, 0, fragP->fr_next),
+ fragP->fr_offset, 1, BFD_RELOC_8_PCREL);
+
+ /* Now create the unconditional branch + fixup to the
+ final target. */
+ fragP->fr_literal[offset + 2] = 0xfc;
+ fix_new (fragP, fragP->fr_fix + 3, 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 5;
+ }
+ else if (fragP->fr_subtype == 2)
+ {
+ /* Reverse the condition of the first branch. */
+ int offset = fragP->fr_fix;
+ int opcode = fragP->fr_literal[offset] & 0xff;
+
+ switch (opcode)
+ {
+ case 0xe8:
+ opcode = 0xe9;
+ break;
+ case 0xe9:
+ opcode = 0xe8;
+ break;
+ case 0xe0:
+ opcode = 0xe2;
+ break;
+ case 0xe2:
+ opcode = 0xe0;
+ break;
+ case 0xe3:
+ opcode = 0xe1;
+ break;
+ case 0xe1:
+ opcode = 0xe3;
+ break;
+ case 0xe4:
+ opcode = 0xe6;
+ break;
+ case 0xe6:
+ opcode = 0xe4;
+ break;
+ case 0xe7:
+ opcode = 0xe5;
+ break;
+ case 0xe5:
+ opcode = 0xe7;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_literal[offset] = opcode;
+
+ /* Create a fixup for the reversed conditional branch. */
+ sprintf (buf, ".%s_%d", FAKE_LABEL_NAME, label_count++);
+ fix_new (fragP, fragP->fr_fix + 1, 1,
+ symbol_new (buf, sec, 0, fragP->fr_next),
+ fragP->fr_offset, 1, BFD_RELOC_8_PCREL);
+
+ /* Now create the unconditional branch + fixup to the
+ final target. */
+ fragP->fr_literal[offset + 2] = 0xf4;
+ fragP->fr_literal[offset + 3] = 0xe0;
+ fix_new (fragP, fragP->fr_fix + 4, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_24_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 7;
+ }
+ else if (fragP->fr_subtype == 3)
+ {
+ fix_new (fragP, fragP->fr_fix + 2, 1, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_8_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 3;
+ }
+ else if (fragP->fr_subtype == 4)
+ {
+ /* Reverse the condition of the first branch. */
+ int offset = fragP->fr_fix;
+ int opcode = fragP->fr_literal[offset + 1] & 0xff;
+
+ switch (opcode)
+ {
+ case 0xfc:
+ opcode = 0xfd;
+ break;
+ case 0xfd:
+ opcode = 0xfc;
+ break;
+ case 0xfe:
+ opcode = 0xff;
+ break;
+ case 0xff:
+ opcode = 0xfe;
+ case 0xe8:
+ opcode = 0xe9;
+ break;
+ case 0xe9:
+ opcode = 0xe8;
+ break;
+ case 0xe0:
+ opcode = 0xe2;
+ break;
+ case 0xe2:
+ opcode = 0xe0;
+ break;
+ case 0xe3:
+ opcode = 0xe1;
+ break;
+ case 0xe1:
+ opcode = 0xe3;
+ break;
+ case 0xe4:
+ opcode = 0xe6;
+ break;
+ case 0xe6:
+ opcode = 0xe4;
+ break;
+ case 0xe7:
+ opcode = 0xe5;
+ break;
+ case 0xe5:
+ opcode = 0xe7;
+ break;
+ case 0xec:
+ opcode = 0xed;
+ break;
+ case 0xed:
+ opcode = 0xec;
+ break;
+ case 0xee:
+ opcode = 0xef;
+ break;
+ case 0xef:
+ opcode = 0xee;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_literal[offset + 1] = opcode;
+
+ /* Create a fixup for the reversed conditional branch. */
+ sprintf (buf, ".%s_%d", FAKE_LABEL_NAME, label_count++);
+ fix_new (fragP, fragP->fr_fix + 2, 1,
+ symbol_new (buf, sec, 0, fragP->fr_next),
+ fragP->fr_offset, 1, BFD_RELOC_8_PCREL);
+
+ /* Now create the unconditional branch + fixup to the
+ final target. */
+ fragP->fr_literal[offset + 3] = 0xfc;
+ fix_new (fragP, fragP->fr_fix + 4, 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 6;
+ }
+ else if (fragP->fr_subtype == 5)
+ {
+ /* Reverse the condition of the first branch. */
+ int offset = fragP->fr_fix;
+ int opcode = fragP->fr_literal[offset + 1] & 0xff;
+
+ switch (opcode)
+ {
+ case 0xfc:
+ opcode = 0xfd;
+ break;
+ case 0xfd:
+ opcode = 0xfc;
+ break;
+ case 0xfe:
+ opcode = 0xff;
+ break;
+ case 0xff:
+ opcode = 0xfe;
+ case 0xe8:
+ opcode = 0xe9;
+ break;
+ case 0xe9:
+ opcode = 0xe8;
+ break;
+ case 0xe0:
+ opcode = 0xe2;
+ break;
+ case 0xe2:
+ opcode = 0xe0;
+ break;
+ case 0xe3:
+ opcode = 0xe1;
+ break;
+ case 0xe1:
+ opcode = 0xe3;
+ break;
+ case 0xe4:
+ opcode = 0xe6;
+ break;
+ case 0xe6:
+ opcode = 0xe4;
+ break;
+ case 0xe7:
+ opcode = 0xe5;
+ break;
+ case 0xe5:
+ opcode = 0xe7;
+ break;
+ case 0xec:
+ opcode = 0xed;
+ break;
+ case 0xed:
+ opcode = 0xec;
+ break;
+ case 0xee:
+ opcode = 0xef;
+ break;
+ case 0xef:
+ opcode = 0xee;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_literal[offset + 1] = opcode;
+
+ /* Create a fixup for the reversed conditional branch. */
+ sprintf (buf, ".%s_%d", FAKE_LABEL_NAME, label_count++);
+ fix_new (fragP, fragP->fr_fix + 2, 1,
+ symbol_new (buf, sec, 0, fragP->fr_next),
+ fragP->fr_offset, 1, BFD_RELOC_8_PCREL);
+
+ /* Now create the unconditional branch + fixup to the
+ final target. */
+ fragP->fr_literal[offset + 3] = 0xf4;
+ fragP->fr_literal[offset + 4] = 0xe0;
+ fix_new (fragP, fragP->fr_fix + 5, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_24_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 8;
+ }
+ else if (fragP->fr_subtype == 6)
+ {
+ fix_new (fragP, fragP->fr_fix + 1, 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 3;
+ }
+ else if (fragP->fr_subtype == 7)
+ {
+ int offset = fragP->fr_fix;
+ fragP->fr_literal[offset] = 0xf4;
+ fragP->fr_literal[offset + 1] = 0xe1;
+
+ fix_new (fragP, fragP->fr_fix + 2, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_24_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 5;
+ }
+ else if (fragP->fr_subtype == 8)
+ {
+ fragP->fr_literal[fragP->fr_fix] = 0xea;
+ fix_new (fragP, fragP->fr_fix + 1, 1, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_8_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 2;
+ }
+ else if (fragP->fr_subtype == 9)
+ {
+ int offset = fragP->fr_fix;
+ fragP->fr_literal[offset] = 0xfc;
+
+ fix_new (fragP, fragP->fr_fix + 1, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 3;
+ }
+ else if (fragP->fr_subtype == 10)
+ {
+ int offset = fragP->fr_fix;
+ fragP->fr_literal[offset] = 0xf4;
+ fragP->fr_literal[offset + 1] = 0xe0;
+
+ fix_new (fragP, fragP->fr_fix + 2, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_24_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 5;
+ }
+ else
+ abort ();
+}
+
+valueT
+md_section_align (seg, addr)
+ asection *seg;
+ valueT addr;
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+void
+md_begin ()
+{
+ char *prev_name = "";
+ register const struct mn10200_opcode *op;
+
+ mn10200_hash = hash_new();
+
+ /* Insert unique names into hash table. The MN10200 instruction set
+ has many identical opcode names that have different opcodes based
+ on the operands. This hash table then provides a quick index to
+ the first opcode with a particular name in the opcode table. */
+
+ op = mn10200_opcodes;
+ while (op->name)
+ {
+ if (strcmp (prev_name, op->name))
+ {
+ prev_name = (char *) op->name;
+ hash_insert (mn10200_hash, op->name, (char *) op);
+ }
+ op++;
+ }
+
+ /* This is both a simplification (we don't have to write md_apply_fix)
+ and support for future optimizations (branch shortening and similar
+ stuff in the linker. */
+ linkrelax = 1;
+}
+
+void
+md_assemble (str)
+ char *str;
+{
+ char *s;
+ struct mn10200_opcode *opcode;
+ struct mn10200_opcode *next_opcode;
+ const unsigned char *opindex_ptr;
+ int next_opindex, relaxable;
+ unsigned long insn, extension, size = 0;
+ char *f;
+ int i;
+ int match;
+
+ /* Get the opcode. */
+ for (s = str; *s != '\0' && ! isspace (*s); s++)
+ ;
+ if (*s != '\0')
+ *s++ = '\0';
+
+ /* find the first opcode with the proper name */
+ opcode = (struct mn10200_opcode *)hash_find (mn10200_hash, str);
+ if (opcode == NULL)
+ {
+ as_bad (_("Unrecognized opcode: `%s'"), str);
+ return;
+ }
+
+ str = s;
+ while (isspace (*str))
+ ++str;
+
+ input_line_pointer = str;
+
+ for(;;)
+ {
+ const char *errmsg = NULL;
+ int op_idx;
+ char *hold;
+ int extra_shift = 0;
+
+ relaxable = 0;
+ fc = 0;
+ match = 0;
+ next_opindex = 0;
+ insn = opcode->opcode;
+ extension = 0;
+ for (op_idx = 1, opindex_ptr = opcode->operands;
+ *opindex_ptr != 0;
+ opindex_ptr++, op_idx++)
+ {
+ const struct mn10200_operand *operand;
+ expressionS ex;
+
+ if (next_opindex == 0)
+ {
+ operand = &mn10200_operands[*opindex_ptr];
+ }
+ else
+ {
+ operand = &mn10200_operands[next_opindex];
+ next_opindex = 0;
+ }
+
+ errmsg = NULL;
+
+ while (*str == ' ' || *str == ',')
+ ++str;
+
+ if (operand->flags & MN10200_OPERAND_RELAX)
+ relaxable = 1;
+
+ /* Gather the operand. */
+ hold = input_line_pointer;
+ input_line_pointer = str;
+
+ if (operand->flags & MN10200_OPERAND_PAREN)
+ {
+ if (*input_line_pointer != ')' && *input_line_pointer != '(')
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ input_line_pointer++;
+ goto keep_going;
+ }
+ /* See if we can match the operands. */
+ else if (operand->flags & MN10200_OPERAND_DREG)
+ {
+ if (!data_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ }
+ else if (operand->flags & MN10200_OPERAND_AREG)
+ {
+ if (!address_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ }
+ else if (operand->flags & MN10200_OPERAND_PSW)
+ {
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcmp (start, "psw") != 0)
+ {
+ *input_line_pointer = c;
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ *input_line_pointer = c;
+ goto keep_going;
+ }
+ else if (operand->flags & MN10200_OPERAND_MDR)
+ {
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcmp (start, "mdr") != 0)
+ {
+ *input_line_pointer = c;
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ *input_line_pointer = c;
+ goto keep_going;
+ }
+ else if (data_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else if (address_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else if (other_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else if (*str == ')' || *str == '(')
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else
+ {
+ expression (&ex);
+ }
+
+ switch (ex.X_op)
+ {
+ case O_illegal:
+ errmsg = _("illegal operand");
+ goto error;
+ case O_absent:
+ errmsg = _("missing operand");
+ goto error;
+ case O_register:
+ if ((operand->flags
+ & (MN10200_OPERAND_DREG | MN10200_OPERAND_AREG)) == 0)
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+
+ if (opcode->format == FMT_2 || opcode->format == FMT_5)
+ extra_shift = 8;
+ else if (opcode->format == FMT_3 || opcode->format == FMT_6
+ || opcode->format == FMT_7)
+ extra_shift = 16;
+ else
+ extra_shift = 0;
+
+ mn10200_insert_operand (&insn, &extension, operand,
+ ex.X_add_number, (char *) NULL,
+ 0, extra_shift);
+
+ break;
+
+ case O_constant:
+ /* If this operand can be promoted, and it doesn't
+ fit into the allocated bitfield for this insn,
+ then promote it (ie this opcode does not match). */
+ if (operand->flags
+ & (MN10200_OPERAND_PROMOTE | MN10200_OPERAND_RELAX)
+ && ! check_operand (insn, operand, ex.X_add_number))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+
+ mn10200_insert_operand (&insn, &extension, operand,
+ ex.X_add_number, (char *) NULL,
+ 0, 0);
+ break;
+
+ default:
+ /* If this operand can be promoted, then this opcode didn't
+ match since we can't know if it needed promotion! */
+ if (operand->flags & MN10200_OPERAND_PROMOTE)
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+
+ /* We need to generate a fixup for this expression. */
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = *opindex_ptr;
+ fixups[fc].reloc = BFD_RELOC_UNUSED;
+ ++fc;
+ break;
+ }
+
+keep_going:
+ str = input_line_pointer;
+ input_line_pointer = hold;
+
+ while (*str == ' ' || *str == ',')
+ ++str;
+
+ }
+
+ /* Make sure we used all the operands! */
+ if (*str != ',')
+ match = 1;
+
+ error:
+ if (match == 0)
+ {
+ next_opcode = opcode + 1;
+ if (!strcmp(next_opcode->name, opcode->name))
+ {
+ opcode = next_opcode;
+ continue;
+ }
+
+ as_bad ("%s", errmsg);
+ return;
+ }
+ break;
+ }
+
+ while (isspace (*str))
+ ++str;
+
+ if (*str != '\0')
+ as_bad (_("junk at end of line: `%s'"), str);
+
+ input_line_pointer = str;
+
+ if (opcode->format == FMT_1)
+ size = 1;
+ else if (opcode->format == FMT_2 || opcode->format == FMT_4)
+ size = 2;
+ else if (opcode->format == FMT_3 || opcode->format == FMT_5)
+ size = 3;
+ else if (opcode->format == FMT_6)
+ size = 4;
+ else if (opcode->format == FMT_7)
+ size = 5;
+ else
+ abort ();
+
+ /* Write out the instruction. */
+
+ if (relaxable && fc > 0)
+ {
+ int type;
+
+ /* bCC */
+ if (size == 2 && opcode->opcode != 0xfc0000)
+ {
+ /* Handle bra specially. Basically treat it like jmp so
+ that we automatically handle 8, 16 and 32 bit offsets
+ correctly as well as jumps to an undefined address.
+
+ It is also important to not treat it like other bCC
+ instructions since the long forms of bra is different
+ from other bCC instructions. */
+ if (opcode->opcode == 0xea00)
+ type = 8;
+ else
+ type = 0;
+ }
+ /* jsr */
+ else if (size == 3 && opcode->opcode == 0xfd0000)
+ type = 6;
+ /* jmp */
+ else if (size == 3 && opcode->opcode == 0xfc0000)
+ type = 8;
+ /* bCCx */
+ else
+ type = 3;
+
+ f = frag_var (rs_machine_dependent, 8, 8 - size, type,
+ fixups[0].exp.X_add_symbol,
+ fixups[0].exp.X_add_number,
+ (char *)fixups[0].opindex);
+ number_to_chars_bigendian (f, insn, size);
+ if (8 - size > 4)
+ {
+ number_to_chars_bigendian (f + size, 0, 4);
+ number_to_chars_bigendian (f + size + 4, 0, 8 - size - 4);
+ }
+ else
+ number_to_chars_bigendian (f + size, 0, 8 - size);
+ }
+
+ else
+ {
+ f = frag_more (size);
+
+ /* Oh, what a mess. The instruction is in big endian format, but
+ 16 and 24bit immediates are little endian! */
+ if (opcode->format == FMT_3)
+ {
+ number_to_chars_bigendian (f, (insn >> 16) & 0xff, 1);
+ number_to_chars_littleendian (f + 1, insn & 0xffff, 2);
+ }
+ else if (opcode->format == FMT_6)
+ {
+ number_to_chars_bigendian (f, (insn >> 16) & 0xffff, 2);
+ number_to_chars_littleendian (f + 2, insn & 0xffff, 2);
+ }
+ else if (opcode->format == FMT_7)
+ {
+ number_to_chars_bigendian (f, (insn >> 16) & 0xffff, 2);
+ number_to_chars_littleendian (f + 2, insn & 0xffff, 2);
+ number_to_chars_littleendian (f + 4, extension & 0xff, 1);
+ }
+ else
+ {
+ number_to_chars_bigendian (f, insn, size > 4 ? 4 : size);
+ }
+
+ /* Create any fixups. */
+ for (i = 0; i < fc; i++)
+ {
+ const struct mn10200_operand *operand;
+
+ operand = &mn10200_operands[fixups[i].opindex];
+ if (fixups[i].reloc != BFD_RELOC_UNUSED)
+ {
+ reloc_howto_type *reloc_howto;
+ int size;
+ int offset;
+ fixS *fixP;
+
+ reloc_howto = bfd_reloc_type_lookup (stdoutput, fixups[i].reloc);
+
+ if (!reloc_howto)
+ abort();
+
+ size = bfd_get_reloc_size (reloc_howto);
+
+ if (size < 1 || size > 4)
+ abort();
+
+ offset = 4 - size;
+ fixP = fix_new_exp (frag_now, f - frag_now->fr_literal + offset,
+ size,
+ &fixups[i].exp,
+ reloc_howto->pc_relative,
+ fixups[i].reloc);
+
+ /* PC-relative offsets are from the first byte of the next
+ instruction, not from the start of the current instruction. */
+ if (reloc_howto->pc_relative)
+ fixP->fx_offset += size;
+ }
+ else
+ {
+ int reloc, pcrel, reloc_size, offset;
+ fixS *fixP;
+
+ reloc = BFD_RELOC_NONE;
+ /* How big is the reloc? Remember SPLIT relocs are
+ implicitly 32bits. */
+ reloc_size = operand->bits;
+
+ offset = size - reloc_size / 8;
+
+ /* Is the reloc pc-relative? */
+ pcrel = (operand->flags & MN10200_OPERAND_PCREL) != 0;
+
+
+ /* Choose a proper BFD relocation type. */
+ if (pcrel)
+ {
+ if (reloc_size == 8)
+ reloc = BFD_RELOC_8_PCREL;
+ else if (reloc_size == 24)
+ reloc = BFD_RELOC_24_PCREL;
+ else
+ abort ();
+ }
+ else
+ {
+ if (reloc_size == 32)
+ reloc = BFD_RELOC_32;
+ else if (reloc_size == 16)
+ reloc = BFD_RELOC_16;
+ else if (reloc_size == 8)
+ reloc = BFD_RELOC_8;
+ else if (reloc_size == 24)
+ reloc = BFD_RELOC_24;
+ else
+ abort ();
+ }
+
+ /* Convert the size of the reloc into what fix_new_exp wants. */
+ reloc_size = reloc_size / 8;
+ if (reloc_size == 8)
+ reloc_size = 0;
+ else if (reloc_size == 16)
+ reloc_size = 1;
+ else if (reloc_size == 32 || reloc_size == 24)
+ reloc_size = 2;
+
+ fixP = fix_new_exp (frag_now, f - frag_now->fr_literal + offset,
+ reloc_size, &fixups[i].exp, pcrel,
+ ((bfd_reloc_code_real_type) reloc));
+
+ /* PC-relative offsets are from the first byte of the next
+ instruction, not from the start of the current instruction. */
+ if (pcrel)
+ fixP->fx_offset += size;
+ }
+ }
+ }
+}
+
+
+/* if while processing a fixup, a reloc really needs to be created */
+/* then it is done here */
+
+arelent *
+tc_gen_reloc (seg, fixp)
+ asection *seg;
+ fixS *fixp;
+{
+ arelent *reloc;
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("reloc %d not supported by object file format"),
+ (int)fixp->fx_r_type);
+ return NULL;
+ }
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ if (fixp->fx_addsy && fixp->fx_subsy)
+ {
+ if ((S_GET_SEGMENT (fixp->fx_addsy) != S_GET_SEGMENT (fixp->fx_subsy))
+ || S_GET_SEGMENT (fixp->fx_addsy) == undefined_section)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ "Difference of symbols in different sections is not supported");
+ return NULL;
+ }
+ reloc->sym_ptr_ptr = &bfd_abs_symbol;
+ reloc->addend = (S_GET_VALUE (fixp->fx_addsy)
+ - S_GET_VALUE (fixp->fx_subsy) + fixp->fx_offset);
+ }
+ else
+ {
+ reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+ reloc->addend = fixp->fx_offset;
+ }
+ return reloc;
+}
+
+int
+md_estimate_size_before_relax (fragp, seg)
+ fragS *fragp;
+ asection *seg;
+{
+ if (fragp->fr_subtype == 0)
+ return 2;
+ if (fragp->fr_subtype == 3)
+ return 3;
+ if (fragp->fr_subtype == 6)
+ {
+ if (!S_IS_DEFINED (fragp->fr_symbol)
+ || seg != S_GET_SEGMENT (fragp->fr_symbol))
+ {
+ fragp->fr_subtype = 7;
+ return 5;
+ }
+ return 3;
+ }
+ if (fragp->fr_subtype == 8)
+ {
+ if (!S_IS_DEFINED (fragp->fr_symbol))
+ {
+ fragp->fr_subtype = 10;
+ return 5;
+ }
+ return 2;
+ }
+}
+
+long
+md_pcrel_from (fixp)
+ fixS *fixp;
+{
+ return fixp->fx_frag->fr_address;
+#if 0
+ if (fixp->fx_addsy != (symbolS *) NULL && ! S_IS_DEFINED (fixp->fx_addsy))
+ {
+ /* The symbol is undefined. Let the linker figure it out. */
+ return 0;
+ }
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+#endif
+}
+
+int
+md_apply_fix3 (fixp, valuep, seg)
+ fixS *fixp;
+ valueT *valuep;
+ segT seg;
+{
+ /* We shouldn't ever get here because linkrelax is nonzero. */
+ abort ();
+ fixp->fx_done = 1;
+ return 0;
+}
+
+/* Insert an operand value into an instruction. */
+
+static void
+mn10200_insert_operand (insnp, extensionp, operand, val, file, line, shift)
+ unsigned long *insnp;
+ unsigned long *extensionp;
+ const struct mn10200_operand *operand;
+ offsetT val;
+ char *file;
+ unsigned int line;
+ unsigned int shift;
+{
+ /* No need to check 24 or 32bit operands for a bit. */
+ if (operand->bits < 24
+ && (operand->flags & MN10200_OPERAND_NOCHECK) == 0)
+ {
+ long min, max;
+ offsetT test;
+
+ if ((operand->flags & MN10200_OPERAND_SIGNED) != 0)
+ {
+ max = (1 << (operand->bits - 1)) - 1;
+ min = - (1 << (operand->bits - 1));
+ }
+ else
+ {
+ max = (1 << operand->bits) - 1;
+ min = 0;
+ }
+
+ test = val;
+
+
+ if (test < (offsetT) min || test > (offsetT) max)
+ {
+ const char *err =
+ _("operand out of range (%s not between %ld and %ld)");
+ char buf[100];
+
+ sprint_value (buf, test);
+ if (file == (char *) NULL)
+ as_warn (err, buf, min, max);
+ else
+ as_warn_where (file, line, err, buf, min, max);
+ }
+ }
+
+ if ((operand->flags & MN10200_OPERAND_EXTENDED) == 0)
+ {
+ *insnp |= (((long) val & ((1 << operand->bits) - 1))
+ << (operand->shift + shift));
+
+ if ((operand->flags & MN10200_OPERAND_REPEATED) != 0)
+ *insnp |= (((long) val & ((1 << operand->bits) - 1))
+ << (operand->shift + shift + 2));
+ }
+ else
+ {
+ *extensionp |= (val >> 16) & 0xff;
+ *insnp |= val & 0xffff;
+ }
+}
+
+static unsigned long
+check_operand (insn, operand, val)
+ unsigned long insn;
+ const struct mn10200_operand *operand;
+ offsetT val;
+{
+ /* No need to check 24bit or 32bit operands for a bit. */
+ if (operand->bits < 24
+ && (operand->flags & MN10200_OPERAND_NOCHECK) == 0)
+ {
+ long min, max;
+ offsetT test;
+
+ if ((operand->flags & MN10200_OPERAND_SIGNED) != 0)
+ {
+ max = (1 << (operand->bits - 1)) - 1;
+ min = - (1 << (operand->bits - 1));
+ }
+ else
+ {
+ max = (1 << operand->bits) - 1;
+ min = 0;
+ }
+
+ test = val;
+
+
+ if (test < (offsetT) min || test > (offsetT) max)
+ return 0;
+ else
+ return 1;
+ }
+ return 1;
+}
diff --git a/gas/config/tc-mn10200.h b/gas/config/tc-mn10200.h
new file mode 100644
index 0000000..e53e569
--- /dev/null
+++ b/gas/config/tc-mn10200.h
@@ -0,0 +1,51 @@
+/* tc-mn10200.h -- Header file for tc-mn10200.c.
+ Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#define TC_MN10200
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#ifndef BFD_ASSEMBLER
+ #error MN10200 support requires BFD_ASSEMBLER
+#endif
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_mn10200
+
+#define TARGET_FORMAT "elf32-mn10200"
+
+#define MD_APPLY_FIX3
+#define md_operand(x)
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+#define md_number_to_chars number_to_chars_littleendian
+
+/* Don't bother to adjust relocs. */
+#define tc_fix_adjustable(FIX) 0
+
+/* We do relaxing in the assembler as well as the linker. */
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
diff --git a/gas/config/tc-mn10300.c b/gas/config/tc-mn10300.c
new file mode 100644
index 0000000..3f9e9ce
--- /dev/null
+++ b/gas/config/tc-mn10300.c
@@ -0,0 +1,1649 @@
+/* tc-mn10300.c -- Assembler code for the Matsushita 10300
+
+ Copyright (C) 1996, 1997, 1998 Free Software Foundation.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "as.h"
+#include "subsegs.h"
+#include "opcode/mn10300.h"
+
+/* Structure to hold information about predefined registers. */
+struct reg_name
+{
+ const char *name;
+ int value;
+};
+
+/* Generic assembler global variables which must be defined by all targets. */
+
+/* Characters which always start a comment. */
+const char comment_chars[] = "#";
+
+/* Characters which start a comment at the beginning of a line. */
+const char line_comment_chars[] = ";#";
+
+/* Characters which may be used to separate multiple commands on a
+ single line. */
+const char line_separator_chars[] = ";";
+
+/* Characters which are used to indicate an exponent in a floating
+ point number. */
+const char EXP_CHARS[] = "eE";
+
+/* Characters which mean that a number is a floating point constant,
+ as in 0d1.0. */
+const char FLT_CHARS[] = "dD";
+
+
+const relax_typeS md_relax_table[] = {
+ /* bCC relaxing */
+ {0x7f, -0x80, 2, 1},
+ {0x7fff, -0x8000, 5, 2},
+ {0x7fffffff, -0x80000000, 7, 0},
+
+ /* bCC relaxing (uncommon cases) */
+ {0x7f, -0x80, 3, 4},
+ {0x7fff, -0x8000, 6, 5},
+ {0x7fffffff, -0x80000000, 8, 0},
+
+ /* call relaxing */
+ {0x7fff, -0x8000, 5, 7},
+ {0x7fffffff, -0x80000000, 7, 0},
+
+ /* calls relaxing */
+ {0x7fff, -0x8000, 4, 9},
+ {0x7fffffff, -0x80000000, 6, 0},
+
+ /* jmp relaxing */
+ {0x7f, -0x80, 2, 11},
+ {0x7fff, -0x8000, 3, 12},
+ {0x7fffffff, -0x80000000, 5, 0},
+
+};
+
+/* local functions */
+static void mn10300_insert_operand PARAMS ((unsigned long *, unsigned long *,
+ const struct mn10300_operand *,
+ offsetT, char *, unsigned,
+ unsigned));
+static unsigned long check_operand PARAMS ((unsigned long,
+ const struct mn10300_operand *,
+ offsetT));
+static int reg_name_search PARAMS ((const struct reg_name *, int, const char *));
+static boolean data_register_name PARAMS ((expressionS *expressionP));
+static boolean address_register_name PARAMS ((expressionS *expressionP));
+static boolean other_register_name PARAMS ((expressionS *expressionP));
+static void set_arch_mach PARAMS ((int));
+
+static int current_machine;
+
+/* fixups */
+#define MAX_INSN_FIXUPS (5)
+struct mn10300_fixup
+{
+ expressionS exp;
+ int opindex;
+ bfd_reloc_code_real_type reloc;
+};
+struct mn10300_fixup fixups[MAX_INSN_FIXUPS];
+static int fc;
+
+/* We must store the value of each register operand so that we can
+ verify that certain registers do not match. */
+int mn10300_reg_operands[MN10300_MAX_OPERANDS];
+
+const char *md_shortopts = "";
+struct option md_longopts[] = {
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "am30", set_arch_mach, 300 },
+ { "mn10300", set_arch_mach, 300 },
+ {NULL, 0, 0}
+};
+
+/* Opcode hash table. */
+static struct hash_control *mn10300_hash;
+
+/* This table is sorted. Suitable for searching by a binary search. */
+static const struct reg_name data_registers[] =
+{
+ { "d0", 0 },
+ { "d1", 1 },
+ { "d2", 2 },
+ { "d3", 3 },
+};
+#define DATA_REG_NAME_CNT (sizeof(data_registers) / sizeof(struct reg_name))
+
+static const struct reg_name address_registers[] =
+{
+ { "a0", 0 },
+ { "a1", 1 },
+ { "a2", 2 },
+ { "a3", 3 },
+};
+#define ADDRESS_REG_NAME_CNT (sizeof(address_registers) / sizeof(struct reg_name))
+
+
+static const struct reg_name other_registers[] =
+{
+ { "mdr", 0 },
+ { "psw", 0 },
+ { "sp", 0 },
+};
+#define OTHER_REG_NAME_CNT (sizeof(other_registers) / sizeof(struct reg_name))
+
+/* reg_name_search does a binary search of the given register table
+ to see if "name" is a valid regiter name. Returns the register
+ number from the array on success, or -1 on failure. */
+
+static int
+reg_name_search (regs, regcount, name)
+ const struct reg_name *regs;
+ int regcount;
+ const char *name;
+{
+ int middle, low, high;
+ int cmp;
+
+ low = 0;
+ high = regcount - 1;
+
+ do
+ {
+ middle = (low + high) / 2;
+ cmp = strcasecmp (name, regs[middle].name);
+ if (cmp < 0)
+ high = middle - 1;
+ else if (cmp > 0)
+ low = middle + 1;
+ else
+ return regs[middle].value;
+ }
+ while (low <= high);
+ return -1;
+}
+
+
+
+/* Summary of register_name().
+ *
+ * in: Input_line_pointer points to 1st char of operand.
+ *
+ * out: A expressionS.
+ * The operand may have been a register: in this case, X_op == O_register,
+ * X_add_number is set to the register number, and truth is returned.
+ * Input_line_pointer->(next non-blank) char after operand, or is in
+ * its original state.
+ */
+static boolean
+data_register_name (expressionP)
+ expressionS *expressionP;
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (data_registers, DATA_REG_NAME_CNT, name);
+
+ /* look to see if it's in the register table */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ /* make the rest nice */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+ *input_line_pointer = c; /* put back the delimiting char */
+ return true;
+ }
+ else
+ {
+ /* reset the line as if we had not done anything */
+ *input_line_pointer = c; /* put back the delimiting char */
+ input_line_pointer = start; /* reset input_line pointer */
+ return false;
+ }
+}
+
+/* Summary of register_name().
+ *
+ * in: Input_line_pointer points to 1st char of operand.
+ *
+ * out: A expressionS.
+ * The operand may have been a register: in this case, X_op == O_register,
+ * X_add_number is set to the register number, and truth is returned.
+ * Input_line_pointer->(next non-blank) char after operand, or is in
+ * its original state.
+ */
+static boolean
+address_register_name (expressionP)
+ expressionS *expressionP;
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (address_registers, ADDRESS_REG_NAME_CNT, name);
+
+ /* look to see if it's in the register table */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ /* make the rest nice */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+ *input_line_pointer = c; /* put back the delimiting char */
+ return true;
+ }
+ else
+ {
+ /* reset the line as if we had not done anything */
+ *input_line_pointer = c; /* put back the delimiting char */
+ input_line_pointer = start; /* reset input_line pointer */
+ return false;
+ }
+}
+
+/* Summary of register_name().
+ *
+ * in: Input_line_pointer points to 1st char of operand.
+ *
+ * out: A expressionS.
+ * The operand may have been a register: in this case, X_op == O_register,
+ * X_add_number is set to the register number, and truth is returned.
+ * Input_line_pointer->(next non-blank) char after operand, or is in
+ * its original state.
+ */
+static boolean
+other_register_name (expressionP)
+ expressionS *expressionP;
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (other_registers, OTHER_REG_NAME_CNT, name);
+
+ /* look to see if it's in the register table */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ /* make the rest nice */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+ *input_line_pointer = c; /* put back the delimiting char */
+ return true;
+ }
+ else
+ {
+ /* reset the line as if we had not done anything */
+ *input_line_pointer = c; /* put back the delimiting char */
+ input_line_pointer = start; /* reset input_line pointer */
+ return false;
+ }
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ fprintf(stream, _("MN10300 options:\n\
+none yet\n"));
+}
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ return 0;
+}
+
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ return 0;
+}
+
+char *
+md_atof (type, litp, sizep)
+ int type;
+ char *litp;
+ int *sizep;
+{
+ int prec;
+ LITTLENUM_TYPE words[4];
+ char *t;
+ int i;
+
+ switch (type)
+ {
+ case 'f':
+ prec = 2;
+ break;
+
+ case 'd':
+ prec = 4;
+ break;
+
+ default:
+ *sizep = 0;
+ return "bad call to md_atof";
+ }
+
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+
+ *sizep = prec * 2;
+
+ for (i = prec - 1; i >= 0; i--)
+ {
+ md_number_to_chars (litp, (valueT) words[i], 2);
+ litp += 2;
+ }
+
+ return NULL;
+}
+
+
+void
+md_convert_frag (abfd, sec, fragP)
+ bfd *abfd;
+ asection *sec;
+ fragS *fragP;
+{
+ static unsigned long label_count = 0;
+ char buf[40];
+
+ subseg_change (sec, 0);
+ if (fragP->fr_subtype == 0)
+ {
+ fix_new (fragP, fragP->fr_fix + 1, 1, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_8_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 2;
+ }
+ else if (fragP->fr_subtype == 1)
+ {
+ /* Reverse the condition of the first branch. */
+ int offset = fragP->fr_fix;
+ int opcode = fragP->fr_literal[offset] & 0xff;
+
+ switch (opcode)
+ {
+ case 0xc8:
+ opcode = 0xc9;
+ break;
+ case 0xc9:
+ opcode = 0xc8;
+ break;
+ case 0xc0:
+ opcode = 0xc2;
+ break;
+ case 0xc2:
+ opcode = 0xc0;
+ break;
+ case 0xc3:
+ opcode = 0xc1;
+ break;
+ case 0xc1:
+ opcode = 0xc3;
+ break;
+ case 0xc4:
+ opcode = 0xc6;
+ break;
+ case 0xc6:
+ opcode = 0xc4;
+ break;
+ case 0xc7:
+ opcode = 0xc5;
+ break;
+ case 0xc5:
+ opcode = 0xc7;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_literal[offset] = opcode;
+
+ /* Create a fixup for the reversed conditional branch. */
+ sprintf (buf, ".%s_%d", FAKE_LABEL_NAME, label_count++);
+ fix_new (fragP, fragP->fr_fix + 1, 1,
+ symbol_new (buf, sec, 0, fragP->fr_next),
+ fragP->fr_offset + 1, 1, BFD_RELOC_8_PCREL);
+
+ /* Now create the unconditional branch + fixup to the
+ final target. */
+ fragP->fr_literal[offset + 2] = 0xcc;
+ fix_new (fragP, fragP->fr_fix + 3, 2, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 5;
+ }
+ else if (fragP->fr_subtype == 2)
+ {
+ /* Reverse the condition of the first branch. */
+ int offset = fragP->fr_fix;
+ int opcode = fragP->fr_literal[offset] & 0xff;
+
+ switch (opcode)
+ {
+ case 0xc8:
+ opcode = 0xc9;
+ break;
+ case 0xc9:
+ opcode = 0xc8;
+ break;
+ case 0xc0:
+ opcode = 0xc2;
+ break;
+ case 0xc2:
+ opcode = 0xc0;
+ break;
+ case 0xc3:
+ opcode = 0xc1;
+ break;
+ case 0xc1:
+ opcode = 0xc3;
+ break;
+ case 0xc4:
+ opcode = 0xc6;
+ break;
+ case 0xc6:
+ opcode = 0xc4;
+ break;
+ case 0xc7:
+ opcode = 0xc5;
+ break;
+ case 0xc5:
+ opcode = 0xc7;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_literal[offset] = opcode;
+
+ /* Create a fixup for the reversed conditional branch. */
+ sprintf (buf, ".%s_%d", FAKE_LABEL_NAME, label_count++);
+ fix_new (fragP, fragP->fr_fix + 1, 1,
+ symbol_new (buf, sec, 0, fragP->fr_next),
+ fragP->fr_offset + 1, 1, BFD_RELOC_8_PCREL);
+
+ /* Now create the unconditional branch + fixup to the
+ final target. */
+ fragP->fr_literal[offset + 2] = 0xdc;
+ fix_new (fragP, fragP->fr_fix + 3, 4, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_32_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 7;
+ }
+ else if (fragP->fr_subtype == 3)
+ {
+ fix_new (fragP, fragP->fr_fix + 2, 1, fragP->fr_symbol,
+ fragP->fr_offset + 2, 1, BFD_RELOC_8_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 3;
+ }
+ else if (fragP->fr_subtype == 4)
+ {
+ /* Reverse the condition of the first branch. */
+ int offset = fragP->fr_fix;
+ int opcode = fragP->fr_literal[offset + 1] & 0xff;
+
+ switch (opcode)
+ {
+ case 0xe8:
+ opcode = 0xe9;
+ break;
+ case 0xe9:
+ opcode = 0xe8;
+ break;
+ case 0xea:
+ opcode = 0xeb;
+ break;
+ case 0xeb:
+ opcode = 0xea;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_literal[offset + 1] = opcode;
+
+ /* Create a fixup for the reversed conditional branch. */
+ sprintf (buf, ".%s_%d", FAKE_LABEL_NAME, label_count++);
+ fix_new (fragP, fragP->fr_fix + 2, 1,
+ symbol_new (buf, sec, 0, fragP->fr_next),
+ fragP->fr_offset + 2, 1, BFD_RELOC_8_PCREL);
+
+ /* Now create the unconditional branch + fixup to the
+ final target. */
+ fragP->fr_literal[offset + 3] = 0xcc;
+ fix_new (fragP, fragP->fr_fix + 4, 2, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 6;
+ }
+ else if (fragP->fr_subtype == 5)
+ {
+ /* Reverse the condition of the first branch. */
+ int offset = fragP->fr_fix;
+ int opcode = fragP->fr_literal[offset + 1] & 0xff;
+
+ switch (opcode)
+ {
+ case 0xe8:
+ opcode = 0xe9;
+ break;
+ case 0xea:
+ opcode = 0xeb;
+ break;
+ case 0xeb:
+ opcode = 0xea;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_literal[offset + 1] = opcode;
+
+ /* Create a fixup for the reversed conditional branch. */
+ sprintf (buf, ".%s_%d", FAKE_LABEL_NAME, label_count++);
+ fix_new (fragP, fragP->fr_fix + 2, 1,
+ symbol_new (buf, sec, 0, fragP->fr_next),
+ fragP->fr_offset + 2, 1, BFD_RELOC_8_PCREL);
+
+ /* Now create the unconditional branch + fixup to the
+ final target. */
+ fragP->fr_literal[offset + 3] = 0xdc;
+ fix_new (fragP, fragP->fr_fix + 4, 4, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_32_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 8;
+ }
+ else if (fragP->fr_subtype == 6)
+ {
+ int offset = fragP->fr_fix;
+ fragP->fr_literal[offset] = 0xcd;
+ fix_new (fragP, fragP->fr_fix + 1, 2, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 5;
+ }
+ else if (fragP->fr_subtype == 7)
+ {
+ int offset = fragP->fr_fix;
+ fragP->fr_literal[offset] = 0xdd;
+ fragP->fr_literal[offset + 5] = fragP->fr_literal[offset + 3];
+ fragP->fr_literal[offset + 6] = fragP->fr_literal[offset + 4];
+
+ fix_new (fragP, fragP->fr_fix + 1, 4, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_32_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 7;
+ }
+ else if (fragP->fr_subtype == 8)
+ {
+ int offset = fragP->fr_fix;
+ fragP->fr_literal[offset] = 0xfa;
+ fragP->fr_literal[offset + 1] = 0xff;
+ fix_new (fragP, fragP->fr_fix + 2, 2, fragP->fr_symbol,
+ fragP->fr_offset + 2, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 4;
+ }
+ else if (fragP->fr_subtype == 9)
+ {
+ int offset = fragP->fr_fix;
+ fragP->fr_literal[offset] = 0xfc;
+ fragP->fr_literal[offset + 1] = 0xff;
+
+ fix_new (fragP, fragP->fr_fix + 2, 4, fragP->fr_symbol,
+ fragP->fr_offset + 2, 1, BFD_RELOC_32_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 6;
+ }
+ else if (fragP->fr_subtype == 10)
+ {
+ fragP->fr_literal[fragP->fr_fix] = 0xca;
+ fix_new (fragP, fragP->fr_fix + 1, 1, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_8_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 2;
+ }
+ else if (fragP->fr_subtype == 11)
+ {
+ int offset = fragP->fr_fix;
+ fragP->fr_literal[offset] = 0xcc;
+
+ fix_new (fragP, fragP->fr_fix + 1, 4, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 3;
+ }
+ else if (fragP->fr_subtype == 12)
+ {
+ int offset = fragP->fr_fix;
+ fragP->fr_literal[offset] = 0xdc;
+
+ fix_new (fragP, fragP->fr_fix + 1, 4, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_32_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 5;
+ }
+ else
+ abort ();
+}
+
+valueT
+md_section_align (seg, addr)
+ asection *seg;
+ valueT addr;
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+void
+md_begin ()
+{
+ char *prev_name = "";
+ register const struct mn10300_opcode *op;
+
+ mn10300_hash = hash_new();
+
+ /* Insert unique names into hash table. The MN10300 instruction set
+ has many identical opcode names that have different opcodes based
+ on the operands. This hash table then provides a quick index to
+ the first opcode with a particular name in the opcode table. */
+
+ op = mn10300_opcodes;
+ while (op->name)
+ {
+ if (strcmp (prev_name, op->name))
+ {
+ prev_name = (char *) op->name;
+ hash_insert (mn10300_hash, op->name, (char *) op);
+ }
+ op++;
+ }
+
+ /* This is both a simplification (we don't have to write md_apply_fix)
+ and support for future optimizations (branch shortening and similar
+ stuff in the linker). */
+ linkrelax = 1;
+
+ /* Set the default machine type. */
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_mn10300, 300))
+ as_warn (_("could not set architecture and machine"));
+
+ current_machine = 300;
+}
+
+void
+md_assemble (str)
+ char *str;
+{
+ char *s;
+ struct mn10300_opcode *opcode;
+ struct mn10300_opcode *next_opcode;
+ const unsigned char *opindex_ptr;
+ int next_opindex, relaxable;
+ unsigned long insn, extension, size = 0;
+ char *f;
+ int i;
+ int match;
+
+ /* Get the opcode. */
+ for (s = str; *s != '\0' && ! isspace (*s); s++)
+ ;
+ if (*s != '\0')
+ *s++ = '\0';
+
+ /* find the first opcode with the proper name */
+ opcode = (struct mn10300_opcode *)hash_find (mn10300_hash, str);
+ if (opcode == NULL)
+ {
+ as_bad (_("Unrecognized opcode: `%s'"), str);
+ return;
+ }
+
+ str = s;
+ while (isspace (*str))
+ ++str;
+
+ input_line_pointer = str;
+
+ for(;;)
+ {
+ const char *errmsg;
+ int op_idx;
+ char *hold;
+ int extra_shift = 0;
+
+
+ errmsg = _("Invalid opcode/operands");
+
+ /* Reset the array of register operands. */
+ memset (mn10300_reg_operands, -1, sizeof (mn10300_reg_operands));
+
+ relaxable = 0;
+ fc = 0;
+ match = 0;
+ next_opindex = 0;
+ insn = opcode->opcode;
+ extension = 0;
+
+ /* If the instruction is not available on the current machine
+ then it can not possibly match. */
+ if (opcode->machine
+ && (opcode->machine != current_machine))
+ goto error;
+
+ for (op_idx = 1, opindex_ptr = opcode->operands;
+ *opindex_ptr != 0;
+ opindex_ptr++, op_idx++)
+ {
+ const struct mn10300_operand *operand;
+ expressionS ex;
+
+ if (next_opindex == 0)
+ {
+ operand = &mn10300_operands[*opindex_ptr];
+ }
+ else
+ {
+ operand = &mn10300_operands[next_opindex];
+ next_opindex = 0;
+ }
+
+ while (*str == ' ' || *str == ',')
+ ++str;
+
+ if (operand->flags & MN10300_OPERAND_RELAX)
+ relaxable = 1;
+
+ /* Gather the operand. */
+ hold = input_line_pointer;
+ input_line_pointer = str;
+
+ if (operand->flags & MN10300_OPERAND_PAREN)
+ {
+ if (*input_line_pointer != ')' && *input_line_pointer != '(')
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ input_line_pointer++;
+ goto keep_going;
+ }
+ /* See if we can match the operands. */
+ else if (operand->flags & MN10300_OPERAND_DREG)
+ {
+ if (!data_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ }
+ else if (operand->flags & MN10300_OPERAND_AREG)
+ {
+ if (!address_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ }
+ else if (operand->flags & MN10300_OPERAND_SP)
+ {
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcasecmp (start, "sp") != 0)
+ {
+ *input_line_pointer = c;
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ *input_line_pointer = c;
+ goto keep_going;
+ }
+ else if (operand->flags & MN10300_OPERAND_PSW)
+ {
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcasecmp (start, "psw") != 0)
+ {
+ *input_line_pointer = c;
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ *input_line_pointer = c;
+ goto keep_going;
+ }
+ else if (operand->flags & MN10300_OPERAND_MDR)
+ {
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcasecmp (start, "mdr") != 0)
+ {
+ *input_line_pointer = c;
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ *input_line_pointer = c;
+ goto keep_going;
+ }
+ else if (operand->flags & MN10300_OPERAND_REG_LIST)
+ {
+ unsigned int value = 0;
+ if (*input_line_pointer != '[')
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+
+ /* Eat the '['. */
+ input_line_pointer++;
+
+ /* We used to reject a null register list here; however,
+ we accept it now so the compiler can emit "call" instructions
+ for all calls to named functions.
+
+ The linker can then fill in the appropriate bits for the
+ register list and stack size or change the instruction
+ into a "calls" if using "call" is not profitable. */
+ while (*input_line_pointer != ']')
+ {
+ char *start;
+ char c;
+
+ if (*input_line_pointer == ',')
+ input_line_pointer++;
+
+ start = input_line_pointer;
+ c = get_symbol_end ();
+
+ if (strcasecmp (start, "d2") == 0)
+ {
+ value |= 0x80;
+ *input_line_pointer = c;
+ }
+ else if (strcasecmp (start, "d3") == 0)
+ {
+ value |= 0x40;
+ *input_line_pointer = c;
+ }
+ else if (strcasecmp (start, "a2") == 0)
+ {
+ value |= 0x20;
+ *input_line_pointer = c;
+ }
+ else if (strcasecmp (start, "a3") == 0)
+ {
+ value |= 0x10;
+ *input_line_pointer = c;
+ }
+ else if (strcasecmp (start, "other") == 0)
+ {
+ value |= 0x08;
+ *input_line_pointer = c;
+ }
+ else
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ }
+ input_line_pointer++;
+ mn10300_insert_operand (&insn, &extension, operand,
+ value, (char *) NULL, 0, 0);
+ goto keep_going;
+
+ }
+ else if (data_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else if (address_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else if (other_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else if (*str == ')' || *str == '(')
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else
+ {
+ expression (&ex);
+ }
+
+ switch (ex.X_op)
+ {
+ case O_illegal:
+ errmsg = _("illegal operand");
+ goto error;
+ case O_absent:
+ errmsg = _("missing operand");
+ goto error;
+ case O_register:
+ {
+ int mask;
+
+ mask = MN10300_OPERAND_DREG | MN10300_OPERAND_AREG;
+ if ((operand->flags & mask) == 0)
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+
+ if (opcode->format == FMT_D1 || opcode->format == FMT_S1)
+ extra_shift = 8;
+ else if (opcode->format == FMT_D2
+ || opcode->format == FMT_D4
+ || opcode->format == FMT_S2
+ || opcode->format == FMT_S4
+ || opcode->format == FMT_S6
+ || opcode->format == FMT_D5)
+ extra_shift = 16;
+ else
+ extra_shift = 0;
+
+ mn10300_insert_operand (&insn, &extension, operand,
+ ex.X_add_number, (char *) NULL,
+ 0, extra_shift);
+
+
+ /* And note the register number in the register array. */
+ mn10300_reg_operands[op_idx - 1] = ex.X_add_number;
+ break;
+ }
+
+ case O_constant:
+ /* If this operand can be promoted, and it doesn't
+ fit into the allocated bitfield for this insn,
+ then promote it (ie this opcode does not match). */
+ if (operand->flags
+ & (MN10300_OPERAND_PROMOTE | MN10300_OPERAND_RELAX)
+ && ! check_operand (insn, operand, ex.X_add_number))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+
+ mn10300_insert_operand (&insn, &extension, operand,
+ ex.X_add_number, (char *) NULL,
+ 0, 0);
+ break;
+
+ default:
+ /* If this operand can be promoted, then this opcode didn't
+ match since we can't know if it needed promotion! */
+ if (operand->flags & MN10300_OPERAND_PROMOTE)
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+
+ /* We need to generate a fixup for this expression. */
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = *opindex_ptr;
+ fixups[fc].reloc = BFD_RELOC_UNUSED;
+ ++fc;
+ break;
+ }
+
+keep_going:
+ str = input_line_pointer;
+ input_line_pointer = hold;
+
+ while (*str == ' ' || *str == ',')
+ ++str;
+
+ }
+
+ /* Make sure we used all the operands! */
+ if (*str != ',')
+ match = 1;
+
+ /* If this instruction has registers that must not match, verify
+ that they do indeed not match. */
+ if (opcode->no_match_operands)
+ {
+ int i;
+
+ /* Look at each operand to see if it's marked. */
+ for (i = 0; i < MN10300_MAX_OPERANDS; i++)
+ {
+ if ((1 << i) & opcode->no_match_operands)
+ {
+ int j;
+
+ /* operand I is marked. Check that it does not match any
+ operands > I which are marked. */
+ for (j = i + 1; j < MN10300_MAX_OPERANDS; j++)
+ {
+ if (((1 << j) & opcode->no_match_operands)
+ && mn10300_reg_operands[i] == mn10300_reg_operands[j])
+ {
+ errmsg = _("Invalid register specification.");
+ match = 0;
+ goto error;
+ }
+ }
+ }
+ }
+ }
+
+ error:
+ if (match == 0)
+ {
+ next_opcode = opcode + 1;
+ if (!strcmp(next_opcode->name, opcode->name))
+ {
+ opcode = next_opcode;
+ continue;
+ }
+
+ as_bad ("%s", errmsg);
+ return;
+ }
+ break;
+ }
+
+ while (isspace (*str))
+ ++str;
+
+ if (*str != '\0')
+ as_bad (_("junk at end of line: `%s'"), str);
+
+ input_line_pointer = str;
+
+ /* Determine the size of the instruction. */
+ if (opcode->format == FMT_S0)
+ size = 1;
+
+ if (opcode->format == FMT_S1 || opcode->format == FMT_D0)
+ size = 2;
+
+ if (opcode->format == FMT_S2 || opcode->format == FMT_D1)
+ size = 3;
+
+
+ if (opcode->format == FMT_S4)
+ size = 5;
+
+ if (opcode->format == FMT_S6 || opcode->format == FMT_D5)
+ size = 7;
+
+ if (opcode->format == FMT_D2)
+ size = 4;
+
+ if (opcode->format == FMT_D4)
+ size = 6;
+
+ if (relaxable && fc > 0)
+ {
+ int type;
+
+ /* bCC */
+ if (size == 2)
+ {
+ /* Handle bra specially. Basically treat it like jmp so
+ that we automatically handle 8, 16 and 32 bit offsets
+ correctly as well as jumps to an undefined address.
+
+ It is also important to not treat it like other bCC
+ instructions since the long forms of bra is different
+ from other bCC instructions. */
+ if (opcode->opcode == 0xca00)
+ type = 10;
+ else
+ type = 0;
+ }
+ /* call */
+ else if (size == 5)
+ type = 6;
+ /* calls */
+ else if (size == 4)
+ type = 8;
+ /* jmp */
+ else if (size == 3 && opcode->opcode == 0xcc0000)
+ type = 10;
+ /* bCC (uncommon cases) */
+ else
+ type = 3;
+
+ f = frag_var (rs_machine_dependent, 8, 8 - size, type,
+ fixups[0].exp.X_add_symbol,
+ fixups[0].exp.X_add_number,
+ (char *)fixups[0].opindex);
+
+ /* This is pretty hokey. We basically just care about the
+ opcode, so we have to write out the first word big endian.
+
+ The exception is "call", which has two operands that we
+ care about.
+
+ The first operand (the register list) happens to be in the
+ first instruction word, and will be in the right place if
+ we output the first word in big endian mode.
+
+ The second operand (stack size) is in the extension word,
+ and we want it to appear as the first character in the extension
+ word (as it appears in memory). Luckily, writing the extension
+ word in big endian format will do what we want. */
+ number_to_chars_bigendian (f, insn, size > 4 ? 4 : size);
+ if (size > 8)
+ {
+ number_to_chars_bigendian (f + 4, extension, 4);
+ number_to_chars_bigendian (f + 8, 0, size - 8);
+ }
+ else if (size > 4)
+ number_to_chars_bigendian (f + 4, extension, size - 4);
+ }
+ else
+ {
+ /* Allocate space for the instruction. */
+ f = frag_more (size);
+
+ /* Fill in bytes for the instruction. Note that opcode fields
+ are written big-endian, 16 & 32bit immediates are written
+ little endian. Egad. */
+ if (opcode->format == FMT_S0
+ || opcode->format == FMT_S1
+ || opcode->format == FMT_D0
+ || opcode->format == FMT_D1)
+ {
+ number_to_chars_bigendian (f, insn, size);
+ }
+ else if (opcode->format == FMT_S2
+ && opcode->opcode != 0xdf0000
+ && opcode->opcode != 0xde0000)
+ {
+ /* A format S2 instruction that is _not_ "ret" and "retf". */
+ number_to_chars_bigendian (f, (insn >> 16) & 0xff, 1);
+ number_to_chars_littleendian (f + 1, insn & 0xffff, 2);
+ }
+ else if (opcode->format == FMT_S2)
+ {
+ /* This must be a ret or retf, which is written entirely in
+ big-endian format. */
+ number_to_chars_bigendian (f, insn, 3);
+ }
+ else if (opcode->format == FMT_S4
+ && opcode->opcode != 0xdc000000)
+ {
+ /* This must be a format S4 "call" instruction. What a pain. */
+ unsigned long temp = (insn >> 8) & 0xffff;
+ number_to_chars_bigendian (f, (insn >> 24) & 0xff, 1);
+ number_to_chars_littleendian (f + 1, temp, 2);
+ number_to_chars_bigendian (f + 3, insn & 0xff, 1);
+ number_to_chars_bigendian (f + 4, extension & 0xff, 1);
+ }
+ else if (opcode->format == FMT_S4)
+ {
+ /* This must be a format S4 "jmp" instruction. */
+ unsigned long temp = ((insn & 0xffffff) << 8) | (extension & 0xff);
+ number_to_chars_bigendian (f, (insn >> 24) & 0xff, 1);
+ number_to_chars_littleendian (f + 1, temp, 4);
+ }
+ else if (opcode->format == FMT_S6)
+ {
+ unsigned long temp = ((insn & 0xffffff) << 8)
+ | ((extension >> 16) & 0xff);
+ number_to_chars_bigendian (f, (insn >> 24) & 0xff, 1);
+ number_to_chars_littleendian (f + 1, temp, 4);
+ number_to_chars_bigendian (f + 5, (extension >> 8) & 0xff, 1);
+ number_to_chars_bigendian (f + 6, extension & 0xff, 1);
+ }
+ else if (opcode->format == FMT_D2
+ && opcode->opcode != 0xfaf80000
+ && opcode->opcode != 0xfaf00000
+ && opcode->opcode != 0xfaf40000)
+ {
+ /* A format D2 instruction where the 16bit immediate is
+ really a single 16bit value, not two 8bit values. */
+ number_to_chars_bigendian (f, (insn >> 16) & 0xffff, 2);
+ number_to_chars_littleendian (f + 2, insn & 0xffff, 2);
+ }
+ else if (opcode->format == FMT_D2)
+ {
+ /* A format D2 instruction where the 16bit immediate
+ is really two 8bit immediates. */
+ number_to_chars_bigendian (f, insn, 4);
+ }
+ else if (opcode->format == FMT_D4)
+ {
+ unsigned long temp = ((insn & 0xffff) << 16) | (extension & 0xffff);
+ number_to_chars_bigendian (f, (insn >> 16) & 0xffff, 2);
+ number_to_chars_littleendian (f + 2, temp, 4);
+ }
+ else if (opcode->format == FMT_D5)
+ {
+ unsigned long temp = ((insn & 0xffff) << 16)
+ | ((extension >> 8) & 0xffff);
+ number_to_chars_bigendian (f, (insn >> 16) & 0xffff, 2);
+ number_to_chars_littleendian (f + 2, temp, 4);
+ number_to_chars_bigendian (f + 6, extension & 0xff, 1);
+ }
+
+ /* Create any fixups. */
+ for (i = 0; i < fc; i++)
+ {
+ const struct mn10300_operand *operand;
+
+ operand = &mn10300_operands[fixups[i].opindex];
+ if (fixups[i].reloc != BFD_RELOC_UNUSED)
+ {
+ reloc_howto_type *reloc_howto;
+ int size;
+ int offset;
+ fixS *fixP;
+
+ reloc_howto = bfd_reloc_type_lookup (stdoutput, fixups[i].reloc);
+
+ if (!reloc_howto)
+ abort();
+
+ size = bfd_get_reloc_size (reloc_howto);
+
+ if (size < 1 || size > 4)
+ abort();
+
+ offset = 4 - size;
+ fixP = fix_new_exp (frag_now, f - frag_now->fr_literal + offset,
+ size, &fixups[i].exp,
+ reloc_howto->pc_relative,
+ fixups[i].reloc);
+ }
+ else
+ {
+ int reloc, pcrel, reloc_size, offset;
+ fixS *fixP;
+
+ reloc = BFD_RELOC_NONE;
+ /* How big is the reloc? Remember SPLIT relocs are
+ implicitly 32bits. */
+ if ((operand->flags & MN10300_OPERAND_SPLIT) != 0)
+ reloc_size = 32;
+ else
+ reloc_size = operand->bits;
+
+ /* Is the reloc pc-relative? */
+ pcrel = (operand->flags & MN10300_OPERAND_PCREL) != 0;
+
+ /* Gross. This disgusting hack is to make sure we
+ get the right offset for the 16/32 bit reloc in
+ "call" instructions. Basically they're a pain
+ because the reloc isn't at the end of the instruction. */
+ if ((size == 5 || size == 7)
+ && (((insn >> 24) & 0xff) == 0xcd
+ || ((insn >> 24) & 0xff) == 0xdd))
+ size -= 2;
+
+ /* Similarly for certain bit instructions which don't
+ hav their 32bit reloc at the tail of the instruction. */
+ if (size == 7
+ && (((insn >> 16) & 0xffff) == 0xfe00
+ || ((insn >> 16) & 0xffff) == 0xfe01
+ || ((insn >> 16) & 0xffff) == 0xfe02))
+ size -= 1;
+
+ offset = size - reloc_size / 8;
+
+ /* Choose a proper BFD relocation type. */
+ if (pcrel)
+ {
+ if (reloc_size == 32)
+ reloc = BFD_RELOC_32_PCREL;
+ else if (reloc_size == 16)
+ reloc = BFD_RELOC_16_PCREL;
+ else if (reloc_size == 8)
+ reloc = BFD_RELOC_8_PCREL;
+ else
+ abort ();
+ }
+ else
+ {
+ if (reloc_size == 32)
+ reloc = BFD_RELOC_32;
+ else if (reloc_size == 16)
+ reloc = BFD_RELOC_16;
+ else if (reloc_size == 8)
+ reloc = BFD_RELOC_8;
+ else
+ abort ();
+ }
+
+ /* Convert the size of the reloc into what fix_new_exp wants. */
+ reloc_size = reloc_size / 8;
+ if (reloc_size == 8)
+ reloc_size = 0;
+ else if (reloc_size == 16)
+ reloc_size = 1;
+ else if (reloc_size == 32)
+ reloc_size = 2;
+
+ fixP = fix_new_exp (frag_now, f - frag_now->fr_literal + offset,
+ reloc_size, &fixups[i].exp, pcrel,
+ ((bfd_reloc_code_real_type) reloc));
+
+ if (pcrel)
+ fixP->fx_offset += offset;
+ }
+ }
+ }
+}
+
+
+/* if while processing a fixup, a reloc really needs to be created */
+/* then it is done here */
+
+arelent *
+tc_gen_reloc (seg, fixp)
+ asection *seg;
+ fixS *fixp;
+{
+ arelent *reloc;
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("reloc %d not supported by object file format"),
+ (int)fixp->fx_r_type);
+ return NULL;
+ }
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ if (fixp->fx_addsy && fixp->fx_subsy)
+ {
+
+ if ((S_GET_SEGMENT (fixp->fx_addsy) != S_GET_SEGMENT (fixp->fx_subsy))
+ || S_GET_SEGMENT (fixp->fx_addsy) == undefined_section)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ "Difference of symbols in different sections is not supported");
+ return NULL;
+ }
+
+ reloc->sym_ptr_ptr = &bfd_abs_symbol;
+ reloc->addend = (S_GET_VALUE (fixp->fx_addsy)
+ - S_GET_VALUE (fixp->fx_subsy) + fixp->fx_offset);
+ }
+ else
+ {
+ reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+ reloc->addend = fixp->fx_offset;
+ }
+ return reloc;
+}
+
+int
+md_estimate_size_before_relax (fragp, seg)
+ fragS *fragp;
+ asection *seg;
+{
+ if (fragp->fr_subtype == 0)
+ return 2;
+ if (fragp->fr_subtype == 3)
+ return 3;
+ if (fragp->fr_subtype == 6)
+ {
+ if (!S_IS_DEFINED (fragp->fr_symbol)
+ || seg != S_GET_SEGMENT (fragp->fr_symbol))
+ {
+ fragp->fr_subtype = 7;
+ return 7;
+ }
+ else
+ return 5;
+ }
+ if (fragp->fr_subtype == 8)
+ {
+ if (!S_IS_DEFINED (fragp->fr_symbol)
+ || seg != S_GET_SEGMENT (fragp->fr_symbol))
+ {
+ fragp->fr_subtype = 9;
+ return 6;
+ }
+ else
+ return 4;
+ }
+ if (fragp->fr_subtype == 10)
+ {
+ if (!S_IS_DEFINED (fragp->fr_symbol)
+ || seg != S_GET_SEGMENT (fragp->fr_symbol))
+ {
+ fragp->fr_subtype = 12;
+ return 5;
+ }
+ else
+ return 2;
+ }
+}
+
+long
+md_pcrel_from (fixp)
+ fixS *fixp;
+{
+ return fixp->fx_frag->fr_address;
+#if 0
+ if (fixp->fx_addsy != (symbolS *) NULL && ! S_IS_DEFINED (fixp->fx_addsy))
+ {
+ /* The symbol is undefined. Let the linker figure it out. */
+ return 0;
+ }
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+#endif
+}
+
+int
+md_apply_fix3 (fixp, valuep, seg)
+ fixS *fixp;
+ valueT *valuep;
+ segT seg;
+{
+ /* We shouldn't ever get here because linkrelax is nonzero. */
+ abort ();
+ fixp->fx_done = 1;
+ return 0;
+}
+
+/* Insert an operand value into an instruction. */
+
+static void
+mn10300_insert_operand (insnp, extensionp, operand, val, file, line, shift)
+ unsigned long *insnp;
+ unsigned long *extensionp;
+ const struct mn10300_operand *operand;
+ offsetT val;
+ char *file;
+ unsigned int line;
+ unsigned int shift;
+{
+ /* No need to check 32bit operands for a bit. Note that
+ MN10300_OPERAND_SPLIT is an implicit 32bit operand. */
+ if (operand->bits != 32
+ && (operand->flags & MN10300_OPERAND_SPLIT) == 0)
+ {
+ long min, max;
+ offsetT test;
+ int bits;
+
+ bits = operand->bits;
+
+ if ((operand->flags & MN10300_OPERAND_SIGNED) != 0)
+ {
+ max = (1 << (bits - 1)) - 1;
+ min = - (1 << (bits - 1));
+ }
+ else
+ {
+ max = (1 << bits) - 1;
+ min = 0;
+ }
+
+ test = val;
+
+
+ if (test < (offsetT) min || test > (offsetT) max)
+ {
+ const char *err =
+ _("operand out of range (%s not between %ld and %ld)");
+ char buf[100];
+
+ sprint_value (buf, test);
+ if (file == (char *) NULL)
+ as_warn (err, buf, min, max);
+ else
+ as_warn_where (file, line, err, buf, min, max);
+ }
+ }
+
+ if ((operand->flags & MN10300_OPERAND_SPLIT) != 0)
+ {
+ *insnp |= (val >> (32 - operand->bits)) & ((1 << operand->bits) - 1);
+ *extensionp |= ((val & ((1 << (32 - operand->bits)) - 1))
+ << operand->shift);
+ }
+ else if ((operand->flags & MN10300_OPERAND_EXTENDED) == 0)
+ {
+ *insnp |= (((long) val & ((1 << operand->bits) - 1))
+ << (operand->shift + shift));
+
+ if ((operand->flags & MN10300_OPERAND_REPEATED) != 0)
+ *insnp |= (((long) val & ((1 << operand->bits) - 1))
+ << (operand->shift + shift + operand->bits));
+ }
+ else
+ {
+ *extensionp |= (((long) val & ((1 << operand->bits) - 1))
+ << (operand->shift + shift));
+
+ if ((operand->flags & MN10300_OPERAND_REPEATED) != 0)
+ *extensionp |= (((long) val & ((1 << operand->bits) - 1))
+ << (operand->shift + shift + operand->bits));
+ }
+}
+
+static unsigned long
+check_operand (insn, operand, val)
+ unsigned long insn;
+ const struct mn10300_operand *operand;
+ offsetT val;
+{
+ /* No need to check 32bit operands for a bit. Note that
+ MN10300_OPERAND_SPLIT is an implicit 32bit operand. */
+ if (operand->bits != 32
+ && (operand->flags & MN10300_OPERAND_SPLIT) == 0)
+ {
+ long min, max;
+ offsetT test;
+ int bits;
+
+ bits = operand->bits;
+
+ if ((operand->flags & MN10300_OPERAND_SIGNED) != 0)
+ {
+ max = (1 << (bits - 1)) - 1;
+ min = - (1 << (bits - 1));
+ }
+ else
+ {
+ max = (1 << bits) - 1;
+ min = 0;
+ }
+
+ test = val;
+
+
+ if (test < (offsetT) min || test > (offsetT) max)
+ return 0;
+ else
+ return 1;
+ }
+ return 1;
+}
+
+static void
+set_arch_mach (mach)
+ int mach;
+{
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_mn10300, mach))
+ as_warn (_("could not set architecture and machine"));
+
+ current_machine = mach;
+}
diff --git a/gas/config/tc-mn10300.h b/gas/config/tc-mn10300.h
new file mode 100644
index 0000000..ffcb227
--- /dev/null
+++ b/gas/config/tc-mn10300.h
@@ -0,0 +1,50 @@
+/* tc-mn10300.h -- Header file for tc-mn10300.c.
+ Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#define TC_MN10300
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#ifndef BFD_ASSEMBLER
+ #error MN10300 support requires BFD_ASSEMBLER
+#endif
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_mn10300
+
+#define TARGET_FORMAT "elf32-mn10300"
+
+#define MD_APPLY_FIX3
+#define md_operand(x)
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+#define md_number_to_chars number_to_chars_littleendian
+
+/* Don't bother to adjust relocs. */
+#define tc_fix_adjustable(FIX) 0
+
+/* We do relaxing in the assembler as well as the linker. */
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
diff --git a/gas/config/tc-ns32k.c b/gas/config/tc-ns32k.c
new file mode 100644
index 0000000..42dc528
--- /dev/null
+++ b/gas/config/tc-ns32k.c
@@ -0,0 +1,2328 @@
+/* ns32k.c -- Assemble on the National Semiconductor 32k series
+ Copyright (C) 1987, 92, 93, 94, 95, 96, 1997, 1998 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+/*#define SHOW_NUM 1*//* uncomment for debugging */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "as.h"
+#include "opcode/ns32k.h"
+
+#include "obstack.h"
+
+/* Macros */
+#define IIF_ENTRIES 13 /* number of entries in iif */
+#define PRIVATE_SIZE 256 /* size of my garbage memory */
+#define MAX_ARGS 4
+#define DEFAULT -1 /* addr_mode returns this value when
+ plain constant or label is
+ encountered */
+
+#define IIF(ptr,a1,c1,e1,g1,i1,k1,m1,o1,q1,s1,u1) \
+ iif.iifP[ptr].type= a1; \
+ iif.iifP[ptr].size= c1; \
+ iif.iifP[ptr].object= e1; \
+ iif.iifP[ptr].object_adjust= g1; \
+ iif.iifP[ptr].pcrel= i1; \
+ iif.iifP[ptr].pcrel_adjust= k1; \
+ iif.iifP[ptr].im_disp= m1; \
+ iif.iifP[ptr].relax_substate= o1; \
+ iif.iifP[ptr].bit_fixP= q1; \
+ iif.iifP[ptr].addr_mode= s1; \
+ iif.iifP[ptr].bsr= u1;
+
+#ifdef SEQUENT_COMPATABILITY
+#define LINE_COMMENT_CHARS "|"
+#define ABSOLUTE_PREFIX '@'
+#define IMMEDIATE_PREFIX '#'
+#endif
+
+#ifndef LINE_COMMENT_CHARS
+#define LINE_COMMENT_CHARS "#"
+#endif
+
+const char comment_chars[] = "#";
+const char line_comment_chars[] = LINE_COMMENT_CHARS;
+const char line_separator_chars[] = "";
+#if !defined(ABSOLUTE_PREFIX) && !defined(IMMEDIATE_PREFIX)
+#define ABSOLUTE_PREFIX '@' /* One or the other MUST be defined */
+#endif
+
+struct addr_mode
+ {
+ char mode; /* addressing mode of operand (0-31) */
+ char scaled_mode; /* mode combined with scaled mode */
+ char scaled_reg; /* register used in scaled+1 (1-8) */
+ char float_flag; /* set if R0..R7 was F0..F7 ie a
+ floating-point-register */
+ char am_size; /* estimated max size of general addr-mode
+ parts */
+ char im_disp; /* if im_disp==1 we have a displacement */
+ char pcrel; /* 1 if pcrel, this is really redundant info */
+ char disp_suffix[2]; /* length of displacement(s), 0=undefined */
+ char *disp[2]; /* pointer(s) at displacement(s)
+ or immediates(s) (ascii) */
+ char index_byte; /* index byte */
+ };
+typedef struct addr_mode addr_modeS;
+
+
+char *freeptr, *freeptr_static; /* points at some number of free bytes */
+struct hash_control *inst_hash_handle;
+
+struct ns32k_opcode *desc; /* pointer at description of instruction */
+addr_modeS addr_modeP;
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "fd"; /* we don't want to support lowercase, do we */
+
+/* UPPERCASE denotes live names when an instruction is built, IIF is
+ * used as an intermediate form to store the actual parts of the
+ * instruction. A ns32k machine instruction can be divided into a
+ * couple of sub PARTs. When an instruction is assembled the
+ * appropriate PART get an assignment. When an IIF has been completed
+ * it is converted to a FRAGment as specified in AS.H */
+
+/* internal structs */
+struct ns32k_option
+ {
+ char *pattern;
+ unsigned long or;
+ unsigned long and;
+ };
+
+typedef struct
+ {
+ int type; /* how to interpret object */
+ int size; /* Estimated max size of object */
+ unsigned long object; /* binary data */
+ int object_adjust; /* number added to object */
+ int pcrel; /* True if object is pcrel */
+ int pcrel_adjust; /* length in bytes from the
+ instruction start to the
+ displacement */
+ int im_disp; /* True if the object is a displacement */
+ relax_substateT relax_substate; /* Initial relaxsubstate */
+ bit_fixS *bit_fixP; /* Pointer at bit_fix struct */
+ int addr_mode; /* What addrmode do we associate with this
+ iif-entry */
+ char bsr; /* Sequent hack */
+ } iif_entryT; /* Internal Instruction Format */
+
+struct int_ins_form
+ {
+ int instr_size; /* Max size of instruction in bytes. */
+ iif_entryT iifP[IIF_ENTRIES + 1];
+ };
+struct int_ins_form iif;
+expressionS exprP;
+char *input_line_pointer;
+/* description of the PARTs in IIF
+ *object[n]:
+ * 0 total length in bytes of entries in iif
+ * 1 opcode
+ * 2 index_byte_a
+ * 3 index_byte_b
+ * 4 disp_a_1
+ * 5 disp_a_2
+ * 6 disp_b_1
+ * 7 disp_b_2
+ * 8 imm_a
+ * 9 imm_b
+ * 10 implied1
+ * 11 implied2
+ *
+ * For every entry there is a datalength in bytes. This is stored in size[n].
+ * 0, the objectlength is not explicitly given by the instruction
+ * and the operand is undefined. This is a case for relaxation.
+ * Reserve 4 bytes for the final object.
+ *
+ * 1, the entry contains one byte
+ * 2, the entry contains two bytes
+ * 3, the entry contains three bytes
+ * 4, the entry contains four bytes
+ * etc
+ *
+ * Furthermore, every entry has a data type identifier in type[n].
+ *
+ * 0, the entry is void, ignore it.
+ * 1, the entry is a binary number.
+ * 2, the entry is a pointer at an expression.
+ * Where expression may be as simple as a single '1',
+ * and as complicated as foo-bar+12,
+ * foo and bar may be undefined but suffixed by :{b|w|d} to
+ * control the length of the object.
+ *
+ * 3, the entry is a pointer at a bignum struct
+ *
+ *
+ * The low-order-byte coresponds to low physical memory.
+ * Obviously a FRAGment must be created for each valid disp in PART whose
+ * datalength is undefined (to bad) .
+ * The case where just the expression is undefined is less severe and is
+ * handled by fix. Here the number of bytes in the objectfile is known.
+ * With this representation we simplify the assembly and separates the
+ * machine dependent/independent parts in a more clean way (said OE)
+ */
+
+struct ns32k_option opt1[] = /* restore, exit */
+{
+ {"r0", 0x80, 0xff},
+ {"r1", 0x40, 0xff},
+ {"r2", 0x20, 0xff},
+ {"r3", 0x10, 0xff},
+ {"r4", 0x08, 0xff},
+ {"r5", 0x04, 0xff},
+ {"r6", 0x02, 0xff},
+ {"r7", 0x01, 0xff},
+ {0, 0x00, 0xff}
+};
+struct ns32k_option opt2[] = /* save, enter */
+{
+ {"r0", 0x01, 0xff},
+ {"r1", 0x02, 0xff},
+ {"r2", 0x04, 0xff},
+ {"r3", 0x08, 0xff},
+ {"r4", 0x10, 0xff},
+ {"r5", 0x20, 0xff},
+ {"r6", 0x40, 0xff},
+ {"r7", 0x80, 0xff},
+ {0, 0x00, 0xff}
+};
+struct ns32k_option opt3[] = /* setcfg */
+{
+ {"c", 0x8, 0xff},
+ {"m", 0x4, 0xff},
+ {"f", 0x2, 0xff},
+ {"i", 0x1, 0xff},
+ {0, 0x0, 0xff}
+};
+struct ns32k_option opt4[] = /* cinv */
+{
+ {"a", 0x4, 0xff},
+ {"i", 0x2, 0xff},
+ {"d", 0x1, 0xff},
+ {0, 0x0, 0xff}
+};
+struct ns32k_option opt5[] = /* string inst */
+{
+ {"b", 0x2, 0xff},
+ {"u", 0xc, 0xff},
+ {"w", 0x4, 0xff},
+ {0, 0x0, 0xff}
+};
+struct ns32k_option opt6[] = /* plain reg ext,cvtp etc */
+{
+ {"r0", 0x00, 0xff},
+ {"r1", 0x01, 0xff},
+ {"r2", 0x02, 0xff},
+ {"r3", 0x03, 0xff},
+ {"r4", 0x04, 0xff},
+ {"r5", 0x05, 0xff},
+ {"r6", 0x06, 0xff},
+ {"r7", 0x07, 0xff},
+ {0, 0x00, 0xff}
+};
+
+#if !defined(NS32032) && !defined(NS32532)
+#define NS32532
+#endif
+
+struct ns32k_option cpureg_532[] = /* lpr spr */
+{
+ {"us", 0x0, 0xff},
+ {"dcr", 0x1, 0xff},
+ {"bpc", 0x2, 0xff},
+ {"dsr", 0x3, 0xff},
+ {"car", 0x4, 0xff},
+ {"fp", 0x8, 0xff},
+ {"sp", 0x9, 0xff},
+ {"sb", 0xa, 0xff},
+ {"usp", 0xb, 0xff},
+ {"cfg", 0xc, 0xff},
+ {"psr", 0xd, 0xff},
+ {"intbase", 0xe, 0xff},
+ {"mod", 0xf, 0xff},
+ {0, 0x00, 0xff}
+};
+struct ns32k_option mmureg_532[] = /* lmr smr */
+{
+ {"mcr", 0x9, 0xff},
+ {"msr", 0xa, 0xff},
+ {"tear", 0xb, 0xff},
+ {"ptb0", 0xc, 0xff},
+ {"ptb1", 0xd, 0xff},
+ {"ivar0", 0xe, 0xff},
+ {"ivar1", 0xf, 0xff},
+ {0, 0x0, 0xff}
+};
+
+struct ns32k_option cpureg_032[] = /* lpr spr */
+{
+ {"upsr", 0x0, 0xff},
+ {"fp", 0x8, 0xff},
+ {"sp", 0x9, 0xff},
+ {"sb", 0xa, 0xff},
+ {"psr", 0xd, 0xff},
+ {"intbase", 0xe, 0xff},
+ {"mod", 0xf, 0xff},
+ {0, 0x0, 0xff}
+};
+struct ns32k_option mmureg_032[] = /* lmr smr */
+{
+ {"bpr0", 0x0, 0xff},
+ {"bpr1", 0x1, 0xff},
+ {"pf0", 0x4, 0xff},
+ {"pf1", 0x5, 0xff},
+ {"sc", 0x8, 0xff},
+ {"msr", 0xa, 0xff},
+ {"bcnt", 0xb, 0xff},
+ {"ptb0", 0xc, 0xff},
+ {"ptb1", 0xd, 0xff},
+ {"eia", 0xf, 0xff},
+ {0, 0x0, 0xff}
+};
+
+#if defined(NS32532)
+struct ns32k_option *cpureg = cpureg_532;
+struct ns32k_option *mmureg = mmureg_532;
+#else
+struct ns32k_option *cpureg = cpureg_032;
+struct ns32k_option *mmureg = mmureg_032;
+#endif
+
+
+const pseudo_typeS md_pseudo_table[] =
+{ /* so far empty */
+ {0, 0, 0}
+};
+
+#define IND(x,y) (((x)<<2)+(y))
+
+/* those are index's to relax groups in md_relax_table ie it must be
+ multiplied by 4 to point at a group start. Viz IND(x,y) Se function
+ relax_segment in write.c for more info */
+
+#define BRANCH 1
+#define PCREL 2
+
+/* those are index's to entries in a relax group */
+
+#define BYTE 0
+#define WORD 1
+#define DOUBLE 2
+#define UNDEF 3
+/* Those limits are calculated from the displacement start in memory.
+ The ns32k uses the begining of the instruction as displacement
+ base. This type of displacements could be handled here by moving
+ the limit window up or down. I choose to use an internal
+ displacement base-adjust as there are other routines that must
+ consider this. Also, as we have two various offset-adjusts in the
+ ns32k (acb versus br/brs/jsr/bcond), two set of limits would have
+ had to be used. Now we dont have to think about that. */
+
+
+const relax_typeS md_relax_table[] =
+{
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+
+ {(63), (-64), 1, IND (BRANCH, WORD)},
+ {(8192), (-8192), 2, IND (BRANCH, DOUBLE)},
+ {0, 0, 4, 0},
+ {1, 1, 0, 0}
+};
+
+/* Array used to test if mode contains displacements.
+ Value is true if mode contains displacement. */
+
+char disp_test[] =
+{0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 0, 0, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1};
+
+/* Array used to calculate max size of displacements */
+
+char disp_size[] =
+{4, 1, 2, 0, 4};
+
+static void evaluate_expr PARAMS ((expressionS * resultP, char *ptr));
+static void md_number_to_disp PARAMS ((char *buf, long val, int n));
+static void md_number_to_imm PARAMS ((char *buf, long val, int n));
+
+/* Parses a general operand into an addressingmode struct
+
+ in: pointer at operand in ascii form
+ pointer at addr_mode struct for result
+ the level of recursion. (always 0 or 1)
+
+ out: data in addr_mode struct
+ */
+int
+addr_mode (operand, addr_modeP, recursive_level)
+ char *operand;
+ register addr_modeS *addr_modeP;
+ int recursive_level;
+{
+ register char *str;
+ register int i;
+ register int strl;
+ register int mode;
+ int j;
+ mode = DEFAULT; /* default */
+ addr_modeP->scaled_mode = 0; /* why not */
+ addr_modeP->scaled_reg = 0; /* if 0, not scaled index */
+ addr_modeP->float_flag = 0;
+ addr_modeP->am_size = 0;
+ addr_modeP->im_disp = 0;
+ addr_modeP->pcrel = 0; /* not set in this function */
+ addr_modeP->disp_suffix[0] = 0;
+ addr_modeP->disp_suffix[1] = 0;
+ addr_modeP->disp[0] = NULL;
+ addr_modeP->disp[1] = NULL;
+ str = operand;
+ if (str[0] == 0)
+ {
+ return (0);
+ } /* we don't want this */
+ strl = strlen (str);
+ switch (str[0])
+ {
+ /* the following three case statements controls the mode-chars
+ this is the place to ed if you want to change them */
+#ifdef ABSOLUTE_PREFIX
+ case ABSOLUTE_PREFIX:
+ if (str[strl - 1] == ']')
+ break;
+ addr_modeP->mode = 21; /* absolute */
+ addr_modeP->disp[0] = str + 1;
+ return (-1);
+#endif
+#ifdef IMMEDIATE_PREFIX
+ case IMMEDIATE_PREFIX:
+ if (str[strl - 1] == ']')
+ break;
+ addr_modeP->mode = 20; /* immediate */
+ addr_modeP->disp[0] = str + 1;
+ return (-1);
+#endif
+ case '.':
+ if (str[strl - 1] != ']')
+ {
+ switch (str[1])
+ {
+ case '-':
+ case '+':
+ if (str[2] != '\000')
+ {
+ addr_modeP->mode = 27; /* pc-relativ */
+ addr_modeP->disp[0] = str + 2;
+ return (-1);
+ }
+ default:
+ as_warn (_("Invalid syntax in PC-relative addressing mode"));
+ return (0);
+ }
+ }
+ break;
+ case 'e':
+ if (str[strl - 1] != ']')
+ {
+ if ((!strncmp (str, "ext(", 4)) && strl > 7)
+ { /* external */
+ addr_modeP->disp[0] = str + 4;
+ i = 0;
+ j = 2;
+ do
+ { /* disp[0]'s termination point */
+ j += 1;
+ if (str[j] == '(')
+ i++;
+ if (str[j] == ')')
+ i--;
+ }
+ while (j < strl && i != 0);
+ if (i != 0 || !(str[j + 1] == '-' || str[j + 1] == '+'))
+ {
+ as_warn (_("Invalid syntax in External addressing mode"));
+ return (0);
+ }
+ str[j] = '\000'; /* null terminate disp[0] */
+ addr_modeP->disp[1] = str + j + 2;
+ addr_modeP->mode = 22;
+ return (-1);
+ }
+ }
+ break;
+ default:;
+ }
+ strl = strlen (str);
+ switch (strl)
+ {
+ case 2:
+ switch (str[0])
+ {
+ case 'f':
+ addr_modeP->float_flag = 1;
+ case 'r':
+ if (str[1] >= '0' && str[1] < '8')
+ {
+ addr_modeP->mode = str[1] - '0';
+ return (-1);
+ }
+ }
+ case 3:
+ if (!strncmp (str, "tos", 3))
+ {
+ addr_modeP->mode = 23; /* TopOfStack */
+ return (-1);
+ }
+ default:;
+ }
+ if (strl > 4)
+ {
+ if (str[strl - 1] == ')')
+ {
+ if (str[strl - 2] == ')')
+ {
+ if (!strncmp (&str[strl - 5], "(fp", 3))
+ {
+ mode = 16; /* Memory Relative */
+ }
+ if (!strncmp (&str[strl - 5], "(sp", 3))
+ {
+ mode = 17;
+ }
+ if (!strncmp (&str[strl - 5], "(sb", 3))
+ {
+ mode = 18;
+ }
+ if (mode != DEFAULT)
+ { /* memory relative */
+ addr_modeP->mode = mode;
+ j = strl - 5; /* temp for end of disp[0] */
+ i = 0;
+ do
+ {
+ strl -= 1;
+ if (str[strl] == ')')
+ i++;
+ if (str[strl] == '(')
+ i--;
+ }
+ while (strl > -1 && i != 0);
+ if (i != 0)
+ {
+ as_warn (_("Invalid syntax in Memory Relative addressing mode"));
+ return (0);
+ }
+ addr_modeP->disp[1] = str;
+ addr_modeP->disp[0] = str + strl + 1;
+ str[j] = '\000'; /* null terminate disp[0] */
+ str[strl] = '\000'; /* null terminate disp[1] */
+ return (-1);
+ }
+ }
+ switch (str[strl - 3])
+ {
+ case 'r':
+ case 'R':
+ if (str[strl - 2] >= '0'
+ && str[strl - 2] < '8'
+ && str[strl - 4] == '(')
+ {
+ addr_modeP->mode = str[strl - 2] - '0' + 8;
+ addr_modeP->disp[0] = str;
+ str[strl - 4] = 0;
+ return (-1); /* reg rel */
+ }
+ default:
+ if (!strncmp (&str[strl - 4], "(fp", 3))
+ {
+ mode = 24;
+ }
+ if (!strncmp (&str[strl - 4], "(sp", 3))
+ {
+ mode = 25;
+ }
+ if (!strncmp (&str[strl - 4], "(sb", 3))
+ {
+ mode = 26;
+ }
+ if (!strncmp (&str[strl - 4], "(pc", 3))
+ {
+ mode = 27;
+ }
+ if (mode != DEFAULT)
+ {
+ addr_modeP->mode = mode;
+ addr_modeP->disp[0] = str;
+ str[strl - 4] = '\0';
+ return (-1); /* memory space */
+ }
+ }
+ }
+ /* no trailing ')' do we have a ']' ? */
+ if (str[strl - 1] == ']')
+ {
+ switch (str[strl - 2])
+ {
+ case 'b':
+ mode = 28;
+ break;
+ case 'w':
+ mode = 29;
+ break;
+ case 'd':
+ mode = 30;
+ break;
+ case 'q':
+ mode = 31;
+ break;
+ default:;
+ as_warn (_("Invalid scaled-indexed mode, use (b,w,d,q)"));
+ if (str[strl - 3] != ':' || str[strl - 6] != '['
+ || str[strl - 5] == 'r' || str[strl - 4] < '0'
+ || str[strl - 4] > '7')
+ {
+ as_warn (_("Syntax in scaled-indexed mode, use [Rn:m] where n=[0..7] m={b,w,d,q}"));
+ }
+ } /* scaled index */
+ {
+ if (recursive_level > 0)
+ {
+ as_warn (_("Scaled-indexed addressing mode combined with scaled-index"));
+ return (0);
+ }
+ addr_modeP->am_size += 1; /* scaled index byte */
+ j = str[strl - 4] - '0'; /* store temporary */
+ str[strl - 6] = '\000'; /* nullterminate for recursive call */
+ i = addr_mode (str, addr_modeP, 1);
+ if (!i || addr_modeP->mode == 20)
+ {
+ as_warn (_("Invalid or illegal addressing mode combined with scaled-index"));
+ return (0);
+ }
+ addr_modeP->scaled_mode = addr_modeP->mode; /* store the inferior
+ mode */
+ addr_modeP->mode = mode;
+ addr_modeP->scaled_reg = j + 1;
+ return (-1);
+ }
+ }
+ }
+ addr_modeP->mode = DEFAULT; /* default to whatever */
+ addr_modeP->disp[0] = str;
+ return (-1);
+}
+
+/* ptr points at string addr_modeP points at struct with result This
+ routine calls addr_mode to determine the general addr.mode of the
+ operand. When this is ready it parses the displacements for size
+ specifying suffixes and determines size of immediate mode via
+ ns32k-opcode. Also builds index bytes if needed. */
+int
+get_addr_mode (ptr, addr_modeP)
+ char *ptr;
+ addr_modeS *addr_modeP;
+{
+ int tmp;
+ addr_mode (ptr, addr_modeP, 0);
+ if (addr_modeP->mode == DEFAULT || addr_modeP->scaled_mode == -1)
+ {
+ /* resolve ambigious operands, this shouldn't be necessary if
+ one uses standard NSC operand syntax. But the sequent
+ compiler doesn't!!! This finds a proper addressinging mode
+ if it is implicitly stated. See ns32k-opcode.h */
+ (void) evaluate_expr (&exprP, ptr); /* this call takes time Sigh! */
+ if (addr_modeP->mode == DEFAULT)
+ {
+ if (exprP.X_add_symbol || exprP.X_op_symbol)
+ {
+ addr_modeP->mode = desc->default_model; /* we have a label */
+ }
+ else
+ {
+ addr_modeP->mode = desc->default_modec; /* we have a constant */
+ }
+ }
+ else
+ {
+ if (exprP.X_add_symbol || exprP.X_op_symbol)
+ {
+ addr_modeP->scaled_mode = desc->default_model;
+ }
+ else
+ {
+ addr_modeP->scaled_mode = desc->default_modec;
+ }
+ }
+ /* must put this mess down in addr_mode to handle the scaled
+ case better */
+ }
+ /* It appears as the sequent compiler wants an absolute when we have
+ a label without @. Constants becomes immediates besides the addr
+ case. Think it does so with local labels too, not optimum, pcrel
+ is better. When I have time I will make gas check this and
+ select pcrel when possible Actually that is trivial. */
+ if (tmp = addr_modeP->scaled_reg)
+ { /* build indexbyte */
+ tmp--; /* remember regnumber comes incremented for
+ flagpurpose */
+ tmp |= addr_modeP->scaled_mode << 3;
+ addr_modeP->index_byte = (char) tmp;
+ addr_modeP->am_size += 1;
+ }
+ if (disp_test[addr_modeP->mode])
+ { /* there was a displacement, probe for length
+ specifying suffix */
+ {
+ register char c;
+ register char suffix;
+ register char suffix_sub;
+ register int i;
+ register char *toP;
+ register char *fromP;
+
+ addr_modeP->pcrel = 0;
+ if (disp_test[addr_modeP->mode])
+ { /* there is a displacement */
+ if (addr_modeP->mode == 27 || addr_modeP->scaled_mode == 27)
+ { /* do we have pcrel. mode */
+ addr_modeP->pcrel = 1;
+ }
+ addr_modeP->im_disp = 1;
+ for (i = 0; i < 2; i++)
+ {
+ suffix_sub = suffix = 0;
+ if (toP = addr_modeP->disp[i])
+ { /* suffix of expression, the largest size
+ rules */
+ fromP = toP;
+ while (c = *fromP++)
+ {
+ *toP++ = c;
+ if (c == ':')
+ {
+ switch (*fromP)
+ {
+ case '\0':
+ as_warn (_("Premature end of suffix -- Defaulting to d"));
+ suffix = 4;
+ continue;
+ case 'b':
+ suffix_sub = 1;
+ break;
+ case 'w':
+ suffix_sub = 2;
+ break;
+ case 'd':
+ suffix_sub = 4;
+ break;
+ default:
+ as_warn (_("Bad suffix after ':' use {b|w|d} Defaulting to d"));
+ suffix = 4;
+ }
+ fromP++;
+ toP--; /* So we write over the ':' */
+ if (suffix < suffix_sub)
+ suffix = suffix_sub;
+ }
+ }
+ *toP = '\0';/* terminate properly */
+ addr_modeP->disp_suffix[i] = suffix;
+ addr_modeP->am_size += suffix ? suffix : 4;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if (addr_modeP->mode == 20)
+ { /* look in ns32k_opcode for size */
+ addr_modeP->disp_suffix[0] = addr_modeP->am_size = desc->im_size;
+ addr_modeP->im_disp = 0;
+ }
+ }
+ return addr_modeP->mode;
+}
+
+
+/* read an optionlist */
+void
+optlist (str, optionP, default_map)
+ char *str; /* the string to extract options from */
+ struct ns32k_option *optionP; /* how to search the string */
+ unsigned long *default_map; /* default pattern and output */
+{
+ register int i, j, k, strlen1, strlen2;
+ register char *patternP, *strP;
+ strlen1 = strlen (str);
+ if (strlen1 < 1)
+ {
+ as_fatal (_("Very short instr to option, ie you can't do it on a NULLstr"));
+ }
+ for (i = 0; optionP[i].pattern != 0; i++)
+ {
+ strlen2 = strlen (optionP[i].pattern);
+ for (j = 0; j < strlen1; j++)
+ {
+ patternP = optionP[i].pattern;
+ strP = &str[j];
+ for (k = 0; k < strlen2; k++)
+ {
+ if (*(strP++) != *(patternP++))
+ break;
+ }
+ if (k == strlen2)
+ { /* match */
+ *default_map |= optionP[i].or;
+ *default_map &= optionP[i].and;
+ }
+ }
+ }
+}
+
+/* search struct for symbols
+ This function is used to get the short integer form of reg names in
+ the instructions lmr, smr, lpr, spr return true if str is found in
+ list */
+
+int
+list_search (str, optionP, default_map)
+ char *str; /* the string to match */
+ struct ns32k_option *optionP; /* list to search */
+ unsigned long *default_map; /* default pattern and output */
+{
+ register int i;
+ for (i = 0; optionP[i].pattern != 0; i++)
+ {
+ if (!strncmp (optionP[i].pattern, str, 20))
+ { /* use strncmp to be safe */
+ *default_map |= optionP[i].or;
+ *default_map &= optionP[i].and;
+ return -1;
+ }
+ }
+ as_warn (_("No such entry in list. (cpu/mmu register)"));
+ return 0;
+}
+
+static void
+evaluate_expr (resultP, ptr)
+ expressionS *resultP;
+ char *ptr;
+{
+ register char *tmp_line;
+
+ tmp_line = input_line_pointer;
+ input_line_pointer = ptr;
+ expression (&exprP);
+ input_line_pointer = tmp_line;
+}
+
+/* Convert operands to iif-format and adds bitfields to the opcode.
+ Operands are parsed in such an order that the opcode is updated from
+ its most significant bit, that is when the operand need to alter the
+ opcode.
+ Be carefull not to put to objects in the same iif-slot.
+ */
+
+void
+encode_operand (argc, argv, operandsP, suffixP, im_size, opcode_bit_ptr)
+ int argc;
+ char **argv;
+ char *operandsP;
+ char *suffixP;
+ char im_size;
+ char opcode_bit_ptr;
+{
+ register int i, j;
+ char d;
+ int pcrel, tmp, b, loop, pcrel_adjust;
+ for (loop = 0; loop < argc; loop++)
+ {
+ i = operandsP[loop << 1] - '1'; /* what operand are we supposed
+ to work on */
+ if (i > 3)
+ as_fatal (_("Internal consistency error. check ns32k-opcode.h"));
+ pcrel = 0;
+ pcrel_adjust = 0;
+ tmp = 0;
+ switch ((d = operandsP[(loop << 1) + 1]))
+ {
+ case 'f': /* operand of sfsr turns out to be a nasty
+ specialcase */
+ opcode_bit_ptr -= 5;
+ case 'Z': /* float not immediate */
+ case 'F': /* 32 bit float general form */
+ case 'L': /* 64 bit float */
+ case 'I': /* integer not immediate */
+ case 'B': /* byte */
+ case 'W': /* word */
+ case 'D': /* double-word */
+ case 'A': /* double-word gen-address-form ie no regs
+ allowed */
+ get_addr_mode (argv[i], &addr_modeP);
+ if((addr_modeP.mode == 20) &&
+ (d == 'I' || d == 'Z' || d == 'A')) {
+ as_fatal(d == 'A'? _("Address of immediate operand"):
+ _("Invalid immediate write operand."));
+ }
+
+ if (opcode_bit_ptr == desc->opcode_size)
+ b = 4;
+ else
+ b = 6;
+ for (j = b; j < (b + 2); j++)
+ {
+ if (addr_modeP.disp[j - b])
+ {
+ IIF (j,
+ 2,
+ addr_modeP.disp_suffix[j - b],
+ (unsigned long) addr_modeP.disp[j - b],
+ 0,
+ addr_modeP.pcrel,
+ iif.instr_size,
+ addr_modeP.im_disp,
+ IND (BRANCH, BYTE),
+ NULL,
+ (addr_modeP.scaled_reg ? addr_modeP.scaled_mode
+ : addr_modeP.mode),
+ 0);
+ }
+ }
+ opcode_bit_ptr -= 5;
+ iif.iifP[1].object |= ((long) addr_modeP.mode) << opcode_bit_ptr;
+ if (addr_modeP.scaled_reg)
+ {
+ j = b / 2;
+ IIF (j, 1, 1, (unsigned long) addr_modeP.index_byte,
+ 0, 0, 0, 0, 0, NULL, -1, 0);
+ }
+ break;
+ case 'b': /* multiple instruction disp */
+ freeptr++; /* OVE:this is an useful hack */
+ sprintf (freeptr, "((%s-1)*%d)\000", argv[i], desc->im_size);
+ argv[i] = freeptr;
+ pcrel -= 1; /* make pcrel 0 inspite of what case 'p':
+ wants */
+ /* fall thru */
+ case 'p': /* displacement - pc relative addressing */
+ pcrel += 1;
+ /* fall thru */
+ case 'd': /* displacement */
+ iif.instr_size += suffixP[i] ? suffixP[i] : 4;
+ IIF (12, 2, suffixP[i], (unsigned long) argv[i], 0,
+ pcrel, pcrel_adjust, 1, IND (BRANCH, BYTE), NULL, -1, 0);
+ break;
+ case 'H': /* sequent-hack: the linker wants a bit set
+ when bsr */
+ pcrel = 1;
+ iif.instr_size += suffixP[i] ? suffixP[i] : 4;
+ IIF (12, 2, suffixP[i], (unsigned long) argv[i], 0,
+ pcrel, pcrel_adjust, 1, IND (BRANCH, BYTE), NULL, -1, 1);
+ break;
+ case 'q': /* quick */
+ opcode_bit_ptr -= 4;
+ IIF (11, 2, 42, (unsigned long) argv[i], 0, 0, 0, 0, 0,
+ bit_fix_new (4, opcode_bit_ptr, -8, 7, 0, 1, 0), -1, 0);
+ break;
+ case 'r': /* register number (3 bits) */
+ list_search (argv[i], opt6, &tmp);
+ opcode_bit_ptr -= 3;
+ iif.iifP[1].object |= tmp << opcode_bit_ptr;
+ break;
+ case 'O': /* setcfg instruction optionslist */
+ optlist (argv[i], opt3, &tmp);
+ opcode_bit_ptr -= 4;
+ iif.iifP[1].object |= tmp << 15;
+ break;
+ case 'C': /* cinv instruction optionslist */
+ optlist (argv[i], opt4, &tmp);
+ opcode_bit_ptr -= 4;
+ iif.iifP[1].object |= tmp << 15; /* insert the regtype in opcode */
+ break;
+ case 'S': /* stringinstruction optionslist */
+ optlist (argv[i], opt5, &tmp);
+ opcode_bit_ptr -= 4;
+ iif.iifP[1].object |= tmp << 15;
+ break;
+ case 'u':
+ case 'U': /* registerlist */
+ IIF (10, 1, 1, 0, 0, 0, 0, 0, 0, NULL, -1, 0);
+ switch (operandsP[(i << 1) + 1])
+ {
+ case 'u': /* restore, exit */
+ optlist (argv[i], opt1, &iif.iifP[10].object);
+ break;
+ case 'U': /* save,enter */
+ optlist (argv[i], opt2, &iif.iifP[10].object);
+ break;
+ }
+ iif.instr_size += 1;
+ break;
+ case 'M': /* mmu register */
+ list_search (argv[i], mmureg, &tmp);
+ opcode_bit_ptr -= 4;
+ iif.iifP[1].object |= tmp << opcode_bit_ptr;
+ break;
+ case 'P': /* cpu register */
+ list_search (argv[i], cpureg, &tmp);
+ opcode_bit_ptr -= 4;
+ iif.iifP[1].object |= tmp << opcode_bit_ptr;
+ break;
+ case 'g': /* inss exts */
+ iif.instr_size += 1; /* 1 byte is allocated after the opcode */
+ IIF (10, 2, 1,
+ (unsigned long) argv[i], /* i always 2 here */
+ 0, 0, 0, 0, 0,
+ bit_fix_new (3, 5, 0, 7, 0, 0, 0), /* a bit_fix is targeted to
+ the byte */
+ -1, 0);
+ break;
+ case 'G':
+ IIF (11, 2, 42,
+ (unsigned long) argv[i], /* i always 3 here */
+ 0, 0, 0, 0, 0,
+ bit_fix_new (5, 0, 1, 32, -1, 0, -1), -1, 0);
+ break;
+ case 'i':
+ iif.instr_size += 1;
+ b = 2 + i; /* put the extension byte after opcode */
+ IIF (b, 2, 1, 0, 0, 0, 0, 0, 0, 0, -1, 0);
+ break;
+ default:
+ as_fatal (_("Bad opcode-table-option, check in file ns32k-opcode.h"));
+ }
+ }
+}
+
+/* in: instruction line
+ out: internal structure of instruction
+ that has been prepared for direct conversion to fragment(s) and
+ fixes in a systematical fashion
+ Return-value = recursive_level
+ */
+/* build iif of one assembly text line */
+int
+parse (line, recursive_level)
+ char *line;
+ int recursive_level;
+{
+ register char *lineptr, c, suffix_separator;
+ register int i;
+ int argc, arg_type;
+ char sqr, sep;
+ char suffix[MAX_ARGS], *argv[MAX_ARGS]; /* no more than 4 operands */
+ if (recursive_level <= 0)
+ { /* called from md_assemble */
+ for (lineptr = line; (*lineptr) != '\0' && (*lineptr) != ' '; lineptr++);
+ c = *lineptr;
+ *lineptr = '\0';
+ if (!(desc = (struct ns32k_opcode *) hash_find (inst_hash_handle, line)))
+ {
+ as_fatal (_("No such opcode"));
+ }
+ *lineptr = c;
+ }
+ else
+ {
+ lineptr = line;
+ }
+ argc = 0;
+ if (*desc->operands)
+ {
+ if (*lineptr++ != '\0')
+ {
+ sqr = '[';
+ sep = ',';
+ while (*lineptr != '\0')
+ {
+ if (desc->operands[argc << 1])
+ {
+ suffix[argc] = 0;
+ arg_type = desc->operands[(argc << 1) + 1];
+ switch (arg_type)
+ {
+ case 'd':
+ case 'b':
+ case 'p':
+ case 'H': /* the operand is supposed to be a
+ displacement */
+ /* Hackwarning: do not forget to update the 4
+ cases above when editing ns32k-opcode.h */
+ suffix_separator = ':';
+ break;
+ default:
+ suffix_separator = '\255'; /* if this char occurs we
+ loose */
+ }
+ suffix[argc] = 0; /* 0 when no ':' is encountered */
+ argv[argc] = freeptr;
+ *freeptr = '\0';
+ while ((c = *lineptr) != '\0' && c != sep)
+ {
+ if (c == sqr)
+ {
+ if (sqr == '[')
+ {
+ sqr = ']';
+ sep = '\0';
+ }
+ else
+ {
+ sqr = '[';
+ sep = ',';
+ }
+ }
+ if (c == suffix_separator)
+ { /* ':' - label/suffix separator */
+ switch (lineptr[1])
+ {
+ case 'b':
+ suffix[argc] = 1;
+ break;
+ case 'w':
+ suffix[argc] = 2;
+ break;
+ case 'd':
+ suffix[argc] = 4;
+ break;
+ default:
+ as_warn (_("Bad suffix, defaulting to d"));
+ suffix[argc] = 4;
+ if (lineptr[1] == '\0' || lineptr[1] == sep)
+ {
+ lineptr += 1;
+ continue;
+ }
+ }
+ lineptr += 2;
+ continue;
+ }
+ *freeptr++ = c;
+ lineptr++;
+ }
+ *freeptr++ = '\0';
+ argc += 1;
+ if (*lineptr == '\0')
+ continue;
+ lineptr += 1;
+ }
+ else
+ {
+ as_fatal (_("Too many operands passed to instruction"));
+ }
+ }
+ }
+ }
+ if (argc != strlen (desc->operands) / 2)
+ {
+ if (strlen (desc->default_args))
+ { /* we can apply default, dont goof */
+ if (parse (desc->default_args, 1) != 1)
+ { /* check error in default */
+ as_fatal (_("Wrong numbers of operands in default, check ns32k-opcodes.h"));
+ }
+ }
+ else
+ {
+ as_fatal (_("Wrong number of operands"));
+ }
+
+ }
+ for (i = 0; i < IIF_ENTRIES; i++)
+ {
+ iif.iifP[i].type = 0; /* mark all entries as void*/
+ }
+
+ /* build opcode iif-entry */
+ iif.instr_size = desc->opcode_size / 8;
+ IIF (1, 1, iif.instr_size, desc->opcode_seed, 0, 0, 0, 0, 0, 0, -1, 0);
+
+ /* this call encodes operands to iif format */
+ if (argc)
+ {
+ encode_operand (argc,
+ argv,
+ &desc->operands[0],
+ &suffix[0],
+ desc->im_size,
+ desc->opcode_size);
+ }
+ return recursive_level;
+}
+
+
+/* Convert iif to fragments. From this point we start to dribble with
+ * functions in other files than this one.(Except hash.c) So, if it's
+ * possible to make an iif for an other CPU, you don't need to know
+ * what frags, relax, obstacks, etc is in order to port this
+ * assembler. You only need to know if it's possible to reduce your
+ * cpu-instruction to iif-format (takes some work) and adopt the other
+ * md_? parts according to given instructions Note that iif was
+ * invented for the clean ns32k`s architecure.
+ */
+
+/* GAS for the ns32k has a problem. PC relative displacements are
+ * relative to the address of the opcode, not the address of the
+ * operand. We used to keep track of the offset between the operand
+ * and the opcode in pcrel_adjust for each frag and each fix. However,
+ * we get into trouble where there are two or more pc-relative
+ * operands and the size of the first one can't be determined. Then in
+ * the relax phase, the size of the first operand will change and
+ * pcrel_adjust will no longer be correct. The current solution is
+ * keep a pointer to the frag with the opcode in it and the offset in
+ * that frag for each frag and each fix. Then, when needed, we can
+ * always figure out how far it is between the opcode and the pcrel
+ * object. See also md_pcrel_adjust and md_fix_pcrel_adjust. For
+ * objects not part of an instruction, the pointer to the opcode frag
+ * is always zero. */
+
+void
+convert_iif ()
+{
+ int i;
+ bit_fixS *j;
+ fragS *inst_frag;
+ unsigned int inst_offset;
+ char *inst_opcode;
+ char *memP;
+ int l;
+ int k;
+ char type;
+ char size = 0;
+ int size_so_far;
+
+ memP = frag_more (0);
+ inst_opcode = memP;
+ inst_offset = (memP - frag_now->fr_literal);
+ inst_frag = frag_now;
+
+ for (i = 0; i < IIF_ENTRIES; i++)
+ {
+ if (type = iif.iifP[i].type)
+ { /* the object exist, so handle it */
+ switch (size = iif.iifP[i].size)
+ {
+ case 42:
+ size = 0; /* it's a bitfix that operates on an existing
+ object*/
+ if (iif.iifP[i].bit_fixP->fx_bit_base)
+ { /* expand fx_bit_base to point at opcode */
+ iif.iifP[i].bit_fixP->fx_bit_base = (long) inst_opcode;
+ }
+ case 8: /* bignum or doublefloat */
+ case 1:
+ case 2:
+ case 3:
+ case 4: /* the final size in objectmemory is known */
+ memP = frag_more(size);
+ j = iif.iifP[i].bit_fixP;
+ switch (type)
+ {
+ case 1: /* the object is pure binary */
+ if (j || iif.iifP[i].pcrel)
+ {
+ fix_new_ns32k (frag_now,
+ (long) (memP - frag_now->fr_literal),
+ size,
+ 0,
+ iif.iifP[i].object,
+ iif.iifP[i].pcrel,
+ iif.iifP[i].im_disp,
+ j,
+ iif.iifP[i].bsr, /* sequent hack */
+ inst_frag, inst_offset);
+ }
+ else
+ { /* good, just put them bytes out */
+ switch (iif.iifP[i].im_disp)
+ {
+ case 0:
+ md_number_to_chars (memP, iif.iifP[i].object, size);
+ break;
+ case 1:
+ md_number_to_disp (memP, iif.iifP[i].object, size);
+ break;
+ default:
+ as_fatal (_("iif convert internal pcrel/binary"));
+ }
+ }
+ break;
+ case 2:
+ /* the object is a pointer at an expression, so
+ unpack it, note that bignums may result from the
+ expression */
+ evaluate_expr (&exprP, (char *) iif.iifP[i].object);
+ if (exprP.X_op == O_big || size == 8)
+ {
+ if ((k = exprP.X_add_number) > 0)
+ {
+ /* we have a bignum ie a quad. This can only
+ happens in a long suffixed instruction */
+ if (k * 2 > size)
+ as_warn (_("Bignum too big for long"));
+ if (k == 3)
+ memP += 2;
+ for (l = 0; k > 0; k--, l += 2)
+ {
+ md_number_to_chars (memP + l,
+ generic_bignum[l >> 1],
+ sizeof (LITTLENUM_TYPE));
+ }
+ }
+ else
+ { /* flonum */
+ LITTLENUM_TYPE words[4];
+
+ switch (size)
+ {
+ case 4:
+ gen_to_words (words, 2, 8);
+ md_number_to_imm (memP, (long) words[0],
+ sizeof (LITTLENUM_TYPE));
+ md_number_to_imm (memP + sizeof (LITTLENUM_TYPE),
+ (long) words[1],
+ sizeof (LITTLENUM_TYPE));
+ break;
+ case 8:
+ gen_to_words (words, 4, 11);
+ md_number_to_imm (memP, (long) words[0],
+ sizeof (LITTLENUM_TYPE));
+ md_number_to_imm (memP + sizeof (LITTLENUM_TYPE),
+ (long) words[1],
+ sizeof (LITTLENUM_TYPE));
+ md_number_to_imm ((memP + 2
+ * sizeof (LITTLENUM_TYPE)),
+ (long) words[2],
+ sizeof (LITTLENUM_TYPE));
+ md_number_to_imm ((memP + 3
+ * sizeof (LITTLENUM_TYPE)),
+ (long) words[3],
+ sizeof (LITTLENUM_TYPE));
+ break;
+ }
+ }
+ break;
+ }
+ if (j ||
+ exprP.X_add_symbol ||
+ exprP.X_op_symbol ||
+ iif.iifP[i].pcrel)
+ {
+ /* The expression was undefined due to an
+ undefined label. Create a fix so we can fix
+ the object later. */
+ exprP.X_add_number += iif.iifP[i].object_adjust;
+ fix_new_ns32k_exp (frag_now,
+ (long) (memP - frag_now->fr_literal),
+ size,
+ &exprP,
+ iif.iifP[i].pcrel,
+ iif.iifP[i].im_disp,
+ j,
+ iif.iifP[i].bsr,
+ inst_frag, inst_offset);
+ }
+ else
+ {
+ /* good, just put them bytes out */
+ switch (iif.iifP[i].im_disp)
+ {
+ case 0:
+ md_number_to_imm (memP, exprP.X_add_number, size);
+ break;
+ case 1:
+ md_number_to_disp (memP, exprP.X_add_number, size);
+ break;
+ default:
+ as_fatal (_("iif convert internal pcrel/pointer"));
+ }
+ }
+ break;
+ default:
+ as_fatal (_("Internal logic error in iif.iifP[n].type"));
+ }
+ break;
+ case 0:
+ /* To bad, the object may be undefined as far as its
+ final nsize in object memory is concerned. The size
+ of the object in objectmemory is not explicitly
+ given. If the object is defined its length can be
+ determined and a fix can replace the frag. */
+ {
+ evaluate_expr (&exprP, (char *) iif.iifP[i].object);
+ if ((exprP.X_add_symbol || exprP.X_op_symbol) &&
+ !iif.iifP[i].pcrel)
+ {
+ /* Size is unknown until link time so have to
+ allow 4 bytes. */
+ size = 4;
+ memP = frag_more(size);
+ fix_new_ns32k_exp (frag_now,
+ (long) (memP - frag_now->fr_literal),
+ size,
+ &exprP,
+ 0, /* never iif.iifP[i].pcrel, */
+ 1, /* always iif.iifP[i].im_disp */
+ (bit_fixS *) 0, 0,
+ inst_frag,
+ inst_offset);
+ break; /* exit this absolute hack */
+ }
+
+ if (exprP.X_add_symbol || exprP.X_op_symbol)
+ { /* frag it */
+ if (exprP.X_op_symbol)
+ { /* We cant relax this case */
+ as_fatal (_("Can't relax difference"));
+ }
+ else
+ {
+
+ /* Size is not important. This gets fixed by relax,
+ * but we assume 0 in what follows
+ */
+ memP = frag_more(4); /* Max size */
+ size = 0;
+
+ {
+ fragS *old_frag = frag_now;
+ frag_variant (rs_machine_dependent,
+ 4, /* Max size */
+ 0, /* size */
+ IND (BRANCH, UNDEF), /* expecting the worst */
+ exprP.X_add_symbol,
+ exprP.X_add_number,
+ inst_opcode);
+ frag_opcode_frag(old_frag) = inst_frag;
+ frag_opcode_offset(old_frag) = inst_offset;
+ frag_bsr(old_frag) = iif.iifP[i].bsr;
+ }
+ }
+ }
+ else
+ {
+ /* This duplicates code in md_number_to_disp */
+ if (-64 <= exprP.X_add_number && exprP.X_add_number <= 63)
+ {
+ size = 1;
+ }
+ else
+ {
+ if (-8192 <= exprP.X_add_number
+ && exprP.X_add_number <= 8191)
+ {
+ size = 2;
+ }
+ else
+ {
+ if (-0x20000000<=exprP.X_add_number &&
+ exprP.X_add_number<=0x1fffffff)
+ {
+ size = 4;
+ }
+ else
+ {
+ as_warn (_("Displacement to large for :d"));
+ size = 4;
+ }
+ }
+ }
+ memP = frag_more(size);
+ md_number_to_disp (memP, exprP.X_add_number, size);
+ }
+ }
+ break;
+ default:
+ as_fatal (_("Internal logic error in iif.iifP[].type"));
+ }
+ }
+ }
+}
+
+#ifdef BFD_ASSEMBLER
+/* This functionality should really be in the bfd library */
+static bfd_reloc_code_real_type
+reloc (int size, int pcrel, int type)
+{
+ int length, index;
+ bfd_reloc_code_real_type relocs[] = {
+ BFD_RELOC_NS32K_IMM_8,
+ BFD_RELOC_NS32K_IMM_16,
+ BFD_RELOC_NS32K_IMM_32,
+ BFD_RELOC_NS32K_IMM_8_PCREL,
+ BFD_RELOC_NS32K_IMM_16_PCREL,
+ BFD_RELOC_NS32K_IMM_32_PCREL,
+
+ /* ns32k displacements */
+ BFD_RELOC_NS32K_DISP_8,
+ BFD_RELOC_NS32K_DISP_16,
+ BFD_RELOC_NS32K_DISP_32,
+ BFD_RELOC_NS32K_DISP_8_PCREL,
+ BFD_RELOC_NS32K_DISP_16_PCREL,
+ BFD_RELOC_NS32K_DISP_32_PCREL,
+
+ /* Normal 2's complement */
+ BFD_RELOC_8,
+ BFD_RELOC_16,
+ BFD_RELOC_32,
+ BFD_RELOC_8_PCREL,
+ BFD_RELOC_16_PCREL,
+ BFD_RELOC_32_PCREL
+ };
+ switch (size)
+ {
+ case 1:
+ length = 0;
+ break;
+ case 2:
+ length = 1;
+ break;
+ case 4:
+ length = 2;
+ break;
+ default:
+ length = -1;
+ break;
+ }
+ index = length + 3 * pcrel + 6 * type;
+ if (index >= 0 && index < sizeof(relocs)/sizeof(relocs[0]))
+ return relocs[index];
+ if (pcrel)
+ as_bad (_("Can not do %d byte pc-relative relocation for storage type %d"),
+ size, type);
+ else
+ as_bad (_("Can not do %d byte relocation for storage type %d"),
+ size, type);
+ return BFD_RELOC_NONE;
+
+}
+
+#endif
+
+void
+md_assemble (line)
+ char *line;
+{
+ freeptr = freeptr_static;
+ parse (line, 0); /* explode line to more fix form in iif */
+ convert_iif (); /* convert iif to frags, fix's etc */
+#ifdef SHOW_NUM
+ printf (" \t\t\t%s\n", line);
+#endif
+}
+
+
+void
+md_begin ()
+{
+ /* build a hashtable of the instructions */
+ const struct ns32k_opcode *ptr;
+ const char *stat;
+ inst_hash_handle = hash_new ();
+ for (ptr = ns32k_opcodes; ptr < endop; ptr++)
+ {
+ if ((stat = hash_insert (inst_hash_handle, ptr->name, (char *) ptr)))
+ {
+ as_fatal (_("Can't hash %s: %s"), ptr->name, stat); /*fatal*/
+ }
+ }
+ freeptr_static = (char *) malloc (PRIVATE_SIZE); /* some private space
+ please! */
+}
+
+/* Must be equal to MAX_PRECISON in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+/* Turn the string pointed to by litP into a floating point constant
+ of type type, and emit the appropriate bytes. The number of
+ LITTLENUMS emitted is stored in *sizeP . An error message is
+ returned, or NULL on OK. */
+char *
+md_atof (type, litP, sizeP)
+ char type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+
+ switch (type)
+ {
+ case 'f':
+ prec = 2;
+ break;
+
+ case 'd':
+ prec = 4;
+ break;
+ default:
+ *sizeP = 0;
+ return _("Bad call to MD_ATOF()");
+ }
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+
+ *sizeP = prec * sizeof (LITTLENUM_TYPE);
+ for (wordP = words + prec; prec--;)
+ {
+ md_number_to_chars (litP, (long) (*--wordP), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ return 0;
+}
+
+/* Convert number to chars in correct order */
+
+void
+md_number_to_chars (buf, value, nbytes)
+ char *buf;
+ valueT value;
+ int nbytes;
+{
+ number_to_chars_littleendian (buf, value, nbytes);
+}
+
+
+/* This is a variant of md_numbers_to_chars. The reason for its'
+ existence is the fact that ns32k uses Huffman coded
+ displacements. This implies that the bit order is reversed in
+ displacements and that they are prefixed with a size-tag.
+
+ binary: msb -> lsb
+ 0xxxxxxx byte
+ 10xxxxxx xxxxxxxx word
+ 11xxxxxx xxxxxxxx xxxxxxxx xxxxxxxx double word
+
+ This must be taken care of and we do it here! */
+static void
+md_number_to_disp (buf, val, n)
+ char *buf;
+ long val;
+ char n;
+{
+ switch (n)
+ {
+ case 1:
+ if (val < -64 || val > 63)
+ as_warn (_("Byte displacement out of range. line number not valid"));
+ val &= 0x7f;
+#ifdef SHOW_NUM
+ printf ("%x ", val & 0xff);
+#endif
+ *buf++ = val;
+ break;
+ case 2:
+ if (val < -8192 || val > 8191)
+ as_warn (_("Word displacement out of range. line number not valid"));
+ val &= 0x3fff;
+ val |= 0x8000;
+#ifdef SHOW_NUM
+ printf ("%x ", val >> 8 & 0xff);
+#endif
+ *buf++ = (val >> 8);
+#ifdef SHOW_NUM
+ printf ("%x ", val & 0xff);
+#endif
+ *buf++ = val;
+ break;
+ case 4:
+ if (val < -0x20000000 || val >= 0x20000000)
+ as_warn (_("Double word displacement out of range"));
+ val |= 0xc0000000;
+#ifdef SHOW_NUM
+ printf ("%x ", val >> 24 & 0xff);
+#endif
+ *buf++ = (val >> 24);
+#ifdef SHOW_NUM
+ printf ("%x ", val >> 16 & 0xff);
+#endif
+ *buf++ = (val >> 16);
+#ifdef SHOW_NUM
+ printf ("%x ", val >> 8 & 0xff);
+#endif
+ *buf++ = (val >> 8);
+#ifdef SHOW_NUM
+ printf ("%x ", val & 0xff);
+#endif
+ *buf++ = val;
+ break;
+ default:
+ as_fatal (_("Internal logic error. line %s, file \"%s\""),
+ __LINE__, __FILE__);
+ }
+}
+
+static void
+md_number_to_imm (buf, val, n)
+ char *buf;
+ long val;
+ char n;
+{
+ switch (n)
+ {
+ case 1:
+#ifdef SHOW_NUM
+ printf ("%x ", val & 0xff);
+#endif
+ *buf++ = val;
+ break;
+ case 2:
+#ifdef SHOW_NUM
+ printf ("%x ", val >> 8 & 0xff);
+#endif
+ *buf++ = (val >> 8);
+#ifdef SHOW_NUM
+ printf ("%x ", val & 0xff);
+#endif
+ *buf++ = val;
+ break;
+ case 4:
+#ifdef SHOW_NUM
+ printf ("%x ", val >> 24 & 0xff);
+#endif
+ *buf++ = (val >> 24);
+#ifdef SHOW_NUM
+ printf ("%x ", val >> 16 & 0xff);
+#endif
+ *buf++ = (val >> 16);
+#ifdef SHOW_NUM
+ printf ("%x ", val >> 8 & 0xff);
+#endif
+ *buf++ = (val >> 8);
+#ifdef SHOW_NUM
+ printf ("%x ", val & 0xff);
+#endif
+ *buf++ = val;
+ break;
+ default:
+ as_fatal (_("Internal logic error. line %s, file \"%s\""),
+ __LINE__, __FILE__);
+ }
+}
+
+
+/* fast bitfiddling support */
+/* mask used to zero bitfield before oring in the true field */
+
+static unsigned long l_mask[] =
+{
+ 0xffffffff, 0xfffffffe, 0xfffffffc, 0xfffffff8,
+ 0xfffffff0, 0xffffffe0, 0xffffffc0, 0xffffff80,
+ 0xffffff00, 0xfffffe00, 0xfffffc00, 0xfffff800,
+ 0xfffff000, 0xffffe000, 0xffffc000, 0xffff8000,
+ 0xffff0000, 0xfffe0000, 0xfffc0000, 0xfff80000,
+ 0xfff00000, 0xffe00000, 0xffc00000, 0xff800000,
+ 0xff000000, 0xfe000000, 0xfc000000, 0xf8000000,
+ 0xf0000000, 0xe0000000, 0xc0000000, 0x80000000,
+};
+static unsigned long r_mask[] =
+{
+ 0x00000000, 0x00000001, 0x00000003, 0x00000007,
+ 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f,
+ 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff,
+ 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff,
+ 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff,
+ 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff,
+ 0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff,
+ 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
+};
+#define MASK_BITS 31
+/* Insert bitfield described by field_ptr and val at buf
+ This routine is written for modification of the first 4 bytes pointed
+ to by buf, to yield speed.
+ The ifdef stuff is for selection between a ns32k-dependent routine
+ and a general version. (My advice: use the general version!)
+ */
+
+static void
+md_number_to_field (buf, val, field_ptr)
+ register char *buf;
+ register long val;
+ register bit_fixS *field_ptr;
+{
+ register unsigned long object;
+ register unsigned long mask;
+ /* define ENDIAN on a ns32k machine */
+#ifdef ENDIAN
+ register unsigned long *mem_ptr;
+#else
+ register char *mem_ptr;
+#endif
+ if (field_ptr->fx_bit_min <= val && val <= field_ptr->fx_bit_max)
+ {
+#ifdef ENDIAN
+ if (field_ptr->fx_bit_base)
+ { /* override buf */
+ mem_ptr = (unsigned long *) field_ptr->fx_bit_base;
+ }
+ else
+ {
+ mem_ptr = (unsigned long *) buf;
+ }
+ mem_ptr = ((unsigned long *)
+ ((char *) mem_ptr + field_ptr->fx_bit_base_adj));
+#else
+ if (field_ptr->fx_bit_base)
+ { /* override buf */
+ mem_ptr = (char *) field_ptr->fx_bit_base;
+ }
+ else
+ {
+ mem_ptr = buf;
+ }
+ mem_ptr += field_ptr->fx_bit_base_adj;
+#endif
+#ifdef ENDIAN /* we have a nice ns32k machine with lowbyte
+ at low-physical mem */
+ object = *mem_ptr; /* get some bytes */
+#else /* OVE Goof! the machine is a m68k or dito */
+ /* That takes more byte fiddling */
+ object = 0;
+ object |= mem_ptr[3] & 0xff;
+ object <<= 8;
+ object |= mem_ptr[2] & 0xff;
+ object <<= 8;
+ object |= mem_ptr[1] & 0xff;
+ object <<= 8;
+ object |= mem_ptr[0] & 0xff;
+#endif
+ mask = 0;
+ mask |= (r_mask[field_ptr->fx_bit_offset]);
+ mask |= (l_mask[field_ptr->fx_bit_offset + field_ptr->fx_bit_size]);
+ object &= mask;
+ val += field_ptr->fx_bit_add;
+ object |= ((val << field_ptr->fx_bit_offset) & (mask ^ 0xffffffff));
+#ifdef ENDIAN
+ *mem_ptr = object;
+#else
+ mem_ptr[0] = (char) object;
+ object >>= 8;
+ mem_ptr[1] = (char) object;
+ object >>= 8;
+ mem_ptr[2] = (char) object;
+ object >>= 8;
+ mem_ptr[3] = (char) object;
+#endif
+ }
+ else
+ {
+ as_warn (_("Bit field out of range"));
+ }
+}
+
+int md_pcrel_adjust (fragS *fragP)
+{
+ fragS *opcode_frag;
+ addressT opcode_address;
+ unsigned int offset;
+ opcode_frag = frag_opcode_frag(fragP);
+ if (opcode_frag == 0)
+ return 0;
+ offset = frag_opcode_offset(fragP);
+ opcode_address = offset + opcode_frag->fr_address;
+ return fragP->fr_address + fragP->fr_fix - opcode_address;
+}
+
+int md_fix_pcrel_adjust (fixS *fixP)
+{
+ fragS *fragP = fixP->fx_frag;
+ fragS *opcode_frag;
+ addressT opcode_address;
+ unsigned int offset;
+ opcode_frag = fix_opcode_frag(fixP);
+ if (opcode_frag == 0)
+ return 0;
+ offset = fix_opcode_offset(fixP);
+ opcode_address = offset + opcode_frag->fr_address;
+ return fixP->fx_where + fixP->fx_frag->fr_address - opcode_address;
+}
+
+/* Apply a fixS (fixup of an instruction or data that we didn't have
+ enough info to complete immediately) to the data in a frag.
+
+ On the ns32k, everything is in a different format, so we have broken
+ out separate functions for each kind of thing we could be fixing.
+ They all get called from here. */
+
+#ifdef BFD_ASSEMBLER
+int
+md_apply_fix (fixP, valp)
+ fixS *fixP;
+ valueT *valp;
+#else
+void
+md_apply_fix (fixP, val)
+ fixS *fixP;
+ long val;
+#endif
+{
+#ifdef BFD_ASSEMBLER
+ long val = *valp;
+#endif
+ fragS *fragP = fixP->fx_frag;
+
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ if (fix_bit_fixP(fixP))
+ { /* Bitfields to fix, sigh */
+ md_number_to_field (buf, val, fix_bit_fixP(fixP));
+ }
+ else
+ switch (fix_im_disp(fixP))
+ {
+
+ case 0: /* Immediate field */
+ md_number_to_imm (buf, val, fixP->fx_size);
+ break;
+
+ case 1: /* Displacement field */
+ /* Calculate offset */
+ {
+ md_number_to_disp (buf,
+ (fixP->fx_pcrel ? val + md_fix_pcrel_adjust(fixP)
+ : val), fixP->fx_size);
+ }
+ break;
+
+ case 2: /* Pointer in a data object */
+ md_number_to_chars (buf, val, fixP->fx_size);
+ break;
+ }
+#ifdef BSD_ASSEMBLER
+ return 1;
+#endif
+}
+
+/* Convert a relaxed displacement to ditto in final output */
+
+#ifndef BFD_ASSEMBLER
+void
+md_convert_frag (headers, sec, fragP)
+ object_headers *headers;
+ segT sec;
+ register fragS *fragP;
+#else
+void
+md_convert_frag (abfd, sec, fragP)
+ bfd *abfd;
+ segT sec;
+ register fragS *fragP;
+#endif
+{
+ long disp;
+ long ext = 0;
+
+ /* Address in gas core of the place to store the displacement. */
+ register char *buffer_address = fragP->fr_fix + fragP->fr_literal;
+ /* Address in object code of the displacement. */
+ int object_address;
+
+ fragS *opcode_frag;
+
+ switch (fragP->fr_subtype)
+ {
+ case IND (BRANCH, BYTE):
+ ext = 1;
+ break;
+ case IND (BRANCH, WORD):
+ ext = 2;
+ break;
+ case IND (BRANCH, DOUBLE):
+ ext = 4;
+ break;
+ }
+
+ if(ext == 0)
+ return;
+
+ know (fragP->fr_symbol);
+
+ object_address = fragP->fr_fix + fragP->fr_address;
+ /* The displacement of the address, from current location. */
+ disp = (S_GET_VALUE (fragP->fr_symbol) + fragP->fr_offset) - object_address;
+#ifdef BFD_ASSEMBLER
+ disp += fragP->fr_symbol->sy_frag->fr_address;
+#endif
+ disp += md_pcrel_adjust(fragP);
+
+ md_number_to_disp (buffer_address, (long) disp, (int) ext);
+ fragP->fr_fix += ext;
+}
+
+/* This function returns the estimated size a variable object will occupy,
+ one can say that we tries to guess the size of the objects before we
+ actually know it */
+
+int
+md_estimate_size_before_relax (fragP, segment)
+ register fragS *fragP;
+ segT segment;
+{
+ int old_fix;
+ old_fix = fragP->fr_fix;
+ switch (fragP->fr_subtype)
+ {
+ case IND (BRANCH, UNDEF):
+ if (S_GET_SEGMENT (fragP->fr_symbol) == segment)
+ {
+ /* the symbol has been assigned a value */
+ fragP->fr_subtype = IND (BRANCH, BYTE);
+ }
+ else
+ {
+ /* we don't relax symbols defined in an other segment the
+ thing to do is to assume the object will occupy 4 bytes */
+ fix_new_ns32k (fragP,
+ (int) (fragP->fr_fix),
+ 4,
+ fragP->fr_symbol,
+ fragP->fr_offset,
+ 1,
+ 1,
+ 0,
+ frag_bsr(fragP), /*sequent hack */
+ frag_opcode_frag(fragP),
+ frag_opcode_offset(fragP));
+ fragP->fr_fix += 4;
+ /* fragP->fr_opcode[1]=0xff; */
+ frag_wane (fragP);
+ break;
+ }
+ case IND (BRANCH, BYTE):
+ fragP->fr_var += 1;
+ break;
+ default:
+ break;
+ }
+ return fragP->fr_var + fragP->fr_fix - old_fix;
+}
+
+int md_short_jump_size = 3;
+int md_long_jump_size = 5;
+const int md_reloc_size = 8; /* Size of relocation record */
+
+void
+md_create_short_jump (ptr, from_addr, to_addr, frag, to_symbol)
+ char *ptr;
+ addressT from_addr, to_addr;
+ fragS *frag;
+ symbolS *to_symbol;
+{
+ valueT offset;
+
+ offset = to_addr - from_addr;
+ md_number_to_chars (ptr, (valueT) 0xEA, 1);
+ md_number_to_disp (ptr + 1, (valueT) offset, 2);
+}
+
+void
+md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol)
+ char *ptr;
+ addressT from_addr, to_addr;
+ fragS *frag;
+ symbolS *to_symbol;
+{
+ valueT offset;
+
+ offset = to_addr - from_addr;
+ md_number_to_chars (ptr, (valueT) 0xEA, 1);
+ md_number_to_disp (ptr + 1, (valueT) offset, 4);
+}
+
+CONST char *md_shortopts = "m:";
+struct option md_longopts[] = {
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ switch (c)
+ {
+ case 'm':
+ if (!strcmp (arg, "32032"))
+ {
+ cpureg = cpureg_032;
+ mmureg = mmureg_032;
+ }
+ else if (!strcmp (arg, "32532"))
+ {
+ cpureg = cpureg_532;
+ mmureg = mmureg_532;
+ }
+ else
+ {
+ as_bad (_("invalid architecture option -m%s"), arg);
+ return 0;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ fprintf(stream, _("\
+NS32K options:\n\
+-m32032 | -m32532 select variant of NS32K architecture\n"));
+}
+
+
+/*
+ * bit_fix_new()
+ *
+ * Create a bit_fixS in obstack 'notes'.
+ * This struct is used to profile the normal fix. If the bit_fixP is a
+ * valid pointer (not NULL) the bit_fix data will be used to format the fix.
+ */
+bit_fixS *
+bit_fix_new (size, offset, min, max, add, base_type, base_adj)
+ char size; /* Length of bitfield */
+ char offset; /* Bit offset to bitfield */
+ long min; /* Signextended min for bitfield */
+ long max; /* Signextended max for bitfield */
+ long add; /* Add mask, used for huffman prefix */
+ long base_type; /* 0 or 1, if 1 it's exploded to opcode ptr */
+ long base_adj;
+{
+ register bit_fixS *bit_fixP;
+
+ bit_fixP = (bit_fixS *) obstack_alloc (&notes, sizeof (bit_fixS));
+
+ bit_fixP->fx_bit_size = size;
+ bit_fixP->fx_bit_offset = offset;
+ bit_fixP->fx_bit_base = base_type;
+ bit_fixP->fx_bit_base_adj = base_adj;
+ bit_fixP->fx_bit_max = max;
+ bit_fixP->fx_bit_min = min;
+ bit_fixP->fx_bit_add = add;
+
+ return (bit_fixP);
+}
+
+void
+fix_new_ns32k (frag, where, size, add_symbol, offset, pcrel,
+ im_disp, bit_fixP, bsr, opcode_frag, opcode_offset)
+ fragS *frag; /* Which frag? */
+ int where; /* Where in that frag? */
+ int size; /* 1, 2 or 4 usually. */
+ symbolS *add_symbol; /* X_add_symbol. */
+ long offset; /* X_add_number. */
+ int pcrel; /* TRUE if PC-relative relocation. */
+ char im_disp; /* true if the value to write is a
+ displacement */
+ bit_fixS *bit_fixP; /* pointer at struct of bit_fix's, ignored if
+ NULL */
+ char bsr; /* sequent-linker-hack: 1 when relocobject is
+ a bsr */
+ fragS *opcode_frag;
+ unsigned int opcode_offset;
+
+{
+ fixS *fixP = fix_new (frag, where, size, add_symbol,
+ offset, pcrel,
+#ifdef BFD_ASSEMBLER
+ bit_fixP? NO_RELOC: reloc(size, pcrel, im_disp)
+#else
+ NO_RELOC
+#endif
+ );
+
+ fix_opcode_frag(fixP) = opcode_frag;
+ fix_opcode_offset(fixP) = opcode_offset;
+ fix_im_disp(fixP) = im_disp;
+ fix_bsr(fixP) = bsr;
+ fix_bit_fixP(fixP) = bit_fixP;
+} /* fix_new_ns32k() */
+
+void
+fix_new_ns32k_exp (frag, where, size, exp, pcrel,
+ im_disp, bit_fixP, bsr, opcode_frag, opcode_offset)
+ fragS *frag; /* Which frag? */
+ int where; /* Where in that frag? */
+ int size; /* 1, 2 or 4 usually. */
+ expressionS *exp; /* Expression. */
+ int pcrel; /* TRUE if PC-relative relocation. */
+ char im_disp; /* true if the value to write is a
+ displacement */
+ bit_fixS *bit_fixP; /* pointer at struct of bit_fix's, ignored if
+ NULL */
+ char bsr; /* sequent-linker-hack: 1 when relocobject is
+ a bsr */
+ fragS *opcode_frag;
+ unsigned int opcode_offset;
+{
+ fixS *fixP = fix_new_exp (frag, where, size, exp, pcrel,
+#ifdef BFD_ASSEMBLER
+ bit_fixP? NO_RELOC: reloc(size, pcrel, im_disp)
+#else
+ NO_RELOC
+#endif
+ );
+
+ fix_opcode_frag(fixP) = opcode_frag;
+ fix_opcode_offset(fixP) = opcode_offset;
+ fix_im_disp(fixP) = im_disp;
+ fix_bsr(fixP) = bsr;
+ fix_bit_fixP(fixP) = bit_fixP;
+} /* fix_new_ns32k() */
+
+/* This is TC_CONS_FIX_NEW, called by emit_expr in read.c. */
+
+void
+cons_fix_new_ns32k (frag, where, size, exp)
+ fragS *frag; /* Which frag? */
+ int where; /* Where in that frag? */
+ int size; /* 1, 2 or 4 usually. */
+ expressionS *exp; /* Expression. */
+{
+ fix_new_ns32k_exp (frag, where, size, exp,
+ 0, 2, 0, 0, 0, 0);
+}
+
+/* We have no need to default values of symbols. */
+
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ return 0;
+}
+
+/* Round up a section size to the appropriate boundary. */
+valueT
+md_section_align (segment, size)
+ segT segment;
+ valueT size;
+{
+ return size; /* Byte alignment is fine */
+}
+
+/* Exactly what point is a PC-relative offset relative TO? On the
+ ns32k, they're relative to the start of the instruction. */
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ long res;
+ res = fixP->fx_where + fixP->fx_frag->fr_address;
+#ifdef SEQUENT_COMPATABILITY
+ if (frag_bsr(fixP->fx_frag))
+ res += 0x12 /* FOO Kludge alert! */
+#endif
+ return res;
+}
+
+#ifdef BFD_ASSEMBLER
+
+arelent *
+tc_gen_reloc (section, fixp)
+ asection *section;
+ fixS *fixp;
+{
+ arelent *rel;
+ bfd_reloc_code_real_type code;
+
+ code = reloc(fixp->fx_size, fixp->fx_pcrel, fix_im_disp(fixp));
+
+ rel = (arelent *) xmalloc (sizeof (arelent));
+ rel->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+ rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ if (fixp->fx_pcrel)
+ rel->addend = fixp->fx_addnumber;
+ else
+ rel->addend = 0;
+
+ rel->howto = bfd_reloc_type_lookup (stdoutput, code);
+ if (!rel->howto)
+ {
+ const char *name;
+
+ name = S_GET_NAME (fixp->fx_addsy);
+ if (name == NULL)
+ name = _("<unknown>");
+ as_fatal (_("Cannot find relocation type for symbol %s, code %d"),
+ name, (int) code);
+ }
+
+ return rel;
+}
+#else /* BFD_ASSEMBLER */
+
+#ifdef OBJ_AOUT
+void
+cons_fix_new_ns32k (where, fixP, segment_address_in_file)
+ char *where;
+ struct fix *fixP;
+ relax_addressT segment_address_in_file;
+{
+ /*
+ * In: length of relocation (or of address) in chars: 1, 2 or 4.
+ * Out: GNU LD relocation length code: 0, 1, or 2.
+ */
+
+ static unsigned char nbytes_r_length[] = {42, 0, 1, 42, 2};
+ long r_symbolnum;
+
+ know (fixP->fx_addsy != NULL);
+
+ md_number_to_chars (where,
+ fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file,
+ 4);
+
+ r_symbolnum = (S_IS_DEFINED (fixP->fx_addsy)
+ ? S_GET_TYPE (fixP->fx_addsy)
+ : fixP->fx_addsy->sy_number);
+
+ md_number_to_chars (where + 4,
+ ((long) (r_symbolnum)
+ | (long) (fixP->fx_pcrel << 24)
+ | (long) (nbytes_r_length[fixP->fx_size] << 25)
+ | (long) ((!S_IS_DEFINED (fixP->fx_addsy)) << 27)
+ | (long) (fix_bsr(fixP) << 28)
+ | (long) (fix_im_disp(fixP) << 29)),
+ 4);
+}
+
+#endif /* OBJ_AOUT */
+#endif /* BFD_ASSMEBLER */
+
+/* end of tc-ns32k.c */
diff --git a/gas/config/tc-ns32k.h b/gas/config/tc-ns32k.h
new file mode 100644
index 0000000..4b038eb
--- /dev/null
+++ b/gas/config/tc-ns32k.h
@@ -0,0 +1,155 @@
+/* tc-ns32k.h -- Opcode table for National Semi 32k processor
+ Copyright (C) 1987, 92, 93, 94, 95, 1997 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#define TC_NS32K
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define TC_PCREL_ADJUST(F) md_pcrel_adjust(F)
+
+#ifdef BFD_ASSEMBLER
+#define NO_RELOC BFD_RELOC_NONE
+
+#define TARGET_ARCH bfd_arch_ns32k
+
+#ifndef TARGET_FORMAT /* Maybe defined in te-*.h */
+#define TARGET_FORMAT "a.out-pc532-mach"
+#endif
+#else
+#define NO_RELOC 0
+#endif
+
+#define LOCAL_LABELS_FB 1
+
+#include "bit_fix.h"
+
+#define tc_aout_pre_write_hook(x) {;} /* not used */
+#define tc_crawl_symbol_chain(a) {;} /* not used */
+#define tc_headers_hook(a) {;} /* not used */
+
+#ifdef SEQUENT_COMPATABILITY
+#define DEF_MODEC 20
+#define DEF_MODEL 21
+#endif
+
+#ifndef DEF_MODEC
+#define DEF_MODEC 20
+#endif
+
+#ifndef DEF_MODEL
+#define DEF_MODEL 20
+#endif
+
+#define MAX_ARGS 4
+#define ARG_LEN 50
+
+#define TC_CONS_FIX_NEW cons_fix_new_ns32k
+extern void fix_new_ns32k_exp PARAMS((fragS *frag,
+ int where,
+ int size,
+ expressionS *exp,
+ int pcrel,
+ int im_disp,
+ bit_fixS *bit_fixP, /* really bit_fixS */
+ int bsr,
+ fragS *opcode_frag,
+ unsigned int opcode_offset));
+
+
+extern void fix_new_ns32k PARAMS ((fragS *frag,
+ int where,
+ int size,
+ struct symbol *add_symbol,
+ long offset,
+ int pcrel,
+ int im_disp,
+ bit_fixS *bit_fixP, /* really bit_fixS */
+ int bsr,
+ fragS *opcode_frag,
+ unsigned int opcode_offset));
+
+extern void cons_fix_new_ns32k PARAMS ((fragS *frag,
+ int where,
+ int size,
+ expressionS *exp));
+
+/* the NS32x32 has a non 0 nop instruction which should be used in aligns */
+#define NOP_OPCODE 0xa2
+
+#define md_operand(x)
+
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+#define TC_FRAG_TYPE \
+struct { \
+ fragS *fr_opcode_fragP; \
+ unsigned int fr_opcode_offset; \
+ char fr_bsr; \
+}
+
+#define TC_FRAG_INIT(X) \
+ do \
+ { \
+ frag_opcode_frag (X) = NULL; \
+ frag_opcode_offset (X) = 0; \
+ frag_bsr (X) = 0; \
+ } \
+ while(0)
+
+/* Accessor macros for things which may move around */
+#define frag_opcode_frag(X) (X)->tc_frag_data.fr_opcode_fragP
+#define frag_opcode_offset(X) (X)->tc_frag_data.fr_opcode_offset
+#define frag_bsr(X) (X)->tc_frag_data.fr_bsr
+
+#define TC_FIX_TYPE \
+struct \
+{ \
+ fragS *opcode_fragP; \
+ unsigned int opcode_offset; \
+ unsigned int bsr : 1; \
+}
+
+/* Accessor macros for things which may move around.
+ See comments in write.h. */
+#define fix_im_disp(X) (X)->fx_im_disp
+#define fix_bit_fixP(X) (X)->fx_bit_fixP
+#define fix_opcode_frag(X) (X)->tc_fix_data.opcode_fragP
+#define fix_opcode_offset(X) (X)->tc_fix_data.opcode_offset
+#define fix_bsr(X) (X)->tc_fix_data.bsr
+
+#define TC_INIT_FIX_DATA(X) \
+ do \
+ { \
+ fix_opcode_frag(X) = NULL; \
+ fix_opcode_offset(X) = 0; \
+ fix_bsr(X) = 0; \
+ } \
+ while(0)
+
+#define TC_FIX_DATA_PRINT(FILE, FIXP) \
+ do \
+ { \
+ fprintf((FILE), "opcode_frag=%ld, operand offset=%d, bsr=%d\n", \
+ (unsigned long) fix_opcode_frag (FIXP), \
+ fix_opcode_offset (FIXP), \
+ fix_bsr (FIXP)); \
+ } \
+ while(0)
diff --git a/gas/config/tc-ppc.c b/gas/config/tc-ppc.c
new file mode 100644
index 0000000..8c31ba6
--- /dev/null
+++ b/gas/config/tc-ppc.c
@@ -0,0 +1,5003 @@
+/* tc-ppc.c -- Assemble for the PowerPC or POWER (RS/6000)
+ Copyright (C) 1994, 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Cygnus Support.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "as.h"
+#include "subsegs.h"
+
+#include "opcode/ppc.h"
+
+#ifdef OBJ_ELF
+#include "elf/ppc.h"
+#endif
+
+#ifdef TE_PE
+#include "coff/pe.h"
+#endif
+
+/* This is the assembler for the PowerPC or POWER (RS/6000) chips. */
+
+/* Tell the main code what the endianness is. */
+extern int target_big_endian;
+
+/* Whether or not, we've set target_big_endian. */
+static int set_target_endian = 0;
+
+/* Whether to use user friendly register names. */
+#ifndef TARGET_REG_NAMES_P
+#ifdef TE_PE
+#define TARGET_REG_NAMES_P true
+#else
+#define TARGET_REG_NAMES_P false
+#endif
+#endif
+
+static boolean reg_names_p = TARGET_REG_NAMES_P;
+
+static boolean register_name PARAMS ((expressionS *));
+static void ppc_set_cpu PARAMS ((void));
+static unsigned long ppc_insert_operand
+ PARAMS ((unsigned long insn, const struct powerpc_operand *operand,
+ offsetT val, char *file, unsigned int line));
+static void ppc_macro PARAMS ((char *str, const struct powerpc_macro *macro));
+static void ppc_byte PARAMS ((int));
+static int ppc_is_toc_sym PARAMS ((symbolS *sym));
+static void ppc_tc PARAMS ((int));
+
+#ifdef OBJ_XCOFF
+static void ppc_comm PARAMS ((int));
+static void ppc_bb PARAMS ((int));
+static void ppc_bc PARAMS ((int));
+static void ppc_bf PARAMS ((int));
+static void ppc_biei PARAMS ((int));
+static void ppc_bs PARAMS ((int));
+static void ppc_eb PARAMS ((int));
+static void ppc_ec PARAMS ((int));
+static void ppc_ef PARAMS ((int));
+static void ppc_es PARAMS ((int));
+static void ppc_csect PARAMS ((int));
+static void ppc_change_csect PARAMS ((symbolS *));
+static void ppc_function PARAMS ((int));
+static void ppc_extern PARAMS ((int));
+static void ppc_lglobl PARAMS ((int));
+static void ppc_section PARAMS ((int));
+static void ppc_named_section PARAMS ((int));
+static void ppc_stabx PARAMS ((int));
+static void ppc_rename PARAMS ((int));
+static void ppc_toc PARAMS ((int));
+static void ppc_xcoff_cons PARAMS ((int));
+static void ppc_vbyte PARAMS ((int));
+#endif
+
+#ifdef OBJ_ELF
+static bfd_reloc_code_real_type ppc_elf_suffix PARAMS ((char **, expressionS *));
+static void ppc_elf_cons PARAMS ((int));
+static void ppc_elf_rdata PARAMS ((int));
+static void ppc_elf_lcomm PARAMS ((int));
+static void ppc_elf_validate_fix PARAMS ((fixS *, segT));
+#endif
+
+#ifdef TE_PE
+static void ppc_set_current_section PARAMS ((segT));
+static void ppc_previous PARAMS ((int));
+static void ppc_pdata PARAMS ((int));
+static void ppc_ydata PARAMS ((int));
+static void ppc_reldata PARAMS ((int));
+static void ppc_rdata PARAMS ((int));
+static void ppc_ualong PARAMS ((int));
+static void ppc_znop PARAMS ((int));
+static void ppc_pe_comm PARAMS ((int));
+static void ppc_pe_section PARAMS ((int));
+static void ppc_pe_function PARAMS ((int));
+static void ppc_pe_tocd PARAMS ((int));
+#endif
+
+/* Generic assembler global variables which must be defined by all
+ targets. */
+
+#ifdef OBJ_ELF
+/* This string holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful. The macro
+ tc_comment_chars points to this. We use this, rather than the
+ usual comment_chars, so that we can switch for Solaris conventions. */
+static const char ppc_solaris_comment_chars[] = "#!";
+static const char ppc_eabi_comment_chars[] = "#";
+
+#ifdef TARGET_SOLARIS_COMMENT
+const char *ppc_comment_chars = ppc_solaris_comment_chars;
+#else
+const char *ppc_comment_chars = ppc_eabi_comment_chars;
+#endif
+#else
+const char comment_chars[] = "#";
+#endif
+
+/* Characters which start a comment at the beginning of a line. */
+const char line_comment_chars[] = "#";
+
+/* Characters which may be used to separate multiple commands on a
+ single line. */
+const char line_separator_chars[] = ";";
+
+/* Characters which are used to indicate an exponent in a floating
+ point number. */
+const char EXP_CHARS[] = "eE";
+
+/* Characters which mean that a number is a floating point constant,
+ as in 0d1.0. */
+const char FLT_CHARS[] = "dD";
+
+/* The target specific pseudo-ops which we support. */
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ /* Pseudo-ops which must be overridden. */
+ { "byte", ppc_byte, 0 },
+
+#ifdef OBJ_XCOFF
+ /* Pseudo-ops specific to the RS/6000 XCOFF format. Some of these
+ legitimately belong in the obj-*.c file. However, XCOFF is based
+ on COFF, and is only implemented for the RS/6000. We just use
+ obj-coff.c, and add what we need here. */
+ { "comm", ppc_comm, 0 },
+ { "lcomm", ppc_comm, 1 },
+ { "bb", ppc_bb, 0 },
+ { "bc", ppc_bc, 0 },
+ { "bf", ppc_bf, 0 },
+ { "bi", ppc_biei, 0 },
+ { "bs", ppc_bs, 0 },
+ { "csect", ppc_csect, 0 },
+ { "data", ppc_section, 'd' },
+ { "eb", ppc_eb, 0 },
+ { "ec", ppc_ec, 0 },
+ { "ef", ppc_ef, 0 },
+ { "ei", ppc_biei, 1 },
+ { "es", ppc_es, 0 },
+ { "extern", ppc_extern, 0 },
+ { "function", ppc_function, 0 },
+ { "lglobl", ppc_lglobl, 0 },
+ { "rename", ppc_rename, 0 },
+ { "section", ppc_named_section, 0 },
+ { "stabx", ppc_stabx, 0 },
+ { "text", ppc_section, 't' },
+ { "toc", ppc_toc, 0 },
+ { "long", ppc_xcoff_cons, 2 },
+ { "word", ppc_xcoff_cons, 1 },
+ { "short", ppc_xcoff_cons, 1 },
+ { "vbyte", ppc_vbyte, 0 },
+#endif
+
+#ifdef OBJ_ELF
+ { "long", ppc_elf_cons, 4 },
+ { "word", ppc_elf_cons, 2 },
+ { "short", ppc_elf_cons, 2 },
+ { "rdata", ppc_elf_rdata, 0 },
+ { "rodata", ppc_elf_rdata, 0 },
+ { "lcomm", ppc_elf_lcomm, 0 },
+#endif
+
+#ifdef TE_PE
+ /* Pseudo-ops specific to the Windows NT PowerPC PE (coff) format */
+ { "previous", ppc_previous, 0 },
+ { "pdata", ppc_pdata, 0 },
+ { "ydata", ppc_ydata, 0 },
+ { "reldata", ppc_reldata, 0 },
+ { "rdata", ppc_rdata, 0 },
+ { "ualong", ppc_ualong, 0 },
+ { "znop", ppc_znop, 0 },
+ { "comm", ppc_pe_comm, 0 },
+ { "lcomm", ppc_pe_comm, 1 },
+ { "section", ppc_pe_section, 0 },
+ { "function", ppc_pe_function,0 },
+ { "tocd", ppc_pe_tocd, 0 },
+#endif
+
+ /* This pseudo-op is used even when not generating XCOFF output. */
+ { "tc", ppc_tc, 0 },
+
+ { NULL, NULL, 0 }
+};
+
+
+/* Predefined register names if -mregnames (or default for Windows NT). */
+/* In general, there are lots of them, in an attempt to be compatible */
+/* with a number of other Windows NT assemblers. */
+
+/* Structure to hold information about predefined registers. */
+struct pd_reg
+ {
+ char *name;
+ int value;
+ };
+
+/* List of registers that are pre-defined:
+
+ Each general register has predefined names of the form:
+ 1. r<reg_num> which has the value <reg_num>.
+ 2. r.<reg_num> which has the value <reg_num>.
+
+
+ Each floating point register has predefined names of the form:
+ 1. f<reg_num> which has the value <reg_num>.
+ 2. f.<reg_num> which has the value <reg_num>.
+
+ Each condition register has predefined names of the form:
+ 1. cr<reg_num> which has the value <reg_num>.
+ 2. cr.<reg_num> which has the value <reg_num>.
+
+ There are individual registers as well:
+ sp or r.sp has the value 1
+ rtoc or r.toc has the value 2
+ fpscr has the value 0
+ xer has the value 1
+ lr has the value 8
+ ctr has the value 9
+ pmr has the value 0
+ dar has the value 19
+ dsisr has the value 18
+ dec has the value 22
+ sdr1 has the value 25
+ srr0 has the value 26
+ srr1 has the value 27
+
+ The table is sorted. Suitable for searching by a binary search. */
+
+static const struct pd_reg pre_defined_registers[] =
+{
+ { "cr.0", 0 }, /* Condition Registers */
+ { "cr.1", 1 },
+ { "cr.2", 2 },
+ { "cr.3", 3 },
+ { "cr.4", 4 },
+ { "cr.5", 5 },
+ { "cr.6", 6 },
+ { "cr.7", 7 },
+
+ { "cr0", 0 },
+ { "cr1", 1 },
+ { "cr2", 2 },
+ { "cr3", 3 },
+ { "cr4", 4 },
+ { "cr5", 5 },
+ { "cr6", 6 },
+ { "cr7", 7 },
+
+ { "ctr", 9 },
+
+ { "dar", 19 }, /* Data Access Register */
+ { "dec", 22 }, /* Decrementer */
+ { "dsisr", 18 }, /* Data Storage Interrupt Status Register */
+
+ { "f.0", 0 }, /* Floating point registers */
+ { "f.1", 1 },
+ { "f.10", 10 },
+ { "f.11", 11 },
+ { "f.12", 12 },
+ { "f.13", 13 },
+ { "f.14", 14 },
+ { "f.15", 15 },
+ { "f.16", 16 },
+ { "f.17", 17 },
+ { "f.18", 18 },
+ { "f.19", 19 },
+ { "f.2", 2 },
+ { "f.20", 20 },
+ { "f.21", 21 },
+ { "f.22", 22 },
+ { "f.23", 23 },
+ { "f.24", 24 },
+ { "f.25", 25 },
+ { "f.26", 26 },
+ { "f.27", 27 },
+ { "f.28", 28 },
+ { "f.29", 29 },
+ { "f.3", 3 },
+ { "f.30", 30 },
+ { "f.31", 31 },
+ { "f.4", 4 },
+ { "f.5", 5 },
+ { "f.6", 6 },
+ { "f.7", 7 },
+ { "f.8", 8 },
+ { "f.9", 9 },
+
+ { "f0", 0 },
+ { "f1", 1 },
+ { "f10", 10 },
+ { "f11", 11 },
+ { "f12", 12 },
+ { "f13", 13 },
+ { "f14", 14 },
+ { "f15", 15 },
+ { "f16", 16 },
+ { "f17", 17 },
+ { "f18", 18 },
+ { "f19", 19 },
+ { "f2", 2 },
+ { "f20", 20 },
+ { "f21", 21 },
+ { "f22", 22 },
+ { "f23", 23 },
+ { "f24", 24 },
+ { "f25", 25 },
+ { "f26", 26 },
+ { "f27", 27 },
+ { "f28", 28 },
+ { "f29", 29 },
+ { "f3", 3 },
+ { "f30", 30 },
+ { "f31", 31 },
+ { "f4", 4 },
+ { "f5", 5 },
+ { "f6", 6 },
+ { "f7", 7 },
+ { "f8", 8 },
+ { "f9", 9 },
+
+ { "fpscr", 0 },
+
+ { "lr", 8 }, /* Link Register */
+
+ { "pmr", 0 },
+
+ { "r.0", 0 }, /* General Purpose Registers */
+ { "r.1", 1 },
+ { "r.10", 10 },
+ { "r.11", 11 },
+ { "r.12", 12 },
+ { "r.13", 13 },
+ { "r.14", 14 },
+ { "r.15", 15 },
+ { "r.16", 16 },
+ { "r.17", 17 },
+ { "r.18", 18 },
+ { "r.19", 19 },
+ { "r.2", 2 },
+ { "r.20", 20 },
+ { "r.21", 21 },
+ { "r.22", 22 },
+ { "r.23", 23 },
+ { "r.24", 24 },
+ { "r.25", 25 },
+ { "r.26", 26 },
+ { "r.27", 27 },
+ { "r.28", 28 },
+ { "r.29", 29 },
+ { "r.3", 3 },
+ { "r.30", 30 },
+ { "r.31", 31 },
+ { "r.4", 4 },
+ { "r.5", 5 },
+ { "r.6", 6 },
+ { "r.7", 7 },
+ { "r.8", 8 },
+ { "r.9", 9 },
+
+ { "r.sp", 1 }, /* Stack Pointer */
+
+ { "r.toc", 2 }, /* Pointer to the table of contents */
+
+ { "r0", 0 }, /* More general purpose registers */
+ { "r1", 1 },
+ { "r10", 10 },
+ { "r11", 11 },
+ { "r12", 12 },
+ { "r13", 13 },
+ { "r14", 14 },
+ { "r15", 15 },
+ { "r16", 16 },
+ { "r17", 17 },
+ { "r18", 18 },
+ { "r19", 19 },
+ { "r2", 2 },
+ { "r20", 20 },
+ { "r21", 21 },
+ { "r22", 22 },
+ { "r23", 23 },
+ { "r24", 24 },
+ { "r25", 25 },
+ { "r26", 26 },
+ { "r27", 27 },
+ { "r28", 28 },
+ { "r29", 29 },
+ { "r3", 3 },
+ { "r30", 30 },
+ { "r31", 31 },
+ { "r4", 4 },
+ { "r5", 5 },
+ { "r6", 6 },
+ { "r7", 7 },
+ { "r8", 8 },
+ { "r9", 9 },
+
+ { "rtoc", 2 }, /* Table of contents */
+
+ { "sdr1", 25 }, /* Storage Description Register 1 */
+
+ { "sp", 1 },
+
+ { "srr0", 26 }, /* Machine Status Save/Restore Register 0 */
+ { "srr1", 27 }, /* Machine Status Save/Restore Register 1 */
+
+ { "xer", 1 },
+
+};
+
+#define REG_NAME_CNT (sizeof(pre_defined_registers) / sizeof(struct pd_reg))
+
+/* Given NAME, find the register number associated with that name, return
+ the integer value associated with the given name or -1 on failure. */
+
+static int reg_name_search
+ PARAMS ((const struct pd_reg *, int, const char * name));
+
+static int
+reg_name_search (regs, regcount, name)
+ const struct pd_reg *regs;
+ int regcount;
+ const char *name;
+{
+ int middle, low, high;
+ int cmp;
+
+ low = 0;
+ high = regcount - 1;
+
+ do
+ {
+ middle = (low + high) / 2;
+ cmp = strcasecmp (name, regs[middle].name);
+ if (cmp < 0)
+ high = middle - 1;
+ else if (cmp > 0)
+ low = middle + 1;
+ else
+ return regs[middle].value;
+ }
+ while (low <= high);
+
+ return -1;
+}
+
+/*
+ * Summary of register_name().
+ *
+ * in: Input_line_pointer points to 1st char of operand.
+ *
+ * out: A expressionS.
+ * The operand may have been a register: in this case, X_op == O_register,
+ * X_add_number is set to the register number, and truth is returned.
+ * Input_line_pointer->(next non-blank) char after operand, or is in its
+ * original state.
+ */
+
+static boolean
+register_name (expressionP)
+ expressionS *expressionP;
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand */
+ start = name = input_line_pointer;
+ if (name[0] == '%' && isalpha (name[1]))
+ name = ++input_line_pointer;
+
+ else if (!reg_names_p || !isalpha (name[0]))
+ return false;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (pre_defined_registers, REG_NAME_CNT, name);
+
+ /* look to see if it's in the register table */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ /* make the rest nice */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+ *input_line_pointer = c; /* put back the delimiting char */
+ return true;
+ }
+ else
+ {
+ /* reset the line as if we had not done anything */
+ *input_line_pointer = c; /* put back the delimiting char */
+ input_line_pointer = start; /* reset input_line pointer */
+ return false;
+ }
+}
+
+/* This function is called for each symbol seen in an expression. It
+ handles the special parsing which PowerPC assemblers are supposed
+ to use for condition codes. */
+
+/* Whether to do the special parsing. */
+static boolean cr_operand;
+
+/* Names to recognize in a condition code. This table is sorted. */
+static const struct pd_reg cr_names[] =
+{
+ { "cr0", 0 },
+ { "cr1", 1 },
+ { "cr2", 2 },
+ { "cr3", 3 },
+ { "cr4", 4 },
+ { "cr5", 5 },
+ { "cr6", 6 },
+ { "cr7", 7 },
+ { "eq", 2 },
+ { "gt", 1 },
+ { "lt", 0 },
+ { "so", 3 },
+ { "un", 3 }
+};
+
+/* Parsing function. This returns non-zero if it recognized an
+ expression. */
+
+int
+ppc_parse_name (name, expr)
+ const char *name;
+ expressionS *expr;
+{
+ int val;
+
+ if (! cr_operand)
+ return 0;
+
+ val = reg_name_search (cr_names, sizeof cr_names / sizeof cr_names[0],
+ name);
+ if (val < 0)
+ return 0;
+
+ expr->X_op = O_constant;
+ expr->X_add_number = val;
+
+ return 1;
+}
+
+/* Local variables. */
+
+/* The type of processor we are assembling for. This is one or more
+ of the PPC_OPCODE flags defined in opcode/ppc.h. */
+static int ppc_cpu = 0;
+
+/* The size of the processor we are assembling for. This is either
+ PPC_OPCODE_32 or PPC_OPCODE_64. */
+static int ppc_size = PPC_OPCODE_32;
+
+/* Opcode hash table. */
+static struct hash_control *ppc_hash;
+
+/* Macro hash table. */
+static struct hash_control *ppc_macro_hash;
+
+#ifdef OBJ_ELF
+/* What type of shared library support to use */
+static enum { SHLIB_NONE, SHLIB_PIC, SHILB_MRELOCATABLE } shlib = SHLIB_NONE;
+
+/* Flags to set in the elf header */
+static flagword ppc_flags = 0;
+
+/* Whether this is Solaris or not. */
+#ifdef TARGET_SOLARIS_COMMENT
+#define SOLARIS_P true
+#else
+#define SOLARIS_P false
+#endif
+
+static boolean msolaris = SOLARIS_P;
+#endif
+
+#ifdef OBJ_XCOFF
+
+/* The RS/6000 assembler uses the .csect pseudo-op to generate code
+ using a bunch of different sections. These assembler sections,
+ however, are all encompassed within the .text or .data sections of
+ the final output file. We handle this by using different
+ subsegments within these main segments. */
+
+/* Next subsegment to allocate within the .text segment. */
+static subsegT ppc_text_subsegment = 2;
+
+/* Linked list of csects in the text section. */
+static symbolS *ppc_text_csects;
+
+/* Next subsegment to allocate within the .data segment. */
+static subsegT ppc_data_subsegment = 2;
+
+/* Linked list of csects in the data section. */
+static symbolS *ppc_data_csects;
+
+/* The current csect. */
+static symbolS *ppc_current_csect;
+
+/* The RS/6000 assembler uses a TOC which holds addresses of functions
+ and variables. Symbols are put in the TOC with the .tc pseudo-op.
+ A special relocation is used when accessing TOC entries. We handle
+ the TOC as a subsegment within the .data segment. We set it up if
+ we see a .toc pseudo-op, and save the csect symbol here. */
+static symbolS *ppc_toc_csect;
+
+/* The first frag in the TOC subsegment. */
+static fragS *ppc_toc_frag;
+
+/* The first frag in the first subsegment after the TOC in the .data
+ segment. NULL if there are no subsegments after the TOC. */
+static fragS *ppc_after_toc_frag;
+
+/* The current static block. */
+static symbolS *ppc_current_block;
+
+/* The COFF debugging section; set by md_begin. This is not the
+ .debug section, but is instead the secret BFD section which will
+ cause BFD to set the section number of a symbol to N_DEBUG. */
+static asection *ppc_coff_debug_section;
+
+#endif /* OBJ_XCOFF */
+
+#ifdef TE_PE
+
+/* Various sections that we need for PE coff support. */
+static segT ydata_section;
+static segT pdata_section;
+static segT reldata_section;
+static segT rdata_section;
+static segT tocdata_section;
+
+/* The current section and the previous section. See ppc_previous. */
+static segT ppc_previous_section;
+static segT ppc_current_section;
+
+#endif /* TE_PE */
+
+#ifdef OBJ_ELF
+symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE" */
+#endif /* OBJ_ELF */
+
+#ifdef OBJ_ELF
+CONST char *md_shortopts = "b:l:usm:K:VQ:";
+#else
+CONST char *md_shortopts = "um:";
+#endif
+struct option md_longopts[] = {
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ switch (c)
+ {
+ case 'u':
+ /* -u means that any undefined symbols should be treated as
+ external, which is the default for gas anyhow. */
+ break;
+
+#ifdef OBJ_ELF
+ case 'l':
+ /* Solaris as takes -le (presumably for little endian). For completeness
+ sake, recognize -be also. */
+ if (strcmp (arg, "e") == 0)
+ {
+ target_big_endian = 0;
+ set_target_endian = 1;
+ }
+ else
+ return 0;
+
+ break;
+
+ case 'b':
+ if (strcmp (arg, "e") == 0)
+ {
+ target_big_endian = 1;
+ set_target_endian = 1;
+ }
+ else
+ return 0;
+
+ break;
+
+ case 'K':
+ /* Recognize -K PIC */
+ if (strcmp (arg, "PIC") == 0 || strcmp (arg, "pic") == 0)
+ {
+ shlib = SHLIB_PIC;
+ ppc_flags |= EF_PPC_RELOCATABLE_LIB;
+ }
+ else
+ return 0;
+
+ break;
+#endif
+
+ case 'm':
+ /* -mpwrx and -mpwr2 mean to assemble for the IBM POWER/2
+ (RIOS2). */
+ if (strcmp (arg, "pwrx") == 0 || strcmp (arg, "pwr2") == 0)
+ ppc_cpu = PPC_OPCODE_POWER | PPC_OPCODE_POWER2;
+ /* -mpwr means to assemble for the IBM POWER (RIOS1). */
+ else if (strcmp (arg, "pwr") == 0)
+ ppc_cpu = PPC_OPCODE_POWER;
+ /* -m601 means to assemble for the Motorola PowerPC 601, which includes
+ instructions that are holdovers from the Power. */
+ else if (strcmp (arg, "601") == 0)
+ ppc_cpu = PPC_OPCODE_PPC | PPC_OPCODE_601;
+ /* -mppc, -mppc32, -m603, and -m604 mean to assemble for the
+ Motorola PowerPC 603/604. */
+ else if (strcmp (arg, "ppc") == 0
+ || strcmp (arg, "ppc32") == 0
+ || strcmp (arg, "403") == 0
+ || strcmp (arg, "603") == 0
+ || strcmp (arg, "604") == 0)
+ ppc_cpu = PPC_OPCODE_PPC;
+ /* -mppc64 and -m620 mean to assemble for the 64-bit PowerPC
+ 620. */
+ else if (strcmp (arg, "ppc64") == 0 || strcmp (arg, "620") == 0)
+ {
+ ppc_cpu = PPC_OPCODE_PPC;
+ ppc_size = PPC_OPCODE_64;
+ }
+ /* -mcom means assemble for the common intersection between Power
+ and PowerPC. At present, we just allow the union, rather
+ than the intersection. */
+ else if (strcmp (arg, "com") == 0)
+ ppc_cpu = PPC_OPCODE_COMMON;
+ /* -many means to assemble for any architecture (PWR/PWRX/PPC). */
+ else if (strcmp (arg, "any") == 0)
+ ppc_cpu = PPC_OPCODE_ANY;
+
+ else if (strcmp (arg, "regnames") == 0)
+ reg_names_p = true;
+
+ else if (strcmp (arg, "no-regnames") == 0)
+ reg_names_p = false;
+
+#ifdef OBJ_ELF
+ /* -mrelocatable/-mrelocatable-lib -- warn about initializations that require relocation */
+ else if (strcmp (arg, "relocatable") == 0)
+ {
+ shlib = SHILB_MRELOCATABLE;
+ ppc_flags |= EF_PPC_RELOCATABLE;
+ }
+
+ else if (strcmp (arg, "relocatable-lib") == 0)
+ {
+ shlib = SHILB_MRELOCATABLE;
+ ppc_flags |= EF_PPC_RELOCATABLE_LIB;
+ }
+
+ /* -memb, set embedded bit */
+ else if (strcmp (arg, "emb") == 0)
+ ppc_flags |= EF_PPC_EMB;
+
+ /* -mlittle/-mbig set the endianess */
+ else if (strcmp (arg, "little") == 0 || strcmp (arg, "little-endian") == 0)
+ {
+ target_big_endian = 0;
+ set_target_endian = 1;
+ }
+
+ else if (strcmp (arg, "big") == 0 || strcmp (arg, "big-endian") == 0)
+ {
+ target_big_endian = 1;
+ set_target_endian = 1;
+ }
+
+ else if (strcmp (arg, "solaris") == 0)
+ {
+ msolaris = true;
+ ppc_comment_chars = ppc_solaris_comment_chars;
+ }
+
+ else if (strcmp (arg, "no-solaris") == 0)
+ {
+ msolaris = false;
+ ppc_comment_chars = ppc_eabi_comment_chars;
+ }
+#endif
+ else
+ {
+ as_bad (_("invalid switch -m%s"), arg);
+ return 0;
+ }
+ break;
+
+#ifdef OBJ_ELF
+ /* -V: SVR4 argument to print version ID. */
+ case 'V':
+ print_version_id ();
+ break;
+
+ /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section
+ should be emitted or not. FIXME: Not implemented. */
+ case 'Q':
+ break;
+
+ /* Solaris takes -s to specify that .stabs go in a .stabs section,
+ rather than .stabs.excl, which is ignored by the linker.
+ FIXME: Not implemented. */
+ case 's':
+ if (arg)
+ return 0;
+
+ break;
+#endif
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ fprintf(stream, _("\
+PowerPC options:\n\
+-u ignored\n\
+-mpwrx, -mpwr2 generate code for IBM POWER/2 (RIOS2)\n\
+-mpwr generate code for IBM POWER (RIOS1)\n\
+-m601 generate code for Motorola PowerPC 601\n\
+-mppc, -mppc32, -m403, -m603, -m604\n\
+ generate code for Motorola PowerPC 603/604\n\
+-mppc64, -m620 generate code for Motorola PowerPC 620\n\
+-mcom generate code Power/PowerPC common instructions\n\
+-many generate code for any architecture (PWR/PWRX/PPC)\n\
+-mregnames Allow symbolic names for registers\n\
+-mno-regnames Do not allow symbolic names for registers\n"));
+#ifdef OBJ_ELF
+ fprintf(stream, _("\
+-mrelocatable support for GCC's -mrelocatble option\n\
+-mrelocatable-lib support for GCC's -mrelocatble-lib option\n\
+-memb set PPC_EMB bit in ELF flags\n\
+-mlittle, -mlittle-endian\n\
+ generate code for a little endian machine\n\
+-mbig, -mbig-endian generate code for a big endian machine\n\
+-msolaris generate code for Solaris\n\
+-mno-solaris do not generate code for Solaris\n\
+-V print assembler version number\n\
+-Qy, -Qn ignored\n"));
+#endif
+}
+
+/* Set ppc_cpu if it is not already set. */
+
+static void
+ppc_set_cpu ()
+{
+ const char *default_os = TARGET_OS;
+ const char *default_cpu = TARGET_CPU;
+
+ if (ppc_cpu == 0)
+ {
+ if (strncmp (default_os, "aix", 3) == 0
+ && default_os[3] >= '4' && default_os[3] <= '9')
+ ppc_cpu = PPC_OPCODE_COMMON;
+ else if (strncmp (default_os, "aix3", 4) == 0)
+ ppc_cpu = PPC_OPCODE_POWER;
+ else if (strcmp (default_cpu, "rs6000") == 0)
+ ppc_cpu = PPC_OPCODE_POWER;
+ else if (strcmp (default_cpu, "powerpc") == 0
+ || strcmp (default_cpu, "powerpcle") == 0)
+ ppc_cpu = PPC_OPCODE_PPC;
+ else
+ as_fatal (_("Unknown default cpu = %s, os = %s"), default_cpu, default_os);
+ }
+}
+
+/* Figure out the BFD architecture to use. */
+
+enum bfd_architecture
+ppc_arch ()
+{
+ const char *default_cpu = TARGET_CPU;
+ ppc_set_cpu ();
+
+ if ((ppc_cpu & PPC_OPCODE_PPC) != 0)
+ return bfd_arch_powerpc;
+ else if ((ppc_cpu & PPC_OPCODE_POWER) != 0)
+ return bfd_arch_rs6000;
+ else if ((ppc_cpu & (PPC_OPCODE_COMMON | PPC_OPCODE_ANY)) != 0)
+ {
+ if (strcmp (default_cpu, "rs6000") == 0)
+ return bfd_arch_rs6000;
+ else if (strcmp (default_cpu, "powerpc") == 0
+ || strcmp (default_cpu, "powerpcle") == 0)
+ return bfd_arch_powerpc;
+ }
+
+ as_fatal (_("Neither Power nor PowerPC opcodes were selected."));
+ return bfd_arch_unknown;
+}
+
+/* This function is called when the assembler starts up. It is called
+ after the options have been parsed and the output file has been
+ opened. */
+
+void
+md_begin ()
+{
+ register const struct powerpc_opcode *op;
+ const struct powerpc_opcode *op_end;
+ const struct powerpc_macro *macro;
+ const struct powerpc_macro *macro_end;
+ boolean dup_insn = false;
+
+ ppc_set_cpu ();
+
+#ifdef OBJ_ELF
+ /* Set the ELF flags if desired. */
+ if (ppc_flags && !msolaris)
+ bfd_set_private_flags (stdoutput, ppc_flags);
+#endif
+
+ /* Insert the opcodes into a hash table. */
+ ppc_hash = hash_new ();
+
+ op_end = powerpc_opcodes + powerpc_num_opcodes;
+ for (op = powerpc_opcodes; op < op_end; op++)
+ {
+ know ((op->opcode & op->mask) == op->opcode);
+
+ if ((op->flags & ppc_cpu) != 0
+ && ((op->flags & (PPC_OPCODE_32 | PPC_OPCODE_64)) == 0
+ || (op->flags & (PPC_OPCODE_32 | PPC_OPCODE_64)) == ppc_size))
+ {
+ const char *retval;
+
+ retval = hash_insert (ppc_hash, op->name, (PTR) op);
+ if (retval != (const char *) NULL)
+ {
+ /* Ignore Power duplicates for -m601 */
+ if ((ppc_cpu & PPC_OPCODE_601) != 0
+ && (op->flags & PPC_OPCODE_POWER) != 0)
+ continue;
+
+ as_bad (_("Internal assembler error for instruction %s"), op->name);
+ dup_insn = true;
+ }
+ }
+ }
+
+ /* Insert the macros into a hash table. */
+ ppc_macro_hash = hash_new ();
+
+ macro_end = powerpc_macros + powerpc_num_macros;
+ for (macro = powerpc_macros; macro < macro_end; macro++)
+ {
+ if ((macro->flags & ppc_cpu) != 0)
+ {
+ const char *retval;
+
+ retval = hash_insert (ppc_macro_hash, macro->name, (PTR) macro);
+ if (retval != (const char *) NULL)
+ {
+ as_bad (_("Internal assembler error for macro %s"), macro->name);
+ dup_insn = true;
+ }
+ }
+ }
+
+ if (dup_insn)
+ abort ();
+
+ /* Tell the main code what the endianness is if it is not overidden by the user. */
+ if (!set_target_endian)
+ {
+ set_target_endian = 1;
+ target_big_endian = PPC_BIG_ENDIAN;
+ }
+
+#ifdef OBJ_XCOFF
+ ppc_coff_debug_section = coff_section_from_bfd_index (stdoutput, N_DEBUG);
+
+ /* Create dummy symbols to serve as initial csects. This forces the
+ text csects to precede the data csects. These symbols will not
+ be output. */
+ ppc_text_csects = symbol_make ("dummy\001");
+ ppc_text_csects->sy_tc.within = ppc_text_csects;
+ ppc_data_csects = symbol_make ("dummy\001");
+ ppc_data_csects->sy_tc.within = ppc_data_csects;
+#endif
+
+#ifdef TE_PE
+
+ ppc_current_section = text_section;
+ ppc_previous_section = 0;
+
+#endif
+}
+
+/* Insert an operand value into an instruction. */
+
+static unsigned long
+ppc_insert_operand (insn, operand, val, file, line)
+ unsigned long insn;
+ const struct powerpc_operand *operand;
+ offsetT val;
+ char *file;
+ unsigned int line;
+{
+ if (operand->bits != 32)
+ {
+ long min, max;
+ offsetT test;
+
+ if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+ {
+ if ((operand->flags & PPC_OPERAND_SIGNOPT) != 0
+ && ppc_size == PPC_OPCODE_32)
+ max = (1 << operand->bits) - 1;
+ else
+ max = (1 << (operand->bits - 1)) - 1;
+ min = - (1 << (operand->bits - 1));
+
+ if (ppc_size == PPC_OPCODE_32)
+ {
+ /* Some people write 32 bit hex constants with the sign
+ extension done by hand. This shouldn't really be
+ valid, but, to permit this code to assemble on a 64
+ bit host, we sign extend the 32 bit value. */
+ if (val > 0
+ && (val & 0x80000000) != 0
+ && (val & 0xffffffff) == val)
+ {
+ val -= 0x80000000;
+ val -= 0x80000000;
+ }
+ }
+ }
+ else
+ {
+ max = (1 << operand->bits) - 1;
+ min = 0;
+ }
+
+ if ((operand->flags & PPC_OPERAND_NEGATIVE) != 0)
+ test = - val;
+ else
+ test = val;
+
+ if (test < (offsetT) min || test > (offsetT) max)
+ {
+ const char *err =
+ _("operand out of range (%s not between %ld and %ld)");
+ char buf[100];
+
+ sprint_value (buf, test);
+ if (file == (char *) NULL)
+ as_bad (err, buf, min, max);
+ else
+ as_bad_where (file, line, err, buf, min, max);
+ }
+ }
+
+ if (operand->insert)
+ {
+ const char *errmsg;
+
+ errmsg = NULL;
+ insn = (*operand->insert) (insn, (long) val, &errmsg);
+ if (errmsg != (const char *) NULL)
+ as_bad (errmsg);
+ }
+ else
+ insn |= (((long) val & ((1 << operand->bits) - 1))
+ << operand->shift);
+
+ return insn;
+}
+
+
+#ifdef OBJ_ELF
+/* Parse @got, etc. and return the desired relocation. */
+static bfd_reloc_code_real_type
+ppc_elf_suffix (str_p, exp_p)
+ char **str_p;
+ expressionS *exp_p;
+{
+ struct map_bfd {
+ char *string;
+ int length;
+ bfd_reloc_code_real_type reloc;
+ };
+
+ char ident[20];
+ char *str = *str_p;
+ char *str2;
+ int ch;
+ int len;
+ struct map_bfd *ptr;
+
+#define MAP(str,reloc) { str, sizeof(str)-1, reloc }
+
+ static struct map_bfd mapping[] = {
+ MAP ("l", BFD_RELOC_LO16),
+ MAP ("h", BFD_RELOC_HI16),
+ MAP ("ha", BFD_RELOC_HI16_S),
+ MAP ("brtaken", BFD_RELOC_PPC_B16_BRTAKEN),
+ MAP ("brntaken", BFD_RELOC_PPC_B16_BRNTAKEN),
+ MAP ("got", BFD_RELOC_16_GOTOFF),
+ MAP ("got@l", BFD_RELOC_LO16_GOTOFF),
+ MAP ("got@h", BFD_RELOC_HI16_GOTOFF),
+ MAP ("got@ha", BFD_RELOC_HI16_S_GOTOFF),
+ MAP ("fixup", BFD_RELOC_CTOR), /* warnings with -mrelocatable */
+ MAP ("plt", BFD_RELOC_24_PLT_PCREL),
+ MAP ("pltrel24", BFD_RELOC_24_PLT_PCREL),
+ MAP ("copy", BFD_RELOC_PPC_COPY),
+ MAP ("globdat", BFD_RELOC_PPC_GLOB_DAT),
+ MAP ("local24pc", BFD_RELOC_PPC_LOCAL24PC),
+ MAP ("local", BFD_RELOC_PPC_LOCAL24PC),
+ MAP ("pltrel", BFD_RELOC_32_PLT_PCREL),
+ MAP ("plt@l", BFD_RELOC_LO16_PLTOFF),
+ MAP ("plt@h", BFD_RELOC_HI16_PLTOFF),
+ MAP ("plt@ha", BFD_RELOC_HI16_S_PLTOFF),
+ MAP ("sdarel", BFD_RELOC_GPREL16),
+ MAP ("sectoff", BFD_RELOC_32_BASEREL),
+ MAP ("sectoff@l", BFD_RELOC_LO16_BASEREL),
+ MAP ("sectoff@h", BFD_RELOC_HI16_BASEREL),
+ MAP ("sectoff@ha", BFD_RELOC_HI16_S_BASEREL),
+ MAP ("naddr", BFD_RELOC_PPC_EMB_NADDR32),
+ MAP ("naddr16", BFD_RELOC_PPC_EMB_NADDR16),
+ MAP ("naddr@l", BFD_RELOC_PPC_EMB_NADDR16_LO),
+ MAP ("naddr@h", BFD_RELOC_PPC_EMB_NADDR16_HI),
+ MAP ("naddr@ha", BFD_RELOC_PPC_EMB_NADDR16_HA),
+ MAP ("sdai16", BFD_RELOC_PPC_EMB_SDAI16),
+ MAP ("sda2rel", BFD_RELOC_PPC_EMB_SDA2REL),
+ MAP ("sda2i16", BFD_RELOC_PPC_EMB_SDA2I16),
+ MAP ("sda21", BFD_RELOC_PPC_EMB_SDA21),
+ MAP ("mrkref", BFD_RELOC_PPC_EMB_MRKREF),
+ MAP ("relsect", BFD_RELOC_PPC_EMB_RELSEC16),
+ MAP ("relsect@l", BFD_RELOC_PPC_EMB_RELST_LO),
+ MAP ("relsect@h", BFD_RELOC_PPC_EMB_RELST_HI),
+ MAP ("relsect@ha", BFD_RELOC_PPC_EMB_RELST_HA),
+ MAP ("bitfld", BFD_RELOC_PPC_EMB_BIT_FLD),
+ MAP ("relsda", BFD_RELOC_PPC_EMB_RELSDA),
+ MAP ("xgot", BFD_RELOC_PPC_TOC16),
+
+ { (char *)0, 0, BFD_RELOC_UNUSED }
+ };
+
+ if (*str++ != '@')
+ return BFD_RELOC_UNUSED;
+
+ for (ch = *str, str2 = ident;
+ (str2 < ident + sizeof (ident) - 1
+ && (isalnum (ch) || ch == '@'));
+ ch = *++str)
+ {
+ *str2++ = (islower (ch)) ? ch : tolower (ch);
+ }
+
+ *str2 = '\0';
+ len = str2 - ident;
+
+ ch = ident[0];
+ for (ptr = &mapping[0]; ptr->length > 0; ptr++)
+ if (ch == ptr->string[0]
+ && len == ptr->length
+ && memcmp (ident, ptr->string, ptr->length) == 0)
+ {
+ if (exp_p->X_add_number != 0
+ && (ptr->reloc == BFD_RELOC_16_GOTOFF
+ || ptr->reloc == BFD_RELOC_LO16_GOTOFF
+ || ptr->reloc == BFD_RELOC_HI16_GOTOFF
+ || ptr->reloc == BFD_RELOC_HI16_S_GOTOFF))
+ as_warn (_("identifier+constant@got means identifier@got+constant"));
+
+ /* Now check for identifier@suffix+constant */
+ if (*str == '-' || *str == '+')
+ {
+ char *orig_line = input_line_pointer;
+ expressionS new_exp;
+
+ input_line_pointer = str;
+ expression (&new_exp);
+ if (new_exp.X_op == O_constant)
+ {
+ exp_p->X_add_number += new_exp.X_add_number;
+ str = input_line_pointer;
+ }
+
+ if (&input_line_pointer != str_p)
+ input_line_pointer = orig_line;
+ }
+
+ *str_p = str;
+ return ptr->reloc;
+ }
+
+ return BFD_RELOC_UNUSED;
+}
+
+/* Like normal .long/.short/.word, except support @got, etc. */
+/* clobbers input_line_pointer, checks */
+/* end-of-line. */
+static void
+ppc_elf_cons (nbytes)
+ register int nbytes; /* 1=.byte, 2=.word, 4=.long */
+{
+ expressionS exp;
+ bfd_reloc_code_real_type reloc;
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ do
+ {
+ expression (&exp);
+ if (exp.X_op == O_symbol
+ && *input_line_pointer == '@'
+ && (reloc = ppc_elf_suffix (&input_line_pointer, &exp)) != BFD_RELOC_UNUSED)
+ {
+ reloc_howto_type *reloc_howto = bfd_reloc_type_lookup (stdoutput, reloc);
+ int size = bfd_get_reloc_size (reloc_howto);
+
+ if (size > nbytes)
+ as_bad (_("%s relocations do not fit in %d bytes\n"), reloc_howto->name, nbytes);
+
+ else
+ {
+ register char *p = frag_more ((int) nbytes);
+ int offset = nbytes - size;
+
+ fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size, &exp, 0, reloc);
+ }
+ }
+ else
+ emit_expr (&exp, (unsigned int) nbytes);
+ }
+ while (*input_line_pointer++ == ',');
+
+ input_line_pointer--; /* Put terminator back into stream. */
+ demand_empty_rest_of_line ();
+}
+
+/* Solaris pseduo op to change to the .rodata section. */
+static void
+ppc_elf_rdata (xxx)
+ int xxx;
+{
+ char *save_line = input_line_pointer;
+ static char section[] = ".rodata\n";
+
+ /* Just pretend this is .section .rodata */
+ input_line_pointer = section;
+ obj_elf_section (xxx);
+
+ input_line_pointer = save_line;
+}
+
+/* Pseudo op to make file scope bss items */
+static void
+ppc_elf_lcomm(xxx)
+ int xxx;
+{
+ register char *name;
+ register char c;
+ register char *p;
+ offsetT size;
+ register symbolS *symbolP;
+ offsetT align;
+ segT old_sec;
+ int old_subsec;
+ char *pfrag;
+ int align2;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ /* just after name is now '\0' */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("Expected comma after symbol-name: rest of line ignored."));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ input_line_pointer++; /* skip ',' */
+ if ((size = get_absolute_expression ()) < 0)
+ {
+ as_warn (_(".COMMon length (%ld.) <0! Ignored."), (long) size);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* The third argument to .lcomm is the alignment. */
+ if (*input_line_pointer != ',')
+ align = 8;
+ else
+ {
+ ++input_line_pointer;
+ align = get_absolute_expression ();
+ if (align <= 0)
+ {
+ as_warn (_("ignoring bad alignment"));
+ align = 8;
+ }
+ }
+
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+
+ if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
+ {
+ as_bad (_("Ignoring attempt to re-define symbol `%s'."),
+ S_GET_NAME (symbolP));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (S_GET_VALUE (symbolP) && S_GET_VALUE (symbolP) != (valueT) size)
+ {
+ as_bad (_("Length of .lcomm \"%s\" is already %ld. Not changed to %ld."),
+ S_GET_NAME (symbolP),
+ (long) S_GET_VALUE (symbolP),
+ (long) size);
+
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* allocate_bss: */
+ old_sec = now_seg;
+ old_subsec = now_subseg;
+ if (align)
+ {
+ /* convert to a power of 2 alignment */
+ for (align2 = 0; (align & 1) == 0; align >>= 1, ++align2);
+ if (align != 1)
+ {
+ as_bad (_("Common alignment not a power of 2"));
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ else
+ align2 = 0;
+
+ record_alignment (bss_section, align2);
+ subseg_set (bss_section, 0);
+ if (align2)
+ frag_align (align2, 0, 0);
+ if (S_GET_SEGMENT (symbolP) == bss_section)
+ symbolP->sy_frag->fr_symbol = 0;
+ symbolP->sy_frag = frag_now;
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP, size,
+ (char *) 0);
+ *pfrag = 0;
+ S_SET_SIZE (symbolP, size);
+ S_SET_SEGMENT (symbolP, bss_section);
+ subseg_set (old_sec, old_subsec);
+ demand_empty_rest_of_line ();
+}
+
+/* Validate any relocations emitted for -mrelocatable, possibly adding
+ fixups for word relocations in writable segments, so we can adjust
+ them at runtime. */
+static void
+ppc_elf_validate_fix (fixp, seg)
+ fixS *fixp;
+ segT seg;
+{
+ if (fixp->fx_done || fixp->fx_pcrel)
+ return;
+
+ switch (shlib)
+ {
+ case SHLIB_NONE:
+ case SHLIB_PIC:
+ return;
+
+ case SHILB_MRELOCATABLE:
+ if (fixp->fx_r_type <= BFD_RELOC_UNUSED
+ && fixp->fx_r_type != BFD_RELOC_16_GOTOFF
+ && fixp->fx_r_type != BFD_RELOC_HI16_GOTOFF
+ && fixp->fx_r_type != BFD_RELOC_LO16_GOTOFF
+ && fixp->fx_r_type != BFD_RELOC_HI16_S_GOTOFF
+ && fixp->fx_r_type != BFD_RELOC_32_BASEREL
+ && fixp->fx_r_type != BFD_RELOC_LO16_BASEREL
+ && fixp->fx_r_type != BFD_RELOC_HI16_BASEREL
+ && fixp->fx_r_type != BFD_RELOC_HI16_S_BASEREL
+ && strcmp (segment_name (seg), ".got2") != 0
+ && strcmp (segment_name (seg), ".dtors") != 0
+ && strcmp (segment_name (seg), ".ctors") != 0
+ && strcmp (segment_name (seg), ".fixup") != 0
+ && strcmp (segment_name (seg), ".stab") != 0
+ && strcmp (segment_name (seg), ".gcc_except_table") != 0
+ && strcmp (segment_name (seg), ".eh_frame") != 0
+ && strcmp (segment_name (seg), ".ex_shared") != 0)
+ {
+ if ((seg->flags & (SEC_READONLY | SEC_CODE)) != 0
+ || fixp->fx_r_type != BFD_RELOC_CTOR)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Relocation cannot be done when using -mrelocatable"));
+ }
+ }
+ return;
+ }
+}
+#endif /* OBJ_ELF */
+
+#ifdef TE_PE
+
+/*
+ * Summary of parse_toc_entry().
+ *
+ * in: Input_line_pointer points to the '[' in one of:
+ *
+ * [toc] [tocv] [toc32] [toc64]
+ *
+ * Anything else is an error of one kind or another.
+ *
+ * out:
+ * return value: success or failure
+ * toc_kind: kind of toc reference
+ * input_line_pointer:
+ * success: first char after the ']'
+ * failure: unchanged
+ *
+ * settings:
+ *
+ * [toc] - rv == success, toc_kind = default_toc
+ * [tocv] - rv == success, toc_kind = data_in_toc
+ * [toc32] - rv == success, toc_kind = must_be_32
+ * [toc64] - rv == success, toc_kind = must_be_64
+ *
+ */
+
+enum toc_size_qualifier
+{
+ default_toc, /* The toc cell constructed should be the system default size */
+ data_in_toc, /* This is a direct reference to a toc cell */
+ must_be_32, /* The toc cell constructed must be 32 bits wide */
+ must_be_64 /* The toc cell constructed must be 64 bits wide */
+};
+
+static int
+parse_toc_entry(toc_kind)
+ enum toc_size_qualifier *toc_kind;
+{
+ char *start;
+ char *toc_spec;
+ char c;
+ enum toc_size_qualifier t;
+
+ /* save the input_line_pointer */
+ start = input_line_pointer;
+
+ /* skip over the '[' , and whitespace */
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+
+ /* find the spelling of the operand */
+ toc_spec = input_line_pointer;
+ c = get_symbol_end ();
+
+ if (strcmp(toc_spec, "toc") == 0)
+ {
+ t = default_toc;
+ }
+ else if (strcmp(toc_spec, "tocv") == 0)
+ {
+ t = data_in_toc;
+ }
+ else if (strcmp(toc_spec, "toc32") == 0)
+ {
+ t = must_be_32;
+ }
+ else if (strcmp(toc_spec, "toc64") == 0)
+ {
+ t = must_be_64;
+ }
+ else
+ {
+ as_bad (_("syntax error: invalid toc specifier `%s'"), toc_spec);
+ *input_line_pointer = c; /* put back the delimiting char */
+ input_line_pointer = start; /* reset input_line pointer */
+ return 0;
+ }
+
+ /* now find the ']' */
+ *input_line_pointer = c; /* put back the delimiting char */
+
+ SKIP_WHITESPACE (); /* leading whitespace could be there. */
+ c = *input_line_pointer++; /* input_line_pointer->past char in c. */
+
+ if (c != ']')
+ {
+ as_bad (_("syntax error: expected `]', found `%c'"), c);
+ input_line_pointer = start; /* reset input_line pointer */
+ return 0;
+ }
+
+ *toc_kind = t; /* set return value */
+ return 1;
+}
+#endif
+
+
+/* We need to keep a list of fixups. We can't simply generate them as
+ we go, because that would require us to first create the frag, and
+ that would screw up references to ``.''. */
+
+struct ppc_fixup
+{
+ expressionS exp;
+ int opindex;
+ bfd_reloc_code_real_type reloc;
+};
+
+#define MAX_INSN_FIXUPS (5)
+
+/* This routine is called for each instruction to be assembled. */
+
+void
+md_assemble (str)
+ char *str;
+{
+ char *s;
+ const struct powerpc_opcode *opcode;
+ unsigned long insn;
+ const unsigned char *opindex_ptr;
+ int skip_optional;
+ int need_paren;
+ int next_opindex;
+ struct ppc_fixup fixups[MAX_INSN_FIXUPS];
+ int fc;
+ char *f;
+ int i;
+#ifdef OBJ_ELF
+ bfd_reloc_code_real_type reloc;
+#endif
+
+ /* Get the opcode. */
+ for (s = str; *s != '\0' && ! isspace (*s); s++)
+ ;
+ if (*s != '\0')
+ *s++ = '\0';
+
+ /* Look up the opcode in the hash table. */
+ opcode = (const struct powerpc_opcode *) hash_find (ppc_hash, str);
+ if (opcode == (const struct powerpc_opcode *) NULL)
+ {
+ const struct powerpc_macro *macro;
+
+ macro = (const struct powerpc_macro *) hash_find (ppc_macro_hash, str);
+ if (macro == (const struct powerpc_macro *) NULL)
+ as_bad (_("Unrecognized opcode: `%s'"), str);
+ else
+ ppc_macro (s, macro);
+
+ return;
+ }
+
+ insn = opcode->opcode;
+
+ str = s;
+ while (isspace (*str))
+ ++str;
+
+ /* PowerPC operands are just expressions. The only real issue is
+ that a few operand types are optional. All cases which might use
+ an optional operand separate the operands only with commas (in
+ some cases parentheses are used, as in ``lwz 1,0(1)'' but such
+ cases never have optional operands). There is never more than
+ one optional operand for an instruction. So, before we start
+ seriously parsing the operands, we check to see if we have an
+ optional operand, and, if we do, we count the number of commas to
+ see whether the operand should be omitted. */
+ skip_optional = 0;
+ for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++)
+ {
+ const struct powerpc_operand *operand;
+
+ operand = &powerpc_operands[*opindex_ptr];
+ if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0)
+ {
+ unsigned int opcount;
+
+ /* There is an optional operand. Count the number of
+ commas in the input line. */
+ if (*str == '\0')
+ opcount = 0;
+ else
+ {
+ opcount = 1;
+ s = str;
+ while ((s = strchr (s, ',')) != (char *) NULL)
+ {
+ ++opcount;
+ ++s;
+ }
+ }
+
+ /* If there are fewer operands in the line then are called
+ for by the instruction, we want to skip the optional
+ operand. */
+ if (opcount < strlen (opcode->operands))
+ skip_optional = 1;
+
+ break;
+ }
+ }
+
+ /* Gather the operands. */
+ need_paren = 0;
+ next_opindex = 0;
+ fc = 0;
+ for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++)
+ {
+ const struct powerpc_operand *operand;
+ const char *errmsg;
+ char *hold;
+ expressionS ex;
+ char endc;
+
+ if (next_opindex == 0)
+ operand = &powerpc_operands[*opindex_ptr];
+ else
+ {
+ operand = &powerpc_operands[next_opindex];
+ next_opindex = 0;
+ }
+
+ errmsg = NULL;
+
+ /* If this is a fake operand, then we do not expect anything
+ from the input. */
+ if ((operand->flags & PPC_OPERAND_FAKE) != 0)
+ {
+ insn = (*operand->insert) (insn, 0L, &errmsg);
+ if (errmsg != (const char *) NULL)
+ as_bad (errmsg);
+ continue;
+ }
+
+ /* If this is an optional operand, and we are skipping it, just
+ insert a zero. */
+ if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0
+ && skip_optional)
+ {
+ if (operand->insert)
+ {
+ insn = (*operand->insert) (insn, 0L, &errmsg);
+ if (errmsg != (const char *) NULL)
+ as_bad (errmsg);
+ }
+ if ((operand->flags & PPC_OPERAND_NEXT) != 0)
+ next_opindex = *opindex_ptr + 1;
+ continue;
+ }
+
+ /* Gather the operand. */
+ hold = input_line_pointer;
+ input_line_pointer = str;
+
+#ifdef TE_PE
+ if (*input_line_pointer == '[')
+ {
+ /* We are expecting something like the second argument here:
+
+ lwz r4,[toc].GS.0.static_int(rtoc)
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ The argument following the `]' must be a symbol name, and the
+ register must be the toc register: 'rtoc' or '2'
+
+ The effect is to 0 as the displacement field
+ in the instruction, and issue an IMAGE_REL_PPC_TOCREL16 (or
+ the appropriate variation) reloc against it based on the symbol.
+ The linker will build the toc, and insert the resolved toc offset.
+
+ Note:
+ o The size of the toc entry is currently assumed to be
+ 32 bits. This should not be assumed to be a hard coded
+ number.
+ o In an effort to cope with a change from 32 to 64 bits,
+ there are also toc entries that are specified to be
+ either 32 or 64 bits:
+ lwz r4,[toc32].GS.0.static_int(rtoc)
+ lwz r4,[toc64].GS.0.static_int(rtoc)
+ These demand toc entries of the specified size, and the
+ instruction probably requires it.
+ */
+
+ int valid_toc;
+ enum toc_size_qualifier toc_kind;
+ bfd_reloc_code_real_type toc_reloc;
+
+ /* go parse off the [tocXX] part */
+ valid_toc = parse_toc_entry(&toc_kind);
+
+ if (!valid_toc)
+ {
+ /* Note: message has already been issued. */
+ /* FIXME: what sort of recovery should we do? */
+ /* demand_rest_of_line(); return; ? */
+ }
+
+ /* Now get the symbol following the ']' */
+ expression(&ex);
+
+ switch (toc_kind)
+ {
+ case default_toc:
+ /* In this case, we may not have seen the symbol yet, since */
+ /* it is allowed to appear on a .extern or .globl or just be */
+ /* a label in the .data section. */
+ toc_reloc = BFD_RELOC_PPC_TOC16;
+ break;
+ case data_in_toc:
+ /* 1. The symbol must be defined and either in the toc */
+ /* section, or a global. */
+ /* 2. The reloc generated must have the TOCDEFN flag set in */
+ /* upper bit mess of the reloc type. */
+ /* FIXME: It's a little confusing what the tocv qualifier can */
+ /* be used for. At the very least, I've seen three */
+ /* uses, only one of which I'm sure I can explain. */
+ if (ex.X_op == O_symbol)
+ {
+ assert (ex.X_add_symbol != NULL);
+ if (ex.X_add_symbol->bsym->section != tocdata_section)
+ {
+ as_bad(_("[tocv] symbol is not a toc symbol"));
+ }
+ }
+
+ toc_reloc = BFD_RELOC_PPC_TOC16;
+ break;
+ case must_be_32:
+ /* FIXME: these next two specifically specify 32/64 bit toc */
+ /* entries. We don't support them today. Is this the */
+ /* right way to say that? */
+ toc_reloc = BFD_RELOC_UNUSED;
+ as_bad (_("Unimplemented toc32 expression modifier"));
+ break;
+ case must_be_64:
+ /* FIXME: see above */
+ toc_reloc = BFD_RELOC_UNUSED;
+ as_bad (_("Unimplemented toc64 expression modifier"));
+ break;
+ default:
+ fprintf(stderr,
+ _("Unexpected return value [%d] from parse_toc_entry!\n"),
+ toc_kind);
+ abort();
+ break;
+ }
+
+ /* We need to generate a fixup for this expression. */
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ fixups[fc].reloc = toc_reloc;
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = *opindex_ptr;
+ ++fc;
+
+ /* Ok. We've set up the fixup for the instruction. Now make it
+ look like the constant 0 was found here */
+ ex.X_unsigned = 1;
+ ex.X_op = O_constant;
+ ex.X_add_number = 0;
+ ex.X_add_symbol = NULL;
+ ex.X_op_symbol = NULL;
+ }
+
+ else
+#endif /* TE_PE */
+ {
+ if (! register_name (&ex))
+ {
+ if ((operand->flags & PPC_OPERAND_CR) != 0)
+ cr_operand = true;
+ expression (&ex);
+ cr_operand = false;
+ }
+ }
+
+ str = input_line_pointer;
+ input_line_pointer = hold;
+
+ if (ex.X_op == O_illegal)
+ as_bad (_("illegal operand"));
+ else if (ex.X_op == O_absent)
+ as_bad (_("missing operand"));
+ else if (ex.X_op == O_register)
+ {
+ insn = ppc_insert_operand (insn, operand, ex.X_add_number,
+ (char *) NULL, 0);
+ }
+ else if (ex.X_op == O_constant)
+ {
+#ifdef OBJ_ELF
+ /* Allow @HA, @L, @H on constants. */
+ char *orig_str = str;
+
+ if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_UNUSED)
+ switch (reloc)
+ {
+ default:
+ str = orig_str;
+ break;
+
+ case BFD_RELOC_LO16:
+ /* X_unsigned is the default, so if the user has done
+ something which cleared it, we always produce a
+ signed value. */
+ if (ex.X_unsigned
+ && (operand->flags & PPC_OPERAND_SIGNED) == 0)
+ ex.X_add_number &= 0xffff;
+ else
+ ex.X_add_number = (((ex.X_add_number & 0xffff)
+ ^ 0x8000)
+ - 0x8000);
+ break;
+
+ case BFD_RELOC_HI16:
+ ex.X_add_number = (ex.X_add_number >> 16) & 0xffff;
+ break;
+
+ case BFD_RELOC_HI16_S:
+ ex.X_add_number = (((ex.X_add_number >> 16) & 0xffff)
+ + ((ex.X_add_number >> 15) & 1));
+ break;
+ }
+#endif
+ insn = ppc_insert_operand (insn, operand, ex.X_add_number,
+ (char *) NULL, 0);
+ }
+#ifdef OBJ_ELF
+ else if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_UNUSED)
+ {
+ /* For the absoulte forms of branchs, convert the PC relative form back into
+ the absolute. */
+ if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0)
+ {
+ switch (reloc)
+ {
+ case BFD_RELOC_PPC_B26:
+ reloc = BFD_RELOC_PPC_BA26;
+ break;
+ case BFD_RELOC_PPC_B16:
+ reloc = BFD_RELOC_PPC_BA16;
+ break;
+ case BFD_RELOC_PPC_B16_BRTAKEN:
+ reloc = BFD_RELOC_PPC_BA16_BRTAKEN;
+ break;
+ case BFD_RELOC_PPC_B16_BRNTAKEN:
+ reloc = BFD_RELOC_PPC_BA16_BRNTAKEN;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* We need to generate a fixup for this expression. */
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = 0;
+ fixups[fc].reloc = reloc;
+ ++fc;
+ }
+#endif /* OBJ_ELF */
+
+ else
+ {
+ /* We need to generate a fixup for this expression. */
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = *opindex_ptr;
+ fixups[fc].reloc = BFD_RELOC_UNUSED;
+ ++fc;
+ }
+
+ if (need_paren)
+ {
+ endc = ')';
+ need_paren = 0;
+ }
+ else if ((operand->flags & PPC_OPERAND_PARENS) != 0)
+ {
+ endc = '(';
+ need_paren = 1;
+ }
+ else
+ endc = ',';
+
+ /* The call to expression should have advanced str past any
+ whitespace. */
+ if (*str != endc
+ && (endc != ',' || *str != '\0'))
+ {
+ as_bad (_("syntax error; found `%c' but expected `%c'"), *str, endc);
+ break;
+ }
+
+ if (*str != '\0')
+ ++str;
+ }
+
+ while (isspace (*str))
+ ++str;
+
+ if (*str != '\0')
+ as_bad (_("junk at end of line: `%s'"), str);
+
+ /* Write out the instruction. */
+ f = frag_more (4);
+ md_number_to_chars (f, insn, 4);
+
+ /* Create any fixups. At this point we do not use a
+ bfd_reloc_code_real_type, but instead just use the
+ BFD_RELOC_UNUSED plus the operand index. This lets us easily
+ handle fixups for any operand type, although that is admittedly
+ not a very exciting feature. We pick a BFD reloc type in
+ md_apply_fix. */
+ for (i = 0; i < fc; i++)
+ {
+ const struct powerpc_operand *operand;
+
+ operand = &powerpc_operands[fixups[i].opindex];
+ if (fixups[i].reloc != BFD_RELOC_UNUSED)
+ {
+ reloc_howto_type *reloc_howto = bfd_reloc_type_lookup (stdoutput, fixups[i].reloc);
+ int size;
+ int offset;
+ fixS *fixP;
+
+ if (!reloc_howto)
+ abort ();
+
+ size = bfd_get_reloc_size (reloc_howto);
+ offset = target_big_endian ? (4 - size) : 0;
+
+ if (size < 1 || size > 4)
+ abort();
+
+ fixP = fix_new_exp (frag_now, f - frag_now->fr_literal + offset, size,
+ &fixups[i].exp, reloc_howto->pc_relative,
+ fixups[i].reloc);
+
+ /* Turn off complaints that the addend is too large for things like
+ foo+100000@ha. */
+ switch (fixups[i].reloc)
+ {
+ case BFD_RELOC_16_GOTOFF:
+ case BFD_RELOC_PPC_TOC16:
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_HI16:
+ case BFD_RELOC_HI16_S:
+ fixP->fx_no_overflow = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ fix_new_exp (frag_now, f - frag_now->fr_literal, 4,
+ &fixups[i].exp,
+ (operand->flags & PPC_OPERAND_RELATIVE) != 0,
+ ((bfd_reloc_code_real_type)
+ (fixups[i].opindex + (int) BFD_RELOC_UNUSED)));
+ }
+}
+
+/* Handle a macro. Gather all the operands, transform them as
+ described by the macro, and call md_assemble recursively. All the
+ operands are separated by commas; we don't accept parentheses
+ around operands here. */
+
+static void
+ppc_macro (str, macro)
+ char *str;
+ const struct powerpc_macro *macro;
+{
+ char *operands[10];
+ unsigned int count;
+ char *s;
+ unsigned int len;
+ const char *format;
+ int arg;
+ char *send;
+ char *complete;
+
+ /* Gather the users operands into the operands array. */
+ count = 0;
+ s = str;
+ while (1)
+ {
+ if (count >= sizeof operands / sizeof operands[0])
+ break;
+ operands[count++] = s;
+ s = strchr (s, ',');
+ if (s == (char *) NULL)
+ break;
+ *s++ = '\0';
+ }
+
+ if (count != macro->operands)
+ {
+ as_bad (_("wrong number of operands"));
+ return;
+ }
+
+ /* Work out how large the string must be (the size is unbounded
+ because it includes user input). */
+ len = 0;
+ format = macro->format;
+ while (*format != '\0')
+ {
+ if (*format != '%')
+ {
+ ++len;
+ ++format;
+ }
+ else
+ {
+ arg = strtol (format + 1, &send, 10);
+ know (send != format && arg >= 0 && arg < count);
+ len += strlen (operands[arg]);
+ format = send;
+ }
+ }
+
+ /* Put the string together. */
+ complete = s = (char *) alloca (len + 1);
+ format = macro->format;
+ while (*format != '\0')
+ {
+ if (*format != '%')
+ *s++ = *format++;
+ else
+ {
+ arg = strtol (format + 1, &send, 10);
+ strcpy (s, operands[arg]);
+ s += strlen (s);
+ format = send;
+ }
+ }
+ *s = '\0';
+
+ /* Assemble the constructed instruction. */
+ md_assemble (complete);
+}
+
+#ifdef OBJ_ELF
+/* For ELF, add support for SHF_EXCLUDE and SHT_ORDERED */
+
+int
+ppc_section_letter (letter, ptr_msg)
+ int letter;
+ char **ptr_msg;
+{
+ if (letter == 'e')
+ return SHF_EXCLUDE;
+
+ *ptr_msg = _("Bad .section directive: want a,w,x,e in string");
+ return 0;
+}
+
+int
+ppc_section_word (ptr_str)
+ char **ptr_str;
+{
+ if (strncmp (*ptr_str, "exclude", sizeof ("exclude")-1) == 0)
+ {
+ *ptr_str += sizeof ("exclude")-1;
+ return SHF_EXCLUDE;
+ }
+
+ return 0;
+}
+
+int
+ppc_section_type (ptr_str)
+ char **ptr_str;
+{
+ if (strncmp (*ptr_str, "ordered", sizeof ("ordered")-1) == 0)
+ {
+ *ptr_str += sizeof ("ordered")-1;
+ return SHT_ORDERED;
+ }
+
+ return 0;
+}
+
+int
+ppc_section_flags (flags, attr, type)
+ int flags;
+ int attr;
+ int type;
+{
+ if (type == SHT_ORDERED)
+ flags |= SEC_ALLOC | SEC_LOAD | SEC_SORT_ENTRIES;
+
+ if (attr & SHF_EXCLUDE)
+ flags |= SEC_EXCLUDE;
+
+ return flags;
+}
+#endif /* OBJ_ELF */
+
+
+/* Pseudo-op handling. */
+
+/* The .byte pseudo-op. This is similar to the normal .byte
+ pseudo-op, but it can also take a single ASCII string. */
+
+static void
+ppc_byte (ignore)
+ int ignore;
+{
+ if (*input_line_pointer != '\"')
+ {
+ cons (1);
+ return;
+ }
+
+ /* Gather characters. A real double quote is doubled. Unusual
+ characters are not permitted. */
+ ++input_line_pointer;
+ while (1)
+ {
+ char c;
+
+ c = *input_line_pointer++;
+
+ if (c == '\"')
+ {
+ if (*input_line_pointer != '\"')
+ break;
+ ++input_line_pointer;
+ }
+
+ FRAG_APPEND_1_CHAR (c);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+#ifdef OBJ_XCOFF
+
+/* XCOFF specific pseudo-op handling. */
+
+/* This is set if we are creating a .stabx symbol, since we don't want
+ to handle symbol suffixes for such symbols. */
+static boolean ppc_stab_symbol;
+
+/* The .comm and .lcomm pseudo-ops for XCOFF. XCOFF puts common
+ symbols in the .bss segment as though they were local common
+ symbols, and uses a different smclas. */
+
+static void
+ppc_comm (lcomm)
+ int lcomm;
+{
+ asection *current_seg = now_seg;
+ subsegT current_subseg = now_subseg;
+ char *name;
+ char endc;
+ char *end_name;
+ offsetT size;
+ offsetT align;
+ symbolS *lcomm_sym = NULL;
+ symbolS *sym;
+ char *pfrag;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+ end_name = input_line_pointer;
+ *end_name = endc;
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing size"));
+ ignore_rest_of_line ();
+ return;
+ }
+ ++input_line_pointer;
+
+ size = get_absolute_expression ();
+ if (size < 0)
+ {
+ as_bad (_("negative size"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (! lcomm)
+ {
+ /* The third argument to .comm is the alignment. */
+ if (*input_line_pointer != ',')
+ align = 3;
+ else
+ {
+ ++input_line_pointer;
+ align = get_absolute_expression ();
+ if (align <= 0)
+ {
+ as_warn (_("ignoring bad alignment"));
+ align = 3;
+ }
+ }
+ }
+ else
+ {
+ char *lcomm_name;
+ char lcomm_endc;
+
+ if (size <= 1)
+ align = 0;
+ else if (size <= 2)
+ align = 1;
+ else if (size <= 4)
+ align = 2;
+ else
+ align = 3;
+
+ /* The third argument to .lcomm appears to be the real local
+ common symbol to create. References to the symbol named in
+ the first argument are turned into references to the third
+ argument. */
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing real symbol name"));
+ ignore_rest_of_line ();
+ return;
+ }
+ ++input_line_pointer;
+
+ lcomm_name = input_line_pointer;
+ lcomm_endc = get_symbol_end ();
+
+ lcomm_sym = symbol_find_or_make (lcomm_name);
+
+ *input_line_pointer = lcomm_endc;
+ }
+
+ *end_name = '\0';
+ sym = symbol_find_or_make (name);
+ *end_name = endc;
+
+ if (S_IS_DEFINED (sym)
+ || S_GET_VALUE (sym) != 0)
+ {
+ as_bad (_("attempt to redefine symbol"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ record_alignment (bss_section, align);
+
+ if (! lcomm
+ || ! S_IS_DEFINED (lcomm_sym))
+ {
+ symbolS *def_sym;
+ offsetT def_size;
+
+ if (! lcomm)
+ {
+ def_sym = sym;
+ def_size = size;
+ S_SET_EXTERNAL (sym);
+ }
+ else
+ {
+ lcomm_sym->sy_tc.output = 1;
+ def_sym = lcomm_sym;
+ def_size = 0;
+ }
+
+ subseg_set (bss_section, 1);
+ frag_align (align, 0, 0);
+
+ def_sym->sy_frag = frag_now;
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, def_sym,
+ def_size, (char *) NULL);
+ *pfrag = 0;
+ S_SET_SEGMENT (def_sym, bss_section);
+ def_sym->sy_tc.align = align;
+ }
+ else if (lcomm)
+ {
+ /* Align the size of lcomm_sym. */
+ lcomm_sym->sy_frag->fr_offset =
+ ((lcomm_sym->sy_frag->fr_offset + (1 << align) - 1)
+ &~ ((1 << align) - 1));
+ if (align > lcomm_sym->sy_tc.align)
+ lcomm_sym->sy_tc.align = align;
+ }
+
+ if (lcomm)
+ {
+ /* Make sym an offset from lcomm_sym. */
+ S_SET_SEGMENT (sym, bss_section);
+ sym->sy_frag = lcomm_sym->sy_frag;
+ S_SET_VALUE (sym, lcomm_sym->sy_frag->fr_offset);
+ lcomm_sym->sy_frag->fr_offset += size;
+ }
+
+ subseg_set (current_seg, current_subseg);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .csect pseudo-op. This switches us into a different
+ subsegment. The first argument is a symbol whose value is the
+ start of the .csect. In COFF, csect symbols get special aux
+ entries defined by the x_csect field of union internal_auxent. The
+ optional second argument is the alignment (the default is 2). */
+
+static void
+ppc_csect (ignore)
+ int ignore;
+{
+ char *name;
+ char endc;
+ symbolS *sym;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ if (S_GET_NAME (sym)[0] == '\0')
+ {
+ /* An unnamed csect is assumed to be [PR]. */
+ sym->sy_tc.class = XMC_PR;
+ }
+
+ ppc_change_csect (sym);
+
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ sym->sy_tc.align = get_absolute_expression ();
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Change to a different csect. */
+
+static void
+ppc_change_csect (sym)
+ symbolS *sym;
+{
+ if (S_IS_DEFINED (sym))
+ subseg_set (S_GET_SEGMENT (sym), sym->sy_tc.subseg);
+ else
+ {
+ symbolS **list_ptr;
+ int after_toc;
+ int hold_chunksize;
+ symbolS *list;
+
+ /* This is a new csect. We need to look at the symbol class to
+ figure out whether it should go in the text section or the
+ data section. */
+ after_toc = 0;
+ switch (sym->sy_tc.class)
+ {
+ case XMC_PR:
+ case XMC_RO:
+ case XMC_DB:
+ case XMC_GL:
+ case XMC_XO:
+ case XMC_SV:
+ case XMC_TI:
+ case XMC_TB:
+ S_SET_SEGMENT (sym, text_section);
+ sym->sy_tc.subseg = ppc_text_subsegment;
+ ++ppc_text_subsegment;
+ list_ptr = &ppc_text_csects;
+ break;
+ case XMC_RW:
+ case XMC_TC0:
+ case XMC_TC:
+ case XMC_DS:
+ case XMC_UA:
+ case XMC_BS:
+ case XMC_UC:
+ if (ppc_toc_csect != NULL
+ && ppc_toc_csect->sy_tc.subseg + 1 == ppc_data_subsegment)
+ after_toc = 1;
+ S_SET_SEGMENT (sym, data_section);
+ sym->sy_tc.subseg = ppc_data_subsegment;
+ ++ppc_data_subsegment;
+ list_ptr = &ppc_data_csects;
+ break;
+ default:
+ abort ();
+ }
+
+ /* We set the obstack chunk size to a small value before
+ changing subsegments, so that we don't use a lot of memory
+ space for what may be a small section. */
+ hold_chunksize = chunksize;
+ chunksize = 64;
+
+ subseg_new (segment_name (S_GET_SEGMENT (sym)), sym->sy_tc.subseg);
+
+ chunksize = hold_chunksize;
+
+ if (after_toc)
+ ppc_after_toc_frag = frag_now;
+
+ sym->sy_frag = frag_now;
+ S_SET_VALUE (sym, (valueT) frag_now_fix ());
+
+ sym->sy_tc.align = 2;
+ sym->sy_tc.output = 1;
+ sym->sy_tc.within = sym;
+
+ for (list = *list_ptr;
+ list->sy_tc.next != (symbolS *) NULL;
+ list = list->sy_tc.next)
+ ;
+ list->sy_tc.next = sym;
+
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ symbol_append (sym, list->sy_tc.within, &symbol_rootP, &symbol_lastP);
+ }
+
+ ppc_current_csect = sym;
+}
+
+/* This function handles the .text and .data pseudo-ops. These
+ pseudo-ops aren't really used by XCOFF; we implement them for the
+ convenience of people who aren't used to XCOFF. */
+
+static void
+ppc_section (type)
+ int type;
+{
+ const char *name;
+ symbolS *sym;
+
+ if (type == 't')
+ name = ".text[PR]";
+ else if (type == 'd')
+ name = ".data[RW]";
+ else
+ abort ();
+
+ sym = symbol_find_or_make (name);
+
+ ppc_change_csect (sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* This function handles the .section pseudo-op. This is mostly to
+ give an error, since XCOFF only supports .text, .data and .bss, but
+ we do permit the user to name the text or data section. */
+
+static void
+ppc_named_section (ignore)
+ int ignore;
+{
+ char *user_name;
+ const char *real_name;
+ char c;
+ symbolS *sym;
+
+ user_name = input_line_pointer;
+ c = get_symbol_end ();
+
+ if (strcmp (user_name, ".text") == 0)
+ real_name = ".text[PR]";
+ else if (strcmp (user_name, ".data") == 0)
+ real_name = ".data[RW]";
+ else
+ {
+ as_bad (_("The XCOFF file format does not support arbitrary sections"));
+ *input_line_pointer = c;
+ ignore_rest_of_line ();
+ return;
+ }
+
+ *input_line_pointer = c;
+
+ sym = symbol_find_or_make (real_name);
+
+ ppc_change_csect (sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .extern pseudo-op. We create an undefined symbol. */
+
+static void
+ppc_extern (ignore)
+ int ignore;
+{
+ char *name;
+ char endc;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ (void) symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .lglobl pseudo-op. Keep the symbol in the symbol table. */
+
+static void
+ppc_lglobl (ignore)
+ int ignore;
+{
+ char *name;
+ char endc;
+ symbolS *sym;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ sym->sy_tc.output = 1;
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .rename pseudo-op. The RS/6000 assembler can rename symbols,
+ although I don't know why it bothers. */
+
+static void
+ppc_rename (ignore)
+ int ignore;
+{
+ char *name;
+ char endc;
+ symbolS *sym;
+ int len;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing rename string"));
+ ignore_rest_of_line ();
+ return;
+ }
+ ++input_line_pointer;
+
+ sym->sy_tc.real_name = demand_copy_C_string (&len);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .stabx pseudo-op. This is similar to a normal .stabs
+ pseudo-op, but slightly different. A sample is
+ .stabx "main:F-1",.main,142,0
+ The first argument is the symbol name to create. The second is the
+ value, and the third is the storage class. The fourth seems to be
+ always zero, and I am assuming it is the type. */
+
+static void
+ppc_stabx (ignore)
+ int ignore;
+{
+ char *name;
+ int len;
+ symbolS *sym;
+ expressionS exp;
+
+ name = demand_copy_C_string (&len);
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing value"));
+ return;
+ }
+ ++input_line_pointer;
+
+ ppc_stab_symbol = true;
+ sym = symbol_make (name);
+ ppc_stab_symbol = false;
+
+ sym->sy_tc.real_name = name;
+
+ (void) expression (&exp);
+
+ switch (exp.X_op)
+ {
+ case O_illegal:
+ case O_absent:
+ case O_big:
+ as_bad (_("illegal .stabx expression; zero assumed"));
+ exp.X_add_number = 0;
+ /* Fall through. */
+ case O_constant:
+ S_SET_VALUE (sym, (valueT) exp.X_add_number);
+ sym->sy_frag = &zero_address_frag;
+ break;
+
+ case O_symbol:
+ if (S_GET_SEGMENT (exp.X_add_symbol) == undefined_section)
+ sym->sy_value = exp;
+ else
+ {
+ S_SET_VALUE (sym,
+ exp.X_add_number + S_GET_VALUE (exp.X_add_symbol));
+ sym->sy_frag = exp.X_add_symbol->sy_frag;
+ }
+ break;
+
+ default:
+ /* The value is some complex expression. This will probably
+ fail at some later point, but this is probably the right
+ thing to do here. */
+ sym->sy_value = exp;
+ break;
+ }
+
+ S_SET_SEGMENT (sym, ppc_coff_debug_section);
+ sym->bsym->flags |= BSF_DEBUGGING;
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing class"));
+ return;
+ }
+ ++input_line_pointer;
+
+ S_SET_STORAGE_CLASS (sym, get_absolute_expression ());
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing type"));
+ return;
+ }
+ ++input_line_pointer;
+
+ S_SET_DATA_TYPE (sym, get_absolute_expression ());
+
+ sym->sy_tc.output = 1;
+
+ if (S_GET_STORAGE_CLASS (sym) == C_STSYM)
+ sym->sy_tc.within = ppc_current_block;
+
+ if (exp.X_op != O_symbol
+ || ! S_IS_EXTERNAL (exp.X_add_symbol)
+ || S_GET_SEGMENT (exp.X_add_symbol) != bss_section)
+ ppc_frob_label (sym);
+ else
+ {
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ symbol_append (sym, exp.X_add_symbol, &symbol_rootP, &symbol_lastP);
+ if (ppc_current_csect->sy_tc.within == exp.X_add_symbol)
+ ppc_current_csect->sy_tc.within = sym;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .function pseudo-op. This takes several arguments. The first
+ argument seems to be the external name of the symbol. The second
+ argment seems to be the label for the start of the function. gcc
+ uses the same name for both. I have no idea what the third and
+ fourth arguments are meant to be. The optional fifth argument is
+ an expression for the size of the function. In COFF this symbol
+ gets an aux entry like that used for a csect. */
+
+static void
+ppc_function (ignore)
+ int ignore;
+{
+ char *name;
+ char endc;
+ char *s;
+ symbolS *ext_sym;
+ symbolS *lab_sym;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ /* Ignore any [PR] suffix. */
+ name = ppc_canonicalize_symbol_name (name);
+ s = strchr (name, '[');
+ if (s != (char *) NULL
+ && strcmp (s + 1, "PR]") == 0)
+ *s = '\0';
+
+ ext_sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing symbol name"));
+ ignore_rest_of_line ();
+ return;
+ }
+ ++input_line_pointer;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ lab_sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ if (ext_sym != lab_sym)
+ {
+ ext_sym->sy_value.X_op = O_symbol;
+ ext_sym->sy_value.X_add_symbol = lab_sym;
+ ext_sym->sy_value.X_op_symbol = NULL;
+ ext_sym->sy_value.X_add_number = 0;
+ }
+
+ if (ext_sym->sy_tc.class == -1)
+ ext_sym->sy_tc.class = XMC_PR;
+ ext_sym->sy_tc.output = 1;
+
+ if (*input_line_pointer == ',')
+ {
+ expressionS ignore;
+
+ /* Ignore the third argument. */
+ ++input_line_pointer;
+ expression (&ignore);
+ if (*input_line_pointer == ',')
+ {
+ /* Ignore the fourth argument. */
+ ++input_line_pointer;
+ expression (&ignore);
+ if (*input_line_pointer == ',')
+ {
+ /* The fifth argument is the function size. */
+ ++input_line_pointer;
+ ext_sym->sy_tc.size = symbol_new ("L0\001",
+ absolute_section,
+ (valueT) 0,
+ &zero_address_frag);
+ pseudo_set (ext_sym->sy_tc.size);
+ }
+ }
+ }
+
+ S_SET_DATA_TYPE (ext_sym, DT_FCN << N_BTSHFT);
+ SF_SET_FUNCTION (ext_sym);
+ SF_SET_PROCESS (ext_sym);
+ coff_add_linesym (ext_sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .bf pseudo-op. This is just like a COFF C_FCN symbol named
+ ".bf". */
+
+static void
+ppc_bf (ignore)
+ int ignore;
+{
+ symbolS *sym;
+
+ sym = symbol_make (".bf");
+ S_SET_SEGMENT (sym, text_section);
+ sym->sy_frag = frag_now;
+ S_SET_VALUE (sym, frag_now_fix ());
+ S_SET_STORAGE_CLASS (sym, C_FCN);
+
+ coff_line_base = get_absolute_expression ();
+
+ S_SET_NUMBER_AUXILIARY (sym, 1);
+ SA_SET_SYM_LNNO (sym, coff_line_base);
+
+ sym->sy_tc.output = 1;
+
+ ppc_frob_label (sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .ef pseudo-op. This is just like a COFF C_FCN symbol named
+ ".ef", except that the line number is absolute, not relative to the
+ most recent ".bf" symbol. */
+
+static void
+ppc_ef (ignore)
+ int ignore;
+{
+ symbolS *sym;
+
+ sym = symbol_make (".ef");
+ S_SET_SEGMENT (sym, text_section);
+ sym->sy_frag = frag_now;
+ S_SET_VALUE (sym, frag_now_fix ());
+ S_SET_STORAGE_CLASS (sym, C_FCN);
+ S_SET_NUMBER_AUXILIARY (sym, 1);
+ SA_SET_SYM_LNNO (sym, get_absolute_expression ());
+ sym->sy_tc.output = 1;
+
+ ppc_frob_label (sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .bi and .ei pseudo-ops. These take a string argument and
+ generates a C_BINCL or C_EINCL symbol, which goes at the start of
+ the symbol list. */
+
+static void
+ppc_biei (ei)
+ int ei;
+{
+ static symbolS *last_biei;
+
+ char *name;
+ int len;
+ symbolS *sym;
+ symbolS *look;
+
+ name = demand_copy_C_string (&len);
+
+ /* The value of these symbols is actually file offset. Here we set
+ the value to the index into the line number entries. In
+ ppc_frob_symbols we set the fix_line field, which will cause BFD
+ to do the right thing. */
+
+ sym = symbol_make (name);
+ /* obj-coff.c currently only handles line numbers correctly in the
+ .text section. */
+ S_SET_SEGMENT (sym, text_section);
+ S_SET_VALUE (sym, coff_n_line_nos);
+ sym->bsym->flags |= BSF_DEBUGGING;
+
+ S_SET_STORAGE_CLASS (sym, ei ? C_EINCL : C_BINCL);
+ sym->sy_tc.output = 1;
+
+ for (look = last_biei ? last_biei : symbol_rootP;
+ (look != (symbolS *) NULL
+ && (S_GET_STORAGE_CLASS (look) == C_FILE
+ || S_GET_STORAGE_CLASS (look) == C_BINCL
+ || S_GET_STORAGE_CLASS (look) == C_EINCL));
+ look = symbol_next (look))
+ ;
+ if (look != (symbolS *) NULL)
+ {
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ symbol_insert (sym, look, &symbol_rootP, &symbol_lastP);
+ last_biei = sym;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .bs pseudo-op. This generates a C_BSTAT symbol named ".bs".
+ There is one argument, which is a csect symbol. The value of the
+ .bs symbol is the index of this csect symbol. */
+
+static void
+ppc_bs (ignore)
+ int ignore;
+{
+ char *name;
+ char endc;
+ symbolS *csect;
+ symbolS *sym;
+
+ if (ppc_current_block != NULL)
+ as_bad (_("nested .bs blocks"));
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ csect = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ sym = symbol_make (".bs");
+ S_SET_SEGMENT (sym, now_seg);
+ S_SET_STORAGE_CLASS (sym, C_BSTAT);
+ sym->bsym->flags |= BSF_DEBUGGING;
+ sym->sy_tc.output = 1;
+
+ sym->sy_tc.within = csect;
+
+ ppc_frob_label (sym);
+
+ ppc_current_block = sym;
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .es pseudo-op. Generate a C_ESTART symbol named .es. */
+
+static void
+ppc_es (ignore)
+ int ignore;
+{
+ symbolS *sym;
+
+ if (ppc_current_block == NULL)
+ as_bad (_(".es without preceding .bs"));
+
+ sym = symbol_make (".es");
+ S_SET_SEGMENT (sym, now_seg);
+ S_SET_STORAGE_CLASS (sym, C_ESTAT);
+ sym->bsym->flags |= BSF_DEBUGGING;
+ sym->sy_tc.output = 1;
+
+ ppc_frob_label (sym);
+
+ ppc_current_block = NULL;
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .bb pseudo-op. Generate a C_BLOCK symbol named .bb, with a
+ line number. */
+
+static void
+ppc_bb (ignore)
+ int ignore;
+{
+ symbolS *sym;
+
+ sym = symbol_make (".bb");
+ S_SET_SEGMENT (sym, text_section);
+ sym->sy_frag = frag_now;
+ S_SET_VALUE (sym, frag_now_fix ());
+ S_SET_STORAGE_CLASS (sym, C_BLOCK);
+
+ S_SET_NUMBER_AUXILIARY (sym, 1);
+ SA_SET_SYM_LNNO (sym, get_absolute_expression ());
+
+ sym->sy_tc.output = 1;
+
+ SF_SET_PROCESS (sym);
+
+ ppc_frob_label (sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .eb pseudo-op. Generate a C_BLOCK symbol named .eb, with a
+ line number. */
+
+static void
+ppc_eb (ignore)
+ int ignore;
+{
+ symbolS *sym;
+
+ sym = symbol_make (".eb");
+ S_SET_SEGMENT (sym, text_section);
+ sym->sy_frag = frag_now;
+ S_SET_VALUE (sym, frag_now_fix ());
+ S_SET_STORAGE_CLASS (sym, C_BLOCK);
+ S_SET_NUMBER_AUXILIARY (sym, 1);
+ SA_SET_SYM_LNNO (sym, get_absolute_expression ());
+ sym->sy_tc.output = 1;
+
+ SF_SET_PROCESS (sym);
+
+ ppc_frob_label (sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .bc pseudo-op. This just creates a C_BCOMM symbol with a
+ specified name. */
+
+static void
+ppc_bc (ignore)
+ int ignore;
+{
+ char *name;
+ int len;
+ symbolS *sym;
+
+ name = demand_copy_C_string (&len);
+ sym = symbol_make (name);
+ S_SET_SEGMENT (sym, ppc_coff_debug_section);
+ sym->bsym->flags |= BSF_DEBUGGING;
+ S_SET_STORAGE_CLASS (sym, C_BCOMM);
+ S_SET_VALUE (sym, 0);
+ sym->sy_tc.output = 1;
+
+ ppc_frob_label (sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .ec pseudo-op. This just creates a C_ECOMM symbol. */
+
+static void
+ppc_ec (ignore)
+ int ignore;
+{
+ symbolS *sym;
+
+ sym = symbol_make (".ec");
+ S_SET_SEGMENT (sym, ppc_coff_debug_section);
+ sym->bsym->flags |= BSF_DEBUGGING;
+ S_SET_STORAGE_CLASS (sym, C_ECOMM);
+ S_SET_VALUE (sym, 0);
+ sym->sy_tc.output = 1;
+
+ ppc_frob_label (sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .toc pseudo-op. Switch to the .toc subsegment. */
+
+static void
+ppc_toc (ignore)
+ int ignore;
+{
+ if (ppc_toc_csect != (symbolS *) NULL)
+ subseg_set (data_section, ppc_toc_csect->sy_tc.subseg);
+ else
+ {
+ subsegT subseg;
+ symbolS *sym;
+ symbolS *list;
+
+ subseg = ppc_data_subsegment;
+ ++ppc_data_subsegment;
+
+ subseg_new (segment_name (data_section), subseg);
+ ppc_toc_frag = frag_now;
+
+ sym = symbol_find_or_make ("TOC[TC0]");
+ sym->sy_frag = frag_now;
+ S_SET_SEGMENT (sym, data_section);
+ S_SET_VALUE (sym, (valueT) frag_now_fix ());
+ sym->sy_tc.subseg = subseg;
+ sym->sy_tc.output = 1;
+ sym->sy_tc.within = sym;
+
+ ppc_toc_csect = sym;
+
+ for (list = ppc_data_csects;
+ list->sy_tc.next != (symbolS *) NULL;
+ list = list->sy_tc.next)
+ ;
+ list->sy_tc.next = sym;
+
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ symbol_append (sym, list->sy_tc.within, &symbol_rootP, &symbol_lastP);
+ }
+
+ ppc_current_csect = ppc_toc_csect;
+
+ demand_empty_rest_of_line ();
+}
+
+/* The AIX assembler automatically aligns the operands of a .long or
+ .short pseudo-op, and we want to be compatible. */
+
+static void
+ppc_xcoff_cons (log_size)
+ int log_size;
+{
+ frag_align (log_size, 0, 0);
+ record_alignment (now_seg, log_size);
+ cons (1 << log_size);
+}
+
+static void
+ppc_vbyte (dummy)
+ int dummy;
+{
+ expressionS exp;
+ int byte_count;
+
+ (void) expression (&exp);
+
+ if (exp.X_op != O_constant)
+ {
+ as_bad (_("non-constant byte count"));
+ return;
+ }
+
+ byte_count = exp.X_add_number;
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing value"));
+ return;
+ }
+
+ ++input_line_pointer;
+ cons (byte_count);
+}
+
+#endif /* OBJ_XCOFF */
+
+/* The .tc pseudo-op. This is used when generating either XCOFF or
+ ELF. This takes two or more arguments.
+
+ When generating XCOFF output, the first argument is the name to
+ give to this location in the toc; this will be a symbol with class
+ TC. The rest of the arguments are 4 byte values to actually put at
+ this location in the TOC; often there is just one more argument, a
+ relocateable symbol reference.
+
+ When not generating XCOFF output, the arguments are the same, but
+ the first argument is simply ignored. */
+
+static void
+ppc_tc (ignore)
+ int ignore;
+{
+#ifdef OBJ_XCOFF
+
+ /* Define the TOC symbol name. */
+ {
+ char *name;
+ char endc;
+ symbolS *sym;
+
+ if (ppc_toc_csect == (symbolS *) NULL
+ || ppc_toc_csect != ppc_current_csect)
+ {
+ as_bad (_(".tc not in .toc section"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ if (S_IS_DEFINED (sym))
+ {
+ symbolS *label;
+
+ label = ppc_current_csect->sy_tc.within;
+ if (label->sy_tc.class != XMC_TC0)
+ {
+ as_bad (_(".tc with no label"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ S_SET_SEGMENT (label, S_GET_SEGMENT (sym));
+ label->sy_frag = sym->sy_frag;
+ S_SET_VALUE (label, S_GET_VALUE (sym));
+
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+
+ return;
+ }
+
+ S_SET_SEGMENT (sym, now_seg);
+ sym->sy_frag = frag_now;
+ S_SET_VALUE (sym, (valueT) frag_now_fix ());
+ sym->sy_tc.class = XMC_TC;
+ sym->sy_tc.output = 1;
+
+ ppc_frob_label (sym);
+ }
+
+#else /* ! defined (OBJ_XCOFF) */
+
+ /* Skip the TOC symbol name. */
+ while (is_part_of_name (*input_line_pointer)
+ || *input_line_pointer == '['
+ || *input_line_pointer == ']'
+ || *input_line_pointer == '{'
+ || *input_line_pointer == '}')
+ ++input_line_pointer;
+
+ /* Align to a four byte boundary. */
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
+
+#endif /* ! defined (OBJ_XCOFF) */
+
+ if (*input_line_pointer != ',')
+ demand_empty_rest_of_line ();
+ else
+ {
+ ++input_line_pointer;
+ cons (4);
+ }
+}
+
+#ifdef TE_PE
+
+/* Pseudo-ops specific to the Windows NT PowerPC PE (coff) format */
+
+/* Set the current section. */
+static void
+ppc_set_current_section (new)
+ segT new;
+{
+ ppc_previous_section = ppc_current_section;
+ ppc_current_section = new;
+}
+
+/* pseudo-op: .previous
+ behaviour: toggles the current section with the previous section.
+ errors: None
+ warnings: "No previous section"
+*/
+static void
+ppc_previous(ignore)
+ int ignore;
+{
+ symbolS *tmp;
+
+ if (ppc_previous_section == NULL)
+ {
+ as_warn(_("No previous section to return to. Directive ignored."));
+ return;
+ }
+
+ subseg_set(ppc_previous_section, 0);
+
+ ppc_set_current_section(ppc_previous_section);
+}
+
+/* pseudo-op: .pdata
+ behaviour: predefined read only data section
+ double word aligned
+ errors: None
+ warnings: None
+ initial: .section .pdata "adr3"
+ a - don't know -- maybe a misprint
+ d - initialized data
+ r - readable
+ 3 - double word aligned (that would be 4 byte boundary)
+
+ commentary:
+ Tag index tables (also known as the function table) for exception
+ handling, debugging, etc.
+
+*/
+static void
+ppc_pdata(ignore)
+ int ignore;
+{
+ if (pdata_section == 0)
+ {
+ pdata_section = subseg_new (".pdata", 0);
+
+ bfd_set_section_flags (stdoutput, pdata_section,
+ (SEC_ALLOC | SEC_LOAD | SEC_RELOC
+ | SEC_READONLY | SEC_DATA ));
+
+ bfd_set_section_alignment (stdoutput, pdata_section, 2);
+ }
+ else
+ {
+ pdata_section = subseg_new(".pdata", 0);
+ }
+ ppc_set_current_section(pdata_section);
+}
+
+/* pseudo-op: .ydata
+ behaviour: predefined read only data section
+ double word aligned
+ errors: None
+ warnings: None
+ initial: .section .ydata "drw3"
+ a - don't know -- maybe a misprint
+ d - initialized data
+ r - readable
+ 3 - double word aligned (that would be 4 byte boundary)
+ commentary:
+ Tag tables (also known as the scope table) for exception handling,
+ debugging, etc.
+*/
+static void
+ppc_ydata(ignore)
+ int ignore;
+{
+ if (ydata_section == 0)
+ {
+ ydata_section = subseg_new (".ydata", 0);
+ bfd_set_section_flags (stdoutput, ydata_section,
+ (SEC_ALLOC | SEC_LOAD | SEC_RELOC
+ | SEC_READONLY | SEC_DATA ));
+
+ bfd_set_section_alignment (stdoutput, ydata_section, 3);
+ }
+ else
+ {
+ ydata_section = subseg_new (".ydata", 0);
+ }
+ ppc_set_current_section(ydata_section);
+}
+
+/* pseudo-op: .reldata
+ behaviour: predefined read write data section
+ double word aligned (4-byte)
+ FIXME: relocation is applied to it
+ FIXME: what's the difference between this and .data?
+ errors: None
+ warnings: None
+ initial: .section .reldata "drw3"
+ d - initialized data
+ r - readable
+ w - writeable
+ 3 - double word aligned (that would be 8 byte boundary)
+
+ commentary:
+ Like .data, but intended to hold data subject to relocation, such as
+ function descriptors, etc.
+*/
+static void
+ppc_reldata(ignore)
+ int ignore;
+{
+ if (reldata_section == 0)
+ {
+ reldata_section = subseg_new (".reldata", 0);
+
+ bfd_set_section_flags (stdoutput, reldata_section,
+ ( SEC_ALLOC | SEC_LOAD | SEC_RELOC
+ | SEC_DATA ));
+
+ bfd_set_section_alignment (stdoutput, reldata_section, 2);
+ }
+ else
+ {
+ reldata_section = subseg_new (".reldata", 0);
+ }
+ ppc_set_current_section(reldata_section);
+}
+
+/* pseudo-op: .rdata
+ behaviour: predefined read only data section
+ double word aligned
+ errors: None
+ warnings: None
+ initial: .section .rdata "dr3"
+ d - initialized data
+ r - readable
+ 3 - double word aligned (that would be 4 byte boundary)
+*/
+static void
+ppc_rdata(ignore)
+ int ignore;
+{
+ if (rdata_section == 0)
+ {
+ rdata_section = subseg_new (".rdata", 0);
+ bfd_set_section_flags (stdoutput, rdata_section,
+ (SEC_ALLOC | SEC_LOAD | SEC_RELOC
+ | SEC_READONLY | SEC_DATA ));
+
+ bfd_set_section_alignment (stdoutput, rdata_section, 2);
+ }
+ else
+ {
+ rdata_section = subseg_new (".rdata", 0);
+ }
+ ppc_set_current_section(rdata_section);
+}
+
+/* pseudo-op: .ualong
+ behaviour: much like .int, with the exception that no alignment is
+ performed.
+ FIXME: test the alignment statement
+ errors: None
+ warnings: None
+*/
+static void
+ppc_ualong(ignore)
+ int ignore;
+{
+ /* try for long */
+ cons ( 4 );
+}
+
+/* pseudo-op: .znop <symbol name>
+ behaviour: Issue a nop instruction
+ Issue a IMAGE_REL_PPC_IFGLUE relocation against it, using
+ the supplied symbol name.
+ errors: None
+ warnings: Missing symbol name
+*/
+static void
+ppc_znop(ignore)
+ int ignore;
+{
+ unsigned long insn;
+ const struct powerpc_opcode *opcode;
+ expressionS ex;
+ char *f;
+
+ symbolS *sym;
+
+ /* Strip out the symbol name */
+ char *symbol_name;
+ char c;
+ char *name;
+ unsigned int exp;
+ flagword flags;
+ asection *sec;
+
+ symbol_name = input_line_pointer;
+ c = get_symbol_end ();
+
+ name = xmalloc (input_line_pointer - symbol_name + 1);
+ strcpy (name, symbol_name);
+
+ sym = symbol_find_or_make (name);
+
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE ();
+
+ /* Look up the opcode in the hash table. */
+ opcode = (const struct powerpc_opcode *) hash_find (ppc_hash, "nop");
+
+ /* stick in the nop */
+ insn = opcode->opcode;
+
+ /* Write out the instruction. */
+ f = frag_more (4);
+ md_number_to_chars (f, insn, 4);
+ fix_new (frag_now,
+ f - frag_now->fr_literal,
+ 4,
+ sym,
+ 0,
+ 0,
+ BFD_RELOC_16_GOT_PCREL);
+
+}
+
+/* pseudo-op:
+ behaviour:
+ errors:
+ warnings:
+*/
+static void
+ppc_pe_comm(lcomm)
+ int lcomm;
+{
+ register char *name;
+ register char c;
+ register char *p;
+ offsetT temp;
+ register symbolS *symbolP;
+ offsetT align;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ /* just after name is now '\0' */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("Expected comma after symbol-name: rest of line ignored."));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ input_line_pointer++; /* skip ',' */
+ if ((temp = get_absolute_expression ()) < 0)
+ {
+ as_warn (_(".COMMon length (%ld.) <0! Ignored."), (long) temp);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (! lcomm)
+ {
+ /* The third argument to .comm is the alignment. */
+ if (*input_line_pointer != ',')
+ align = 3;
+ else
+ {
+ ++input_line_pointer;
+ align = get_absolute_expression ();
+ if (align <= 0)
+ {
+ as_warn (_("ignoring bad alignment"));
+ align = 3;
+ }
+ }
+ }
+
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+
+ *p = c;
+ if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
+ {
+ as_bad (_("Ignoring attempt to re-define symbol `%s'."),
+ S_GET_NAME (symbolP));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (S_GET_VALUE (symbolP))
+ {
+ if (S_GET_VALUE (symbolP) != (valueT) temp)
+ as_bad (_("Length of .comm \"%s\" is already %ld. Not changed to %ld."),
+ S_GET_NAME (symbolP),
+ (long) S_GET_VALUE (symbolP),
+ (long) temp);
+ }
+ else
+ {
+ S_SET_VALUE (symbolP, (valueT) temp);
+ S_SET_EXTERNAL (symbolP);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/*
+ * implement the .section pseudo op:
+ * .section name {, "flags"}
+ * ^ ^
+ * | +--- optional flags: 'b' for bss
+ * | 'i' for info
+ * +-- section name 'l' for lib
+ * 'n' for noload
+ * 'o' for over
+ * 'w' for data
+ * 'd' (apparently m88k for data)
+ * 'x' for text
+ * But if the argument is not a quoted string, treat it as a
+ * subsegment number.
+ *
+ * FIXME: this is a copy of the section processing from obj-coff.c, with
+ * additions/changes for the moto-pas assembler support. There are three
+ * categories:
+ *
+ * FIXME: I just noticed this. This doesn't work at all really. It it
+ * setting bits that bfd probably neither understands or uses. The
+ * correct approach (?) will have to incorporate extra fields attached
+ * to the section to hold the system specific stuff. (krk)
+ *
+ * Section Contents:
+ * 'a' - unknown - referred to in documentation, but no definition supplied
+ * 'c' - section has code
+ * 'd' - section has initialized data
+ * 'u' - section has uninitialized data
+ * 'i' - section contains directives (info)
+ * 'n' - section can be discarded
+ * 'R' - remove section at link time
+ *
+ * Section Protection:
+ * 'r' - section is readable
+ * 'w' - section is writeable
+ * 'x' - section is executable
+ * 's' - section is sharable
+ *
+ * Section Alignment:
+ * '0' - align to byte boundary
+ * '1' - align to halfword undary
+ * '2' - align to word boundary
+ * '3' - align to doubleword boundary
+ * '4' - align to quadword boundary
+ * '5' - align to 32 byte boundary
+ * '6' - align to 64 byte boundary
+ *
+ */
+
+void
+ppc_pe_section (ignore)
+ int ignore;
+{
+ /* Strip out the section name */
+ char *section_name;
+ char c;
+ char *name;
+ unsigned int exp;
+ flagword flags;
+ segT sec;
+ int align;
+
+ section_name = input_line_pointer;
+ c = get_symbol_end ();
+
+ name = xmalloc (input_line_pointer - section_name + 1);
+ strcpy (name, section_name);
+
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE ();
+
+ exp = 0;
+ flags = SEC_NO_FLAGS;
+
+ if (strcmp (name, ".idata$2") == 0)
+ {
+ align = 0;
+ }
+ else if (strcmp (name, ".idata$3") == 0)
+ {
+ align = 0;
+ }
+ else if (strcmp (name, ".idata$4") == 0)
+ {
+ align = 2;
+ }
+ else if (strcmp (name, ".idata$5") == 0)
+ {
+ align = 2;
+ }
+ else if (strcmp (name, ".idata$6") == 0)
+ {
+ align = 1;
+ }
+ else
+ align = 4; /* default alignment to 16 byte boundary */
+
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != '"')
+ exp = get_absolute_expression ();
+ else
+ {
+ ++input_line_pointer;
+ while (*input_line_pointer != '"'
+ && ! is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ switch (*input_line_pointer)
+ {
+ /* Section Contents */
+ case 'a': /* unknown */
+ as_bad (_("Unsupported section attribute -- 'a'"));
+ break;
+ case 'c': /* code section */
+ flags |= SEC_CODE;
+ break;
+ case 'd': /* section has initialized data */
+ flags |= SEC_DATA;
+ break;
+ case 'u': /* section has uninitialized data */
+ /* FIXME: This is IMAGE_SCN_CNT_UNINITIALIZED_DATA
+ in winnt.h */
+ flags |= SEC_ROM;
+ break;
+ case 'i': /* section contains directives (info) */
+ /* FIXME: This is IMAGE_SCN_LNK_INFO
+ in winnt.h */
+ flags |= SEC_HAS_CONTENTS;
+ break;
+ case 'n': /* section can be discarded */
+ flags &=~ SEC_LOAD;
+ break;
+ case 'R': /* Remove section at link time */
+ flags |= SEC_NEVER_LOAD;
+ break;
+
+ /* Section Protection */
+ case 'r': /* section is readable */
+ flags |= IMAGE_SCN_MEM_READ;
+ break;
+ case 'w': /* section is writeable */
+ flags |= IMAGE_SCN_MEM_WRITE;
+ break;
+ case 'x': /* section is executable */
+ flags |= IMAGE_SCN_MEM_EXECUTE;
+ break;
+ case 's': /* section is sharable */
+ flags |= IMAGE_SCN_MEM_SHARED;
+ break;
+
+ /* Section Alignment */
+ case '0': /* align to byte boundary */
+ flags |= IMAGE_SCN_ALIGN_1BYTES;
+ align = 0;
+ break;
+ case '1': /* align to halfword boundary */
+ flags |= IMAGE_SCN_ALIGN_2BYTES;
+ align = 1;
+ break;
+ case '2': /* align to word boundary */
+ flags |= IMAGE_SCN_ALIGN_4BYTES;
+ align = 2;
+ break;
+ case '3': /* align to doubleword boundary */
+ flags |= IMAGE_SCN_ALIGN_8BYTES;
+ align = 3;
+ break;
+ case '4': /* align to quadword boundary */
+ flags |= IMAGE_SCN_ALIGN_16BYTES;
+ align = 4;
+ break;
+ case '5': /* align to 32 byte boundary */
+ flags |= IMAGE_SCN_ALIGN_32BYTES;
+ align = 5;
+ break;
+ case '6': /* align to 64 byte boundary */
+ flags |= IMAGE_SCN_ALIGN_64BYTES;
+ align = 6;
+ break;
+
+ default:
+ as_bad(_("unknown section attribute '%c'"),
+ *input_line_pointer);
+ break;
+ }
+ ++input_line_pointer;
+ }
+ if (*input_line_pointer == '"')
+ ++input_line_pointer;
+ }
+ }
+
+ sec = subseg_new (name, (subsegT) exp);
+
+ ppc_set_current_section(sec);
+
+ if (flags != SEC_NO_FLAGS)
+ {
+ if (! bfd_set_section_flags (stdoutput, sec, flags))
+ as_bad (_("error setting flags for \"%s\": %s"),
+ bfd_section_name (stdoutput, sec),
+ bfd_errmsg (bfd_get_error ()));
+ }
+
+ bfd_set_section_alignment(stdoutput, sec, align);
+
+}
+
+static void
+ppc_pe_function (ignore)
+ int ignore;
+{
+ char *name;
+ char endc;
+ symbolS *ext_sym;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ ext_sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ S_SET_DATA_TYPE (ext_sym, DT_FCN << N_BTSHFT);
+ SF_SET_FUNCTION (ext_sym);
+ SF_SET_PROCESS (ext_sym);
+ coff_add_linesym (ext_sym);
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+ppc_pe_tocd (ignore)
+ int ignore;
+{
+ if (tocdata_section == 0)
+ {
+ tocdata_section = subseg_new (".tocd", 0);
+ /* FIXME: section flags won't work */
+ bfd_set_section_flags (stdoutput, tocdata_section,
+ (SEC_ALLOC | SEC_LOAD | SEC_RELOC
+ | SEC_READONLY | SEC_DATA ));
+
+ bfd_set_section_alignment (stdoutput, tocdata_section, 2);
+ }
+ else
+ {
+ rdata_section = subseg_new (".tocd", 0);
+ }
+
+ ppc_set_current_section(tocdata_section);
+
+ demand_empty_rest_of_line ();
+}
+
+/* Don't adjust TOC relocs to use the section symbol. */
+
+int
+ppc_pe_fix_adjustable (fix)
+ fixS *fix;
+{
+ return fix->fx_r_type != BFD_RELOC_PPC_TOC16;
+}
+
+#endif
+
+#ifdef OBJ_XCOFF
+
+/* XCOFF specific symbol and file handling. */
+
+/* Canonicalize the symbol name. We use the to force the suffix, if
+ any, to use square brackets, and to be in upper case. */
+
+char *
+ppc_canonicalize_symbol_name (name)
+ char *name;
+{
+ char *s;
+
+ if (ppc_stab_symbol)
+ return name;
+
+ for (s = name; *s != '\0' && *s != '{' && *s != '['; s++)
+ ;
+ if (*s != '\0')
+ {
+ char brac;
+
+ if (*s == '[')
+ brac = ']';
+ else
+ {
+ *s = '[';
+ brac = '}';
+ }
+
+ for (s++; *s != '\0' && *s != brac; s++)
+ if (islower (*s))
+ *s = toupper (*s);
+
+ if (*s == '\0' || s[1] != '\0')
+ as_bad (_("bad symbol suffix"));
+
+ *s = ']';
+ }
+
+ return name;
+}
+
+/* Set the class of a symbol based on the suffix, if any. This is
+ called whenever a new symbol is created. */
+
+void
+ppc_symbol_new_hook (sym)
+ symbolS *sym;
+{
+ const char *s;
+
+ sym->sy_tc.next = NULL;
+ sym->sy_tc.output = 0;
+ sym->sy_tc.class = -1;
+ sym->sy_tc.real_name = NULL;
+ sym->sy_tc.subseg = 0;
+ sym->sy_tc.align = 0;
+ sym->sy_tc.size = NULL;
+ sym->sy_tc.within = NULL;
+
+ if (ppc_stab_symbol)
+ return;
+
+ s = strchr (S_GET_NAME (sym), '[');
+ if (s == (const char *) NULL)
+ {
+ /* There is no suffix. */
+ return;
+ }
+
+ ++s;
+
+ switch (s[0])
+ {
+ case 'B':
+ if (strcmp (s, "BS]") == 0)
+ sym->sy_tc.class = XMC_BS;
+ break;
+ case 'D':
+ if (strcmp (s, "DB]") == 0)
+ sym->sy_tc.class = XMC_DB;
+ else if (strcmp (s, "DS]") == 0)
+ sym->sy_tc.class = XMC_DS;
+ break;
+ case 'G':
+ if (strcmp (s, "GL]") == 0)
+ sym->sy_tc.class = XMC_GL;
+ break;
+ case 'P':
+ if (strcmp (s, "PR]") == 0)
+ sym->sy_tc.class = XMC_PR;
+ break;
+ case 'R':
+ if (strcmp (s, "RO]") == 0)
+ sym->sy_tc.class = XMC_RO;
+ else if (strcmp (s, "RW]") == 0)
+ sym->sy_tc.class = XMC_RW;
+ break;
+ case 'S':
+ if (strcmp (s, "SV]") == 0)
+ sym->sy_tc.class = XMC_SV;
+ break;
+ case 'T':
+ if (strcmp (s, "TC]") == 0)
+ sym->sy_tc.class = XMC_TC;
+ else if (strcmp (s, "TI]") == 0)
+ sym->sy_tc.class = XMC_TI;
+ else if (strcmp (s, "TB]") == 0)
+ sym->sy_tc.class = XMC_TB;
+ else if (strcmp (s, "TC0]") == 0 || strcmp (s, "T0]") == 0)
+ sym->sy_tc.class = XMC_TC0;
+ break;
+ case 'U':
+ if (strcmp (s, "UA]") == 0)
+ sym->sy_tc.class = XMC_UA;
+ else if (strcmp (s, "UC]") == 0)
+ sym->sy_tc.class = XMC_UC;
+ break;
+ case 'X':
+ if (strcmp (s, "XO]") == 0)
+ sym->sy_tc.class = XMC_XO;
+ break;
+ }
+
+ if (sym->sy_tc.class == -1)
+ as_bad (_("Unrecognized symbol suffix"));
+}
+
+/* Set the class of a label based on where it is defined. This
+ handles symbols without suffixes. Also, move the symbol so that it
+ follows the csect symbol. */
+
+void
+ppc_frob_label (sym)
+ symbolS *sym;
+{
+ if (ppc_current_csect != (symbolS *) NULL)
+ {
+ if (sym->sy_tc.class == -1)
+ sym->sy_tc.class = ppc_current_csect->sy_tc.class;
+
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ symbol_append (sym, ppc_current_csect->sy_tc.within, &symbol_rootP,
+ &symbol_lastP);
+ ppc_current_csect->sy_tc.within = sym;
+ }
+}
+
+/* This variable is set by ppc_frob_symbol if any absolute symbols are
+ seen. It tells ppc_adjust_symtab whether it needs to look through
+ the symbols. */
+
+static boolean ppc_saw_abs;
+
+/* Change the name of a symbol just before writing it out. Set the
+ real name if the .rename pseudo-op was used. Otherwise, remove any
+ class suffix. Return 1 if the symbol should not be included in the
+ symbol table. */
+
+int
+ppc_frob_symbol (sym)
+ symbolS *sym;
+{
+ static symbolS *ppc_last_function;
+ static symbolS *set_end;
+
+ /* Discard symbols that should not be included in the output symbol
+ table. */
+ if (! sym->sy_used_in_reloc
+ && ((sym->bsym->flags & BSF_SECTION_SYM) != 0
+ || (! S_IS_EXTERNAL (sym)
+ && ! sym->sy_tc.output
+ && S_GET_STORAGE_CLASS (sym) != C_FILE)))
+ return 1;
+
+ if (sym->sy_tc.real_name != (char *) NULL)
+ S_SET_NAME (sym, sym->sy_tc.real_name);
+ else
+ {
+ const char *name;
+ const char *s;
+
+ name = S_GET_NAME (sym);
+ s = strchr (name, '[');
+ if (s != (char *) NULL)
+ {
+ unsigned int len;
+ char *snew;
+
+ len = s - name;
+ snew = xmalloc (len + 1);
+ memcpy (snew, name, len);
+ snew[len] = '\0';
+
+ S_SET_NAME (sym, snew);
+ }
+ }
+
+ if (set_end != (symbolS *) NULL)
+ {
+ SA_SET_SYM_ENDNDX (set_end, sym);
+ set_end = NULL;
+ }
+
+ if (SF_GET_FUNCTION (sym))
+ {
+ if (ppc_last_function != (symbolS *) NULL)
+ as_bad (_("two .function pseudo-ops with no intervening .ef"));
+ ppc_last_function = sym;
+ if (sym->sy_tc.size != (symbolS *) NULL)
+ {
+ resolve_symbol_value (sym->sy_tc.size, 1);
+ SA_SET_SYM_FSIZE (sym, (long) S_GET_VALUE (sym->sy_tc.size));
+ }
+ }
+ else if (S_GET_STORAGE_CLASS (sym) == C_FCN
+ && strcmp (S_GET_NAME (sym), ".ef") == 0)
+ {
+ if (ppc_last_function == (symbolS *) NULL)
+ as_bad (_(".ef with no preceding .function"));
+ else
+ {
+ set_end = ppc_last_function;
+ ppc_last_function = NULL;
+
+ /* We don't have a C_EFCN symbol, but we need to force the
+ COFF backend to believe that it has seen one. */
+ coff_last_function = NULL;
+ }
+ }
+
+ if (! S_IS_EXTERNAL (sym)
+ && (sym->bsym->flags & BSF_SECTION_SYM) == 0
+ && S_GET_STORAGE_CLASS (sym) != C_FILE
+ && S_GET_STORAGE_CLASS (sym) != C_FCN
+ && S_GET_STORAGE_CLASS (sym) != C_BLOCK
+ && S_GET_STORAGE_CLASS (sym) != C_BSTAT
+ && S_GET_STORAGE_CLASS (sym) != C_ESTAT
+ && S_GET_STORAGE_CLASS (sym) != C_BINCL
+ && S_GET_STORAGE_CLASS (sym) != C_EINCL
+ && S_GET_SEGMENT (sym) != ppc_coff_debug_section)
+ S_SET_STORAGE_CLASS (sym, C_HIDEXT);
+
+ if (S_GET_STORAGE_CLASS (sym) == C_EXT
+ || S_GET_STORAGE_CLASS (sym) == C_HIDEXT)
+ {
+ int i;
+ union internal_auxent *a;
+
+ /* Create a csect aux. */
+ i = S_GET_NUMBER_AUXILIARY (sym);
+ S_SET_NUMBER_AUXILIARY (sym, i + 1);
+ a = &coffsymbol (sym->bsym)->native[i + 1].u.auxent;
+ if (sym->sy_tc.class == XMC_TC0)
+ {
+ /* This is the TOC table. */
+ know (strcmp (S_GET_NAME (sym), "TOC") == 0);
+ a->x_csect.x_scnlen.l = 0;
+ a->x_csect.x_smtyp = (2 << 3) | XTY_SD;
+ }
+ else if (sym->sy_tc.subseg != 0)
+ {
+ /* This is a csect symbol. x_scnlen is the size of the
+ csect. */
+ if (sym->sy_tc.next == (symbolS *) NULL)
+ a->x_csect.x_scnlen.l = (bfd_section_size (stdoutput,
+ S_GET_SEGMENT (sym))
+ - S_GET_VALUE (sym));
+ else
+ {
+ resolve_symbol_value (sym->sy_tc.next, 1);
+ a->x_csect.x_scnlen.l = (S_GET_VALUE (sym->sy_tc.next)
+ - S_GET_VALUE (sym));
+ }
+ a->x_csect.x_smtyp = (sym->sy_tc.align << 3) | XTY_SD;
+ }
+ else if (S_GET_SEGMENT (sym) == bss_section)
+ {
+ /* This is a common symbol. */
+ a->x_csect.x_scnlen.l = sym->sy_frag->fr_offset;
+ a->x_csect.x_smtyp = (sym->sy_tc.align << 3) | XTY_CM;
+ if (S_IS_EXTERNAL (sym))
+ sym->sy_tc.class = XMC_RW;
+ else
+ sym->sy_tc.class = XMC_BS;
+ }
+ else if (S_GET_SEGMENT (sym) == absolute_section)
+ {
+ /* This is an absolute symbol. The csect will be created by
+ ppc_adjust_symtab. */
+ ppc_saw_abs = true;
+ a->x_csect.x_smtyp = XTY_LD;
+ if (sym->sy_tc.class == -1)
+ sym->sy_tc.class = XMC_XO;
+ }
+ else if (! S_IS_DEFINED (sym))
+ {
+ /* This is an external symbol. */
+ a->x_csect.x_scnlen.l = 0;
+ a->x_csect.x_smtyp = XTY_ER;
+ }
+ else if (sym->sy_tc.class == XMC_TC)
+ {
+ symbolS *next;
+
+ /* This is a TOC definition. x_scnlen is the size of the
+ TOC entry. */
+ next = symbol_next (sym);
+ while (next->sy_tc.class == XMC_TC0)
+ next = symbol_next (next);
+ if (next == (symbolS *) NULL
+ || next->sy_tc.class != XMC_TC)
+ {
+ if (ppc_after_toc_frag == (fragS *) NULL)
+ a->x_csect.x_scnlen.l = (bfd_section_size (stdoutput,
+ data_section)
+ - S_GET_VALUE (sym));
+ else
+ a->x_csect.x_scnlen.l = (ppc_after_toc_frag->fr_address
+ - S_GET_VALUE (sym));
+ }
+ else
+ {
+ resolve_symbol_value (next, 1);
+ a->x_csect.x_scnlen.l = (S_GET_VALUE (next)
+ - S_GET_VALUE (sym));
+ }
+ a->x_csect.x_smtyp = (2 << 3) | XTY_SD;
+ }
+ else
+ {
+ symbolS *csect;
+
+ /* This is a normal symbol definition. x_scnlen is the
+ symbol index of the containing csect. */
+ if (S_GET_SEGMENT (sym) == text_section)
+ csect = ppc_text_csects;
+ else if (S_GET_SEGMENT (sym) == data_section)
+ csect = ppc_data_csects;
+ else
+ abort ();
+
+ /* Skip the initial dummy symbol. */
+ csect = csect->sy_tc.next;
+
+ if (csect == (symbolS *) NULL)
+ {
+ as_warn (_("warning: symbol %s has no csect"), S_GET_NAME (sym));
+ a->x_csect.x_scnlen.l = 0;
+ }
+ else
+ {
+ while (csect->sy_tc.next != (symbolS *) NULL)
+ {
+ resolve_symbol_value (csect->sy_tc.next, 1);
+ if (S_GET_VALUE (csect->sy_tc.next) > S_GET_VALUE (sym))
+ break;
+ csect = csect->sy_tc.next;
+ }
+
+ a->x_csect.x_scnlen.p = coffsymbol (csect->bsym)->native;
+ coffsymbol (sym->bsym)->native[i + 1].fix_scnlen = 1;
+ }
+ a->x_csect.x_smtyp = XTY_LD;
+ }
+
+ a->x_csect.x_parmhash = 0;
+ a->x_csect.x_snhash = 0;
+ if (sym->sy_tc.class == -1)
+ a->x_csect.x_smclas = XMC_PR;
+ else
+ a->x_csect.x_smclas = sym->sy_tc.class;
+ a->x_csect.x_stab = 0;
+ a->x_csect.x_snstab = 0;
+
+ /* Don't let the COFF backend resort these symbols. */
+ sym->bsym->flags |= BSF_NOT_AT_END;
+ }
+ else if (S_GET_STORAGE_CLASS (sym) == C_BSTAT)
+ {
+ /* We want the value to be the symbol index of the referenced
+ csect symbol. BFD will do that for us if we set the right
+ flags. */
+ S_SET_VALUE (sym,
+ (valueT) coffsymbol (sym->sy_tc.within->bsym)->native);
+ coffsymbol (sym->bsym)->native->fix_value = 1;
+ }
+ else if (S_GET_STORAGE_CLASS (sym) == C_STSYM)
+ {
+ symbolS *block;
+ symbolS *csect;
+
+ /* The value is the offset from the enclosing csect. */
+ block = sym->sy_tc.within;
+ csect = block->sy_tc.within;
+ resolve_symbol_value (csect, 1);
+ S_SET_VALUE (sym, S_GET_VALUE (sym) - S_GET_VALUE (csect));
+ }
+ else if (S_GET_STORAGE_CLASS (sym) == C_BINCL
+ || S_GET_STORAGE_CLASS (sym) == C_EINCL)
+ {
+ /* We want the value to be a file offset into the line numbers.
+ BFD will do that for us if we set the right flags. We have
+ already set the value correctly. */
+ coffsymbol (sym->bsym)->native->fix_line = 1;
+ }
+
+ return 0;
+}
+
+/* Adjust the symbol table. This creates csect symbols for all
+ absolute symbols. */
+
+void
+ppc_adjust_symtab ()
+{
+ symbolS *sym;
+
+ if (! ppc_saw_abs)
+ return;
+
+ for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
+ {
+ symbolS *csect;
+ int i;
+ union internal_auxent *a;
+
+ if (S_GET_SEGMENT (sym) != absolute_section)
+ continue;
+
+ csect = symbol_create (".abs[XO]", absolute_section,
+ S_GET_VALUE (sym), &zero_address_frag);
+ csect->bsym->value = S_GET_VALUE (sym);
+ S_SET_STORAGE_CLASS (csect, C_HIDEXT);
+ i = S_GET_NUMBER_AUXILIARY (csect);
+ S_SET_NUMBER_AUXILIARY (csect, i + 1);
+ a = &coffsymbol (csect->bsym)->native[i + 1].u.auxent;
+ a->x_csect.x_scnlen.l = 0;
+ a->x_csect.x_smtyp = XTY_SD;
+ a->x_csect.x_parmhash = 0;
+ a->x_csect.x_snhash = 0;
+ a->x_csect.x_smclas = XMC_XO;
+ a->x_csect.x_stab = 0;
+ a->x_csect.x_snstab = 0;
+
+ symbol_insert (csect, sym, &symbol_rootP, &symbol_lastP);
+
+ i = S_GET_NUMBER_AUXILIARY (sym);
+ a = &coffsymbol (sym->bsym)->native[i].u.auxent;
+ a->x_csect.x_scnlen.p = coffsymbol (csect->bsym)->native;
+ coffsymbol (sym->bsym)->native[i].fix_scnlen = 1;
+ }
+
+ ppc_saw_abs = false;
+}
+
+/* Set the VMA for a section. This is called on all the sections in
+ turn. */
+
+void
+ppc_frob_section (sec)
+ asection *sec;
+{
+ static bfd_size_type vma = 0;
+
+ bfd_set_section_vma (stdoutput, sec, vma);
+ vma += bfd_section_size (stdoutput, sec);
+}
+
+#endif /* OBJ_XCOFF */
+
+/* Turn a string in input_line_pointer into a floating point constant
+ of type type, and store the appropriate bytes in *litp. The number
+ of LITTLENUMS emitted is stored in *sizep . An error message is
+ returned, or NULL on OK. */
+
+char *
+md_atof (type, litp, sizep)
+ int type;
+ char *litp;
+ int *sizep;
+{
+ int prec;
+ LITTLENUM_TYPE words[4];
+ char *t;
+ int i;
+
+ switch (type)
+ {
+ case 'f':
+ prec = 2;
+ break;
+
+ case 'd':
+ prec = 4;
+ break;
+
+ default:
+ *sizep = 0;
+ return _("bad call to md_atof");
+ }
+
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+
+ *sizep = prec * 2;
+
+ if (target_big_endian)
+ {
+ for (i = 0; i < prec; i++)
+ {
+ md_number_to_chars (litp, (valueT) words[i], 2);
+ litp += 2;
+ }
+ }
+ else
+ {
+ for (i = prec - 1; i >= 0; i--)
+ {
+ md_number_to_chars (litp, (valueT) words[i], 2);
+ litp += 2;
+ }
+ }
+
+ return NULL;
+}
+
+/* Write a value out to the object file, using the appropriate
+ endianness. */
+
+void
+md_number_to_chars (buf, val, n)
+ char *buf;
+ valueT val;
+ int n;
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+/* Align a section (I don't know why this is machine dependent). */
+
+valueT
+md_section_align (seg, addr)
+ asection *seg;
+ valueT addr;
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+/* We don't have any form of relaxing. */
+
+int
+md_estimate_size_before_relax (fragp, seg)
+ fragS *fragp;
+ asection *seg;
+{
+ abort ();
+ return 0;
+}
+
+/* Convert a machine dependent frag. We never generate these. */
+
+void
+md_convert_frag (abfd, sec, fragp)
+ bfd *abfd;
+ asection *sec;
+ fragS *fragp;
+{
+ abort ();
+}
+
+/* We have no need to default values of symbols. */
+
+/*ARGSUSED*/
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ return 0;
+}
+
+/* Functions concerning relocs. */
+
+/* The location from which a PC relative jump should be calculated,
+ given a PC relative reloc. */
+
+long
+md_pcrel_from_section (fixp, sec)
+ fixS *fixp;
+ segT sec;
+{
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+}
+
+#ifdef OBJ_XCOFF
+
+/* This is called to see whether a fixup should be adjusted to use a
+ section symbol. We take the opportunity to change a fixup against
+ a symbol in the TOC subsegment into a reloc against the
+ corresponding .tc symbol. */
+
+int
+ppc_fix_adjustable (fix)
+ fixS *fix;
+{
+ valueT val;
+
+ resolve_symbol_value (fix->fx_addsy, 1);
+ val = S_GET_VALUE (fix->fx_addsy);
+ if (ppc_toc_csect != (symbolS *) NULL
+ && fix->fx_addsy != (symbolS *) NULL
+ && fix->fx_addsy != ppc_toc_csect
+ && S_GET_SEGMENT (fix->fx_addsy) == data_section
+ && val >= ppc_toc_frag->fr_address
+ && (ppc_after_toc_frag == (fragS *) NULL
+ || val < ppc_after_toc_frag->fr_address))
+ {
+ symbolS *sy;
+
+ for (sy = symbol_next (ppc_toc_csect);
+ sy != (symbolS *) NULL;
+ sy = symbol_next (sy))
+ {
+ if (sy->sy_tc.class == XMC_TC0)
+ continue;
+ if (sy->sy_tc.class != XMC_TC)
+ break;
+ resolve_symbol_value (sy, 1);
+ if (val == S_GET_VALUE (sy))
+ {
+ fix->fx_addsy = sy;
+ fix->fx_addnumber = val - ppc_toc_frag->fr_address;
+ return 0;
+ }
+ }
+
+ as_bad_where (fix->fx_file, fix->fx_line,
+ _("symbol in .toc does not match any .tc"));
+ }
+
+ /* Possibly adjust the reloc to be against the csect. */
+ if (fix->fx_addsy != (symbolS *) NULL
+ && fix->fx_addsy->sy_tc.subseg == 0
+ && fix->fx_addsy->sy_tc.class != XMC_TC0
+ && fix->fx_addsy->sy_tc.class != XMC_TC
+ && S_GET_SEGMENT (fix->fx_addsy) != bss_section
+ /* Don't adjust if this is a reloc in the toc section. */
+ && (S_GET_SEGMENT (fix->fx_addsy) != data_section
+ || ppc_toc_csect == NULL
+ || val < ppc_toc_frag->fr_address
+ || (ppc_after_toc_frag != NULL
+ && val >= ppc_after_toc_frag->fr_address)))
+ {
+ symbolS *csect;
+
+ if (S_GET_SEGMENT (fix->fx_addsy) == text_section)
+ csect = ppc_text_csects;
+ else if (S_GET_SEGMENT (fix->fx_addsy) == data_section)
+ csect = ppc_data_csects;
+ else
+ abort ();
+
+ /* Skip the initial dummy symbol. */
+ csect = csect->sy_tc.next;
+
+ if (csect != (symbolS *) NULL)
+ {
+ while (csect->sy_tc.next != (symbolS *) NULL
+ && (csect->sy_tc.next->sy_frag->fr_address
+ <= fix->fx_addsy->sy_frag->fr_address))
+ {
+ /* If the csect address equals the symbol value, then we
+ have to look through the full symbol table to see
+ whether this is the csect we want. Note that we will
+ only get here if the csect has zero length. */
+ if ((csect->sy_frag->fr_address
+ == fix->fx_addsy->sy_frag->fr_address)
+ && S_GET_VALUE (csect) == S_GET_VALUE (fix->fx_addsy))
+ {
+ symbolS *scan;
+
+ for (scan = csect->sy_next;
+ scan != NULL;
+ scan = scan->sy_next)
+ {
+ if (scan->sy_tc.subseg != 0)
+ break;
+ if (scan == fix->fx_addsy)
+ break;
+ }
+
+ /* If we found the symbol before the next csect
+ symbol, then this is the csect we want. */
+ if (scan == fix->fx_addsy)
+ break;
+ }
+
+ csect = csect->sy_tc.next;
+ }
+
+ fix->fx_offset += (S_GET_VALUE (fix->fx_addsy)
+ - csect->sy_frag->fr_address);
+ fix->fx_addsy = csect;
+ }
+ }
+
+ /* Adjust a reloc against a .lcomm symbol to be against the base
+ .lcomm. */
+ if (fix->fx_addsy != (symbolS *) NULL
+ && S_GET_SEGMENT (fix->fx_addsy) == bss_section
+ && ! S_IS_EXTERNAL (fix->fx_addsy))
+ {
+ resolve_symbol_value (fix->fx_addsy->sy_frag->fr_symbol, 1);
+ fix->fx_offset += (S_GET_VALUE (fix->fx_addsy)
+ - S_GET_VALUE (fix->fx_addsy->sy_frag->fr_symbol));
+ fix->fx_addsy = fix->fx_addsy->sy_frag->fr_symbol;
+ }
+
+ return 0;
+}
+
+/* A reloc from one csect to another must be kept. The assembler
+ will, of course, keep relocs between sections, and it will keep
+ absolute relocs, but we need to force it to keep PC relative relocs
+ between two csects in the same section. */
+
+int
+ppc_force_relocation (fix)
+ fixS *fix;
+{
+ /* At this point fix->fx_addsy should already have been converted to
+ a csect symbol. If the csect does not include the fragment, then
+ we need to force the relocation. */
+ if (fix->fx_pcrel
+ && fix->fx_addsy != NULL
+ && fix->fx_addsy->sy_tc.subseg != 0
+ && (fix->fx_addsy->sy_frag->fr_address > fix->fx_frag->fr_address
+ || (fix->fx_addsy->sy_tc.next != NULL
+ && (fix->fx_addsy->sy_tc.next->sy_frag->fr_address
+ <= fix->fx_frag->fr_address))))
+ return 1;
+
+ return 0;
+}
+
+#endif /* OBJ_XCOFF */
+
+/* See whether a symbol is in the TOC section. */
+
+static int
+ppc_is_toc_sym (sym)
+ symbolS *sym;
+{
+#ifdef OBJ_XCOFF
+ return sym->sy_tc.class == XMC_TC;
+#else
+ return strcmp (segment_name (S_GET_SEGMENT (sym)), ".got") == 0;
+#endif
+}
+
+/* Apply a fixup to the object code. This is called for all the
+ fixups we generated by the call to fix_new_exp, above. In the call
+ above we used a reloc code which was the largest legal reloc code
+ plus the operand index. Here we undo that to recover the operand
+ index. At this point all symbol values should be fully resolved,
+ and we attempt to completely resolve the reloc. If we can not do
+ that, we determine the correct reloc code and put it back in the
+ fixup. */
+
+int
+md_apply_fix3 (fixp, valuep, seg)
+ fixS *fixp;
+ valueT *valuep;
+ segT seg;
+{
+ valueT value;
+
+#ifdef OBJ_ELF
+ value = *valuep;
+ if (fixp->fx_addsy != NULL)
+ {
+ /* `*valuep' may contain the value of the symbol on which the reloc
+ will be based; we have to remove it. */
+ if (fixp->fx_addsy->sy_used_in_reloc
+ && S_GET_SEGMENT (fixp->fx_addsy) != absolute_section
+ && S_GET_SEGMENT (fixp->fx_addsy) != undefined_section
+ && ! bfd_is_com_section (S_GET_SEGMENT (fixp->fx_addsy)))
+ value -= S_GET_VALUE (fixp->fx_addsy);
+
+ /* FIXME: Why '+'? Better yet, what exactly is '*valuep'
+ supposed to be? I think this is related to various similar
+ FIXMEs in tc-i386.c and tc-sparc.c. */
+ if (fixp->fx_pcrel)
+ value += fixp->fx_frag->fr_address + fixp->fx_where;
+ }
+ else
+ {
+ fixp->fx_done = 1;
+ }
+#else
+ /* FIXME FIXME FIXME: The value we are passed in *valuep includes
+ the symbol values. Since we are using BFD_ASSEMBLER, if we are
+ doing this relocation the code in write.c is going to call
+ bfd_install_relocation, which is also going to use the symbol
+ value. That means that if the reloc is fully resolved we want to
+ use *valuep since bfd_install_relocation is not being used.
+ However, if the reloc is not fully resolved we do not want to use
+ *valuep, and must use fx_offset instead. However, if the reloc
+ is PC relative, we do want to use *valuep since it includes the
+ result of md_pcrel_from. This is confusing. */
+ if (fixp->fx_addsy == (symbolS *) NULL)
+ {
+ value = *valuep;
+ fixp->fx_done = 1;
+ }
+ else if (fixp->fx_pcrel)
+ value = *valuep;
+ else
+ {
+ value = fixp->fx_offset;
+ if (fixp->fx_subsy != (symbolS *) NULL)
+ {
+ if (S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
+ value -= S_GET_VALUE (fixp->fx_subsy);
+ else
+ {
+ /* We can't actually support subtracting a symbol. */
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("expression too complex"));
+ }
+ }
+ }
+#endif
+
+ if ((int) fixp->fx_r_type >= (int) BFD_RELOC_UNUSED)
+ {
+ int opindex;
+ const struct powerpc_operand *operand;
+ char *where;
+ unsigned long insn;
+
+ opindex = (int) fixp->fx_r_type - (int) BFD_RELOC_UNUSED;
+
+ operand = &powerpc_operands[opindex];
+
+#ifdef OBJ_XCOFF
+ /* It appears that an instruction like
+ l 9,LC..1(30)
+ when LC..1 is not a TOC symbol does not generate a reloc. It
+ uses the offset of LC..1 within its csect. However, .long
+ LC..1 will generate a reloc. I can't find any documentation
+ on how these cases are to be distinguished, so this is a wild
+ guess. These cases are generated by gcc -mminimal-toc. */
+ if ((operand->flags & PPC_OPERAND_PARENS) != 0
+ && operand->bits == 16
+ && operand->shift == 0
+ && operand->insert == NULL
+ && fixp->fx_addsy != NULL
+ && fixp->fx_addsy->sy_tc.subseg != 0
+ && fixp->fx_addsy->sy_tc.class != XMC_TC
+ && fixp->fx_addsy->sy_tc.class != XMC_TC0
+ && S_GET_SEGMENT (fixp->fx_addsy) != bss_section)
+ {
+ value = fixp->fx_offset;
+ fixp->fx_done = 1;
+ }
+#endif
+
+ /* Fetch the instruction, insert the fully resolved operand
+ value, and stuff the instruction back again. */
+ where = fixp->fx_frag->fr_literal + fixp->fx_where;
+ if (target_big_endian)
+ insn = bfd_getb32 ((unsigned char *) where);
+ else
+ insn = bfd_getl32 ((unsigned char *) where);
+ insn = ppc_insert_operand (insn, operand, (offsetT) value,
+ fixp->fx_file, fixp->fx_line);
+ if (target_big_endian)
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ else
+ bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
+
+ if (fixp->fx_done)
+ {
+ /* Nothing else to do here. */
+ return 1;
+ }
+
+ /* Determine a BFD reloc value based on the operand information.
+ We are only prepared to turn a few of the operands into
+ relocs.
+ FIXME: We need to handle the DS field at the very least.
+ FIXME: Selecting the reloc type is a bit haphazard; perhaps
+ there should be a new field in the operand table. */
+ if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
+ && operand->bits == 26
+ && operand->shift == 0)
+ fixp->fx_r_type = BFD_RELOC_PPC_B26;
+ else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
+ && operand->bits == 16
+ && operand->shift == 0)
+ fixp->fx_r_type = BFD_RELOC_PPC_B16;
+ else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0
+ && operand->bits == 26
+ && operand->shift == 0)
+ fixp->fx_r_type = BFD_RELOC_PPC_BA26;
+ else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0
+ && operand->bits == 16
+ && operand->shift == 0)
+ fixp->fx_r_type = BFD_RELOC_PPC_BA16;
+ else if ((operand->flags & PPC_OPERAND_PARENS) != 0
+ && operand->bits == 16
+ && operand->shift == 0
+ && operand->insert == NULL
+ && fixp->fx_addsy != NULL
+ && ppc_is_toc_sym (fixp->fx_addsy))
+ {
+ fixp->fx_size = 2;
+ if (target_big_endian)
+ fixp->fx_where += 2;
+ fixp->fx_r_type = BFD_RELOC_PPC_TOC16;
+ }
+ else
+ {
+ char *sfile;
+ unsigned int sline;
+
+ /* Use expr_symbol_where to see if this is an expression
+ symbol. */
+ if (expr_symbol_where (fixp->fx_addsy, &sfile, &sline))
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("unresolved expression that must be resolved"));
+ else
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("unsupported relocation type"));
+ fixp->fx_done = 1;
+ return 1;
+ }
+ }
+ else
+ {
+#ifdef OBJ_ELF
+ ppc_elf_validate_fix (fixp, seg);
+#endif
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_32:
+ case BFD_RELOC_CTOR:
+ if (fixp->fx_pcrel)
+ fixp->fx_r_type = BFD_RELOC_32_PCREL;
+ /* fall through */
+
+ case BFD_RELOC_RVA:
+ case BFD_RELOC_32_PCREL:
+ case BFD_RELOC_32_BASEREL:
+ case BFD_RELOC_PPC_EMB_NADDR32:
+ md_number_to_chars (fixp->fx_frag->fr_literal + fixp->fx_where,
+ value, 4);
+ break;
+
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_16:
+ case BFD_RELOC_GPREL16:
+ case BFD_RELOC_16_GOT_PCREL:
+ case BFD_RELOC_16_GOTOFF:
+ case BFD_RELOC_LO16_GOTOFF:
+ case BFD_RELOC_HI16_GOTOFF:
+ case BFD_RELOC_HI16_S_GOTOFF:
+ case BFD_RELOC_LO16_BASEREL:
+ case BFD_RELOC_HI16_BASEREL:
+ case BFD_RELOC_HI16_S_BASEREL:
+ case BFD_RELOC_PPC_EMB_NADDR16:
+ case BFD_RELOC_PPC_EMB_NADDR16_LO:
+ case BFD_RELOC_PPC_EMB_NADDR16_HI:
+ case BFD_RELOC_PPC_EMB_NADDR16_HA:
+ case BFD_RELOC_PPC_EMB_SDAI16:
+ case BFD_RELOC_PPC_EMB_SDA2REL:
+ case BFD_RELOC_PPC_EMB_SDA2I16:
+ case BFD_RELOC_PPC_EMB_RELSEC16:
+ case BFD_RELOC_PPC_EMB_RELST_LO:
+ case BFD_RELOC_PPC_EMB_RELST_HI:
+ case BFD_RELOC_PPC_EMB_RELST_HA:
+ case BFD_RELOC_PPC_EMB_RELSDA:
+ case BFD_RELOC_PPC_TOC16:
+ if (fixp->fx_pcrel)
+ {
+ if (fixp->fx_addsy != NULL)
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("cannot emit PC relative %s relocation against %s"),
+ bfd_get_reloc_code_name (fixp->fx_r_type),
+ S_GET_NAME (fixp->fx_addsy));
+ else
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("cannot emit PC relative %s relocation"),
+ bfd_get_reloc_code_name (fixp->fx_r_type));
+ }
+
+ md_number_to_chars (fixp->fx_frag->fr_literal + fixp->fx_where,
+ value, 2);
+ break;
+
+ /* This case happens when you write, for example,
+ lis %r3,(L1-L2)@ha
+ where L1 and L2 are defined later. */
+ case BFD_RELOC_HI16:
+ if (fixp->fx_pcrel)
+ abort ();
+ md_number_to_chars (fixp->fx_frag->fr_literal + fixp->fx_where,
+ value >> 16, 2);
+ break;
+ case BFD_RELOC_HI16_S:
+ if (fixp->fx_pcrel)
+ abort ();
+ md_number_to_chars (fixp->fx_frag->fr_literal + fixp->fx_where,
+ value + 0x8000 >> 16, 2);
+ break;
+
+ /* Because SDA21 modifies the register field, the size is set to 4
+ bytes, rather than 2, so offset it here appropriately */
+ case BFD_RELOC_PPC_EMB_SDA21:
+ if (fixp->fx_pcrel)
+ abort ();
+
+ md_number_to_chars (fixp->fx_frag->fr_literal + fixp->fx_where
+ + ((target_big_endian) ? 2 : 0),
+ value, 2);
+ break;
+
+ case BFD_RELOC_8:
+ if (fixp->fx_pcrel)
+ abort ();
+
+ md_number_to_chars (fixp->fx_frag->fr_literal + fixp->fx_where,
+ value, 1);
+ break;
+
+ case BFD_RELOC_24_PLT_PCREL:
+ case BFD_RELOC_PPC_LOCAL24PC:
+ if (!fixp->fx_pcrel && !fixp->fx_done)
+ abort ();
+
+ if (fixp->fx_done)
+ {
+ char *where;
+ unsigned long insn;
+
+ /* Fetch the instruction, insert the fully resolved operand
+ value, and stuff the instruction back again. */
+ where = fixp->fx_frag->fr_literal + fixp->fx_where;
+ if (target_big_endian)
+ insn = bfd_getb32 ((unsigned char *) where);
+ else
+ insn = bfd_getl32 ((unsigned char *) where);
+ if ((value & 3) != 0)
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("must branch to an address a multiple of 4"));
+ if ((offsetT) value < -0x40000000
+ || (offsetT) value >= 0x40000000)
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("@local or @plt branch destination is too far away, %ld bytes"),
+ value);
+ insn = insn | (value & 0x03fffffc);
+ if (target_big_endian)
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ else
+ bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
+ }
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ fixp->fx_done = 0;
+ if (fixp->fx_addsy
+ && !S_IS_DEFINED (fixp->fx_addsy)
+ && !S_IS_WEAK (fixp->fx_addsy))
+ S_SET_WEAK (fixp->fx_addsy);
+ break;
+
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixp->fx_done = 0;
+ break;
+
+ default:
+ fprintf(stderr,
+ _("Gas failure, reloc value %d\n"), fixp->fx_r_type);
+ fflush(stderr);
+ abort ();
+ }
+ }
+
+#ifdef OBJ_ELF
+ fixp->fx_addnumber = value;
+#else
+ if (fixp->fx_r_type != BFD_RELOC_PPC_TOC16)
+ fixp->fx_addnumber = 0;
+ else
+ {
+#ifdef TE_PE
+ fixp->fx_addnumber = 0;
+#else
+ /* We want to use the offset within the data segment of the
+ symbol, not the actual VMA of the symbol. */
+ fixp->fx_addnumber =
+ - bfd_get_section_vma (stdoutput, S_GET_SEGMENT (fixp->fx_addsy));
+#endif
+ }
+#endif
+
+ return 1;
+}
+
+/* Generate a reloc for a fixup. */
+
+arelent *
+tc_gen_reloc (seg, fixp)
+ asection *seg;
+ fixS *fixp;
+{
+ arelent *reloc;
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+
+ reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("reloc %d not supported by object file format"), (int)fixp->fx_r_type);
+ return NULL;
+ }
+ reloc->addend = fixp->fx_addnumber;
+
+ return reloc;
+}
diff --git a/gas/config/tc-ppc.h b/gas/config/tc-ppc.h
new file mode 100644
index 0000000..0871d13
--- /dev/null
+++ b/gas/config/tc-ppc.h
@@ -0,0 +1,276 @@
+/* tc-ppc.h -- Header file for tc-ppc.c.
+ Copyright (C) 1994, 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Cygnus Support.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#define TC_PPC
+
+#ifdef ANSI_PROTOTYPES
+struct fix;
+#endif
+
+/* Set the endianness we are using. Default to big endian. */
+#ifndef TARGET_BYTES_BIG_ENDIAN
+#define TARGET_BYTES_BIG_ENDIAN 1
+#endif
+
+#ifndef BFD_ASSEMBLER
+ #error PowerPC support requires BFD_ASSEMBLER
+#endif
+
+/* If OBJ_COFF is defined, and TE_PE is not defined, we are assembling
+ XCOFF for AIX or PowerMac. If TE_PE is defined, we are assembling
+ COFF for Windows NT. */
+
+#ifdef OBJ_COFF
+#ifndef TE_PE
+#define OBJ_XCOFF
+#endif
+#endif
+
+/* The target BFD architecture. */
+#define TARGET_ARCH (ppc_arch ())
+extern enum bfd_architecture ppc_arch PARAMS ((void));
+
+/* Whether or not the target is big endian */
+extern int target_big_endian;
+
+/* The target BFD format. */
+#ifdef OBJ_COFF
+#ifdef TE_PE
+#define TARGET_FORMAT (target_big_endian ? "pe-powerpc" : "pe-powerpcle")
+#else
+#define TARGET_FORMAT "aixcoff-rs6000"
+#endif
+#endif
+
+/* PowerMac has a BFD slightly different from AIX's. */
+#ifdef TE_POWERMAC
+#ifdef TARGET_FORMAT
+#undef TARGET_FORMAT
+#endif
+#define TARGET_FORMAT "xcoff-powermac"
+#endif
+
+#ifdef OBJ_ELF
+#define TARGET_FORMAT (target_big_endian ? "elf32-powerpc" : "elf32-powerpcle")
+#endif
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+/* $ is used to refer to the current location. */
+#define DOLLAR_DOT
+
+/* Strings do not use backslash escapes under COFF. */
+#ifdef OBJ_COFF
+#define NO_STRING_ESCAPES
+#endif
+
+#ifdef OBJ_ELF
+#define DIFF_EXPR_OK /* foo-. gets turned into PC relative relocs */
+#endif
+
+#if TARGET_BYTES_BIG_ENDIAN
+#define PPC_BIG_ENDIAN 1
+#else
+#define PPC_BIG_ENDIAN 0
+#endif
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+/* We set the fx_done field appropriately in md_apply_fix. */
+#define TC_HANDLES_FX_DONE
+
+#ifdef TE_PE
+
+/* Question marks are permitted in symbol names. */
+#define LEX_QM 1
+
+/* Don't adjust TOC relocs. */
+#define tc_fix_adjustable(fixp) ppc_pe_fix_adjustable (fixp)
+extern int ppc_pe_fix_adjustable PARAMS ((struct fix *));
+
+#endif
+
+#ifdef OBJ_XCOFF
+
+/* Declarations needed when generating XCOFF code. XCOFF is an
+ extension of COFF, used only on the RS/6000. Rather than create an
+ obj-xcoff, we just use obj-coff, and handle the extensions here in
+ tc-ppc. */
+
+/* We need to keep some information for symbols. */
+struct ppc_tc_sy
+{
+ /* We keep a few linked lists of symbols. */
+ struct symbol *next;
+ /* Non-zero if the symbol should be output. The RS/6000 assembler
+ only outputs symbols that are external or are mentioned in a
+ .globl or .lglobl statement. */
+ int output;
+ /* The symbol class. */
+ int class;
+ /* The real name, if the symbol was renamed. */
+ char *real_name;
+ /* For a csect symbol, the subsegment we are using. This is zero
+ for symbols that are not csects. */
+ subsegT subseg;
+ /* For a csect or common symbol, the alignment to use. */
+ int align;
+ /* For a function symbol, a symbol whose value is the size. The
+ field is NULL if there is no size. */
+ struct symbol *size;
+ /* For a csect symbol, the last symbol which has been defined in
+ this csect, or NULL if none have been defined so far. For a .bs
+ symbol, the referenced csect symbol. */
+ struct symbol *within;
+};
+
+#define TC_SYMFIELD_TYPE struct ppc_tc_sy
+
+/* We need an additional auxent for function symbols. */
+#define OBJ_COFF_MAX_AUXENTRIES 2
+
+/* Square and curly brackets are permitted in symbol names. */
+#define LEX_BR 3
+
+/* Canonicalize the symbol name. */
+#define tc_canonicalize_symbol_name(name) ppc_canonicalize_symbol_name (name)
+extern char *ppc_canonicalize_symbol_name PARAMS ((char *));
+
+/* Get the symbol class from the name. */
+#define tc_symbol_new_hook(sym) ppc_symbol_new_hook (sym)
+extern void ppc_symbol_new_hook PARAMS ((struct symbol *));
+
+/* Set the symbol class of a label based on the csect. */
+#define tc_frob_label(sym) ppc_frob_label (sym)
+extern void ppc_frob_label PARAMS ((struct symbol *));
+
+/* TOC relocs requires special handling. */
+#define tc_fix_adjustable(fixp) ppc_fix_adjustable (fixp)
+extern int ppc_fix_adjustable PARAMS ((struct fix *));
+
+/* A relocation from one csect to another must be kept. */
+#define TC_FORCE_RELOCATION(FIXP) ppc_force_relocation (FIXP)
+extern int ppc_force_relocation PARAMS ((struct fix *));
+
+/* We need to set the section VMA. */
+#define tc_frob_section(sec) ppc_frob_section (sec)
+extern void ppc_frob_section PARAMS ((asection *));
+
+/* Finish up the symbol. */
+#define tc_frob_symbol(sym, punt) punt = ppc_frob_symbol (sym)
+extern int ppc_frob_symbol PARAMS ((struct symbol *));
+
+/* Finish up the entire symtab. */
+#define tc_adjust_symtab() ppc_adjust_symtab ()
+extern void ppc_adjust_symtab PARAMS ((void));
+
+/* Niclas Andersson <nican@ida.liu.se> says this is needed. */
+#define SUB_SEGMENT_ALIGN(SEG) 2
+
+#endif /* OBJ_XCOFF */
+
+#ifdef OBJ_ELF
+
+/* Branch prediction relocations must force relocation, as must
+ the vtable description relocs. */
+#define TC_FORCE_RELOCATION(FIXP) \
+((FIXP)->fx_r_type == BFD_RELOC_PPC_B16_BRTAKEN \
+ || (FIXP)->fx_r_type == BFD_RELOC_PPC_B16_BRNTAKEN \
+ || (FIXP)->fx_r_type == BFD_RELOC_PPC_BA16_BRTAKEN \
+ || (FIXP)->fx_r_type == BFD_RELOC_PPC_BA16_BRNTAKEN \
+ || (FIXP)->fx_r_type == BFD_RELOC_VTABLE_INHERIT \
+ || (FIXP)->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+
+#define TC_FORCE_RELOCATION_SECTION(FIXP,SEC) \
+(TC_FORCE_RELOCATION (FIXP) \
+ || ((FIXP)->fx_addsy && !(FIXP)->fx_subsy && (FIXP)->fx_addsy->bsym \
+ && (FIXP)->fx_addsy->bsym->section != SEC))
+
+/* Support for SHF_EXCLUDE and SHT_ORDERED */
+extern int ppc_section_letter PARAMS ((int, char **));
+extern int ppc_section_type PARAMS ((char **));
+extern int ppc_section_word PARAMS ((char **));
+extern int ppc_section_flags PARAMS ((int, int, int));
+
+#define md_elf_section_letter(LETTER, PTR_MSG) ppc_section_letter (LETTER, PTR_MSG)
+#define md_elf_section_type(PTR_STR) ppc_section_type (PTR_STR)
+#define md_elf_section_word(PTR_STR) ppc_section_word (PTR_STR)
+#define md_elf_section_flags(FLAGS, ATTR, TYPE) ppc_section_flags (FLAGS, ATTR, TYPE)
+
+/* Add extra PPC sections -- Note, for now, make .sbss2 and .PPC.EMB.sbss0 a
+ normal section, and not a bss section so that the linker doesn't crater
+ when trying to make more than 2 sections. */
+#define ELF_TC_SPECIAL_SECTIONS \
+ { ".tags", SHT_ORDERED, SHF_ALLOC }, \
+ { ".sdata", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE }, \
+ { ".sbss", SHT_NOBITS, SHF_ALLOC + SHF_WRITE }, \
+ { ".sdata2", SHT_PROGBITS, SHF_ALLOC }, \
+ { ".sbss2", SHT_PROGBITS, SHF_ALLOC }, \
+ { ".PPC.EMB.sdata0", SHT_PROGBITS, SHF_ALLOC }, \
+ { ".PPC.EMB.sbss0", SHT_PROGBITS, SHF_ALLOC },
+
+#define tc_comment_chars ppc_comment_chars
+extern const char *ppc_comment_chars;
+
+/* Keep relocations relative to the GOT, or non-PC relative. */
+#define tc_fix_adjustable(FIX) \
+ ((FIX)->fx_r_type != BFD_RELOC_16_GOTOFF \
+ && (FIX)->fx_r_type != BFD_RELOC_LO16_GOTOFF \
+ && (FIX)->fx_r_type != BFD_RELOC_HI16_GOTOFF \
+ && (FIX)->fx_r_type != BFD_RELOC_HI16_S_GOTOFF \
+ && (FIX)->fx_r_type != BFD_RELOC_GPREL16 \
+ && (FIX)->fx_r_type != BFD_RELOC_VTABLE_INHERIT \
+ && (FIX)->fx_r_type != BFD_RELOC_VTABLE_ENTRY \
+ && ! S_IS_EXTERNAL ((FIX)->fx_addsy) \
+ && ! S_IS_WEAK ((FIX)->fx_addsy) \
+ && ((FIX)->fx_pcrel \
+ || ((FIX)->fx_subsy != NULL \
+ && (S_GET_SEGMENT ((FIX)->fx_subsy) \
+ == S_GET_SEGMENT ((FIX)->fx_addsy))) \
+ || strchr (S_GET_NAME ((FIX)->fx_addsy), '\001') != NULL \
+ || strchr (S_GET_NAME ((FIX)->fx_addsy), '\002') != NULL))
+
+/* We must never ever try to resolve references to externally visible
+ symbols in the assembler, because the .o file might go into a shared
+ library, and some other shared library might override that symbol. */
+#define TC_RELOC_RTSYM_LOC_FIXUP(FIX) \
+ ((FIX)->fx_addsy == NULL \
+ || (! S_IS_EXTERNAL ((FIX)->fx_addsy) \
+ && ! S_IS_WEAK ((FIX)->fx_addsy) \
+ && S_IS_DEFINED ((FIX)->fx_addsy) \
+ && ! S_IS_COMMON ((FIX)->fx_addsy)))
+
+#endif /* OBJ_ELF */
+
+/* call md_apply_fix3 with segment instead of md_apply_fix */
+#define MD_APPLY_FIX3
+
+/* call md_pcrel_from_section, not md_pcrel_from */
+#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section(FIXP, SEC)
+extern long md_pcrel_from_section PARAMS ((struct fix *, segT));
+
+#define md_parse_name(name, exp) ppc_parse_name (name, exp)
+extern int ppc_parse_name PARAMS ((const char *, struct expressionS *));
+
+#define md_operand(x)
+
diff --git a/gas/config/tc-sh.c b/gas/config/tc-sh.c
new file mode 100644
index 0000000..6d8ca81
--- /dev/null
+++ b/gas/config/tc-sh.c
@@ -0,0 +1,2425 @@
+/* tc-sh.c -- Assemble code for the Hitachi Super-H
+ Copyright (C) 1993, 94, 95, 96, 97, 1998 Free Software Foundation.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/*
+ Written By Steve Chamberlain
+ sac@cygnus.com
+ */
+
+#include <stdio.h>
+#include "as.h"
+#include "bfd.h"
+#include "subsegs.h"
+#define DEFINE_TABLE
+#include "opcodes/sh-opc.h"
+#include <ctype.h>
+const char comment_chars[] = "!";
+const char line_separator_chars[] = ";";
+const char line_comment_chars[] = "!#";
+
+static void s_uses PARAMS ((int));
+
+static void sh_count_relocs PARAMS ((bfd *, segT, PTR));
+static void sh_frob_section PARAMS ((bfd *, segT, PTR));
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ Integer arg to pass to the function
+ */
+
+void cons ();
+void s_align_bytes ();
+static void s_uacons PARAMS ((int));
+
+int shl = 0;
+
+static void
+little (ignore)
+ int ignore;
+{
+ shl = 1;
+ target_big_endian = 0;
+}
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"int", cons, 4},
+ {"word", cons, 2},
+ {"form", listing_psize, 0},
+ {"little", little, 0},
+ {"heading", listing_title, 0},
+ {"import", s_ignore, 0},
+ {"page", listing_eject, 0},
+ {"program", s_ignore, 0},
+ {"uses", s_uses, 0},
+ {"uaword", s_uacons, 2},
+ {"ualong", s_uacons, 4},
+ {0, 0, 0}
+};
+
+/*int md_reloc_size; */
+
+int sh_relax; /* set if -relax seen */
+
+/* Whether -small was seen. */
+
+int sh_small;
+
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+#define C(a,b) ENCODE_RELAX(a,b)
+
+#define JREG 14 /* Register used as a temp when relaxing */
+#define ENCODE_RELAX(what,length) (((what) << 4) + (length))
+#define GET_WHAT(x) ((x>>4))
+
+/* These are the three types of relaxable instrction */
+#define COND_JUMP 1
+#define COND_JUMP_DELAY 2
+#define UNCOND_JUMP 3
+#define END 4
+
+#define UNDEF_DISP 0
+#define COND8 1
+#define COND12 2
+#define COND32 3
+#define UNCOND12 1
+#define UNCOND32 2
+#define UNDEF_WORD_DISP 4
+
+#define UNCOND12 1
+#define UNCOND32 2
+
+/* Branch displacements are from the address of the branch plus
+ four, thus all minimum and maximum values have 4 added to them. */
+#define COND8_F 258
+#define COND8_M -252
+#define COND8_LENGTH 2
+
+/* There is one extra instruction before the branch, so we must add
+ two more bytes to account for it. */
+#define COND12_F 4100
+#define COND12_M -4090
+#define COND12_LENGTH 6
+
+#define COND12_DELAY_LENGTH 4
+
+/* ??? The minimum and maximum values are wrong, but this does not matter
+ since this relocation type is not supported yet. */
+#define COND32_F (1<<30)
+#define COND32_M -(1<<30)
+#define COND32_LENGTH 14
+
+#define UNCOND12_F 4098
+#define UNCOND12_M -4092
+#define UNCOND12_LENGTH 2
+
+/* ??? The minimum and maximum values are wrong, but this does not matter
+ since this relocation type is not supported yet. */
+#define UNCOND32_F (1<<30)
+#define UNCOND32_M -(1<<30)
+#define UNCOND32_LENGTH 14
+
+const relax_typeS md_relax_table[C (END, 0)] = {
+ { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 },
+ { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 },
+
+ { 0 },
+ /* C (COND_JUMP, COND8) */
+ { COND8_F, COND8_M, COND8_LENGTH, C (COND_JUMP, COND12) },
+ /* C (COND_JUMP, COND12) */
+ { COND12_F, COND12_M, COND12_LENGTH, C (COND_JUMP, COND32), },
+ /* C (COND_JUMP, COND32) */
+ { COND32_F, COND32_M, COND32_LENGTH, 0, },
+ { 0 }, { 0 }, { 0 }, { 0 },
+ { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 },
+
+ { 0 },
+ /* C (COND_JUMP_DELAY, COND8) */
+ { COND8_F, COND8_M, COND8_LENGTH, C (COND_JUMP_DELAY, COND12) },
+ /* C (COND_JUMP_DELAY, COND12) */
+ { COND12_F, COND12_M, COND12_DELAY_LENGTH, C (COND_JUMP_DELAY, COND32), },
+ /* C (COND_JUMP_DELAY, COND32) */
+ { COND32_F, COND32_M, COND32_LENGTH, 0, },
+ { 0 }, { 0 }, { 0 }, { 0 },
+ { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 },
+
+ { 0 },
+ /* C (UNCOND_JUMP, UNCOND12) */
+ { UNCOND12_F, UNCOND12_M, UNCOND12_LENGTH, C (UNCOND_JUMP, UNCOND32), },
+ /* C (UNCOND_JUMP, UNCOND32) */
+ { UNCOND32_F, UNCOND32_M, UNCOND32_LENGTH, 0, },
+ { 0 }, { 0 }, { 0 }, { 0 }, { 0 },
+ { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 }, { 0 },
+};
+
+static struct hash_control *opcode_hash_control; /* Opcode mnemonics */
+
+/*
+ This function is called once, at assembler startup time. This should
+ set up all the tables, etc that the MD part of the assembler needs
+ */
+
+void
+md_begin ()
+{
+ sh_opcode_info *opcode;
+ char *prev_name = "";
+
+ if (! shl)
+ target_big_endian = 1;
+
+ opcode_hash_control = hash_new ();
+
+ /* Insert unique names into hash table */
+ for (opcode = sh_table; opcode->name; opcode++)
+ {
+ if (strcmp (prev_name, opcode->name))
+ {
+ prev_name = opcode->name;
+ hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
+ }
+ else
+ {
+ /* Make all the opcodes with the same name point to the same
+ string */
+ opcode->name = prev_name;
+ }
+ }
+}
+
+static int reg_m;
+static int reg_n;
+static int reg_b;
+
+static expressionS immediate; /* absolute expression */
+
+typedef struct
+ {
+ sh_arg_type type;
+ int reg;
+ }
+
+sh_operand_info;
+
+/* try and parse a reg name, returns number of chars consumed */
+static int
+parse_reg (src, mode, reg)
+ char *src;
+ int *mode;
+ int *reg;
+{
+ /* We use !isalnum for the next character after the register name, to
+ make sure that we won't accidentally recognize a symbol name such as
+ 'sram' as being a reference to the register 'sr'. */
+
+ if (src[0] == 'r')
+ {
+ if (src[1] >= '0' && src[1] <= '7' && strncmp(&src[2], "_bank", 5) == 0
+ && ! isalnum ((unsigned char) src[7]))
+ {
+ *mode = A_REG_B;
+ *reg = (src[1] - '0');
+ return 7;
+ }
+ }
+
+ if (src[0] == 'r')
+ {
+ if (src[1] == '1')
+ {
+ if (src[2] >= '0' && src[2] <= '5'
+ && ! isalnum ((unsigned char) src[3]))
+ {
+ *mode = A_REG_N;
+ *reg = 10 + src[2] - '0';
+ return 3;
+ }
+ }
+ if (src[1] >= '0' && src[1] <= '9'
+ && ! isalnum ((unsigned char) src[2]))
+ {
+ *mode = A_REG_N;
+ *reg = (src[1] - '0');
+ return 2;
+ }
+ }
+
+ if (src[0] == 's'
+ && src[1] == 's'
+ && src[2] == 'r' && ! isalnum ((unsigned char) src[3]))
+ {
+ *mode = A_SSR;
+ return 3;
+ }
+
+ if (src[0] == 's' && src[1] == 'p' && src[2] == 'c'
+ && ! isalnum ((unsigned char) src[3]))
+ {
+ *mode = A_SPC;
+ return 3;
+ }
+
+ if (src[0] == 's' && src[1] == 'g' && src[2] == 'r'
+ && ! isalnum ((unsigned char) src[3]))
+ {
+ *mode = A_SGR;
+ return 3;
+ }
+
+ if (src[0] == 'd' && src[1] == 'b' && src[2] == 'r'
+ && ! isalnum ((unsigned char) src[3]))
+ {
+ *mode = A_DBR;
+ return 3;
+ }
+
+ if (src[0] == 's' && src[1] == 'r' && ! isalnum ((unsigned char) src[2]))
+ {
+ *mode = A_SR;
+ return 2;
+ }
+
+ if (src[0] == 's' && src[1] == 'p' && ! isalnum ((unsigned char) src[2]))
+ {
+ *mode = A_REG_N;
+ *reg = 15;
+ return 2;
+ }
+
+ if (src[0] == 'p' && src[1] == 'r' && ! isalnum ((unsigned char) src[2]))
+ {
+ *mode = A_PR;
+ return 2;
+ }
+ if (src[0] == 'p' && src[1] == 'c' && ! isalnum ((unsigned char) src[2]))
+ {
+ *mode = A_DISP_PC;
+ return 2;
+ }
+ if (src[0] == 'g' && src[1] == 'b' && src[2] == 'r'
+ && ! isalnum ((unsigned char) src[3]))
+ {
+ *mode = A_GBR;
+ return 3;
+ }
+ if (src[0] == 'v' && src[1] == 'b' && src[2] == 'r'
+ && ! isalnum ((unsigned char) src[3]))
+ {
+ *mode = A_VBR;
+ return 3;
+ }
+
+ if (src[0] == 'm' && src[1] == 'a' && src[2] == 'c'
+ && ! isalnum ((unsigned char) src[4]))
+ {
+ if (src[3] == 'l')
+ {
+ *mode = A_MACL;
+ return 4;
+ }
+ if (src[3] == 'h')
+ {
+ *mode = A_MACH;
+ return 4;
+ }
+ }
+ if (src[0] == 'f' && src[1] == 'r')
+ {
+ if (src[2] == '1')
+ {
+ if (src[3] >= '0' && src[3] <= '5'
+ && ! isalnum ((unsigned char) src[4]))
+ {
+ *mode = F_REG_N;
+ *reg = 10 + src[3] - '0';
+ return 4;
+ }
+ }
+ if (src[2] >= '0' && src[2] <= '9'
+ && ! isalnum ((unsigned char) src[3]))
+ {
+ *mode = F_REG_N;
+ *reg = (src[2] - '0');
+ return 3;
+ }
+ }
+ if (src[0] == 'd' && src[1] == 'r')
+ {
+ if (src[2] == '1')
+ {
+ if (src[3] >= '0' && src[3] <= '4' && ! ((src[3] - '0') & 1)
+ && ! isalnum ((unsigned char) src[4]))
+ {
+ *mode = D_REG_N;
+ *reg = 10 + src[3] - '0';
+ return 4;
+ }
+ }
+ if (src[2] >= '0' && src[2] <= '8' && ! ((src[2] - '0') & 1)
+ && ! isalnum ((unsigned char) src[3]))
+ {
+ *mode = D_REG_N;
+ *reg = (src[2] - '0');
+ return 3;
+ }
+ }
+ if (src[0] == 'x' && src[1] == 'd')
+ {
+ if (src[2] == '1')
+ {
+ if (src[3] >= '0' && src[3] <= '4' && ! ((src[3] - '0') & 1)
+ && ! isalnum ((unsigned char) src[4]))
+ {
+ *mode = X_REG_N;
+ *reg = 11 + src[3] - '0';
+ return 4;
+ }
+ }
+ if (src[2] >= '0' && src[2] <= '8' && ! ((src[2] - '0') & 1)
+ && ! isalnum ((unsigned char) src[3]))
+ {
+ *mode = X_REG_N;
+ *reg = (src[2] - '0') + 1;
+ return 3;
+ }
+ }
+ if (src[0] == 'f' && src[1] == 'v')
+ {
+ if (src[2] == '1'&& src[3] == '2' && ! isalnum ((unsigned char) src[4]))
+ {
+ *mode = V_REG_N;
+ *reg = 12;
+ return 4;
+ }
+ if ((src[2] == '0' || src[2] == '4' || src[2] == '8')
+ && ! isalnum ((unsigned char) src[3]))
+ {
+ *mode = V_REG_N;
+ *reg = (src[2] - '0');
+ return 3;
+ }
+ }
+ if (src[0] == 'f' && src[1] == 'p' && src[2] == 'u' && src[3] == 'l'
+ && ! isalnum ((unsigned char) src[4]))
+ {
+ *mode = FPUL_N;
+ return 4;
+ }
+
+ if (src[0] == 'f' && src[1] == 'p' && src[2] == 's' && src[3] == 'c'
+ && src[4] == 'r' && ! isalnum ((unsigned char) src[5]))
+ {
+ *mode = FPSCR_N;
+ return 5;
+ }
+
+ if (src[0] == 'x' && src[1] == 'm' && src[2] == 't' && src[3] == 'r'
+ && src[4] == 'x' && ! isalnum ((unsigned char) src[5]))
+ {
+ *mode = XMTRX_M4;
+ return 5;
+ }
+
+ return 0;
+}
+
+static symbolS *dot()
+{
+ const char *fake;
+
+ /* JF: '.' is pseudo symbol with value of current location
+ in current segment. */
+ fake = FAKE_LABEL_NAME;
+ return symbol_new (fake,
+ now_seg,
+ (valueT) frag_now_fix (),
+ frag_now);
+
+}
+
+
+static
+char *
+parse_exp (s)
+ char *s;
+{
+ char *save;
+ char *new;
+
+ save = input_line_pointer;
+ input_line_pointer = s;
+ expression (&immediate);
+ if (immediate.X_op == O_absent)
+ as_bad (_("missing operand"));
+ new = input_line_pointer;
+ input_line_pointer = save;
+ return new;
+}
+
+
+/* The many forms of operand:
+
+ Rn Register direct
+ @Rn Register indirect
+ @Rn+ Autoincrement
+ @-Rn Autodecrement
+ @(disp:4,Rn)
+ @(disp:8,GBR)
+ @(disp:8,PC)
+
+ @(R0,Rn)
+ @(R0,GBR)
+
+ disp:8
+ disp:12
+ #imm8
+ pr, gbr, vbr, macl, mach
+
+ */
+
+static
+char *
+parse_at (src, op)
+ char *src;
+ sh_operand_info *op;
+{
+ int len;
+ int mode;
+ src++;
+ if (src[0] == '-')
+ {
+ /* Must be predecrement */
+ src++;
+
+ len = parse_reg (src, &mode, &(op->reg));
+ if (mode != A_REG_N)
+ as_bad (_("illegal register after @-"));
+
+ op->type = A_DEC_N;
+ src += len;
+ }
+ else if (src[0] == '(')
+ {
+ /* Could be @(disp, rn), @(disp, gbr), @(disp, pc), @(r0, gbr) or
+ @(r0, rn) */
+ src++;
+ len = parse_reg (src, &mode, &(op->reg));
+ if (len && mode == A_REG_N)
+ {
+ src += len;
+ if (op->reg != 0)
+ {
+ as_bad (_("must be @(r0,...)"));
+ }
+ if (src[0] == ',')
+ src++;
+ /* Now can be rn or gbr */
+ len = parse_reg (src, &mode, &(op->reg));
+ if (mode == A_GBR)
+ {
+ op->type = A_R0_GBR;
+ }
+ else if (mode == A_REG_N)
+ {
+ op->type = A_IND_R0_REG_N;
+ }
+ else
+ {
+ as_bad (_("syntax error in @(r0,...)"));
+ }
+ }
+ else
+ {
+ /* Must be an @(disp,.. thing) */
+ src = parse_exp (src);
+ if (src[0] == ',')
+ src++;
+ /* Now can be rn, gbr or pc */
+ len = parse_reg (src, &mode, &op->reg);
+ if (len)
+ {
+ if (mode == A_REG_N)
+ {
+ op->type = A_DISP_REG_N;
+ }
+ else if (mode == A_GBR)
+ {
+ op->type = A_DISP_GBR;
+ }
+ else if (mode == A_DISP_PC)
+ {
+ /* Turn a plain @(4,pc) into @(.+4,pc) */
+ if (immediate.X_op == O_constant) {
+ immediate.X_add_symbol = dot();
+ immediate.X_op = O_symbol;
+ }
+ op->type = A_DISP_PC;
+ }
+ else
+ {
+ as_bad (_("syntax error in @(disp,[Rn, gbr, pc])"));
+ }
+ }
+ else
+ {
+ as_bad (_("syntax error in @(disp,[Rn, gbr, pc])"));
+ }
+ }
+ src += len;
+ if (src[0] != ')')
+ as_bad (_("expecting )"));
+ else
+ src++;
+ }
+ else
+ {
+ src += parse_reg (src, &mode, &(op->reg));
+ if (mode != A_REG_N)
+ {
+ as_bad (_("illegal register after @"));
+ }
+ if (src[0] == '+')
+ {
+ op->type = A_INC_N;
+ src++;
+ }
+ else
+ {
+ op->type = A_IND_N;
+ }
+ }
+ return src;
+}
+
+static void
+get_operand (ptr, op)
+ char **ptr;
+ sh_operand_info *op;
+{
+ char *src = *ptr;
+ int mode = -1;
+ unsigned int len;
+
+ if (src[0] == '#')
+ {
+ src++;
+ *ptr = parse_exp (src);
+ op->type = A_IMM;
+ return;
+ }
+
+ else if (src[0] == '@')
+ {
+ *ptr = parse_at (src, op);
+ return;
+ }
+ len = parse_reg (src, &mode, &(op->reg));
+ if (len)
+ {
+ *ptr = src + len;
+ op->type = mode;
+ return;
+ }
+ else
+ {
+ /* Not a reg, the only thing left is a displacement */
+ *ptr = parse_exp (src);
+ op->type = A_DISP_PC;
+ return;
+ }
+}
+
+static
+char *
+get_operands (info, args, operand)
+ sh_opcode_info *info;
+ char *args;
+ sh_operand_info *operand;
+
+{
+ char *ptr = args;
+ if (info->arg[0])
+ {
+ ptr++;
+
+ get_operand (&ptr, operand + 0);
+ if (info->arg[1])
+ {
+ if (*ptr == ',')
+ {
+ ptr++;
+ }
+ get_operand (&ptr, operand + 1);
+ if (info->arg[2])
+ {
+ if (*ptr == ',')
+ {
+ ptr++;
+ }
+ get_operand (&ptr, operand + 2);
+ }
+ else
+ {
+ operand[2].type = 0;
+ }
+ }
+ else
+ {
+ operand[1].type = 0;
+ operand[2].type = 0;
+ }
+ }
+ else
+ {
+ operand[0].type = 0;
+ operand[1].type = 0;
+ operand[2].type = 0;
+ }
+ return ptr;
+}
+
+/* Passed a pointer to a list of opcodes which use different
+ addressing modes, return the opcode which matches the opcodes
+ provided
+ */
+
+static
+sh_opcode_info *
+get_specific (opcode, operands)
+ sh_opcode_info *opcode;
+ sh_operand_info *operands;
+{
+ sh_opcode_info *this_try = opcode;
+ char *name = opcode->name;
+ int n = 0;
+ while (opcode->name)
+ {
+ this_try = opcode++;
+ if (this_try->name != name)
+ {
+ /* We've looked so far down the table that we've run out of
+ opcodes with the same name */
+ return 0;
+ }
+ /* look at both operands needed by the opcodes and provided by
+ the user - since an arg test will often fail on the same arg
+ again and again, we'll try and test the last failing arg the
+ first on each opcode try */
+
+ for (n = 0; this_try->arg[n]; n++)
+ {
+ sh_operand_info *user = operands + n;
+ sh_arg_type arg = this_try->arg[n];
+ switch (arg)
+ {
+ case A_IMM:
+ case A_BDISP12:
+ case A_BDISP8:
+ case A_DISP_GBR:
+ case A_DISP_PC:
+ case A_MACH:
+ case A_PR:
+ case A_MACL:
+ if (user->type != arg)
+ goto fail;
+ break;
+ case A_R0:
+ /* opcode needs r0 */
+ if (user->type != A_REG_N || user->reg != 0)
+ goto fail;
+ break;
+ case A_R0_GBR:
+ if (user->type != A_R0_GBR || user->reg != 0)
+ goto fail;
+ break;
+ case F_FR0:
+ if (user->type != F_REG_N || user->reg != 0)
+ goto fail;
+ break;
+
+ case A_REG_N:
+ case A_INC_N:
+ case A_DEC_N:
+ case A_IND_N:
+ case A_IND_R0_REG_N:
+ case A_DISP_REG_N:
+ case F_REG_N:
+ case D_REG_N:
+ case X_REG_N:
+ case V_REG_N:
+ case FPUL_N:
+ case FPSCR_N:
+ /* Opcode needs rn */
+ if (user->type != arg)
+ goto fail;
+ reg_n = user->reg;
+ break;
+ case FD_REG_N:
+ if (user->type != F_REG_N && user->type != D_REG_N)
+ goto fail;
+ reg_n = user->reg;
+ break;
+ case DX_REG_N:
+ if (user->type != D_REG_N && user->type != X_REG_N)
+ goto fail;
+ reg_n = user->reg;
+ break;
+ case A_GBR:
+ case A_SR:
+ case A_VBR:
+ case A_SSR:
+ case A_SPC:
+ case A_SGR:
+ case A_DBR:
+ if (user->type != arg)
+ goto fail;
+ break;
+
+ case A_REG_B:
+ if (user->type != arg)
+ goto fail;
+ reg_b = user->reg;
+ break;
+
+ case A_REG_M:
+ case A_INC_M:
+ case A_DEC_M:
+ case A_IND_M:
+ case A_IND_R0_REG_M:
+ case A_DISP_REG_M:
+ /* Opcode needs rn */
+ if (user->type != arg - A_REG_M + A_REG_N)
+ goto fail;
+ reg_m = user->reg;
+ break;
+
+ case F_REG_M:
+ case D_REG_M:
+ case X_REG_M:
+ case V_REG_M:
+ case FPUL_M:
+ case FPSCR_M:
+ /* Opcode needs rn */
+ if (user->type != arg - F_REG_M + F_REG_N)
+ goto fail;
+ reg_m = user->reg;
+ break;
+ case DX_REG_M:
+ if (user->type != D_REG_N && user->type != X_REG_N)
+ goto fail;
+ reg_m = user->reg;
+ break;
+ case XMTRX_M4:
+ if (user->type != XMTRX_M4)
+ goto fail;
+ reg_m = 4;
+ break;
+
+ default:
+ printf (_("unhandled %d\n"), arg);
+ goto fail;
+ }
+ }
+ return this_try;
+ fail:;
+ }
+
+ return 0;
+}
+
+int
+check (operand, low, high)
+ expressionS *operand;
+ int low;
+ int high;
+{
+ if (operand->X_op != O_constant
+ || operand->X_add_number < low
+ || operand->X_add_number > high)
+ {
+ as_bad (_("operand must be absolute in range %d..%d"), low, high);
+ }
+ return operand->X_add_number;
+}
+
+
+static void
+insert (where, how, pcrel)
+ char *where;
+ int how;
+ int pcrel;
+{
+ fix_new_exp (frag_now,
+ where - frag_now->fr_literal,
+ 2,
+ &immediate,
+ pcrel,
+ how);
+}
+
+static void
+build_relax (opcode)
+ sh_opcode_info *opcode;
+{
+ int high_byte = target_big_endian ? 0 : 1;
+ char *p;
+
+ if (opcode->arg[0] == A_BDISP8)
+ {
+ int what = (opcode->nibbles[1] & 4) ? COND_JUMP_DELAY : COND_JUMP;
+ p = frag_var (rs_machine_dependent,
+ md_relax_table[C (what, COND32)].rlx_length,
+ md_relax_table[C (what, COND8)].rlx_length,
+ C (what, 0),
+ immediate.X_add_symbol,
+ immediate.X_add_number,
+ 0);
+ p[high_byte] = (opcode->nibbles[0] << 4) | (opcode->nibbles[1]);
+ }
+ else if (opcode->arg[0] == A_BDISP12)
+ {
+ p = frag_var (rs_machine_dependent,
+ md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_length,
+ md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length,
+ C (UNCOND_JUMP, 0),
+ immediate.X_add_symbol,
+ immediate.X_add_number,
+ 0);
+ p[high_byte] = (opcode->nibbles[0] << 4);
+ }
+
+}
+
+/* Now we know what sort of opcodes it is, lets build the bytes -
+ */
+static void
+build_Mytes (opcode, operand)
+ sh_opcode_info *opcode;
+ sh_operand_info *operand;
+
+{
+ int index;
+ char nbuf[4];
+ char *output = frag_more (2);
+ int low_byte = target_big_endian ? 1 : 0;
+ nbuf[0] = 0;
+ nbuf[1] = 0;
+ nbuf[2] = 0;
+ nbuf[3] = 0;
+
+ for (index = 0; index < 4; index++)
+ {
+ sh_nibble_type i = opcode->nibbles[index];
+ if (i < 16)
+ {
+ nbuf[index] = i;
+ }
+ else
+ {
+ switch (i)
+ {
+ case REG_N:
+ nbuf[index] = reg_n;
+ break;
+ case REG_M:
+ nbuf[index] = reg_m;
+ break;
+ case REG_NM:
+ nbuf[index] = reg_n | (reg_m >> 2);
+ break;
+ case REG_B:
+ nbuf[index] = reg_b | 0x08;
+ break;
+ case DISP_4:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4, 0);
+ break;
+ case IMM_4BY4:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0);
+ break;
+ case IMM_4BY2:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4BY2, 0);
+ break;
+ case IMM_4:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4, 0);
+ break;
+ case IMM_8BY4:
+ insert (output + low_byte, BFD_RELOC_SH_IMM8BY4, 0);
+ break;
+ case IMM_8BY2:
+ insert (output + low_byte, BFD_RELOC_SH_IMM8BY2, 0);
+ break;
+ case IMM_8:
+ insert (output + low_byte, BFD_RELOC_SH_IMM8, 0);
+ break;
+ case PCRELIMM_8BY4:
+ insert (output, BFD_RELOC_SH_PCRELIMM8BY4, 1);
+ break;
+ case PCRELIMM_8BY2:
+ insert (output, BFD_RELOC_SH_PCRELIMM8BY2, 1);
+ break;
+ default:
+ printf (_("failed for %d\n"), i);
+ }
+ }
+ }
+ if (! target_big_endian) {
+ output[1] = (nbuf[0] << 4) | (nbuf[1]);
+ output[0] = (nbuf[2] << 4) | (nbuf[3]);
+ }
+ else {
+ output[0] = (nbuf[0] << 4) | (nbuf[1]);
+ output[1] = (nbuf[2] << 4) | (nbuf[3]);
+ }
+}
+
+/* This is the guts of the machine-dependent assembler. STR points to a
+ machine dependent instruction. This function is supposed to emit
+ the frags/bytes it assembles to.
+ */
+
+void
+md_assemble (str)
+ char *str;
+{
+ unsigned char *op_start;
+ unsigned char *op_end;
+ sh_operand_info operand[3];
+ sh_opcode_info *opcode;
+ char name[20];
+ int nlen = 0;
+ /* Drop leading whitespace */
+ while (*str == ' ')
+ str++;
+
+ /* find the op code end */
+ for (op_start = op_end = (unsigned char *) (str);
+ *op_end
+ && nlen < 20
+ && !is_end_of_line[*op_end] && *op_end != ' ';
+ op_end++)
+ {
+ unsigned char c = op_start[nlen];
+
+ /* The machine independent code will convert CMP/EQ into cmp/EQ
+ because it thinks the '/' is the end of the symbol. Instead of
+ hacking up the machine independent code, we just deal with it
+ here. */
+ c = isupper (c) ? tolower (c) : c;
+ name[nlen] = c;
+ nlen++;
+ }
+ name[nlen] = 0;
+
+ if (nlen == 0)
+ {
+ as_bad (_("can't find opcode "));
+ }
+
+ opcode = (sh_opcode_info *) hash_find (opcode_hash_control, name);
+
+ if (opcode == NULL)
+ {
+ as_bad (_("unknown opcode"));
+ return;
+ }
+
+ if (sh_relax
+ && ! seg_info (now_seg)->tc_segment_info_data.in_code)
+ {
+ /* Output a CODE reloc to tell the linker that the following
+ bytes are instructions, not data. */
+ fix_new (frag_now, frag_now_fix (), 2, &abs_symbol, 0, 0,
+ BFD_RELOC_SH_CODE);
+ seg_info (now_seg)->tc_segment_info_data.in_code = 1;
+ }
+
+ if (opcode->arg[0] == A_BDISP12
+ || opcode->arg[0] == A_BDISP8)
+ {
+ parse_exp (op_end + 1);
+ build_relax (opcode);
+ }
+ else
+ {
+ if (opcode->arg[0] != A_END)
+ {
+ get_operands (opcode, op_end, operand);
+ }
+ opcode = get_specific (opcode, operand);
+
+ if (opcode == 0)
+ {
+ /* Couldn't find an opcode which matched the operands */
+ char *where = frag_more (2);
+
+ where[0] = 0x0;
+ where[1] = 0x0;
+ as_bad (_("invalid operands for opcode"));
+ return;
+ }
+
+ build_Mytes (opcode, operand);
+ }
+
+}
+
+/* This routine is called each time a label definition is seen. It
+ emits a BFD_RELOC_SH_LABEL reloc if necessary. */
+
+void
+sh_frob_label ()
+{
+ static fragS *last_label_frag;
+ static int last_label_offset;
+
+ if (sh_relax
+ && seg_info (now_seg)->tc_segment_info_data.in_code)
+ {
+ int offset;
+
+ offset = frag_now_fix ();
+ if (frag_now != last_label_frag
+ || offset != last_label_offset)
+ {
+ fix_new (frag_now, offset, 2, &abs_symbol, 0, 0, BFD_RELOC_SH_LABEL);
+ last_label_frag = frag_now;
+ last_label_offset = offset;
+ }
+ }
+}
+
+/* This routine is called when the assembler is about to output some
+ data. It emits a BFD_RELOC_SH_DATA reloc if necessary. */
+
+void
+sh_flush_pending_output ()
+{
+ if (sh_relax
+ && seg_info (now_seg)->tc_segment_info_data.in_code)
+ {
+ fix_new (frag_now, frag_now_fix (), 2, &abs_symbol, 0, 0,
+ BFD_RELOC_SH_DATA);
+ seg_info (now_seg)->tc_segment_info_data.in_code = 0;
+ }
+}
+
+symbolS *
+DEFUN (md_undefined_symbol, (name),
+ char *name)
+{
+ return 0;
+}
+
+#ifdef OBJ_COFF
+
+void
+DEFUN (tc_crawl_symbol_chain, (headers),
+ object_headers * headers)
+{
+ printf (_("call to tc_crawl_symbol_chain \n"));
+}
+
+void
+DEFUN (tc_headers_hook, (headers),
+ object_headers * headers)
+{
+ printf (_("call to tc_headers_hook \n"));
+}
+
+#endif
+
+/* Various routines to kill one day */
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+/* Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP . An error message is returned, or NULL on OK.
+ */
+char *
+md_atof (type, litP, sizeP)
+ int type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[4];
+ char *t;
+ int i;
+
+ switch (type)
+ {
+ case 'f':
+ prec = 2;
+ break;
+
+ case 'd':
+ prec = 4;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("bad call to md_atof");
+ }
+
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+
+ *sizeP = prec * 2;
+
+ if (! target_big_endian)
+ {
+ for (i = prec - 1; i >= 0; i--)
+ {
+ md_number_to_chars (litP, (valueT) words[i], 2);
+ litP += 2;
+ }
+ }
+ else
+ {
+ for (i = 0; i < prec; i++)
+ {
+ md_number_to_chars (litP, (valueT) words[i], 2);
+ litP += 2;
+ }
+ }
+
+ return NULL;
+}
+
+/* Handle the .uses pseudo-op. This pseudo-op is used just before a
+ call instruction. It refers to a label of the instruction which
+ loads the register which the call uses. We use it to generate a
+ special reloc for the linker. */
+
+static void
+s_uses (ignore)
+ int ignore;
+{
+ expressionS ex;
+
+ if (! sh_relax)
+ as_warn (_(".uses pseudo-op seen when not relaxing"));
+
+ expression (&ex);
+
+ if (ex.X_op != O_symbol || ex.X_add_number != 0)
+ {
+ as_bad (_("bad .uses format"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ fix_new_exp (frag_now, frag_now_fix (), 2, &ex, 1, BFD_RELOC_SH_USES);
+
+ demand_empty_rest_of_line ();
+}
+
+CONST char *md_shortopts = "";
+struct option md_longopts[] = {
+
+#define OPTION_RELAX (OPTION_MD_BASE)
+#define OPTION_LITTLE (OPTION_MD_BASE + 1)
+#define OPTION_SMALL (OPTION_LITTLE + 1)
+
+ {"relax", no_argument, NULL, OPTION_RELAX},
+ {"little", no_argument, NULL, OPTION_LITTLE},
+ {"small", no_argument, NULL, OPTION_SMALL},
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ switch (c)
+ {
+ case OPTION_RELAX:
+ sh_relax = 1;
+ break;
+
+ case OPTION_LITTLE:
+ shl = 1;
+ target_big_endian = 0;
+ break;
+
+ case OPTION_SMALL:
+ sh_small = 1;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ fprintf(stream, _("\
+SH options:\n\
+-little generate little endian code\n\
+-relax alter jump instructions for long displacements\n\
+-small align sections to 4 byte boundaries, not 16\n"));
+}
+
+void
+tc_Nout_fix_to_chars ()
+{
+ printf (_("call to tc_Nout_fix_to_chars \n"));
+ abort ();
+}
+
+/* This struct is used to pass arguments to sh_count_relocs through
+ bfd_map_over_sections. */
+
+struct sh_count_relocs
+{
+ /* Symbol we are looking for. */
+ symbolS *sym;
+ /* Count of relocs found. */
+ int count;
+};
+
+/* Count the number of fixups in a section which refer to a particular
+ symbol. When using BFD_ASSEMBLER, this is called via
+ bfd_map_over_sections. */
+
+/*ARGSUSED*/
+static void
+sh_count_relocs (abfd, sec, data)
+ bfd *abfd;
+ segT sec;
+ PTR data;
+{
+ struct sh_count_relocs *info = (struct sh_count_relocs *) data;
+ segment_info_type *seginfo;
+ symbolS *sym;
+ fixS *fix;
+
+ seginfo = seg_info (sec);
+ if (seginfo == NULL)
+ return;
+
+ sym = info->sym;
+ for (fix = seginfo->fix_root; fix != NULL; fix = fix->fx_next)
+ {
+ if (fix->fx_addsy == sym)
+ {
+ ++info->count;
+ fix->fx_tcbit = 1;
+ }
+ }
+}
+
+/* Handle the count relocs for a particular section. When using
+ BFD_ASSEMBLER, this is called via bfd_map_over_sections. */
+
+/*ARGSUSED*/
+static void
+sh_frob_section (abfd, sec, ignore)
+ bfd *abfd;
+ segT sec;
+ PTR ignore;
+{
+ segment_info_type *seginfo;
+ fixS *fix;
+
+ seginfo = seg_info (sec);
+ if (seginfo == NULL)
+ return;
+
+ for (fix = seginfo->fix_root; fix != NULL; fix = fix->fx_next)
+ {
+ symbolS *sym;
+ bfd_vma val;
+ fixS *fscan;
+ struct sh_count_relocs info;
+
+ if (fix->fx_r_type != BFD_RELOC_SH_USES)
+ continue;
+
+ /* The BFD_RELOC_SH_USES reloc should refer to a defined local
+ symbol in the same section. */
+ sym = fix->fx_addsy;
+ if (sym == NULL
+ || fix->fx_subsy != NULL
+ || fix->fx_addnumber != 0
+ || S_GET_SEGMENT (sym) != sec
+#if ! defined (BFD_ASSEMBLER) && defined (OBJ_COFF)
+ || S_GET_STORAGE_CLASS (sym) == C_EXT
+#endif
+ || S_IS_EXTERNAL (sym))
+ {
+ as_warn_where (fix->fx_file, fix->fx_line,
+ _(".uses does not refer to a local symbol in the same section"));
+ continue;
+ }
+
+ /* Look through the fixups again, this time looking for one
+ at the same location as sym. */
+ val = S_GET_VALUE (sym);
+ for (fscan = seginfo->fix_root;
+ fscan != NULL;
+ fscan = fscan->fx_next)
+ if (val == fscan->fx_frag->fr_address + fscan->fx_where
+ && fscan->fx_r_type != BFD_RELOC_SH_ALIGN
+ && fscan->fx_r_type != BFD_RELOC_SH_CODE
+ && fscan->fx_r_type != BFD_RELOC_SH_DATA
+ && fscan->fx_r_type != BFD_RELOC_SH_LABEL)
+ break;
+ if (fscan == NULL)
+ {
+ as_warn_where (fix->fx_file, fix->fx_line,
+ _("can't find fixup pointed to by .uses"));
+ continue;
+ }
+
+ if (fscan->fx_tcbit)
+ {
+ /* We've already done this one. */
+ continue;
+ }
+
+ /* fscan should also be a fixup to a local symbol in the same
+ section. */
+ sym = fscan->fx_addsy;
+ if (sym == NULL
+ || fscan->fx_subsy != NULL
+ || fscan->fx_addnumber != 0
+ || S_GET_SEGMENT (sym) != sec
+#if ! defined (BFD_ASSEMBLER) && defined (OBJ_COFF)
+ || S_GET_STORAGE_CLASS (sym) == C_EXT
+#endif
+ || S_IS_EXTERNAL (sym))
+ {
+ as_warn_where (fix->fx_file, fix->fx_line,
+ _(".uses target does not refer to a local symbol in the same section"));
+ continue;
+ }
+
+ /* Now we look through all the fixups of all the sections,
+ counting the number of times we find a reference to sym. */
+ info.sym = sym;
+ info.count = 0;
+#ifdef BFD_ASSEMBLER
+ bfd_map_over_sections (stdoutput, sh_count_relocs, (PTR) &info);
+#else
+ {
+ int iscan;
+
+ for (iscan = SEG_E0; iscan < SEG_UNKNOWN; iscan++)
+ sh_count_relocs ((bfd *) NULL, iscan, (PTR) &info);
+ }
+#endif
+
+ if (info.count < 1)
+ abort ();
+
+ /* Generate a BFD_RELOC_SH_COUNT fixup at the location of sym.
+ We have already adjusted the value of sym to include the
+ fragment address, so we undo that adjustment here. */
+ subseg_change (sec, 0);
+ fix_new (sym->sy_frag, S_GET_VALUE (sym) - sym->sy_frag->fr_address,
+ 4, &abs_symbol, info.count, 0, BFD_RELOC_SH_COUNT);
+ }
+}
+
+/* This function is called after the symbol table has been completed,
+ but before the relocs or section contents have been written out.
+ If we have seen any .uses pseudo-ops, they point to an instruction
+ which loads a register with the address of a function. We look
+ through the fixups to find where the function address is being
+ loaded from. We then generate a COUNT reloc giving the number of
+ times that function address is referred to. The linker uses this
+ information when doing relaxing, to decide when it can eliminate
+ the stored function address entirely. */
+
+void
+sh_frob_file ()
+{
+ if (! sh_relax)
+ return;
+
+#ifdef BFD_ASSEMBLER
+ bfd_map_over_sections (stdoutput, sh_frob_section, (PTR) NULL);
+#else
+ {
+ int iseg;
+
+ for (iseg = SEG_E0; iseg < SEG_UNKNOWN; iseg++)
+ sh_frob_section ((bfd *) NULL, iseg, (PTR) NULL);
+ }
+#endif
+}
+
+/* Called after relaxing. Set the correct sizes of the fragments, and
+ create relocs so that md_apply_fix will fill in the correct values. */
+
+void
+md_convert_frag (headers, seg, fragP)
+#ifdef BFD_ASSEMBLER
+ bfd *headers;
+#else
+ object_headers *headers;
+#endif
+ segT seg;
+ fragS *fragP;
+{
+ int donerelax = 0;
+
+ switch (fragP->fr_subtype)
+ {
+ case C (COND_JUMP, COND8):
+ case C (COND_JUMP_DELAY, COND8):
+ subseg_change (seg, 0);
+ fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol, fragP->fr_offset,
+ 1, BFD_RELOC_SH_PCDISP8BY2);
+ fragP->fr_fix += 2;
+ fragP->fr_var = 0;
+ break;
+
+ case C (UNCOND_JUMP, UNCOND12):
+ subseg_change (seg, 0);
+ fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol, fragP->fr_offset,
+ 1, BFD_RELOC_SH_PCDISP12BY2);
+ fragP->fr_fix += 2;
+ fragP->fr_var = 0;
+ break;
+
+ case C (UNCOND_JUMP, UNCOND32):
+ case C (UNCOND_JUMP, UNDEF_WORD_DISP):
+ if (fragP->fr_symbol == NULL)
+ as_bad (_("at 0x%lx, displacement overflows 12-bit field"),
+ (unsigned long) fragP->fr_address);
+ else if (S_IS_DEFINED (fragP->fr_symbol))
+ as_bad (_("at 0x%lx, displacement to defined symbol %s overflows 12-bit field"),
+ (unsigned long) fragP->fr_address,
+ S_GET_NAME (fragP->fr_symbol));
+ else
+ as_bad (_("at 0x%lx, displacement to undefined symbol %s overflows 12-bit field"),
+ (unsigned long) fragP->fr_address,
+ S_GET_NAME (fragP->fr_symbol));
+
+#if 0 /* This code works, but generates poor code and the compiler
+ should never produce a sequence that requires it to be used. */
+
+ /* A jump wont fit in 12 bits, make code which looks like
+ bra foo
+ mov.w @(0, PC), r14
+ .long disp
+ foo: bra @r14
+ */
+ int t = buffer[0] & 0x10;
+
+ buffer[highbyte] = 0xa0; /* branch over move and disp */
+ buffer[lowbyte] = 3;
+ buffer[highbyte+2] = 0xd0 | JREG; /* Build mov insn */
+ buffer[lowbyte+2] = 0x00;
+
+ buffer[highbyte+4] = 0; /* space for 32 bit jump disp */
+ buffer[lowbyte+4] = 0;
+ buffer[highbyte+6] = 0;
+ buffer[lowbyte+6] = 0;
+
+ buffer[highbyte+8] = 0x40 | JREG; /* Build jmp @JREG */
+ buffer[lowbyte+8] = t ? 0xb : 0x2b;
+
+ buffer[highbyte+10] = 0x20; /* build nop */
+ buffer[lowbyte+10] = 0x0b;
+
+ /* Make reloc for the long disp */
+ fix_new (fragP,
+ fragP->fr_fix + 4,
+ 4,
+ fragP->fr_symbol,
+ fragP->fr_offset,
+ 0,
+ BFD_RELOC_32);
+ fragP->fr_fix += UNCOND32_LENGTH;
+ fragP->fr_var = 0;
+ donerelax = 1;
+#endif
+
+ break;
+
+ case C (COND_JUMP, COND12):
+ case C (COND_JUMP_DELAY, COND12):
+ /* A bcond won't fit, so turn it into a b!cond; bra disp; nop */
+ /* I found that a relax failure for gcc.c-torture/execute/930628-1.c
+ was due to gas incorrectly relaxing an out-of-range conditional
+ branch with delay slot. It turned:
+ bf.s L6 (slot mov.l r12,@(44,r0))
+ into:
+
+2c: 8f 01 a0 8b bf.s 32 <_main+32> (slot bra L6)
+30: 00 09 nop
+32: 10 cb mov.l r12,@(44,r0)
+ Therefore, branches with delay slots have to be handled
+ differently from ones without delay slots. */
+ {
+ unsigned char *buffer =
+ (unsigned char *) (fragP->fr_fix + fragP->fr_literal);
+ int highbyte = target_big_endian ? 0 : 1;
+ int lowbyte = target_big_endian ? 1 : 0;
+ int delay = fragP->fr_subtype == C (COND_JUMP_DELAY, COND12);
+
+ /* Toggle the true/false bit of the bcond. */
+ buffer[highbyte] ^= 0x2;
+
+ /* If this is a dalayed branch, we may not put the the bra in the
+ slot. So we change it to a non-delayed branch, like that:
+ b! cond slot_label; bra disp; slot_label: slot_insn
+ ??? We should try if swapping the conditional branch and
+ its delay-slot insn already makes the branch reach. */
+
+ /* Build a relocation to six / four bytes farther on. */
+ subseg_change (seg, 0);
+ fix_new (fragP, fragP->fr_fix, 2,
+#ifdef BFD_ASSEMBLER
+ section_symbol (seg),
+#else
+ seg_info (seg)->dot,
+#endif
+ fragP->fr_address + fragP->fr_fix + (delay ? 4 : 6),
+ 1, BFD_RELOC_SH_PCDISP8BY2);
+
+ /* Set up a jump instruction. */
+ buffer[highbyte + 2] = 0xa0;
+ buffer[lowbyte + 2] = 0;
+ fix_new (fragP, fragP->fr_fix + 2, 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_SH_PCDISP12BY2);
+
+ if (delay)
+ {
+ buffer[highbyte] &= ~0x4; /* Removes delay slot from branch. */
+ fragP->fr_fix += 4;
+ }
+ else
+ {
+ /* Fill in a NOP instruction. */
+ buffer[highbyte + 4] = 0x0;
+ buffer[lowbyte + 4] = 0x9;
+
+ fragP->fr_fix += 6;
+ }
+ fragP->fr_var = 0;
+ donerelax = 1;
+ }
+ break;
+
+ case C (COND_JUMP, COND32):
+ case C (COND_JUMP_DELAY, COND32):
+ case C (COND_JUMP, UNDEF_WORD_DISP):
+ case C (COND_JUMP_DELAY, UNDEF_WORD_DISP):
+ if (fragP->fr_symbol == NULL)
+ as_bad (_("at 0x%lx, displacement overflows 8-bit field"),
+ (unsigned long) fragP->fr_address);
+ else if (S_IS_DEFINED (fragP->fr_symbol))
+ as_bad (_("at 0x%lx, displacement to defined symbol %s overflows 8-bit field "),
+ (unsigned long) fragP->fr_address,
+ S_GET_NAME (fragP->fr_symbol));
+ else
+ as_bad (_("at 0x%lx, displacement to undefined symbol %s overflows 8-bit field "),
+ (unsigned long) fragP->fr_address,
+ S_GET_NAME (fragP->fr_symbol));
+
+#if 0 /* This code works, but generates poor code, and the compiler
+ should never produce a sequence that requires it to be used. */
+
+ /* A bcond won't fit and it won't go into a 12 bit
+ displacement either, the code sequence looks like:
+ b!cond foop
+ mov.w @(n, PC), r14
+ jmp @r14
+ nop
+ .long where
+ foop:
+ */
+
+ buffer[0] ^= 0x2; /* Toggle T/F bit */
+#define JREG 14
+ buffer[1] = 5; /* branch over mov, jump, nop and ptr */
+ buffer[2] = 0xd0 | JREG; /* Build mov insn */
+ buffer[3] = 0x2;
+ buffer[4] = 0x40 | JREG; /* Build jmp @JREG */
+ buffer[5] = 0x0b;
+ buffer[6] = 0x20; /* build nop */
+ buffer[7] = 0x0b;
+ buffer[8] = 0; /* space for 32 bit jump disp */
+ buffer[9] = 0;
+ buffer[10] = 0;
+ buffer[11] = 0;
+ buffer[12] = 0;
+ buffer[13] = 0;
+ /* Make reloc for the long disp */
+ fix_new (fragP,
+ fragP->fr_fix + 8,
+ 4,
+ fragP->fr_symbol,
+ fragP->fr_offset,
+ 0,
+ BFD_RELOC_32);
+ fragP->fr_fix += COND32_LENGTH;
+ fragP->fr_var = 0;
+ donerelax = 1;
+#endif
+
+ break;
+
+ default:
+ abort ();
+ }
+
+ if (donerelax && !sh_relax)
+ as_warn_where (fragP->fr_file, fragP->fr_line,
+ _("overflow in branch to %s; converted into longer instruction sequence"),
+ (fragP->fr_symbol != NULL
+ ? S_GET_NAME (fragP->fr_symbol)
+ : ""));
+}
+
+valueT
+DEFUN (md_section_align, (seg, size),
+ segT seg AND
+ valueT size)
+{
+#ifdef BFD_ASSEMBLER
+#ifdef OBJ_ELF
+ return size;
+#else /* ! OBJ_ELF */
+ return ((size + (1 << bfd_get_section_alignment (stdoutput, seg)) - 1)
+ & (-1 << bfd_get_section_alignment (stdoutput, seg)));
+#endif /* ! OBJ_ELF */
+#else /* ! BFD_ASSEMBLER */
+ return ((size + (1 << section_alignment[(int) seg]) - 1)
+ & (-1 << section_alignment[(int) seg]));
+#endif /* ! BFD_ASSEMBLER */
+}
+
+/* This static variable is set by s_uacons to tell sh_cons_align that
+ the expession does not need to be aligned. */
+
+static int sh_no_align_cons = 0;
+
+/* This handles the unaligned space allocation pseudo-ops, such as
+ .uaword. .uaword is just like .word, but the value does not need
+ to be aligned. */
+
+static void
+s_uacons (bytes)
+ int bytes;
+{
+ /* Tell sh_cons_align not to align this value. */
+ sh_no_align_cons = 1;
+ cons (bytes);
+}
+
+/* If a .word, et. al., pseud-op is seen, warn if the value is not
+ aligned correctly. Note that this can cause warnings to be issued
+ when assembling initialized structured which were declared with the
+ packed attribute. FIXME: Perhaps we should require an option to
+ enable this warning? */
+
+void
+sh_cons_align (nbytes)
+ int nbytes;
+{
+ int nalign;
+ char *p;
+
+ if (sh_no_align_cons)
+ {
+ /* This is an unaligned pseudo-op. */
+ sh_no_align_cons = 0;
+ return;
+ }
+
+ nalign = 0;
+ while ((nbytes & 1) == 0)
+ {
+ ++nalign;
+ nbytes >>= 1;
+ }
+
+ if (nalign == 0)
+ return;
+
+ if (now_seg == absolute_section)
+ {
+ if ((abs_section_offset & ((1 << nalign) - 1)) != 0)
+ as_warn (_("misaligned data"));
+ return;
+ }
+
+ p = frag_var (rs_align_code, 1, 1, (relax_substateT) 0,
+ (symbolS *) NULL, (offsetT) nalign, (char *) NULL);
+
+ record_alignment (now_seg, nalign);
+}
+
+/* When relaxing, we need to output a reloc for any .align directive
+ that requests alignment to a four byte boundary or larger. This is
+ also where we check for misaligned data. */
+
+void
+sh_handle_align (frag)
+ fragS *frag;
+{
+ if (sh_relax
+ && frag->fr_type == rs_align
+ && frag->fr_address + frag->fr_fix > 0
+ && frag->fr_offset > 1
+ && now_seg != bss_section)
+ fix_new (frag, frag->fr_fix, 2, &abs_symbol, frag->fr_offset, 0,
+ BFD_RELOC_SH_ALIGN);
+
+ if (frag->fr_type == rs_align_code
+ && frag->fr_next->fr_address - frag->fr_address - frag->fr_fix != 0)
+ as_warn_where (frag->fr_file, frag->fr_line, _("misaligned data"));
+}
+
+/* This macro decides whether a particular reloc is an entry in a
+ switch table. It is used when relaxing, because the linker needs
+ to know about all such entries so that it can adjust them if
+ necessary. */
+
+#ifdef BFD_ASSEMBLER
+#define SWITCH_TABLE_CONS(fix) (0)
+#else
+#define SWITCH_TABLE_CONS(fix) \
+ ((fix)->fx_r_type == 0 \
+ && ((fix)->fx_size == 2 \
+ || (fix)->fx_size == 1 \
+ || (fix)->fx_size == 4))
+#endif
+
+#define SWITCH_TABLE(fix) \
+ ((fix)->fx_addsy != NULL \
+ && (fix)->fx_subsy != NULL \
+ && S_GET_SEGMENT ((fix)->fx_addsy) == text_section \
+ && S_GET_SEGMENT ((fix)->fx_subsy) == text_section \
+ && ((fix)->fx_r_type == BFD_RELOC_32 \
+ || (fix)->fx_r_type == BFD_RELOC_16 \
+ || (fix)->fx_r_type == BFD_RELOC_8 \
+ || SWITCH_TABLE_CONS (fix)))
+
+/* See whether we need to force a relocation into the output file.
+ This is used to force out switch and PC relative relocations when
+ relaxing. */
+
+int
+sh_force_relocation (fix)
+ fixS *fix;
+{
+
+ if (fix->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fix->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 1;
+
+ if (! sh_relax)
+ return 0;
+
+ return (fix->fx_pcrel
+ || SWITCH_TABLE (fix)
+ || fix->fx_r_type == BFD_RELOC_SH_COUNT
+ || fix->fx_r_type == BFD_RELOC_SH_ALIGN
+ || fix->fx_r_type == BFD_RELOC_SH_CODE
+ || fix->fx_r_type == BFD_RELOC_SH_DATA
+ || fix->fx_r_type == BFD_RELOC_SH_LABEL);
+}
+
+#ifdef OBJ_ELF
+boolean
+sh_fix_adjustable (fixP)
+ fixS *fixP;
+{
+
+ if (fixP->fx_addsy == NULL)
+ return 1;
+
+ /* We need the symbol name for the VTABLE entries */
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ return 1;
+}
+#endif
+
+/* Apply a fixup to the object file. */
+
+#ifdef BFD_ASSEMBLER
+int
+md_apply_fix (fixP, valp)
+ fixS *fixP;
+ valueT *valp;
+#else
+void
+md_apply_fix (fixP, val)
+ fixS *fixP;
+ long val;
+#endif
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ int lowbyte = target_big_endian ? 1 : 0;
+ int highbyte = target_big_endian ? 0 : 1;
+#ifdef BFD_ASSEMBLER
+ long val = *valp;
+#endif
+ long max, min;
+ int shift;
+
+#ifdef BFD_ASSEMBLER
+ /* adjust_reloc_syms won't convert a reloc against a weak symbol
+ into a reloc against a section, but bfd_install_relocation will
+ screw up if the symbol is defined, so we have to adjust val here
+ to avoid the screw up later. */
+ if (fixP->fx_addsy != NULL
+ && S_IS_WEAK (fixP->fx_addsy))
+ val -= S_GET_VALUE (fixP->fx_addsy);
+#endif
+
+#ifndef BFD_ASSEMBLER
+ if (fixP->fx_r_type == 0)
+ {
+ if (fixP->fx_size == 2)
+ fixP->fx_r_type = BFD_RELOC_16;
+ else if (fixP->fx_size == 4)
+ fixP->fx_r_type = BFD_RELOC_32;
+ else if (fixP->fx_size == 1)
+ fixP->fx_r_type = BFD_RELOC_8;
+ else
+ abort ();
+ }
+#endif
+
+ max = min = 0;
+ shift = 0;
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_SH_IMM4:
+ max = 0xf;
+ *buf = (*buf & 0xf0) | (val & 0xf);
+ break;
+
+ case BFD_RELOC_SH_IMM4BY2:
+ max = 0xf;
+ shift = 1;
+ *buf = (*buf & 0xf0) | ((val >> 1) & 0xf);
+ break;
+
+ case BFD_RELOC_SH_IMM4BY4:
+ max = 0xf;
+ shift = 2;
+ *buf = (*buf & 0xf0) | ((val >> 2) & 0xf);
+ break;
+
+ case BFD_RELOC_SH_IMM8BY2:
+ max = 0xff;
+ shift = 1;
+ *buf = val >> 1;
+ break;
+
+ case BFD_RELOC_SH_IMM8BY4:
+ max = 0xff;
+ shift = 2;
+ *buf = val >> 2;
+ break;
+
+ case BFD_RELOC_8:
+ case BFD_RELOC_SH_IMM8:
+ /* Sometimes the 8 bit value is sign extended (e.g., add) and
+ sometimes it is not (e.g., and). We permit any 8 bit value.
+ Note that adding further restrictions may invalidate
+ reasonable looking assembly code, such as ``and -0x1,r0''. */
+ max = 0xff;
+ min = - 0xff;
+ *buf++ = val;
+ break;
+
+ case BFD_RELOC_SH_PCRELIMM8BY4:
+ /* The lower two bits of the PC are cleared before the
+ displacement is added in. We can assume that the destination
+ is on a 4 byte bounday. If this instruction is also on a 4
+ byte boundary, then we want
+ (target - here) / 4
+ and target - here is a multiple of 4.
+ Otherwise, we are on a 2 byte boundary, and we want
+ (target - (here - 2)) / 4
+ and target - here is not a multiple of 4. Computing
+ (target - (here - 2)) / 4 == (target - here + 2) / 4
+ works for both cases, since in the first case the addition of
+ 2 will be removed by the division. target - here is in the
+ variable val. */
+ val = (val + 2) / 4;
+ if (val & ~0xff)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far"));
+ buf[lowbyte] = val;
+ break;
+
+ case BFD_RELOC_SH_PCRELIMM8BY2:
+ val /= 2;
+ if (val & ~0xff)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far"));
+ buf[lowbyte] = val;
+ break;
+
+ case BFD_RELOC_SH_PCDISP8BY2:
+ val /= 2;
+ if (val < -0x80 || val > 0x7f)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far"));
+ buf[lowbyte] = val;
+ break;
+
+ case BFD_RELOC_SH_PCDISP12BY2:
+ val /= 2;
+ if (val < -0x800 || val >= 0x7ff)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far"));
+ buf[lowbyte] = val & 0xff;
+ buf[highbyte] |= (val >> 8) & 0xf;
+ break;
+
+ case BFD_RELOC_32:
+ if (! target_big_endian)
+ {
+ *buf++ = val >> 0;
+ *buf++ = val >> 8;
+ *buf++ = val >> 16;
+ *buf++ = val >> 24;
+ }
+ else
+ {
+ *buf++ = val >> 24;
+ *buf++ = val >> 16;
+ *buf++ = val >> 8;
+ *buf++ = val >> 0;
+ }
+ break;
+
+ case BFD_RELOC_16:
+ if (! target_big_endian)
+ {
+ *buf++ = val >> 0;
+ *buf++ = val >> 8;
+ }
+ else
+ {
+ *buf++ = val >> 8;
+ *buf++ = val >> 0;
+ }
+ break;
+
+ case BFD_RELOC_SH_USES:
+ /* Pass the value into sh_coff_reloc_mangle. */
+ fixP->fx_addnumber = val;
+ break;
+
+ case BFD_RELOC_SH_COUNT:
+ case BFD_RELOC_SH_ALIGN:
+ case BFD_RELOC_SH_CODE:
+ case BFD_RELOC_SH_DATA:
+ case BFD_RELOC_SH_LABEL:
+ /* Nothing to do here. */
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ return;
+
+ default:
+ abort ();
+ }
+
+ if (shift != 0)
+ {
+ if ((val & ((1 << shift) - 1)) != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("misaligned offset"));
+ if (val >= 0)
+ val >>= shift;
+ else
+ val = ((val >> shift)
+ | ((long) -1 & ~ ((long) -1 >> shift)));
+ }
+ if (max != 0 && (val < min || val > max))
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("offset out of range"));
+
+#ifdef BFD_ASSEMBLER
+ return 0;
+#endif
+}
+
+/* Called just before address relaxation. Return the length
+ by which a fragment must grow to reach it's destination. */
+
+int
+md_estimate_size_before_relax (fragP, segment_type)
+ register fragS *fragP;
+ register segT segment_type;
+{
+ switch (fragP->fr_subtype)
+ {
+ case C (UNCOND_JUMP, UNDEF_DISP):
+ /* used to be a branch to somewhere which was unknown */
+ if (!fragP->fr_symbol)
+ {
+ fragP->fr_subtype = C (UNCOND_JUMP, UNCOND12);
+ fragP->fr_var = md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length;
+ }
+ else if (S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
+ {
+ fragP->fr_subtype = C (UNCOND_JUMP, UNCOND12);
+ fragP->fr_var = md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length;
+ }
+ else
+ {
+ fragP->fr_subtype = C (UNCOND_JUMP, UNDEF_WORD_DISP);
+ fragP->fr_var = md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_length;
+ return md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_length;
+ }
+ break;
+
+ default:
+ abort ();
+ case C (COND_JUMP, UNDEF_DISP):
+ case C (COND_JUMP_DELAY, UNDEF_DISP):
+ /* used to be a branch to somewhere which was unknown */
+ if (fragP->fr_symbol
+ && S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
+ {
+ int what = GET_WHAT (fragP->fr_subtype);
+ /* Got a symbol and it's defined in this segment, become byte
+ sized - maybe it will fix up */
+ fragP->fr_subtype = C (what, COND8);
+ fragP->fr_var = md_relax_table[C (what, COND8)].rlx_length;
+ }
+ else if (fragP->fr_symbol)
+ {
+ int what = GET_WHAT (fragP->fr_subtype);
+ /* Its got a segment, but its not ours, so it will always be long */
+ fragP->fr_subtype = C (what, UNDEF_WORD_DISP);
+ fragP->fr_var = md_relax_table[C (what, COND32)].rlx_length;
+ return md_relax_table[C (what, COND32)].rlx_length;
+ }
+ else
+ {
+ int what = GET_WHAT (fragP->fr_subtype);
+ /* We know the abs value */
+ fragP->fr_subtype = C (what, COND8);
+ fragP->fr_var = md_relax_table[C (what, COND8)].rlx_length;
+ }
+
+ break;
+ }
+ return fragP->fr_var;
+}
+
+/* Put number into target byte order */
+
+void
+md_number_to_chars (ptr, use, nbytes)
+ char *ptr;
+ valueT use;
+ int nbytes;
+{
+ if (! target_big_endian)
+ number_to_chars_littleendian (ptr, use, nbytes);
+ else
+ number_to_chars_bigendian (ptr, use, nbytes);
+}
+
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address + 2;
+}
+
+#ifdef OBJ_COFF
+
+int
+tc_coff_sizemachdep (frag)
+ fragS *frag;
+{
+ return md_relax_table[frag->fr_subtype].rlx_length;
+}
+
+#endif /* OBJ_COFF */
+
+/* When we align the .text section, insert the correct NOP pattern. */
+
+int
+sh_do_align (n, fill, len, max)
+ int n;
+ const char *fill;
+ int len;
+ int max;
+{
+ if (fill == NULL
+#ifdef BFD_ASSEMBLER
+ && (now_seg->flags & SEC_CODE) != 0
+#else
+ && now_seg != data_section
+ && now_seg != bss_section
+#endif
+ && n > 1)
+ {
+ static const unsigned char big_nop_pattern[] = { 0x00, 0x09 };
+ static const unsigned char little_nop_pattern[] = { 0x09, 0x00 };
+
+ /* First align to a 2 byte boundary, in case there is an odd
+ .byte. */
+ frag_align (1, 0, 0);
+ if (target_big_endian)
+ frag_align_pattern (n, big_nop_pattern, sizeof big_nop_pattern, max);
+ else
+ frag_align_pattern (n, little_nop_pattern, sizeof little_nop_pattern,
+ max);
+ return 1;
+ }
+
+ return 0;
+}
+
+#ifndef BFD_ASSEMBLER
+#ifdef OBJ_COFF
+
+/* Map BFD relocs to SH COFF relocs. */
+
+struct reloc_map
+{
+ bfd_reloc_code_real_type bfd_reloc;
+ int sh_reloc;
+};
+
+static const struct reloc_map coff_reloc_map[] =
+{
+ { BFD_RELOC_32, R_SH_IMM32 },
+ { BFD_RELOC_16, R_SH_IMM16 },
+ { BFD_RELOC_8, R_SH_IMM8 },
+ { BFD_RELOC_SH_PCDISP8BY2, R_SH_PCDISP8BY2 },
+ { BFD_RELOC_SH_PCDISP12BY2, R_SH_PCDISP },
+ { BFD_RELOC_SH_IMM4, R_SH_IMM4 },
+ { BFD_RELOC_SH_IMM4BY2, R_SH_IMM4BY2 },
+ { BFD_RELOC_SH_IMM4BY4, R_SH_IMM4BY4 },
+ { BFD_RELOC_SH_IMM8, R_SH_IMM8 },
+ { BFD_RELOC_SH_IMM8BY2, R_SH_IMM8BY2 },
+ { BFD_RELOC_SH_IMM8BY4, R_SH_IMM8BY4 },
+ { BFD_RELOC_SH_PCRELIMM8BY2, R_SH_PCRELIMM8BY2 },
+ { BFD_RELOC_SH_PCRELIMM8BY4, R_SH_PCRELIMM8BY4 },
+ { BFD_RELOC_8_PCREL, R_SH_SWITCH8 },
+ { BFD_RELOC_SH_SWITCH16, R_SH_SWITCH16 },
+ { BFD_RELOC_SH_SWITCH32, R_SH_SWITCH32 },
+ { BFD_RELOC_SH_USES, R_SH_USES },
+ { BFD_RELOC_SH_COUNT, R_SH_COUNT },
+ { BFD_RELOC_SH_ALIGN, R_SH_ALIGN },
+ { BFD_RELOC_SH_CODE, R_SH_CODE },
+ { BFD_RELOC_SH_DATA, R_SH_DATA },
+ { BFD_RELOC_SH_LABEL, R_SH_LABEL },
+ { BFD_RELOC_UNUSED, 0 }
+};
+
+/* Adjust a reloc for the SH. This is similar to the generic code,
+ but does some minor tweaking. */
+
+void
+sh_coff_reloc_mangle (seg, fix, intr, paddr)
+ segment_info_type *seg;
+ fixS *fix;
+ struct internal_reloc *intr;
+ unsigned int paddr;
+{
+ symbolS *symbol_ptr = fix->fx_addsy;
+ symbolS *dot;
+
+ intr->r_vaddr = paddr + fix->fx_frag->fr_address + fix->fx_where;
+
+ if (! SWITCH_TABLE (fix))
+ {
+ const struct reloc_map *rm;
+
+ for (rm = coff_reloc_map; rm->bfd_reloc != BFD_RELOC_UNUSED; rm++)
+ if (rm->bfd_reloc == (bfd_reloc_code_real_type) fix->fx_r_type)
+ break;
+ if (rm->bfd_reloc == BFD_RELOC_UNUSED)
+ as_bad_where (fix->fx_file, fix->fx_line,
+ _("Can not represent %s relocation in this object file format"),
+ bfd_get_reloc_code_name (fix->fx_r_type));
+ intr->r_type = rm->sh_reloc;
+ intr->r_offset = 0;
+ }
+ else
+ {
+ know (sh_relax);
+
+ if (fix->fx_r_type == BFD_RELOC_16)
+ intr->r_type = R_SH_SWITCH16;
+ else if (fix->fx_r_type == BFD_RELOC_8)
+ intr->r_type = R_SH_SWITCH8;
+ else if (fix->fx_r_type == BFD_RELOC_32)
+ intr->r_type = R_SH_SWITCH32;
+ else
+ abort ();
+
+ /* For a switch reloc, we set r_offset to the difference between
+ the reloc address and the subtrahend. When the linker is
+ doing relaxing, it can use the determine the starting and
+ ending points of the switch difference expression. */
+ intr->r_offset = intr->r_vaddr - S_GET_VALUE (fix->fx_subsy);
+ }
+
+ /* PC relative relocs are always against the current section. */
+ if (symbol_ptr == NULL)
+ {
+ switch (fix->fx_r_type)
+ {
+ case BFD_RELOC_SH_PCRELIMM8BY2:
+ case BFD_RELOC_SH_PCRELIMM8BY4:
+ case BFD_RELOC_SH_PCDISP8BY2:
+ case BFD_RELOC_SH_PCDISP12BY2:
+ case BFD_RELOC_SH_USES:
+ symbol_ptr = seg->dot;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (fix->fx_r_type == BFD_RELOC_SH_USES)
+ {
+ /* We can't store the offset in the object file, since this
+ reloc does not take up any space, so we store it in r_offset.
+ The fx_addnumber field was set in md_apply_fix. */
+ intr->r_offset = fix->fx_addnumber;
+ }
+ else if (fix->fx_r_type == BFD_RELOC_SH_COUNT)
+ {
+ /* We can't store the count in the object file, since this reloc
+ does not take up any space, so we store it in r_offset. The
+ fx_offset field was set when the fixup was created in
+ sh_coff_frob_file. */
+ intr->r_offset = fix->fx_offset;
+ /* This reloc is always absolute. */
+ symbol_ptr = NULL;
+ }
+ else if (fix->fx_r_type == BFD_RELOC_SH_ALIGN)
+ {
+ /* Store the alignment in the r_offset field. */
+ intr->r_offset = fix->fx_offset;
+ /* This reloc is always absolute. */
+ symbol_ptr = NULL;
+ }
+ else if (fix->fx_r_type == BFD_RELOC_SH_CODE
+ || fix->fx_r_type == BFD_RELOC_SH_DATA
+ || fix->fx_r_type == BFD_RELOC_SH_LABEL)
+ {
+ /* These relocs are always absolute. */
+ symbol_ptr = NULL;
+ }
+
+ /* Turn the segment of the symbol into an offset. */
+ if (symbol_ptr != NULL)
+ {
+ dot = segment_info[S_GET_SEGMENT (symbol_ptr)].dot;
+ if (dot != NULL)
+ intr->r_symndx = dot->sy_number;
+ else
+ intr->r_symndx = symbol_ptr->sy_number;
+ }
+ else
+ intr->r_symndx = -1;
+}
+
+#endif /* OBJ_COFF */
+#endif /* ! BFD_ASSEMBLER */
+
+#ifdef BFD_ASSEMBLER
+
+/* Create a reloc. */
+
+arelent *
+tc_gen_reloc (section, fixp)
+ asection *section;
+ fixS *fixp;
+{
+ arelent *rel;
+ bfd_reloc_code_real_type r_type;
+
+ rel = (arelent *) xmalloc (sizeof (arelent));
+ rel->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+ rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ r_type = fixp->fx_r_type;
+
+ if (SWITCH_TABLE (fixp))
+ {
+ rel->addend = rel->address - S_GET_VALUE (fixp->fx_subsy);
+ if (r_type == BFD_RELOC_16)
+ r_type = BFD_RELOC_SH_SWITCH16;
+ else if (r_type == BFD_RELOC_8)
+ r_type = BFD_RELOC_8_PCREL;
+ else if (r_type == BFD_RELOC_32)
+ r_type = BFD_RELOC_SH_SWITCH32;
+ else
+ abort ();
+ }
+ else if (r_type == BFD_RELOC_SH_USES)
+ rel->addend = fixp->fx_addnumber;
+ else if (r_type == BFD_RELOC_SH_COUNT)
+ rel->addend = fixp->fx_offset;
+ else if (r_type == BFD_RELOC_SH_ALIGN)
+ rel->addend = fixp->fx_offset;
+ else if (r_type == BFD_RELOC_VTABLE_INHERIT
+ || r_type == BFD_RELOC_VTABLE_ENTRY)
+ rel->addend = fixp->fx_offset;
+ else if (fixp->fx_pcrel)
+ rel->addend = fixp->fx_addnumber;
+ else
+ rel->addend = 0;
+
+ rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
+ if (rel->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Cannot represent relocation type %s"),
+ bfd_get_reloc_code_name (r_type));
+ /* Set howto to a garbage value so that we can keep going. */
+ rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
+ assert (rel->howto != NULL);
+ }
+
+ return rel;
+}
+
+#endif /* BFD_ASSEMBLER */
diff --git a/gas/config/tc-sh.h b/gas/config/tc-sh.h
new file mode 100644
index 0000000..cc02eab
--- /dev/null
+++ b/gas/config/tc-sh.h
@@ -0,0 +1,152 @@
+/* This file is tc-sh.h
+ Copyright (C) 1993, 94, 95, 96, 97, 1998 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#define TC_SH
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define TARGET_ARCH bfd_arch_sh
+
+#if ANSI_PROTOTYPES
+struct segment_info_struct;
+struct internal_reloc;
+#endif
+
+/* Whether in little endian mode. */
+extern int shl;
+
+/* Whether -relax was used. */
+extern int sh_relax;
+
+/* Whether -small was used. */
+extern int sh_small;
+
+/* Don't try to break words. */
+#define WORKING_DOT_WORD
+
+/* We require .long, et. al., to be aligned correctly. */
+#define md_cons_align(nbytes) sh_cons_align (nbytes)
+extern void sh_cons_align PARAMS ((int));
+
+/* When relaxing, we need to generate relocations for alignment
+ directives. */
+#define HANDLE_ALIGN(frag) sh_handle_align (frag)
+extern void sh_handle_align PARAMS ((fragS *));
+
+/* We need to force out some relocations when relaxing. */
+#define TC_FORCE_RELOCATION(fix) sh_force_relocation (fix)
+extern int sh_force_relocation ();
+
+#ifdef OBJ_ELF
+#define obj_fix_adjustable(fixP) sh_fix_adjustable(fixP)
+#endif
+
+#define IGNORE_NONSTANDARD_ESCAPES
+
+#define LISTING_HEADER (shl ? "Hitachi Super-H GAS Little Endian" : "Hitachi Super-H GAS Big Endian")
+
+#define md_operand(x)
+
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+/* We use a special alignment function to insert the correct nop
+ pattern. */
+extern int sh_do_align PARAMS ((int, const char *, int, int));
+#define md_do_align(n,fill,len,max,l) if (sh_do_align (n,fill,len,max)) goto l
+
+/* We record, for each section, whether we have most recently output a
+ CODE reloc or a DATA reloc. */
+struct sh_segment_info_type
+{
+ int in_code : 1;
+};
+#define TC_SEGMENT_INFO_TYPE struct sh_segment_info_type
+
+/* We call a routine to emit a reloc for a label, so that the linker
+ can align loads and stores without crossing a label. */
+extern void sh_frob_label PARAMS ((void));
+#define tc_frob_label(sym) sh_frob_label ()
+
+/* We call a routine to flush pending output in order to output a DATA
+ reloc when required. */
+extern void sh_flush_pending_output PARAMS ((void));
+#define md_flush_pending_output() sh_flush_pending_output ()
+
+#ifdef BFD_ASSEMBLER
+#define tc_frob_file_before_adjust sh_frob_file
+#else
+#define tc_frob_file sh_frob_file
+#endif
+extern void sh_frob_file PARAMS ((void));
+
+#ifdef OBJ_COFF
+/* COFF specific definitions. */
+
+#define DO_NOT_STRIP 0
+
+/* This macro translates between an internal fix and an coff reloc type */
+#define TC_COFF_FIX2RTYPE(fix) ((fix)->fx_r_type)
+
+#define BFD_ARCH TARGET_ARCH
+
+#define COFF_MAGIC (shl ? SH_ARCH_MAGIC_LITTLE : SH_ARCH_MAGIC_BIG)
+
+/* We need to write out relocs which have not been completed. */
+#define TC_COUNT_RELOC(fix) ((fix)->fx_addsy != NULL)
+
+#define TC_RELOC_MANGLE(seg, fix, int, paddr) \
+ sh_coff_reloc_mangle ((seg), (fix), (int), (paddr))
+extern void sh_coff_reloc_mangle
+ PARAMS ((struct segment_info_struct *, struct fix *,
+ struct internal_reloc *, unsigned int));
+
+#define tc_coff_symbol_emit_hook(a) ; /* not used */
+
+#define NEED_FX_R_TYPE 1
+
+#define TC_KEEP_FX_OFFSET 1
+
+#define TC_COFF_SIZEMACHDEP(frag) tc_coff_sizemachdep(frag)
+extern int tc_coff_sizemachdep PARAMS ((fragS *));
+
+/* We align most sections to a 16 byte boundary. */
+#define SUB_SEGMENT_ALIGN(SEG) \
+ (strncmp (obj_segment_name (SEG), ".stabstr", 8) == 0 \
+ ? 0 \
+ : ((strncmp (obj_segment_name (SEG), ".stab", 5) == 0 \
+ || strcmp (obj_segment_name (SEG), ".ctors") == 0 \
+ || strcmp (obj_segment_name (SEG), ".dtors") == 0) \
+ ? 2 \
+ : (sh_small ? 2 : 4)))
+
+#endif /* OBJ_COFF */
+
+#ifdef OBJ_ELF
+/* ELF specific definitions. */
+
+/* Whether or not the target is big endian */
+extern int target_big_endian;
+
+#define TARGET_FORMAT (shl ? "elf32-shl" : "elf32-sh")
+
+#endif /* OBJ_ELF */
+
+/* end of tc-sh.h */
diff --git a/gas/config/tc-sparc.c b/gas/config/tc-sparc.c
new file mode 100644
index 0000000..1518a8a
--- /dev/null
+++ b/gas/config/tc-sparc.c
@@ -0,0 +1,3551 @@
+/* tc-sparc.c -- Assemble for the SPARC
+ Copyright (C) 1989, 90-96, 97, 1998 Free Software Foundation, Inc.
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with GAS; see the file COPYING. If not, write
+ to the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "as.h"
+#include "subsegs.h"
+
+#include "opcode/sparc.h"
+
+#ifdef OBJ_ELF
+#include "elf/sparc.h"
+#endif
+
+static struct sparc_arch *lookup_arch PARAMS ((char *));
+static void init_default_arch PARAMS ((void));
+static void sparc_ip PARAMS ((char *, const struct sparc_opcode **));
+static int in_signed_range PARAMS ((bfd_signed_vma, bfd_signed_vma));
+static int in_unsigned_range PARAMS ((bfd_vma, bfd_vma));
+static int in_bitfield_range PARAMS ((bfd_signed_vma, bfd_signed_vma));
+static int sparc_ffs PARAMS ((unsigned int));
+static bfd_vma BSR PARAMS ((bfd_vma, int));
+static int cmp_reg_entry PARAMS ((const PTR, const PTR));
+static int parse_keyword_arg PARAMS ((int (*) (const char *), char **, int *));
+static int parse_const_expr_arg PARAMS ((char **, int *));
+static int get_expression PARAMS ((char *str));
+
+/* Default architecture. */
+/* ??? The default value should be V8, but sparclite support was added
+ by making it the default. GCC now passes -Asparclite, so maybe sometime in
+ the future we can set this to V8. */
+#ifndef DEFAULT_ARCH
+#define DEFAULT_ARCH "sparclite"
+#endif
+static char *default_arch = DEFAULT_ARCH;
+
+/* Non-zero if the initial values of `max_architecture' and `sparc_arch_size'
+ have been set. */
+static int default_init_p;
+
+/* Current architecture. We don't bump up unless necessary. */
+static enum sparc_opcode_arch_val current_architecture = SPARC_OPCODE_ARCH_V6;
+
+/* The maximum architecture level we can bump up to.
+ In a 32 bit environment, don't allow bumping up to v9 by default.
+ The native assembler works this way. The user is required to pass
+ an explicit argument before we'll create v9 object files. However, if
+ we don't see any v9 insns, a v8plus object file is not created. */
+static enum sparc_opcode_arch_val max_architecture;
+
+/* Either 32 or 64, selects file format. */
+static int sparc_arch_size;
+/* Initial (default) value, recorded separately in case a user option
+ changes the value before md_show_usage is called. */
+static int default_arch_size;
+
+#ifdef OBJ_ELF
+/* The currently selected v9 memory model. Currently only used for
+ ELF. */
+static enum { MM_TSO, MM_PSO, MM_RMO } sparc_memory_model = MM_RMO;
+#endif
+
+static int architecture_requested;
+static int warn_on_bump;
+
+/* If warn_on_bump and the needed architecture is higher than this
+ architecture, issue a warning. */
+static enum sparc_opcode_arch_val warn_after_architecture;
+
+/* Non-zero if we are generating PIC code. */
+int sparc_pic_code;
+
+/* Non-zero if we should give an error when misaligned data is seen. */
+static int enforce_aligned_data;
+
+extern int target_big_endian;
+
+static int target_little_endian_data;
+
+/* V9 and 86x have big and little endian data, but instructions are always big
+ endian. The sparclet has bi-endian support but both data and insns have
+ the same endianness. Global `target_big_endian' is used for data.
+ The following macro is used for instructions. */
+#ifndef INSN_BIG_ENDIAN
+#define INSN_BIG_ENDIAN (target_big_endian \
+ || default_arch_type == sparc86x \
+ || SPARC_OPCODE_ARCH_V9_P (max_architecture))
+#endif
+
+/* handle of the OPCODE hash table */
+static struct hash_control *op_hash;
+
+static int log2 PARAMS ((int));
+static void s_data1 PARAMS ((void));
+static void s_seg PARAMS ((int));
+static void s_proc PARAMS ((int));
+static void s_reserve PARAMS ((int));
+static void s_common PARAMS ((int));
+static void s_empty PARAMS ((int));
+static void s_uacons PARAMS ((int));
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"align", s_align_bytes, 0}, /* Defaulting is invalid (0) */
+ {"common", s_common, 0},
+ {"empty", s_empty, 0},
+ {"global", s_globl, 0},
+ {"half", cons, 2},
+ {"optim", s_ignore, 0},
+ {"proc", s_proc, 0},
+ {"reserve", s_reserve, 0},
+ {"seg", s_seg, 0},
+ {"skip", s_space, 0},
+ {"word", cons, 4},
+ {"xword", cons, 8},
+ {"uahalf", s_uacons, 2},
+ {"uaword", s_uacons, 4},
+ {"uaxword", s_uacons, 8},
+#ifdef OBJ_ELF
+ /* these are specific to sparc/svr4 */
+ {"pushsection", obj_elf_section, 0},
+ {"popsection", obj_elf_previous, 0},
+ {"2byte", s_uacons, 2},
+ {"4byte", s_uacons, 4},
+ {"8byte", s_uacons, 8},
+#endif
+ {NULL, 0, 0},
+};
+
+const int md_reloc_size = 12; /* Size of relocation record */
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful */
+const char comment_chars[] = "!"; /* JF removed '|' from comment_chars */
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that comments started like this one will always
+ work if '/' isn't otherwise defined. */
+const char line_comment_chars[] = "#";
+
+const char line_separator_chars[] = "";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c. Ideally it shouldn't have to know about it at all,
+ but nothing is ideal around here. */
+
+static unsigned char octal[256];
+#define isoctal(c) octal[(unsigned char) (c)]
+static unsigned char toHex[256];
+
+struct sparc_it
+ {
+ char *error;
+ unsigned long opcode;
+ struct nlist *nlistp;
+ expressionS exp;
+ int pcrel;
+ bfd_reloc_code_real_type reloc;
+ };
+
+struct sparc_it the_insn, set_insn;
+
+static void output_insn
+ PARAMS ((const struct sparc_opcode *, struct sparc_it *));
+
+/* Table of arguments to -A.
+ The sparc_opcode_arch table in sparc-opc.c is insufficient and incorrect
+ for this use. That table is for opcodes only. This table is for opcodes
+ and file formats. */
+
+enum sparc_arch_types {v6, v7, v8, sparclet, sparclite, sparc86x, v8plus,
+ v8plusa, v9, v9a, v9_64};
+
+static struct sparc_arch {
+ char *name;
+ char *opcode_arch;
+ enum sparc_arch_types arch_type;
+ /* Default word size, as specified during configuration.
+ A value of zero means can't be used to specify default architecture. */
+ int default_arch_size;
+ /* Allowable arg to -A? */
+ int user_option_p;
+} sparc_arch_table[] = {
+ { "v6", "v6", v6, 0, 1 },
+ { "v7", "v7", v7, 0, 1 },
+ { "v8", "v8", v8, 32, 1 },
+ { "sparclet", "sparclet", sparclet, 32, 1 },
+ { "sparclite", "sparclite", sparclite, 32, 1 },
+ { "sparc86x", "sparclite", sparc86x, 32, 1 },
+ { "v8plus", "v9", v9, 0, 1 },
+ { "v8plusa", "v9a", v9, 0, 1 },
+ { "v9", "v9", v9, 0, 1 },
+ { "v9a", "v9a", v9, 0, 1 },
+ /* This exists to allow configure.in/Makefile.in to pass one
+ value to specify both the default machine and default word size. */
+ { "v9-64", "v9", v9, 64, 0 },
+ { NULL, NULL, v8, 0, 0 }
+};
+
+/* Variant of default_arch */
+static enum sparc_arch_types default_arch_type;
+
+static struct sparc_arch *
+lookup_arch (name)
+ char *name;
+{
+ struct sparc_arch *sa;
+
+ for (sa = &sparc_arch_table[0]; sa->name != NULL; sa++)
+ if (strcmp (sa->name, name) == 0)
+ break;
+ if (sa->name == NULL)
+ return NULL;
+ return sa;
+}
+
+/* Initialize the default opcode arch and word size from the default
+ architecture name. */
+
+static void
+init_default_arch ()
+{
+ struct sparc_arch *sa = lookup_arch (default_arch);
+
+ if (sa == NULL
+ || sa->default_arch_size == 0)
+ as_fatal (_("Invalid default architecture, broken assembler."));
+
+ max_architecture = sparc_opcode_lookup_arch (sa->opcode_arch);
+ if (max_architecture == SPARC_OPCODE_ARCH_BAD)
+ as_fatal (_("Bad opcode table, broken assembler."));
+ default_arch_size = sparc_arch_size = sa->default_arch_size;
+ default_init_p = 1;
+ default_arch_type = sa->arch_type;
+}
+
+/* Called by TARGET_FORMAT. */
+
+const char *
+sparc_target_format ()
+{
+ /* We don't get a chance to initialize anything before we're called,
+ so handle that now. */
+ if (! default_init_p)
+ init_default_arch ();
+
+#ifdef OBJ_AOUT
+#ifdef TE_NetBSD
+ return "a.out-sparc-netbsd";
+#else
+#ifdef TE_SPARCAOUT
+ if (target_big_endian)
+ return "a.out-sunos-big";
+ else if (default_arch_type == sparc86x && target_little_endian_data)
+ return "a.out-sunos-big";
+ else return "a.out-sparc-little";
+#else
+ return "a.out-sunos-big";
+#endif
+#endif
+#endif
+
+#ifdef OBJ_BOUT
+ return "b.out.big";
+#endif
+
+#ifdef OBJ_COFF
+#ifdef TE_LYNX
+ return "coff-sparc-lynx";
+#else
+ return "coff-sparc";
+#endif
+#endif
+
+#ifdef OBJ_ELF
+ return sparc_arch_size == 64 ? "elf64-sparc" : "elf32-sparc";
+#endif
+
+ abort ();
+}
+
+/*
+ * md_parse_option
+ * Invocation line includes a switch not recognized by the base assembler.
+ * See if it's a processor-specific option. These are:
+ *
+ * -bump
+ * Warn on architecture bumps. See also -A.
+ *
+ * -Av6, -Av7, -Av8, -Asparclite, -Asparclet
+ * Standard 32 bit architectures.
+ * -Av8plus, -Av8plusa
+ * Sparc64 in a 32 bit world.
+ * -Av9, -Av9a
+ * Sparc64 in either a 32 or 64 bit world (-32/-64 says which).
+ * This used to only mean 64 bits, but properly specifying it
+ * complicated gcc's ASM_SPECs, so now opcode selection is
+ * specified orthogonally to word size (except when specifying
+ * the default, but that is an internal implementation detail).
+ * -xarch=v8plus, -xarch=v8plusa
+ * Same as -Av8plus{,a}, for compatibility with Sun's assembler.
+ *
+ * Select the architecture and possibly the file format.
+ * Instructions or features not supported by the selected
+ * architecture cause fatal errors.
+ *
+ * The default is to start at v6, and bump the architecture up
+ * whenever an instruction is seen at a higher level. In 32 bit
+ * environments, v9 is not bumped up to, the user must pass
+ * -Av8plus{,a}.
+ *
+ * If -bump is specified, a warning is printing when bumping to
+ * higher levels.
+ *
+ * If an architecture is specified, all instructions must match
+ * that architecture. Any higher level instructions are flagged
+ * as errors. Note that in the 32 bit environment specifying
+ * -Av8plus does not automatically create a v8plus object file, a
+ * v9 insn must be seen.
+ *
+ * If both an architecture and -bump are specified, the
+ * architecture starts at the specified level, but bumps are
+ * warnings. Note that we can't set `current_architecture' to
+ * the requested level in this case: in the 32 bit environment,
+ * we still must avoid creating v8plus object files unless v9
+ * insns are seen.
+ *
+ * Note:
+ * Bumping between incompatible architectures is always an
+ * error. For example, from sparclite to v9.
+ */
+
+#ifdef OBJ_ELF
+CONST char *md_shortopts = "A:K:VQ:sq";
+#else
+#ifdef OBJ_AOUT
+CONST char *md_shortopts = "A:k";
+#else
+CONST char *md_shortopts = "A:";
+#endif
+#endif
+struct option md_longopts[] = {
+#define OPTION_BUMP (OPTION_MD_BASE)
+ {"bump", no_argument, NULL, OPTION_BUMP},
+#define OPTION_SPARC (OPTION_MD_BASE + 1)
+ {"sparc", no_argument, NULL, OPTION_SPARC},
+#define OPTION_XARCH (OPTION_MD_BASE + 2)
+ {"xarch", required_argument, NULL, OPTION_XARCH},
+#ifdef OBJ_ELF
+#define OPTION_32 (OPTION_MD_BASE + 3)
+ {"32", no_argument, NULL, OPTION_32},
+#define OPTION_64 (OPTION_MD_BASE + 4)
+ {"64", no_argument, NULL, OPTION_64},
+#define OPTION_TSO (OPTION_MD_BASE + 5)
+ {"TSO", no_argument, NULL, OPTION_TSO},
+#define OPTION_PSO (OPTION_MD_BASE + 6)
+ {"PSO", no_argument, NULL, OPTION_PSO},
+#define OPTION_RMO (OPTION_MD_BASE + 7)
+ {"RMO", no_argument, NULL, OPTION_RMO},
+#endif
+#ifdef SPARC_BIENDIAN
+#define OPTION_LITTLE_ENDIAN (OPTION_MD_BASE + 8)
+ {"EL", no_argument, NULL, OPTION_LITTLE_ENDIAN},
+#define OPTION_BIG_ENDIAN (OPTION_MD_BASE + 9)
+ {"EB", no_argument, NULL, OPTION_BIG_ENDIAN},
+#endif
+#define OPTION_ENFORCE_ALIGNED_DATA (OPTION_MD_BASE + 10)
+ {"enforce-aligned-data", no_argument, NULL, OPTION_ENFORCE_ALIGNED_DATA},
+#define OPTION_LITTLE_ENDIAN_DATA (OPTION_MD_BASE + 11)
+ {"little-endian-data", no_argument, NULL, OPTION_LITTLE_ENDIAN_DATA},
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ /* We don't get a chance to initialize anything before we're called,
+ so handle that now. */
+ if (! default_init_p)
+ init_default_arch ();
+
+ switch (c)
+ {
+ case OPTION_BUMP:
+ warn_on_bump = 1;
+ warn_after_architecture = SPARC_OPCODE_ARCH_V6;
+ break;
+
+ case OPTION_XARCH:
+ /* This is for compatibility with Sun's assembler. */
+ if (strcmp (arg, "v8plus") != 0
+ && strcmp (arg, "v8plusa") != 0)
+ {
+ as_bad (_("invalid architecture -xarch=%s"), arg);
+ return 0;
+ }
+
+ /* fall through */
+
+ case 'A':
+ {
+ struct sparc_arch *sa;
+ enum sparc_opcode_arch_val opcode_arch;
+
+ sa = lookup_arch (arg);
+ if (sa == NULL
+ || ! sa->user_option_p)
+ {
+ as_bad (_("invalid architecture -A%s"), arg);
+ return 0;
+ }
+
+ opcode_arch = sparc_opcode_lookup_arch (sa->opcode_arch);
+ if (opcode_arch == SPARC_OPCODE_ARCH_BAD)
+ as_fatal (_("Bad opcode table, broken assembler."));
+
+ max_architecture = opcode_arch;
+ architecture_requested = 1;
+ }
+ break;
+
+ case OPTION_SPARC:
+ /* Ignore -sparc, used by SunOS make default .s.o rule. */
+ break;
+
+ case OPTION_ENFORCE_ALIGNED_DATA:
+ enforce_aligned_data = 1;
+ break;
+
+#ifdef SPARC_BIENDIAN
+ case OPTION_LITTLE_ENDIAN:
+ target_big_endian = 0;
+ if (default_arch_type != sparclet)
+ as_fatal ("This target does not support -EL");
+ break;
+ case OPTION_LITTLE_ENDIAN_DATA:
+ target_little_endian_data = 1;
+ target_big_endian = 0;
+ if (default_arch_type != sparc86x
+ && default_arch_type != v9)
+ as_fatal ("This target does not support --little-endian-data");
+ break;
+ case OPTION_BIG_ENDIAN:
+ target_big_endian = 1;
+ break;
+#endif
+
+#ifdef OBJ_AOUT
+ case 'k':
+ sparc_pic_code = 1;
+ break;
+#endif
+
+#ifdef OBJ_ELF
+ case OPTION_32:
+ case OPTION_64:
+ {
+ const char **list, **l;
+
+ sparc_arch_size = c == OPTION_32 ? 32 : 64;
+ list = bfd_target_list ();
+ for (l = list; *l != NULL; l++)
+ {
+ if (sparc_arch_size == 32)
+ {
+ if (strcmp (*l, "elf32-sparc") == 0)
+ break;
+ }
+ else
+ {
+ if (strcmp (*l, "elf64-sparc") == 0)
+ break;
+ }
+ }
+ if (*l == NULL)
+ as_fatal (_("No compiled in support for %d bit object file format"),
+ sparc_arch_size);
+ free (list);
+ }
+ break;
+
+ case OPTION_TSO:
+ sparc_memory_model = MM_TSO;
+ break;
+
+ case OPTION_PSO:
+ sparc_memory_model = MM_PSO;
+ break;
+
+ case OPTION_RMO:
+ sparc_memory_model = MM_RMO;
+ break;
+
+ case 'V':
+ print_version_id ();
+ break;
+
+ case 'Q':
+ /* Qy - do emit .comment
+ Qn - do not emit .comment */
+ break;
+
+ case 's':
+ /* use .stab instead of .stab.excl */
+ break;
+
+ case 'q':
+ /* quick -- native assembler does fewer checks */
+ break;
+
+ case 'K':
+ if (strcmp (arg, "PIC") != 0)
+ as_warn (_("Unrecognized option following -K"));
+ else
+ sparc_pic_code = 1;
+ break;
+#endif
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ const struct sparc_arch *arch;
+
+ /* We don't get a chance to initialize anything before we're called,
+ so handle that now. */
+ if (! default_init_p)
+ init_default_arch ();
+
+ fprintf(stream, _("SPARC options:\n"));
+ for (arch = &sparc_arch_table[0]; arch->name; arch++)
+ {
+ if (arch != &sparc_arch_table[0])
+ fprintf (stream, " | ");
+ if (arch->user_option_p)
+ fprintf (stream, "-A%s", arch->name);
+ }
+ fprintf (stream, _("\n-xarch=v8plus | -xarch=v8plusa\n"));
+ fprintf (stream, _("\
+ specify variant of SPARC architecture\n\
+-bump warn when assembler switches architectures\n\
+-sparc ignored\n\
+--enforce-aligned-data force .long, etc., to be aligned correctly\n"));
+#ifdef OBJ_AOUT
+ fprintf (stream, _("\
+-k generate PIC\n"));
+#endif
+#ifdef OBJ_ELF
+ fprintf (stream, _("\
+-32 create 32 bit object file\n\
+-64 create 64 bit object file\n"));
+ fprintf (stream, _("\
+ [default is %d]\n"), default_arch_size);
+ fprintf (stream, _("\
+-TSO use Total Store Ordering\n\
+-PSO use Partial Store Ordering\n\
+-RMO use Relaxed Memory Ordering\n"));
+ fprintf (stream, _("\
+ [default is %s]\n"), (default_arch_size == 64) ? "RMO" : "TSO");
+ fprintf (stream, _("\
+-KPIC generate PIC\n\
+-V print assembler version number\n\
+-q ignored\n\
+-Qy, -Qn ignored\n\
+-s ignored\n"));
+#endif
+#ifdef SPARC_BIENDIAN
+ fprintf (stream, _("\
+-EL generate code for a little endian machine\n\
+-EB generate code for a big endian machine\n\
+--little-endian-data generate code for a machine having big endian\n\
+ instructions and little endian data."));
+#endif
+}
+
+/* sparc64 priviledged registers */
+
+struct priv_reg_entry
+ {
+ char *name;
+ int regnum;
+ };
+
+struct priv_reg_entry priv_reg_table[] =
+{
+ {"tpc", 0},
+ {"tnpc", 1},
+ {"tstate", 2},
+ {"tt", 3},
+ {"tick", 4},
+ {"tba", 5},
+ {"pstate", 6},
+ {"tl", 7},
+ {"pil", 8},
+ {"cwp", 9},
+ {"cansave", 10},
+ {"canrestore", 11},
+ {"cleanwin", 12},
+ {"otherwin", 13},
+ {"wstate", 14},
+ {"fq", 15},
+ {"ver", 31},
+ {"", -1}, /* end marker */
+};
+
+/* v9a specific asrs */
+
+struct priv_reg_entry v9a_asr_table[] =
+{
+ {"tick_cmpr", 23},
+ {"softint", 22},
+ {"set_softint", 20},
+ {"pic", 17},
+ {"pcr", 16},
+ {"gsr", 19},
+ {"dcr", 18},
+ {"clear_softint", 21},
+ {"", -1}, /* end marker */
+};
+
+static int
+cmp_reg_entry (parg, qarg)
+ const PTR parg;
+ const PTR qarg;
+{
+ const struct priv_reg_entry *p = (const struct priv_reg_entry *) parg;
+ const struct priv_reg_entry *q = (const struct priv_reg_entry *) qarg;
+
+ return strcmp (q->name, p->name);
+}
+
+/* This function is called once, at assembler startup time. It should
+ set up all the tables, etc. that the MD part of the assembler will need. */
+
+void
+md_begin ()
+{
+ register const char *retval = NULL;
+ int lose = 0;
+ register unsigned int i = 0;
+
+ /* We don't get a chance to initialize anything before md_parse_option
+ is called, and it may not be called, so handle default initialization
+ now if not already done. */
+ if (! default_init_p)
+ init_default_arch ();
+
+ op_hash = hash_new ();
+
+ while (i < (unsigned int) sparc_num_opcodes)
+ {
+ const char *name = sparc_opcodes[i].name;
+ retval = hash_insert (op_hash, name, (PTR) &sparc_opcodes[i]);
+ if (retval != NULL)
+ {
+ fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
+ sparc_opcodes[i].name, retval);
+ lose = 1;
+ }
+ do
+ {
+ if (sparc_opcodes[i].match & sparc_opcodes[i].lose)
+ {
+ fprintf (stderr, _("internal error: losing opcode: `%s' \"%s\"\n"),
+ sparc_opcodes[i].name, sparc_opcodes[i].args);
+ lose = 1;
+ }
+ ++i;
+ }
+ while (i < (unsigned int) sparc_num_opcodes
+ && !strcmp (sparc_opcodes[i].name, name));
+ }
+
+ if (lose)
+ as_fatal (_("Broken assembler. No assembly attempted."));
+
+ for (i = '0'; i < '8'; ++i)
+ octal[i] = 1;
+ for (i = '0'; i <= '9'; ++i)
+ toHex[i] = i - '0';
+ for (i = 'a'; i <= 'f'; ++i)
+ toHex[i] = i + 10 - 'a';
+ for (i = 'A'; i <= 'F'; ++i)
+ toHex[i] = i + 10 - 'A';
+
+ qsort (priv_reg_table, sizeof (priv_reg_table) / sizeof (priv_reg_table[0]),
+ sizeof (priv_reg_table[0]), cmp_reg_entry);
+
+ /* If -bump, record the architecture level at which we start issuing
+ warnings. The behaviour is different depending upon whether an
+ architecture was explicitly specified. If it wasn't, we issue warnings
+ for all upwards bumps. If it was, we don't start issuing warnings until
+ we need to bump beyond the requested architecture or when we bump between
+ conflicting architectures. */
+
+ if (warn_on_bump
+ && architecture_requested)
+ {
+ /* `max_architecture' records the requested architecture.
+ Issue warnings if we go above it. */
+ warn_after_architecture = max_architecture;
+
+ /* Find the highest architecture level that doesn't conflict with
+ the requested one. */
+ for (max_architecture = SPARC_OPCODE_ARCH_MAX;
+ max_architecture > warn_after_architecture;
+ --max_architecture)
+ if (! SPARC_OPCODE_CONFLICT_P (max_architecture,
+ warn_after_architecture))
+ break;
+ }
+}
+
+/* Called after all assembly has been done. */
+
+void
+sparc_md_end ()
+{
+ if (sparc_arch_size == 64)
+ {
+ if (current_architecture == SPARC_OPCODE_ARCH_V9A)
+ bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_v9a);
+ else
+ bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_v9);
+ }
+ else
+ {
+ if (current_architecture == SPARC_OPCODE_ARCH_V9)
+ bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_v8plus);
+ else if (current_architecture == SPARC_OPCODE_ARCH_V9A)
+ bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_v8plusa);
+ else if (current_architecture == SPARC_OPCODE_ARCH_SPARCLET)
+ bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_sparclet);
+ else if (default_arch_type == sparc86x && target_little_endian_data)
+ bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc_sparclite_le);
+ else
+ {
+ /* The sparclite is treated like a normal sparc. Perhaps it shouldn't
+ be but for now it is (since that's the way it's always been
+ treated). */
+ bfd_set_arch_mach (stdoutput, bfd_arch_sparc, bfd_mach_sparc);
+ }
+ }
+}
+
+/* Return non-zero if VAL is in the range -(MAX+1) to MAX. */
+
+static INLINE int
+in_signed_range (val, max)
+ bfd_signed_vma val, max;
+{
+ if (max <= 0)
+ abort ();
+ /* Sign-extend the value from the architecture word size, so that
+ 0xffffffff is always considered -1 on sparc32. */
+ if (sparc_arch_size == 32)
+ {
+ bfd_signed_vma sign = (bfd_signed_vma)1 << 31;
+ val = ((val & 0xffffffff) ^ sign) - sign;
+ }
+ if (val > max)
+ return 0;
+ if (val < ~max)
+ return 0;
+ return 1;
+}
+
+/* Return non-zero if VAL is in the range 0 to MAX. */
+
+static INLINE int
+in_unsigned_range (val, max)
+ bfd_vma val, max;
+{
+ if (val > max)
+ return 0;
+ return 1;
+}
+
+/* Return non-zero if VAL is in the range -(MAX/2+1) to MAX.
+ (e.g. -15 to +31). */
+
+static INLINE int
+in_bitfield_range (val, max)
+ bfd_signed_vma val, max;
+{
+ if (max <= 0)
+ abort ();
+ if (val > max)
+ return 0;
+ if (val < ~(max >> 1))
+ return 0;
+ return 1;
+}
+
+static int
+sparc_ffs (mask)
+ unsigned int mask;
+{
+ int i;
+
+ if (mask == 0)
+ return -1;
+
+ for (i = 0; (mask & 1) == 0; ++i)
+ mask >>= 1;
+ return i;
+}
+
+/* Implement big shift right. */
+static bfd_vma
+BSR (val, amount)
+ bfd_vma val;
+ int amount;
+{
+ if (sizeof (bfd_vma) <= 4 && amount >= 32)
+ as_fatal (_("Support for 64-bit arithmetic not compiled in."));
+ return val >> amount;
+}
+
+/* For communication between sparc_ip and get_expression. */
+static char *expr_end;
+
+/* For communication between md_assemble and sparc_ip. */
+static int special_case;
+
+/* Values for `special_case'.
+ Instructions that require wierd handling because they're longer than
+ 4 bytes. */
+#define SPECIAL_CASE_NONE 0
+#define SPECIAL_CASE_SET 1
+#define SPECIAL_CASE_SETSW 2
+#define SPECIAL_CASE_SETX 3
+/* FIXME: sparc-opc.c doesn't have necessary "S" trigger to enable this. */
+#define SPECIAL_CASE_FDIV 4
+
+/* Bit masks of various insns. */
+#define NOP_INSN 0x01000000
+#define OR_INSN 0x80100000
+#define FMOVS_INSN 0x81A00020
+#define SETHI_INSN 0x01000000
+#define SLLX_INSN 0x81281000
+#define SRA_INSN 0x81380000
+
+/* The last instruction to be assembled. */
+static const struct sparc_opcode *last_insn;
+/* The assembled opcode of `last_insn'. */
+static unsigned long last_opcode;
+
+/* Main entry point to assemble one instruction. */
+
+void
+md_assemble (str)
+ char *str;
+{
+ const struct sparc_opcode *insn;
+
+ know (str);
+ special_case = SPECIAL_CASE_NONE;
+ sparc_ip (str, &insn);
+
+ /* We warn about attempts to put a floating point branch in a delay slot,
+ unless the delay slot has been annulled. */
+ if (insn != NULL
+ && last_insn != NULL
+ && (insn->flags & F_FBR) != 0
+ && (last_insn->flags & F_DELAYED) != 0
+ /* ??? This test isn't completely accurate. We assume anything with
+ F_{UNBR,CONDBR,FBR} set is annullable. */
+ && ((last_insn->flags & (F_UNBR | F_CONDBR | F_FBR)) == 0
+ || (last_opcode & ANNUL) == 0))
+ as_warn (_("FP branch in delay slot"));
+
+ /* SPARC before v9 requires a nop instruction between a floating
+ point instruction and a floating point branch. We insert one
+ automatically, with a warning. */
+ if (max_architecture < SPARC_OPCODE_ARCH_V9
+ && insn != NULL
+ && last_insn != NULL
+ && (insn->flags & F_FBR) != 0
+ && (last_insn->flags & F_FLOAT) != 0)
+ {
+ struct sparc_it nop_insn;
+
+ nop_insn.opcode = NOP_INSN;
+ nop_insn.reloc = BFD_RELOC_NONE;
+ output_insn (insn, &nop_insn);
+ as_warn (_("FP branch preceded by FP instruction; NOP inserted"));
+ }
+
+ switch (special_case)
+ {
+ case SPECIAL_CASE_NONE:
+ /* normal insn */
+ output_insn (insn, &the_insn);
+ break;
+
+ case SPECIAL_CASE_SET:
+ {
+ int need_hi22_p = 0;
+
+ /* "set" is not defined for negative numbers in v9: it doesn't yield
+ what you expect it to. */
+ if (SPARC_OPCODE_ARCH_V9_P (max_architecture)
+ && the_insn.exp.X_op == O_constant)
+ {
+ if (the_insn.exp.X_add_number < 0)
+ as_warn (_("set: used with negative number"));
+ else if (the_insn.exp.X_add_number > (offsetT) 0xffffffff)
+ as_warn (_("set: number larger than 4294967295"));
+ }
+
+ /* See if operand is absolute and small; skip sethi if so. */
+ if (the_insn.exp.X_op != O_constant
+ || the_insn.exp.X_add_number >= (1 << 12)
+ || the_insn.exp.X_add_number < -(1 << 12))
+ {
+ output_insn (insn, &the_insn);
+ need_hi22_p = 1;
+ }
+ /* See if operand has no low-order bits; skip OR if so. */
+ if (the_insn.exp.X_op != O_constant
+ || (need_hi22_p && (the_insn.exp.X_add_number & 0x3FF) != 0)
+ || ! need_hi22_p)
+ {
+ int rd = (the_insn.opcode & RD (~0)) >> 25;
+ the_insn.opcode = (OR_INSN | (need_hi22_p ? RS1 (rd) : 0)
+ | RD (rd)
+ | IMMED
+ | (the_insn.exp.X_add_number
+ & (need_hi22_p ? 0x3ff : 0x1fff)));
+ the_insn.reloc = (the_insn.exp.X_op != O_constant
+ ? BFD_RELOC_LO10
+ : BFD_RELOC_NONE);
+ output_insn (insn, &the_insn);
+ }
+ break;
+ }
+
+ case SPECIAL_CASE_SETSW:
+ {
+ /* FIXME: Not finished. */
+ break;
+ }
+
+ case SPECIAL_CASE_SETX:
+ {
+#define SIGNEXT32(x) ((((x) & 0xffffffff) ^ 0x80000000) - 0x80000000)
+ int upper32 = SIGNEXT32 (BSR (the_insn.exp.X_add_number, 32));
+ int lower32 = SIGNEXT32 (the_insn.exp.X_add_number);
+#undef SIGNEXT32
+ int tmpreg = (the_insn.opcode & RS1 (~0)) >> 14;
+ int dstreg = (the_insn.opcode & RD (~0)) >> 25;
+ /* Output directly to dst reg if lower 32 bits are all zero. */
+ int upper_dstreg = (the_insn.exp.X_op == O_constant
+ && lower32 == 0) ? dstreg : tmpreg;
+ int need_hh22_p = 0, need_hm10_p = 0, need_hi22_p = 0, need_lo10_p = 0;
+
+ /* The tmp reg should not be the dst reg. */
+ if (tmpreg == dstreg)
+ as_warn (_("setx: temporary register same as destination register"));
+
+ /* Reset X_add_number, we've extracted it as upper32/lower32.
+ Otherwise fixup_segment will complain about not being able to
+ write an 8 byte number in a 4 byte field. */
+ the_insn.exp.X_add_number = 0;
+
+ /* ??? Obviously there are other optimizations we can do
+ (e.g. sethi+shift for 0x1f0000000) and perhaps we shouldn't be
+ doing some of these. Later. If you do change things, try to
+ change all of this to be table driven as well. */
+
+ /* What to output depends on the number if it's constant.
+ Compute that first, then output what we've decided upon. */
+ if (the_insn.exp.X_op != O_constant)
+ need_hh22_p = need_hm10_p = need_hi22_p = need_lo10_p = 1;
+ else
+ {
+ /* Only need hh22 if `or' insn can't handle constant. */
+ if (upper32 < -(1 << 12) || upper32 >= (1 << 12))
+ need_hh22_p = 1;
+
+ /* Does bottom part (after sethi) have bits? */
+ if ((need_hh22_p && (upper32 & 0x3ff) != 0)
+ /* No hh22, but does upper32 still have bits we can't set
+ from lower32? */
+ || (! need_hh22_p
+ && upper32 != 0
+ && (upper32 != -1 || lower32 >= 0)))
+ need_hm10_p = 1;
+
+ /* If the lower half is all zero, we build the upper half directly
+ into the dst reg. */
+ if (lower32 != 0
+ /* Need lower half if number is zero. */
+ || (! need_hh22_p && ! need_hm10_p))
+ {
+ /* No need for sethi if `or' insn can handle constant. */
+ if (lower32 < -(1 << 12) || lower32 >= (1 << 12)
+ /* Note that we can't use a negative constant in the `or'
+ insn unless the upper 32 bits are all ones. */
+ || (lower32 < 0 && upper32 != -1))
+ need_hi22_p = 1;
+
+ /* Does bottom part (after sethi) have bits? */
+ if ((need_hi22_p && (lower32 & 0x3ff) != 0)
+ /* No sethi. */
+ || (! need_hi22_p && (lower32 & 0x1fff) != 0)
+ /* Need `or' if we didn't set anything else. */
+ || (! need_hi22_p && ! need_hh22_p && ! need_hm10_p))
+ need_lo10_p = 1;
+ }
+ }
+
+ if (need_hh22_p)
+ {
+ the_insn.opcode = (SETHI_INSN | RD (upper_dstreg)
+ | ((upper32 >> 10) & 0x3fffff));
+ the_insn.reloc = (the_insn.exp.X_op != O_constant
+ ? BFD_RELOC_SPARC_HH22 : BFD_RELOC_NONE);
+ output_insn (insn, &the_insn);
+ }
+
+ if (need_hm10_p)
+ {
+ the_insn.opcode = (OR_INSN
+ | (need_hh22_p ? RS1 (upper_dstreg) : 0)
+ | RD (upper_dstreg)
+ | IMMED
+ | (upper32
+ & (need_hh22_p ? 0x3ff : 0x1fff)));
+ the_insn.reloc = (the_insn.exp.X_op != O_constant
+ ? BFD_RELOC_SPARC_HM10 : BFD_RELOC_NONE);
+ output_insn (insn, &the_insn);
+ }
+
+ if (need_hi22_p)
+ {
+ the_insn.opcode = (SETHI_INSN | RD (dstreg)
+ | ((lower32 >> 10) & 0x3fffff));
+ the_insn.reloc = BFD_RELOC_HI22;
+ output_insn (insn, &the_insn);
+ }
+
+ if (need_lo10_p)
+ {
+ /* FIXME: One nice optimization to do here is to OR the low part
+ with the highpart if hi22 isn't needed and the low part is
+ positive. */
+ the_insn.opcode = (OR_INSN | (need_hi22_p ? RS1 (dstreg) : 0)
+ | RD (dstreg)
+ | IMMED
+ | (lower32
+ & (need_hi22_p ? 0x3ff : 0x1fff)));
+ the_insn.reloc = BFD_RELOC_LO10;
+ output_insn (insn, &the_insn);
+ }
+
+ /* If we needed to build the upper part, shift it into place. */
+ if (need_hh22_p || need_hm10_p)
+ {
+ the_insn.opcode = (SLLX_INSN | RS1 (upper_dstreg) | RD (upper_dstreg)
+ | IMMED | 32);
+ the_insn.reloc = BFD_RELOC_NONE;
+ output_insn (insn, &the_insn);
+ }
+
+ /* If we needed to build both upper and lower parts, OR them together. */
+ if ((need_hh22_p || need_hm10_p)
+ && (need_hi22_p || need_lo10_p))
+ {
+ the_insn.opcode = (OR_INSN | RS1 (dstreg) | RS2 (upper_dstreg)
+ | RD (dstreg));
+ the_insn.reloc = BFD_RELOC_NONE;
+ output_insn (insn, &the_insn);
+ }
+ /* We didn't need both regs, but we may have to sign extend lower32. */
+ else if (need_hi22_p && upper32 == -1)
+ {
+ the_insn.opcode = (SRA_INSN | RS1 (dstreg) | RD (dstreg)
+ | IMMED | 0);
+ the_insn.reloc = BFD_RELOC_NONE;
+ output_insn (insn, &the_insn);
+ }
+ break;
+ }
+
+ case SPECIAL_CASE_FDIV:
+ {
+ int rd = (the_insn.opcode >> 25) & 0x1f;
+
+ output_insn (insn, &the_insn);
+
+ /* According to information leaked from Sun, the "fdiv" instructions
+ on early SPARC machines would produce incorrect results sometimes.
+ The workaround is to add an fmovs of the destination register to
+ itself just after the instruction. This was true on machines
+ with Weitek 1165 float chips, such as the Sun-4/260 and /280. */
+ assert (the_insn.reloc == BFD_RELOC_NONE);
+ the_insn.opcode = FMOVS_INSN | rd | RD (rd);
+ output_insn (insn, &the_insn);
+ break;
+ }
+
+ default:
+ as_fatal (_("failed special case insn sanity check"));
+ }
+}
+
+/* Subroutine of md_assemble to do the actual parsing. */
+
+static void
+sparc_ip (str, pinsn)
+ char *str;
+ const struct sparc_opcode **pinsn;
+{
+ char *error_message = "";
+ char *s;
+ const char *args;
+ char c;
+ const struct sparc_opcode *insn;
+ char *argsStart;
+ unsigned long opcode;
+ unsigned int mask = 0;
+ int match = 0;
+ int comma = 0;
+ int v9_arg_p;
+
+ s = str;
+ if (islower ((unsigned char) *s))
+ {
+ do
+ ++s;
+ while (islower ((unsigned char) *s) || isdigit ((unsigned char) *s));
+ }
+
+ switch (*s)
+ {
+ case '\0':
+ break;
+
+ case ',':
+ comma = 1;
+
+ /*FALLTHROUGH */
+
+ case ' ':
+ *s++ = '\0';
+ break;
+
+ default:
+ as_fatal (_("Unknown opcode: `%s'"), str);
+ }
+ insn = (struct sparc_opcode *) hash_find (op_hash, str);
+ *pinsn = insn;
+ if (insn == NULL)
+ {
+ as_bad (_("Unknown opcode: `%s'"), str);
+ return;
+ }
+ if (comma)
+ {
+ *--s = ',';
+ }
+
+ argsStart = s;
+ for (;;)
+ {
+ opcode = insn->match;
+ memset (&the_insn, '\0', sizeof (the_insn));
+ the_insn.reloc = BFD_RELOC_NONE;
+ v9_arg_p = 0;
+
+ /*
+ * Build the opcode, checking as we go to make
+ * sure that the operands match
+ */
+ for (args = insn->args;; ++args)
+ {
+ switch (*args)
+ {
+ case 'K':
+ {
+ int kmask = 0;
+
+ /* Parse a series of masks. */
+ if (*s == '#')
+ {
+ while (*s == '#')
+ {
+ int mask;
+
+ if (! parse_keyword_arg (sparc_encode_membar, &s,
+ &mask))
+ {
+ error_message = _(": invalid membar mask name");
+ goto error;
+ }
+ kmask |= mask;
+ while (*s == ' ') { ++s; continue; }
+ if (*s == '|' || *s == '+')
+ ++s;
+ while (*s == ' ') { ++s; continue; }
+ }
+ }
+ else
+ {
+ if (! parse_const_expr_arg (&s, &kmask))
+ {
+ error_message = _(": invalid membar mask expression");
+ goto error;
+ }
+ if (kmask < 0 || kmask > 127)
+ {
+ error_message = _(": invalid membar mask number");
+ goto error;
+ }
+ }
+
+ opcode |= MEMBAR (kmask);
+ continue;
+ }
+
+ case '*':
+ {
+ int fcn = 0;
+
+ /* Parse a prefetch function. */
+ if (*s == '#')
+ {
+ if (! parse_keyword_arg (sparc_encode_prefetch, &s, &fcn))
+ {
+ error_message = _(": invalid prefetch function name");
+ goto error;
+ }
+ }
+ else
+ {
+ if (! parse_const_expr_arg (&s, &fcn))
+ {
+ error_message = _(": invalid prefetch function expression");
+ goto error;
+ }
+ if (fcn < 0 || fcn > 31)
+ {
+ error_message = _(": invalid prefetch function number");
+ goto error;
+ }
+ }
+ opcode |= RD (fcn);
+ continue;
+ }
+
+ case '!':
+ case '?':
+ /* Parse a sparc64 privileged register. */
+ if (*s == '%')
+ {
+ struct priv_reg_entry *p = priv_reg_table;
+ unsigned int len = 9999999; /* init to make gcc happy */
+
+ s += 1;
+ while (p->name[0] > s[0])
+ p++;
+ while (p->name[0] == s[0])
+ {
+ len = strlen (p->name);
+ if (strncmp (p->name, s, len) == 0)
+ break;
+ p++;
+ }
+ if (p->name[0] != s[0])
+ {
+ error_message = _(": unrecognizable privileged register");
+ goto error;
+ }
+ if (*args == '?')
+ opcode |= (p->regnum << 14);
+ else
+ opcode |= (p->regnum << 25);
+ s += len;
+ continue;
+ }
+ else
+ {
+ error_message = _(": unrecognizable privileged register");
+ goto error;
+ }
+
+ case '_':
+ case '/':
+ /* Parse a v9a ancillary state register. */
+ if (*s == '%')
+ {
+ struct priv_reg_entry *p = v9a_asr_table;
+ unsigned int len = 9999999; /* init to make gcc happy */
+
+ s += 1;
+ while (p->name[0] > s[0])
+ p++;
+ while (p->name[0] == s[0])
+ {
+ len = strlen (p->name);
+ if (strncmp (p->name, s, len) == 0)
+ break;
+ p++;
+ }
+ if (p->name[0] != s[0])
+ {
+ error_message = _(": unrecognizable v9a ancillary state register");
+ goto error;
+ }
+ if (*args == '/' && (p->regnum == 20 || p->regnum == 21))
+ {
+ error_message = _(": rd on write only ancillary state register");
+ goto error;
+ }
+ if (*args == '/')
+ opcode |= (p->regnum << 14);
+ else
+ opcode |= (p->regnum << 25);
+ s += len;
+ continue;
+ }
+ else
+ {
+ error_message = _(": unrecognizable v9a ancillary state register");
+ goto error;
+ }
+
+ case 'M':
+ case 'm':
+ if (strncmp (s, "%asr", 4) == 0)
+ {
+ s += 4;
+
+ if (isdigit ((unsigned char) *s))
+ {
+ long num = 0;
+
+ while (isdigit ((unsigned char) *s))
+ {
+ num = num * 10 + *s - '0';
+ ++s;
+ }
+
+ if (current_architecture >= SPARC_OPCODE_ARCH_V9)
+ {
+ if (num < 16 || 31 < num)
+ {
+ error_message = _(": asr number must be between 16 and 31");
+ goto error;
+ }
+ }
+ else
+ {
+ if (num < 0 || 31 < num)
+ {
+ error_message = _(": asr number must be between 0 and 31");
+ goto error;
+ }
+ }
+
+ opcode |= (*args == 'M' ? RS1 (num) : RD (num));
+ continue;
+ }
+ else
+ {
+ error_message = _(": expecting %asrN");
+ goto error;
+ }
+ } /* if %asr */
+ break;
+
+ case 'I':
+ the_insn.reloc = BFD_RELOC_SPARC_11;
+ goto immediate;
+
+ case 'j':
+ the_insn.reloc = BFD_RELOC_SPARC_10;
+ goto immediate;
+
+ case 'X':
+ /* V8 systems don't understand BFD_RELOC_SPARC_5. */
+ if (SPARC_OPCODE_ARCH_V9_P (max_architecture))
+ the_insn.reloc = BFD_RELOC_SPARC_5;
+ else
+ the_insn.reloc = BFD_RELOC_SPARC13;
+ /* These fields are unsigned, but for upward compatibility,
+ allow negative values as well. */
+ goto immediate;
+
+ case 'Y':
+ /* V8 systems don't understand BFD_RELOC_SPARC_6. */
+ if (SPARC_OPCODE_ARCH_V9_P (max_architecture))
+ the_insn.reloc = BFD_RELOC_SPARC_6;
+ else
+ the_insn.reloc = BFD_RELOC_SPARC13;
+ /* These fields are unsigned, but for upward compatibility,
+ allow negative values as well. */
+ goto immediate;
+
+ case 'k':
+ the_insn.reloc = /* RELOC_WDISP2_14 */ BFD_RELOC_SPARC_WDISP16;
+ the_insn.pcrel = 1;
+ goto immediate;
+
+ case 'G':
+ the_insn.reloc = BFD_RELOC_SPARC_WDISP19;
+ the_insn.pcrel = 1;
+ goto immediate;
+
+ case 'N':
+ if (*s == 'p' && s[1] == 'n')
+ {
+ s += 2;
+ continue;
+ }
+ break;
+
+ case 'T':
+ if (*s == 'p' && s[1] == 't')
+ {
+ s += 2;
+ continue;
+ }
+ break;
+
+ case 'z':
+ if (*s == ' ')
+ {
+ ++s;
+ }
+ if (strncmp (s, "%icc", 4) == 0)
+ {
+ s += 4;
+ continue;
+ }
+ break;
+
+ case 'Z':
+ if (*s == ' ')
+ {
+ ++s;
+ }
+ if (strncmp (s, "%xcc", 4) == 0)
+ {
+ s += 4;
+ continue;
+ }
+ break;
+
+ case '6':
+ if (*s == ' ')
+ {
+ ++s;
+ }
+ if (strncmp (s, "%fcc0", 5) == 0)
+ {
+ s += 5;
+ continue;
+ }
+ break;
+
+ case '7':
+ if (*s == ' ')
+ {
+ ++s;
+ }
+ if (strncmp (s, "%fcc1", 5) == 0)
+ {
+ s += 5;
+ continue;
+ }
+ break;
+
+ case '8':
+ if (*s == ' ')
+ {
+ ++s;
+ }
+ if (strncmp (s, "%fcc2", 5) == 0)
+ {
+ s += 5;
+ continue;
+ }
+ break;
+
+ case '9':
+ if (*s == ' ')
+ {
+ ++s;
+ }
+ if (strncmp (s, "%fcc3", 5) == 0)
+ {
+ s += 5;
+ continue;
+ }
+ break;
+
+ case 'P':
+ if (strncmp (s, "%pc", 3) == 0)
+ {
+ s += 3;
+ continue;
+ }
+ break;
+
+ case 'W':
+ if (strncmp (s, "%tick", 5) == 0)
+ {
+ s += 5;
+ continue;
+ }
+ break;
+
+ case '\0': /* end of args */
+ if (*s == '\0')
+ {
+ match = 1;
+ }
+ break;
+
+ case '+':
+ if (*s == '+')
+ {
+ ++s;
+ continue;
+ }
+ if (*s == '-')
+ {
+ continue;
+ }
+ break;
+
+ case '[': /* these must match exactly */
+ case ']':
+ case ',':
+ case ' ':
+ if (*s++ == *args)
+ continue;
+ break;
+
+ case '#': /* must be at least one digit */
+ if (isdigit ((unsigned char) *s++))
+ {
+ while (isdigit ((unsigned char) *s))
+ {
+ ++s;
+ }
+ continue;
+ }
+ break;
+
+ case 'C': /* coprocessor state register */
+ if (strncmp (s, "%csr", 4) == 0)
+ {
+ s += 4;
+ continue;
+ }
+ break;
+
+ case 'b': /* next operand is a coprocessor register */
+ case 'c':
+ case 'D':
+ if (*s++ == '%' && *s++ == 'c' && isdigit ((unsigned char) *s))
+ {
+ mask = *s++;
+ if (isdigit ((unsigned char) *s))
+ {
+ mask = 10 * (mask - '0') + (*s++ - '0');
+ if (mask >= 32)
+ {
+ break;
+ }
+ }
+ else
+ {
+ mask -= '0';
+ }
+ switch (*args)
+ {
+
+ case 'b':
+ opcode |= mask << 14;
+ continue;
+
+ case 'c':
+ opcode |= mask;
+ continue;
+
+ case 'D':
+ opcode |= mask << 25;
+ continue;
+ }
+ }
+ break;
+
+ case 'r': /* next operand must be a register */
+ case 'O':
+ case '1':
+ case '2':
+ case 'd':
+ if (*s++ == '%')
+ {
+ switch (c = *s++)
+ {
+
+ case 'f': /* frame pointer */
+ if (*s++ == 'p')
+ {
+ mask = 0x1e;
+ break;
+ }
+ goto error;
+
+ case 'g': /* global register */
+ if (isoctal (c = *s++))
+ {
+ mask = c - '0';
+ break;
+ }
+ goto error;
+
+ case 'i': /* in register */
+ if (isoctal (c = *s++))
+ {
+ mask = c - '0' + 24;
+ break;
+ }
+ goto error;
+
+ case 'l': /* local register */
+ if (isoctal (c = *s++))
+ {
+ mask = (c - '0' + 16);
+ break;
+ }
+ goto error;
+
+ case 'o': /* out register */
+ if (isoctal (c = *s++))
+ {
+ mask = (c - '0' + 8);
+ break;
+ }
+ goto error;
+
+ case 's': /* stack pointer */
+ if (*s++ == 'p')
+ {
+ mask = 0xe;
+ break;
+ }
+ goto error;
+
+ case 'r': /* any register */
+ if (!isdigit ((unsigned char) (c = *s++)))
+ {
+ goto error;
+ }
+ /* FALLTHROUGH */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (isdigit ((unsigned char) *s))
+ {
+ if ((c = 10 * (c - '0') + (*s++ - '0')) >= 32)
+ {
+ goto error;
+ }
+ }
+ else
+ {
+ c -= '0';
+ }
+ mask = c;
+ break;
+
+ default:
+ goto error;
+ }
+
+ /* Got the register, now figure out where
+ it goes in the opcode. */
+ switch (*args)
+ {
+ case '1':
+ opcode |= mask << 14;
+ continue;
+
+ case '2':
+ opcode |= mask;
+ continue;
+
+ case 'd':
+ opcode |= mask << 25;
+ continue;
+
+ case 'r':
+ opcode |= (mask << 25) | (mask << 14);
+ continue;
+
+ case 'O':
+ opcode |= (mask << 25) | (mask << 0);
+ continue;
+ }
+ }
+ break;
+
+ case 'e': /* next operand is a floating point register */
+ case 'v':
+ case 'V':
+
+ case 'f':
+ case 'B':
+ case 'R':
+
+ case 'g':
+ case 'H':
+ case 'J':
+ {
+ char format;
+
+ if (*s++ == '%'
+ && ((format = *s) == 'f')
+ && isdigit ((unsigned char) *++s))
+ {
+ for (mask = 0; isdigit ((unsigned char) *s); ++s)
+ {
+ mask = 10 * mask + (*s - '0');
+ } /* read the number */
+
+ if ((*args == 'v'
+ || *args == 'B'
+ || *args == 'H')
+ && (mask & 1))
+ {
+ break;
+ } /* register must be even numbered */
+
+ if ((*args == 'V'
+ || *args == 'R'
+ || *args == 'J')
+ && (mask & 3))
+ {
+ break;
+ } /* register must be multiple of 4 */
+
+ if (mask >= 64)
+ {
+ if (SPARC_OPCODE_ARCH_V9_P (max_architecture))
+ error_message = _(": There are only 64 f registers; [0-63]");
+ else
+ error_message = _(": There are only 32 f registers; [0-31]");
+ goto error;
+ } /* on error */
+ else if (mask >= 32)
+ {
+ if (SPARC_OPCODE_ARCH_V9_P (max_architecture))
+ {
+ v9_arg_p = 1;
+ mask -= 31; /* wrap high bit */
+ }
+ else
+ {
+ error_message = _(": There are only 32 f registers; [0-31]");
+ goto error;
+ }
+ }
+ }
+ else
+ {
+ break;
+ } /* if not an 'f' register. */
+
+ switch (*args)
+ {
+ case 'v':
+ case 'V':
+ case 'e':
+ opcode |= RS1 (mask);
+ continue;
+
+
+ case 'f':
+ case 'B':
+ case 'R':
+ opcode |= RS2 (mask);
+ continue;
+
+ case 'g':
+ case 'H':
+ case 'J':
+ opcode |= RD (mask);
+ continue;
+ } /* pack it in. */
+
+ know (0);
+ break;
+ } /* float arg */
+
+ case 'F':
+ if (strncmp (s, "%fsr", 4) == 0)
+ {
+ s += 4;
+ continue;
+ }
+ break;
+
+ case '0': /* 64 bit immediate (setx insn) */
+ the_insn.reloc = BFD_RELOC_NONE; /* reloc handled elsewhere */
+ goto immediate;
+
+ case 'h': /* high 22 bits */
+ the_insn.reloc = BFD_RELOC_HI22;
+ goto immediate;
+
+ case 'l': /* 22 bit PC relative immediate */
+ the_insn.reloc = BFD_RELOC_SPARC_WDISP22;
+ the_insn.pcrel = 1;
+ goto immediate;
+
+ case 'L': /* 30 bit immediate */
+ the_insn.reloc = BFD_RELOC_32_PCREL_S2;
+ the_insn.pcrel = 1;
+ goto immediate;
+
+ case 'n': /* 22 bit immediate */
+ the_insn.reloc = BFD_RELOC_SPARC22;
+ goto immediate;
+
+ case 'i': /* 13 bit immediate */
+ the_insn.reloc = BFD_RELOC_SPARC13;
+
+ /* fallthrough */
+
+ immediate:
+ if (*s == ' ')
+ s++;
+
+ /* Check for %hi, etc. */
+ if (*s == '%')
+ {
+ static struct ops {
+ /* The name as it appears in assembler. */
+ char *name;
+ /* strlen (name), precomputed for speed */
+ int len;
+ /* The reloc this pseudo-op translates to. */
+ int reloc;
+ /* Non-zero if for v9 only. */
+ int v9_p;
+ /* Non-zero if can be used in pc-relative contexts. */
+ int pcrel_p;/*FIXME:wip*/
+ } ops[] = {
+ /* hix/lox must appear before hi/lo so %hix won't be
+ mistaken for %hi. */
+ { "hix", 3, BFD_RELOC_SPARC_HIX22, 1, 0 },
+ { "lox", 3, BFD_RELOC_SPARC_LOX10, 1, 0 },
+ { "hi", 2, BFD_RELOC_HI22, 0, 1 },
+ { "lo", 2, BFD_RELOC_LO10, 0, 1 },
+ { "hh", 2, BFD_RELOC_SPARC_HH22, 1, 1 },
+ { "hm", 2, BFD_RELOC_SPARC_HM10, 1, 1 },
+ { "lm", 2, BFD_RELOC_SPARC_LM22, 1, 1 },
+ { "h44", 3, BFD_RELOC_SPARC_H44, 1, 0 },
+ { "m44", 3, BFD_RELOC_SPARC_M44, 1, 0 },
+ { "l44", 3, BFD_RELOC_SPARC_L44, 1, 0 },
+ { "uhi", 3, BFD_RELOC_SPARC_HH22, 1, 0 },
+ { "ulo", 3, BFD_RELOC_SPARC_HM10, 1, 0 },
+ { NULL }
+ };
+ struct ops *o;
+
+ for (o = ops; o->name; o++)
+ if (strncmp (s + 1, o->name, o->len) == 0)
+ break;
+ if (o->name == NULL)
+ break;
+
+ the_insn.reloc = o->reloc;
+ s += o->len + 1;
+ v9_arg_p = o->v9_p;
+ }
+
+ /* Note that if the get_expression() fails, we will still
+ have created U entries in the symbol table for the
+ 'symbols' in the input string. Try not to create U
+ symbols for registers, etc. */
+ {
+ /* This stuff checks to see if the expression ends in
+ +%reg. If it does, it removes the register from
+ the expression, and re-sets 's' to point to the
+ right place. */
+
+ char *s1;
+
+ for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++) ;
+
+ if (s1 != s && isdigit ((unsigned char) s1[-1]))
+ {
+ if (s1[-2] == '%' && s1[-3] == '+')
+ {
+ s1 -= 3;
+ *s1 = '\0';
+ (void) get_expression (s);
+ *s1 = '+';
+ s = s1;
+ continue;
+ }
+ else if (strchr ("goli0123456789", s1[-2]) && s1[-3] == '%' && s1[-4] == '+')
+ {
+ s1 -= 4;
+ *s1 = '\0';
+ (void) get_expression (s);
+ *s1 = '+';
+ s = s1;
+ continue;
+ }
+ }
+ }
+ (void) get_expression (s);
+ s = expr_end;
+
+ /* Check for constants that don't require emitting a reloc. */
+ if (the_insn.exp.X_op == O_constant
+ && the_insn.exp.X_add_symbol == 0
+ && the_insn.exp.X_op_symbol == 0)
+ {
+ /* For pc-relative call instructions, we reject
+ constants to get better code. */
+ if (the_insn.pcrel
+ && the_insn.reloc == BFD_RELOC_32_PCREL_S2
+ && in_signed_range (the_insn.exp.X_add_number, 0x3fff))
+ {
+ error_message = _(": PC-relative operand can't be a constant");
+ goto error;
+ }
+
+ /* Constants that won't fit are checked in md_apply_fix3
+ and bfd_install_relocation.
+ ??? It would be preferable to install the constants
+ into the insn here and save having to create a fixS
+ for each one. There already exists code to handle
+ all the various cases (e.g. in md_apply_fix3 and
+ bfd_install_relocation) so duplicating all that code
+ here isn't right. */
+ }
+
+ continue;
+
+ case 'a':
+ if (*s++ == 'a')
+ {
+ opcode |= ANNUL;
+ continue;
+ }
+ break;
+
+ case 'A':
+ {
+ int asi = 0;
+
+ /* Parse an asi. */
+ if (*s == '#')
+ {
+ if (! parse_keyword_arg (sparc_encode_asi, &s, &asi))
+ {
+ error_message = _(": invalid ASI name");
+ goto error;
+ }
+ }
+ else
+ {
+ if (! parse_const_expr_arg (&s, &asi))
+ {
+ error_message = _(": invalid ASI expression");
+ goto error;
+ }
+ if (asi < 0 || asi > 255)
+ {
+ error_message = _(": invalid ASI number");
+ goto error;
+ }
+ }
+ opcode |= ASI (asi);
+ continue;
+ } /* alternate space */
+
+ case 'p':
+ if (strncmp (s, "%psr", 4) == 0)
+ {
+ s += 4;
+ continue;
+ }
+ break;
+
+ case 'q': /* floating point queue */
+ if (strncmp (s, "%fq", 3) == 0)
+ {
+ s += 3;
+ continue;
+ }
+ break;
+
+ case 'Q': /* coprocessor queue */
+ if (strncmp (s, "%cq", 3) == 0)
+ {
+ s += 3;
+ continue;
+ }
+ break;
+
+ case 'S':
+ if (strcmp (str, "set") == 0
+ || strcmp (str, "setuw") == 0)
+ {
+ special_case = SPECIAL_CASE_SET;
+ continue;
+ }
+ else if (strcmp (str, "setsw") == 0)
+ {
+ special_case = SPECIAL_CASE_SETSW;
+ continue;
+ }
+ else if (strcmp (str, "setx") == 0)
+ {
+ special_case = SPECIAL_CASE_SETX;
+ continue;
+ }
+ else if (strncmp (str, "fdiv", 4) == 0)
+ {
+ special_case = SPECIAL_CASE_FDIV;
+ continue;
+ }
+ break;
+
+ case 'o':
+ if (strncmp (s, "%asi", 4) != 0)
+ break;
+ s += 4;
+ continue;
+
+ case 's':
+ if (strncmp (s, "%fprs", 5) != 0)
+ break;
+ s += 5;
+ continue;
+
+ case 'E':
+ if (strncmp (s, "%ccr", 4) != 0)
+ break;
+ s += 4;
+ continue;
+
+ case 't':
+ if (strncmp (s, "%tbr", 4) != 0)
+ break;
+ s += 4;
+ continue;
+
+ case 'w':
+ if (strncmp (s, "%wim", 4) != 0)
+ break;
+ s += 4;
+ continue;
+
+ case 'x':
+ {
+ char *push = input_line_pointer;
+ expressionS e;
+
+ input_line_pointer = s;
+ expression (&e);
+ if (e.X_op == O_constant)
+ {
+ int n = e.X_add_number;
+ if (n != e.X_add_number || (n & ~0x1ff) != 0)
+ as_bad (_("OPF immediate operand out of range (0-0x1ff)"));
+ else
+ opcode |= e.X_add_number << 5;
+ }
+ else
+ as_bad (_("non-immediate OPF operand, ignored"));
+ s = input_line_pointer;
+ input_line_pointer = push;
+ continue;
+ }
+
+ case 'y':
+ if (strncmp (s, "%y", 2) != 0)
+ break;
+ s += 2;
+ continue;
+
+ case 'u':
+ case 'U':
+ {
+ /* Parse a sparclet cpreg. */
+ int cpreg;
+ if (! parse_keyword_arg (sparc_encode_sparclet_cpreg, &s, &cpreg))
+ {
+ error_message = _(": invalid cpreg name");
+ goto error;
+ }
+ opcode |= (*args == 'U' ? RS1 (cpreg) : RD (cpreg));
+ continue;
+ }
+
+ default:
+ as_fatal (_("failed sanity check."));
+ } /* switch on arg code */
+
+ /* Break out of for() loop. */
+ break;
+ } /* for each arg that we expect */
+
+ error:
+ if (match == 0)
+ {
+ /* Args don't match. */
+ if (&insn[1] - sparc_opcodes < sparc_num_opcodes
+ && (insn->name == insn[1].name
+ || !strcmp (insn->name, insn[1].name)))
+ {
+ ++insn;
+ s = argsStart;
+ continue;
+ }
+ else
+ {
+ as_bad (_("Illegal operands%s"), error_message);
+ return;
+ }
+ }
+ else
+ {
+ /* We have a match. Now see if the architecture is ok. */
+ int needed_arch_mask = insn->architecture;
+
+ if (v9_arg_p)
+ {
+ needed_arch_mask &= ~ ((1 << SPARC_OPCODE_ARCH_V9)
+ | (1 << SPARC_OPCODE_ARCH_V9A));
+ needed_arch_mask |= (1 << SPARC_OPCODE_ARCH_V9);
+ }
+
+ if (needed_arch_mask & SPARC_OPCODE_SUPPORTED (current_architecture))
+ ; /* ok */
+ /* Can we bump up the architecture? */
+ else if (needed_arch_mask & SPARC_OPCODE_SUPPORTED (max_architecture))
+ {
+ enum sparc_opcode_arch_val needed_architecture =
+ sparc_ffs (SPARC_OPCODE_SUPPORTED (max_architecture)
+ & needed_arch_mask);
+
+ assert (needed_architecture <= SPARC_OPCODE_ARCH_MAX);
+ if (warn_on_bump
+ && needed_architecture > warn_after_architecture)
+ {
+ as_warn (_("architecture bumped from \"%s\" to \"%s\" on \"%s\""),
+ sparc_opcode_archs[current_architecture].name,
+ sparc_opcode_archs[needed_architecture].name,
+ str);
+ warn_after_architecture = needed_architecture;
+ }
+ current_architecture = needed_architecture;
+ }
+ /* Conflict. */
+ /* ??? This seems to be a bit fragile. What if the next entry in
+ the opcode table is the one we want and it is supported?
+ It is possible to arrange the table today so that this can't
+ happen but what about tomorrow? */
+ else
+ {
+ int arch,printed_one_p = 0;
+ char *p;
+ char required_archs[SPARC_OPCODE_ARCH_MAX * 16];
+
+ /* Create a list of the architectures that support the insn. */
+ needed_arch_mask &= ~ SPARC_OPCODE_SUPPORTED (max_architecture);
+ p = required_archs;
+ arch = sparc_ffs (needed_arch_mask);
+ while ((1 << arch) <= needed_arch_mask)
+ {
+ if ((1 << arch) & needed_arch_mask)
+ {
+ if (printed_one_p)
+ *p++ = '|';
+ strcpy (p, sparc_opcode_archs[arch].name);
+ p += strlen (p);
+ printed_one_p = 1;
+ }
+ ++arch;
+ }
+
+ as_bad (_("Architecture mismatch on \"%s\"."), str);
+ as_tsktsk (_(" (Requires %s; requested architecture is %s.)"),
+ required_archs,
+ sparc_opcode_archs[max_architecture].name);
+ return;
+ }
+ } /* if no match */
+
+ break;
+ } /* forever looking for a match */
+
+ the_insn.opcode = opcode;
+}
+
+/* Parse an argument that can be expressed as a keyword.
+ (eg: #StoreStore or %ccfr).
+ The result is a boolean indicating success.
+ If successful, INPUT_POINTER is updated. */
+
+static int
+parse_keyword_arg (lookup_fn, input_pointerP, valueP)
+ int (*lookup_fn) PARAMS ((const char *));
+ char **input_pointerP;
+ int *valueP;
+{
+ int value;
+ char c, *p, *q;
+
+ p = *input_pointerP;
+ for (q = p + (*p == '#' || *p == '%');
+ isalnum ((unsigned char) *q) || *q == '_';
+ ++q)
+ continue;
+ c = *q;
+ *q = 0;
+ value = (*lookup_fn) (p);
+ *q = c;
+ if (value == -1)
+ return 0;
+ *valueP = value;
+ *input_pointerP = q;
+ return 1;
+}
+
+/* Parse an argument that is a constant expression.
+ The result is a boolean indicating success. */
+
+static int
+parse_const_expr_arg (input_pointerP, valueP)
+ char **input_pointerP;
+ int *valueP;
+{
+ char *save = input_line_pointer;
+ expressionS exp;
+
+ input_line_pointer = *input_pointerP;
+ /* The next expression may be something other than a constant
+ (say if we're not processing the right variant of the insn).
+ Don't call expression unless we're sure it will succeed as it will
+ signal an error (which we want to defer until later). */
+ /* FIXME: It might be better to define md_operand and have it recognize
+ things like %asi, etc. but continuing that route through to the end
+ is a lot of work. */
+ if (*input_line_pointer == '%')
+ {
+ input_line_pointer = save;
+ return 0;
+ }
+ expression (&exp);
+ *input_pointerP = input_line_pointer;
+ input_line_pointer = save;
+ if (exp.X_op != O_constant)
+ return 0;
+ *valueP = exp.X_add_number;
+ return 1;
+}
+
+/* Subroutine of sparc_ip to parse an expression. */
+
+static int
+get_expression (str)
+ char *str;
+{
+ char *save_in;
+ segT seg;
+
+ save_in = input_line_pointer;
+ input_line_pointer = str;
+ seg = expression (&the_insn.exp);
+ if (seg != absolute_section
+ && seg != text_section
+ && seg != data_section
+ && seg != bss_section
+ && seg != undefined_section)
+ {
+ the_insn.error = _("bad segment");
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+ return 1;
+ }
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+ return 0;
+}
+
+/* Subroutine of md_assemble to output one insn. */
+
+static void
+output_insn (insn, the_insn)
+ const struct sparc_opcode *insn;
+ struct sparc_it *the_insn;
+{
+ char *toP = frag_more (4);
+
+ /* put out the opcode */
+ if (INSN_BIG_ENDIAN)
+ number_to_chars_bigendian (toP, (valueT) the_insn->opcode, 4);
+ else
+ number_to_chars_littleendian (toP, (valueT) the_insn->opcode, 4);
+
+ /* put out the symbol-dependent stuff */
+ if (the_insn->reloc != BFD_RELOC_NONE)
+ {
+ fixS *fixP = fix_new_exp (frag_now, /* which frag */
+ (toP - frag_now->fr_literal), /* where */
+ 4, /* size */
+ &the_insn->exp,
+ the_insn->pcrel,
+ the_insn->reloc);
+ /* Turn off overflow checking in fixup_segment. We'll do our
+ own overflow checking in md_apply_fix3. This is necessary because
+ the insn size is 4 and fixup_segment will signal an overflow for
+ large 8 byte quantities. */
+ fixP->fx_no_overflow = 1;
+ }
+
+ last_insn = insn;
+ last_opcode = the_insn->opcode;
+}
+
+/*
+ This is identical to the md_atof in m68k.c. I think this is right,
+ but I'm not sure.
+
+ Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP . An error message is returned, or NULL on OK.
+ */
+
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+char *
+md_atof (type, litP, sizeP)
+ char type;
+ char *litP;
+ int *sizeP;
+{
+ int i,prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ char *t;
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("Bad call to MD_ATOF()");
+ }
+
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+ *sizeP = prec * sizeof (LITTLENUM_TYPE);
+
+ if (target_big_endian)
+ {
+ for (i = 0; i < prec; i++)
+ {
+ md_number_to_chars (litP, (valueT) words[i], sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ }
+ else
+ {
+ for (i = prec - 1; i >= 0; i--)
+ {
+ md_number_to_chars (litP, (valueT) words[i], sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ }
+
+ return 0;
+}
+
+/* Write a value out to the object file, using the appropriate
+ endianness. */
+
+void
+md_number_to_chars (buf, val, n)
+ char *buf;
+ valueT val;
+ int n;
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else if (target_little_endian_data
+ && ((n == 4 || n == 2) && ~now_seg->flags & SEC_ALLOC))
+ /* Output debug words, which are not in allocated sections, as big endian */
+ number_to_chars_bigendian (buf, val, n);
+ else if (target_little_endian_data || ! target_big_endian)
+ number_to_chars_littleendian (buf, val, n);
+}
+
+/* Apply a fixS to the frags, now that we know the value it ought to
+ hold. */
+
+int
+md_apply_fix3 (fixP, value, segment)
+ fixS *fixP;
+ valueT *value;
+ segT segment;
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ offsetT val;
+ long insn;
+
+ val = *value;
+
+ assert (fixP->fx_r_type < BFD_RELOC_UNUSED);
+
+ fixP->fx_addnumber = val; /* Remember value for emit_reloc */
+
+#ifdef OBJ_ELF
+ /* FIXME: SPARC ELF relocations don't use an addend in the data
+ field itself. This whole approach should be somehow combined
+ with the calls to bfd_install_relocation. Also, the value passed
+ in by fixup_segment includes the value of a defined symbol. We
+ don't want to include the value of an externally visible symbol. */
+ if (fixP->fx_addsy != NULL)
+ {
+ if (fixP->fx_addsy->sy_used_in_reloc
+ && (S_IS_EXTERNAL (fixP->fx_addsy)
+ || S_IS_WEAK (fixP->fx_addsy)
+ || (sparc_pic_code && ! fixP->fx_pcrel)
+ || (S_GET_SEGMENT (fixP->fx_addsy) != segment
+ && ((bfd_get_section_flags (stdoutput,
+ S_GET_SEGMENT (fixP->fx_addsy))
+ & SEC_LINK_ONCE) != 0
+ || strncmp (segment_name (S_GET_SEGMENT (fixP->fx_addsy)),
+ ".gnu.linkonce",
+ sizeof ".gnu.linkonce" - 1) == 0)))
+ && S_GET_SEGMENT (fixP->fx_addsy) != absolute_section
+ && S_GET_SEGMENT (fixP->fx_addsy) != undefined_section
+ && ! bfd_is_com_section (S_GET_SEGMENT (fixP->fx_addsy)))
+ fixP->fx_addnumber -= S_GET_VALUE (fixP->fx_addsy);
+ return 1;
+ }
+#endif
+
+ /* This is a hack. There should be a better way to
+ handle this. Probably in terms of howto fields, once
+ we can look at these fixups in terms of howtos. */
+ if (fixP->fx_r_type == BFD_RELOC_32_PCREL_S2 && fixP->fx_addsy)
+ val += fixP->fx_where + fixP->fx_frag->fr_address;
+
+#ifdef OBJ_AOUT
+ /* FIXME: More ridiculous gas reloc hacking. If we are going to
+ generate a reloc, then we just want to let the reloc addend set
+ the value. We do not want to also stuff the addend into the
+ object file. Including the addend in the object file works when
+ doing a static link, because the linker will ignore the object
+ file contents. However, the dynamic linker does not ignore the
+ object file contents. */
+ if (fixP->fx_addsy != NULL
+ && fixP->fx_r_type != BFD_RELOC_32_PCREL_S2)
+ val = 0;
+
+ /* When generating PIC code, we do not want an addend for a reloc
+ against a local symbol. We adjust fx_addnumber to cancel out the
+ value already included in val, and to also cancel out the
+ adjustment which bfd_install_relocation will create. */
+ if (sparc_pic_code
+ && fixP->fx_r_type != BFD_RELOC_32_PCREL_S2
+ && fixP->fx_addsy != NULL
+ && ! S_IS_COMMON (fixP->fx_addsy)
+ && (fixP->fx_addsy->bsym->flags & BSF_SECTION_SYM) == 0)
+ fixP->fx_addnumber -= 2 * S_GET_VALUE (fixP->fx_addsy);
+
+ /* When generating PIC code, we need to fiddle to get
+ bfd_install_relocation to do the right thing for a PC relative
+ reloc against a local symbol which we are going to keep. */
+ if (sparc_pic_code
+ && fixP->fx_r_type == BFD_RELOC_32_PCREL_S2
+ && fixP->fx_addsy != NULL
+ && (S_IS_EXTERNAL (fixP->fx_addsy)
+ || S_IS_WEAK (fixP->fx_addsy))
+ && S_IS_DEFINED (fixP->fx_addsy)
+ && ! S_IS_COMMON (fixP->fx_addsy))
+ {
+ val = 0;
+ fixP->fx_addnumber -= 2 * S_GET_VALUE (fixP->fx_addsy);
+ }
+#endif
+
+ /* If this is a data relocation, just output VAL. */
+
+ if (fixP->fx_r_type == BFD_RELOC_16)
+ {
+ md_number_to_chars (buf, val, 2);
+ }
+ else if (fixP->fx_r_type == BFD_RELOC_32
+ || fixP->fx_r_type == BFD_RELOC_SPARC_REV32)
+ {
+ md_number_to_chars (buf, val, 4);
+ }
+ else if (fixP->fx_r_type == BFD_RELOC_64)
+ {
+ md_number_to_chars (buf, val, 8);
+ }
+ else if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ {
+ fixP->fx_done = 0;
+ return 1;
+ }
+ else
+ {
+ /* It's a relocation against an instruction. */
+
+ if (INSN_BIG_ENDIAN)
+ insn = bfd_getb32 ((unsigned char *) buf);
+ else
+ insn = bfd_getl32 ((unsigned char *) buf);
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_32_PCREL_S2:
+ val = val >> 2;
+ /* FIXME: This increment-by-one deserves a comment of why it's
+ being done! */
+ if (! sparc_pic_code
+ || fixP->fx_addsy == NULL
+ || (fixP->fx_addsy->bsym->flags & BSF_SECTION_SYM) != 0)
+ ++val;
+ insn |= val & 0x3fffffff;
+ break;
+
+ case BFD_RELOC_SPARC_11:
+ if (! in_signed_range (val, 0x7ff))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ insn |= val & 0x7ff;
+ break;
+
+ case BFD_RELOC_SPARC_10:
+ if (! in_signed_range (val, 0x3ff))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ insn |= val & 0x3ff;
+ break;
+
+ case BFD_RELOC_SPARC_7:
+ if (! in_bitfield_range (val, 0x7f))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ insn |= val & 0x7f;
+ break;
+
+ case BFD_RELOC_SPARC_6:
+ if (! in_bitfield_range (val, 0x3f))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ insn |= val & 0x3f;
+ break;
+
+ case BFD_RELOC_SPARC_5:
+ if (! in_bitfield_range (val, 0x1f))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ insn |= val & 0x1f;
+ break;
+
+ case BFD_RELOC_SPARC_WDISP16:
+ /* FIXME: simplify */
+ if (((val > 0) && (val & ~0x3fffc))
+ || ((val < 0) && (~(val - 1) & ~0x3fffc)))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ /* FIXME: The +1 deserves a comment. */
+ val = (val >> 2) + 1;
+ insn |= ((val & 0xc000) << 6) | (val & 0x3fff);
+ break;
+
+ case BFD_RELOC_SPARC_WDISP19:
+ /* FIXME: simplify */
+ if (((val > 0) && (val & ~0x1ffffc))
+ || ((val < 0) && (~(val - 1) & ~0x1ffffc)))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ /* FIXME: The +1 deserves a comment. */
+ val = (val >> 2) + 1;
+ insn |= val & 0x7ffff;
+ break;
+
+ case BFD_RELOC_SPARC_HH22:
+ val = BSR (val, 32);
+ /* intentional fallthrough */
+
+ case BFD_RELOC_SPARC_LM22:
+ case BFD_RELOC_HI22:
+ if (!fixP->fx_addsy)
+ {
+ insn |= (val >> 10) & 0x3fffff;
+ }
+ else
+ {
+ /* FIXME: Need comment explaining why we do this. */
+ insn &= ~0xffff;
+ }
+ break;
+
+ case BFD_RELOC_SPARC22:
+ if (val & ~0x003fffff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ insn |= (val & 0x3fffff);
+ break;
+
+ case BFD_RELOC_SPARC_HM10:
+ val = BSR (val, 32);
+ /* intentional fallthrough */
+
+ case BFD_RELOC_LO10:
+ if (!fixP->fx_addsy)
+ {
+ insn |= val & 0x3ff;
+ }
+ else
+ {
+ /* FIXME: Need comment explaining why we do this. */
+ insn &= ~0xff;
+ }
+ break;
+
+ case BFD_RELOC_SPARC13:
+ if (! in_signed_range (val, 0x1fff))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ insn |= val & 0x1fff;
+ break;
+
+ case BFD_RELOC_SPARC_WDISP22:
+ val = (val >> 2) + 1;
+ /* FALLTHROUGH */
+ case BFD_RELOC_SPARC_BASE22:
+ insn |= val & 0x3fffff;
+ break;
+
+ case BFD_RELOC_SPARC_H44:
+ if (!fixP->fx_addsy)
+ {
+ bfd_vma tval = val;
+ tval >>= 22;
+ insn |= tval & 0x3fffff;
+ }
+ break;
+
+ case BFD_RELOC_SPARC_M44:
+ if (!fixP->fx_addsy)
+ insn |= (val >> 12) & 0x3ff;
+ break;
+
+ case BFD_RELOC_SPARC_L44:
+ if (!fixP->fx_addsy)
+ insn |= val & 0xfff;
+ break;
+
+ case BFD_RELOC_SPARC_HIX22:
+ if (!fixP->fx_addsy)
+ {
+ val ^= ~ (offsetT) 0;
+ insn |= (val >> 10) & 0x3fffff;
+ }
+ break;
+
+ case BFD_RELOC_SPARC_LOX10:
+ if (!fixP->fx_addsy)
+ insn |= 0x1c00 | (val & 0x3ff);
+ break;
+
+ case BFD_RELOC_NONE:
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("bad or unhandled relocation type: 0x%02x"),
+ fixP->fx_r_type);
+ break;
+ }
+
+ if (INSN_BIG_ENDIAN)
+ bfd_putb32 (insn, (unsigned char *) buf);
+ else
+ bfd_putl32 (insn, (unsigned char *) buf);
+ }
+
+ /* Are we finished with this relocation now? */
+ if (fixP->fx_addsy == 0 && !fixP->fx_pcrel)
+ fixP->fx_done = 1;
+
+ return 1;
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format. */
+arelent *
+tc_gen_reloc (section, fixp)
+ asection *section;
+ fixS *fixp;
+{
+ arelent *reloc;
+ bfd_reloc_code_real_type code;
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+
+ reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_16:
+ case BFD_RELOC_32:
+ case BFD_RELOC_HI22:
+ case BFD_RELOC_LO10:
+ case BFD_RELOC_32_PCREL_S2:
+ case BFD_RELOC_SPARC13:
+ case BFD_RELOC_SPARC_BASE13:
+ case BFD_RELOC_SPARC_WDISP16:
+ case BFD_RELOC_SPARC_WDISP19:
+ case BFD_RELOC_SPARC_WDISP22:
+ case BFD_RELOC_64:
+ case BFD_RELOC_SPARC_5:
+ case BFD_RELOC_SPARC_6:
+ case BFD_RELOC_SPARC_7:
+ case BFD_RELOC_SPARC_10:
+ case BFD_RELOC_SPARC_11:
+ case BFD_RELOC_SPARC_HH22:
+ case BFD_RELOC_SPARC_HM10:
+ case BFD_RELOC_SPARC_LM22:
+ case BFD_RELOC_SPARC_PC_HH22:
+ case BFD_RELOC_SPARC_PC_HM10:
+ case BFD_RELOC_SPARC_PC_LM22:
+ case BFD_RELOC_SPARC_H44:
+ case BFD_RELOC_SPARC_M44:
+ case BFD_RELOC_SPARC_L44:
+ case BFD_RELOC_SPARC_HIX22:
+ case BFD_RELOC_SPARC_LOX10:
+ case BFD_RELOC_SPARC_REV32:
+ case BFD_RELOC_VTABLE_ENTRY:
+ case BFD_RELOC_VTABLE_INHERIT:
+ code = fixp->fx_r_type;
+ break;
+ default:
+ abort ();
+ return NULL;
+ }
+
+#if defined (OBJ_ELF) || defined (OBJ_AOUT)
+ /* If we are generating PIC code, we need to generate a different
+ set of relocs. */
+
+#ifdef OBJ_ELF
+#define GOT_NAME "_GLOBAL_OFFSET_TABLE_"
+#else
+#define GOT_NAME "__GLOBAL_OFFSET_TABLE_"
+#endif
+
+ if (sparc_pic_code)
+ {
+ switch (code)
+ {
+ case BFD_RELOC_32_PCREL_S2:
+ if (! S_IS_DEFINED (fixp->fx_addsy)
+ || S_IS_COMMON (fixp->fx_addsy)
+ || S_IS_EXTERNAL (fixp->fx_addsy)
+ || S_IS_WEAK (fixp->fx_addsy))
+ code = BFD_RELOC_SPARC_WPLT30;
+ break;
+ case BFD_RELOC_HI22:
+ if (fixp->fx_addsy != NULL
+ && strcmp (S_GET_NAME (fixp->fx_addsy), GOT_NAME) == 0)
+ code = BFD_RELOC_SPARC_PC22;
+ else
+ code = BFD_RELOC_SPARC_GOT22;
+ break;
+ case BFD_RELOC_LO10:
+ if (fixp->fx_addsy != NULL
+ && strcmp (S_GET_NAME (fixp->fx_addsy), GOT_NAME) == 0)
+ code = BFD_RELOC_SPARC_PC10;
+ else
+ code = BFD_RELOC_SPARC_GOT10;
+ break;
+ case BFD_RELOC_SPARC13:
+ code = BFD_RELOC_SPARC_GOT13;
+ break;
+ default:
+ break;
+ }
+ }
+#endif /* defined (OBJ_ELF) || defined (OBJ_AOUT) */
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+ if (reloc->howto == 0)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("internal error: can't export reloc type %d (`%s')"),
+ fixp->fx_r_type, bfd_get_reloc_code_name (code));
+ return 0;
+ }
+
+ /* @@ Why fx_addnumber sometimes and fx_offset other times? */
+#ifdef OBJ_AOUT
+
+ if (reloc->howto->pc_relative == 0
+ || code == BFD_RELOC_SPARC_PC10
+ || code == BFD_RELOC_SPARC_PC22)
+ reloc->addend = fixp->fx_addnumber;
+ else if (sparc_pic_code
+ && fixp->fx_r_type == BFD_RELOC_32_PCREL_S2
+ && fixp->fx_addsy != NULL
+ && (S_IS_EXTERNAL (fixp->fx_addsy)
+ || S_IS_WEAK (fixp->fx_addsy))
+ && S_IS_DEFINED (fixp->fx_addsy)
+ && ! S_IS_COMMON (fixp->fx_addsy))
+ reloc->addend = fixp->fx_addnumber;
+ else
+ reloc->addend = fixp->fx_offset - reloc->address;
+
+#else /* elf or coff */
+
+ if (reloc->howto->pc_relative == 0
+ || code == BFD_RELOC_SPARC_PC10
+ || code == BFD_RELOC_SPARC_PC22)
+ reloc->addend = fixp->fx_addnumber;
+ else if ((fixp->fx_addsy->bsym->flags & BSF_SECTION_SYM) != 0)
+ reloc->addend = (section->vma
+ + fixp->fx_addnumber
+ + md_pcrel_from (fixp));
+ else
+ reloc->addend = fixp->fx_offset;
+#endif
+
+ return reloc;
+}
+
+/* We have no need to default values of symbols. */
+
+/* ARGSUSED */
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ return 0;
+} /* md_undefined_symbol() */
+
+/* Round up a section size to the appropriate boundary. */
+valueT
+md_section_align (segment, size)
+ segT segment;
+ valueT size;
+{
+#ifndef OBJ_ELF
+ /* This is not right for ELF; a.out wants it, and COFF will force
+ the alignment anyways. */
+ valueT align = ((valueT) 1
+ << (valueT) bfd_get_section_alignment (stdoutput, segment));
+ valueT newsize;
+ /* turn alignment value into a mask */
+ align--;
+ newsize = (size + align) & ~align;
+ return newsize;
+#else
+ return size;
+#endif
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the sparc, they're relative to the address of the offset, plus
+ its size. This gets us to the following instruction.
+ (??? Is this right? FIXME-SOON) */
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ long ret;
+
+ ret = fixP->fx_where + fixP->fx_frag->fr_address;
+ if (! sparc_pic_code
+ || fixP->fx_addsy == NULL
+ || (fixP->fx_addsy->bsym->flags & BSF_SECTION_SYM) != 0)
+ ret += fixP->fx_size;
+ return ret;
+}
+
+/* Return log2 (VALUE), or -1 if VALUE is not an exact positive power
+ of two. */
+
+static int
+log2 (value)
+ int value;
+{
+ int shift;
+
+ if (value <= 0)
+ return -1;
+
+ for (shift = 0; (value & 1) == 0; value >>= 1)
+ ++shift;
+
+ return (value == 1) ? shift : -1;
+}
+
+/*
+ * sort of like s_lcomm
+ */
+
+#ifndef OBJ_ELF
+static int max_alignment = 15;
+#endif
+
+static void
+s_reserve (ignore)
+ int ignore;
+{
+ char *name;
+ char *p;
+ char c;
+ int align;
+ int size;
+ int temp;
+ symbolS *symbolP;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("Expected comma after name"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ ++input_line_pointer;
+
+ if ((size = get_absolute_expression ()) < 0)
+ {
+ as_bad (_("BSS length (%d.) <0! Ignored."), size);
+ ignore_rest_of_line ();
+ return;
+ } /* bad length */
+
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+
+ if (strncmp (input_line_pointer, ",\"bss\"", 6) != 0
+ && strncmp (input_line_pointer, ",\".bss\"", 7) != 0)
+ {
+ as_bad (_("bad .reserve segment -- expected BSS segment"));
+ return;
+ }
+
+ if (input_line_pointer[2] == '.')
+ input_line_pointer += 7;
+ else
+ input_line_pointer += 6;
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\n')
+ {
+ as_bad (_("missing alignment"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ align = (int) get_absolute_expression ();
+
+#ifndef OBJ_ELF
+ if (align > max_alignment)
+ {
+ align = max_alignment;
+ as_warn (_("alignment too large; assuming %d"), align);
+ }
+#endif
+
+ if (align < 0)
+ {
+ as_bad (_("negative alignment"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (align != 0)
+ {
+ temp = log2 (align);
+ if (temp < 0)
+ {
+ as_bad (_("alignment not a power of 2"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ align = temp;
+ }
+
+ record_alignment (bss_section, align);
+ }
+ else
+ align = 0;
+
+ if (!S_IS_DEFINED (symbolP)
+#ifdef OBJ_AOUT
+ && S_GET_OTHER (symbolP) == 0
+ && S_GET_DESC (symbolP) == 0
+#endif
+ )
+ {
+ if (! need_pass_2)
+ {
+ char *pfrag;
+ segT current_seg = now_seg;
+ subsegT current_subseg = now_subseg;
+
+ subseg_set (bss_section, 1); /* switch to bss */
+
+ if (align)
+ frag_align (align, 0, 0); /* do alignment */
+
+ /* detach from old frag */
+ if (S_GET_SEGMENT(symbolP) == bss_section)
+ symbolP->sy_frag->fr_symbol = NULL;
+
+ symbolP->sy_frag = frag_now;
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT)0, symbolP,
+ (offsetT) size, (char *)0);
+ *pfrag = 0;
+
+ S_SET_SEGMENT (symbolP, bss_section);
+
+ subseg_set (current_seg, current_subseg);
+
+#ifdef OBJ_ELF
+ S_SET_SIZE (symbolP, size);
+#endif
+ }
+ }
+ else
+ {
+ as_warn("Ignoring attempt to re-define symbol %s",
+ S_GET_NAME (symbolP));
+ } /* if not redefining */
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_common (ignore)
+ int ignore;
+{
+ char *name;
+ char c;
+ char *p;
+ int temp, size;
+ symbolS *symbolP;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ /* just after name is now '\0' */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("Expected comma after symbol-name"));
+ ignore_rest_of_line ();
+ return;
+ }
+ input_line_pointer++; /* skip ',' */
+ if ((temp = get_absolute_expression ()) < 0)
+ {
+ as_bad (_(".COMMon length (%d.) <0! Ignored."), temp);
+ ignore_rest_of_line ();
+ return;
+ }
+ size = temp;
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+ if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
+ {
+ as_bad (_("Ignoring attempt to re-define symbol"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (S_GET_VALUE (symbolP) != 0)
+ {
+ if (S_GET_VALUE (symbolP) != (valueT) size)
+ {
+ as_warn (_("Length of .comm \"%s\" is already %ld. Not changed to %d."),
+ S_GET_NAME (symbolP), (long) S_GET_VALUE (symbolP), size);
+ }
+ }
+ else
+ {
+#ifndef OBJ_ELF
+ S_SET_VALUE (symbolP, (valueT) size);
+ S_SET_EXTERNAL (symbolP);
+#endif
+ }
+ know (symbolP->sy_frag == &zero_address_frag);
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("Expected comma after common length"));
+ ignore_rest_of_line ();
+ return;
+ }
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != '"')
+ {
+ temp = get_absolute_expression ();
+
+#ifndef OBJ_ELF
+ if (temp > max_alignment)
+ {
+ temp = max_alignment;
+ as_warn (_("alignment too large; assuming %d"), temp);
+ }
+#endif
+
+ if (temp < 0)
+ {
+ as_bad (_("negative alignment"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+#ifdef OBJ_ELF
+ if (symbolP->local)
+ {
+ segT old_sec;
+ int old_subsec;
+ char *p;
+ int align;
+
+ old_sec = now_seg;
+ old_subsec = now_subseg;
+
+ if (temp == 0)
+ align = 0;
+ else
+ align = log2 (temp);
+
+ if (align < 0)
+ {
+ as_bad (_("alignment not a power of 2"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ record_alignment (bss_section, align);
+ subseg_set (bss_section, 0);
+ if (align)
+ frag_align (align, 0, 0);
+ if (S_GET_SEGMENT (symbolP) == bss_section)
+ symbolP->sy_frag->fr_symbol = 0;
+ symbolP->sy_frag = frag_now;
+ p = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
+ (offsetT) size, (char *) 0);
+ *p = 0;
+ S_SET_SEGMENT (symbolP, bss_section);
+ S_CLEAR_EXTERNAL (symbolP);
+ S_SET_SIZE (symbolP, size);
+ subseg_set (old_sec, old_subsec);
+ }
+ else
+#endif /* OBJ_ELF */
+ {
+ allocate_common:
+ S_SET_VALUE (symbolP, (valueT) size);
+#ifdef OBJ_ELF
+ S_SET_ALIGN (symbolP, temp);
+ S_SET_SIZE (symbolP, size);
+#endif
+ S_SET_EXTERNAL (symbolP);
+ S_SET_SEGMENT (symbolP, bfd_com_section_ptr);
+ }
+ }
+ else
+ {
+ input_line_pointer++;
+ /* @@ Some use the dot, some don't. Can we get some consistency?? */
+ if (*input_line_pointer == '.')
+ input_line_pointer++;
+ /* @@ Some say data, some say bss. */
+ if (strncmp (input_line_pointer, "bss\"", 4)
+ && strncmp (input_line_pointer, "data\"", 5))
+ {
+ while (*--input_line_pointer != '"')
+ ;
+ input_line_pointer--;
+ goto bad_common_segment;
+ }
+ while (*input_line_pointer++ != '"')
+ ;
+ goto allocate_common;
+ }
+
+#ifdef BFD_ASSEMBLER
+ symbolP->bsym->flags |= BSF_OBJECT;
+#endif
+
+ demand_empty_rest_of_line ();
+ return;
+
+ {
+ bad_common_segment:
+ p = input_line_pointer;
+ while (*p && *p != '\n')
+ p++;
+ c = *p;
+ *p = '\0';
+ as_bad (_("bad .common segment %s"), input_line_pointer + 1);
+ *p = c;
+ input_line_pointer = p;
+ ignore_rest_of_line ();
+ return;
+ }
+}
+
+/* Handle the .empty pseudo-op. This supresses the warnings about
+ invalid delay slot usage. */
+
+static void
+s_empty (ignore)
+ int ignore;
+{
+ /* The easy way to implement is to just forget about the last
+ instruction. */
+ last_insn = NULL;
+}
+
+static void
+s_seg (ignore)
+ int ignore;
+{
+
+ if (strncmp (input_line_pointer, "\"text\"", 6) == 0)
+ {
+ input_line_pointer += 6;
+ s_text (0);
+ return;
+ }
+ if (strncmp (input_line_pointer, "\"data\"", 6) == 0)
+ {
+ input_line_pointer += 6;
+ s_data (0);
+ return;
+ }
+ if (strncmp (input_line_pointer, "\"data1\"", 7) == 0)
+ {
+ input_line_pointer += 7;
+ s_data1 ();
+ return;
+ }
+ if (strncmp (input_line_pointer, "\"bss\"", 5) == 0)
+ {
+ input_line_pointer += 5;
+ /* We only support 2 segments -- text and data -- for now, so
+ things in the "bss segment" will have to go into data for now.
+ You can still allocate SEG_BSS stuff with .lcomm or .reserve. */
+ subseg_set (data_section, 255); /* FIXME-SOMEDAY */
+ return;
+ }
+ as_bad (_("Unknown segment type"));
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_data1 ()
+{
+ subseg_set (data_section, 1);
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_proc (ignore)
+ int ignore;
+{
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ ++input_line_pointer;
+ }
+ ++input_line_pointer;
+}
+
+/* This static variable is set by s_uacons to tell sparc_cons_align
+ that the expession does not need to be aligned. */
+
+static int sparc_no_align_cons = 0;
+
+/* This handles the unaligned space allocation pseudo-ops, such as
+ .uaword. .uaword is just like .word, but the value does not need
+ to be aligned. */
+
+static void
+s_uacons (bytes)
+ int bytes;
+{
+ /* Tell sparc_cons_align not to align this value. */
+ sparc_no_align_cons = 1;
+ cons (bytes);
+}
+
+/* If the --enforce-aligned-data option is used, we require .word,
+ et. al., to be aligned correctly. We do it by setting up an
+ rs_align_code frag, and checking in HANDLE_ALIGN to make sure that
+ no unexpected alignment was introduced.
+
+ The SunOS and Solaris native assemblers enforce aligned data by
+ default. We don't want to do that, because gcc can deliberately
+ generate misaligned data if the packed attribute is used. Instead,
+ we permit misaligned data by default, and permit the user to set an
+ option to check for it. */
+
+void
+sparc_cons_align (nbytes)
+ int nbytes;
+{
+ int nalign;
+ char *p;
+
+ /* Only do this if we are enforcing aligned data. */
+ if (! enforce_aligned_data)
+ return;
+
+ if (sparc_no_align_cons)
+ {
+ /* This is an unaligned pseudo-op. */
+ sparc_no_align_cons = 0;
+ return;
+ }
+
+ nalign = log2 (nbytes);
+ if (nalign == 0)
+ return;
+
+ assert (nalign > 0);
+
+ if (now_seg == absolute_section)
+ {
+ if ((abs_section_offset & ((1 << nalign) - 1)) != 0)
+ as_bad (_("misaligned data"));
+ return;
+ }
+
+ p = frag_var (rs_align_code, 1, 1, (relax_substateT) 0,
+ (symbolS *) NULL, (offsetT) nalign, (char *) NULL);
+
+ record_alignment (now_seg, nalign);
+}
+
+/* This is where we do the unexpected alignment check.
+ This is called from HANDLE_ALIGN in tc-sparc.h. */
+
+void
+sparc_handle_align (fragp)
+ fragS *fragp;
+{
+ if (fragp->fr_type == rs_align_code && !fragp->fr_subtype
+ && fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix != 0)
+ as_bad_where (fragp->fr_file, fragp->fr_line, _("misaligned data"));
+ if (fragp->fr_type == rs_align_code && fragp->fr_subtype == 1024)
+ {
+ int count = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+
+ if (count >= 4
+ && !(count & 3)
+ && count <= 1024
+ && !((long)(fragp->fr_literal + fragp->fr_fix) & 3))
+ {
+ unsigned *p = (unsigned *)(fragp->fr_literal + fragp->fr_fix);
+ int i;
+
+ for (i = 0; i < count; i += 4, p++)
+ if (INSN_BIG_ENDIAN)
+ number_to_chars_bigendian ((char *)p, 0x01000000, 4); /* emit nops */
+ else
+ number_to_chars_littleendian ((char *)p, 0x10000000, 4);
+
+ if (SPARC_OPCODE_ARCH_V9_P (max_architecture) && count > 8)
+ {
+ char *waddr = &fragp->fr_literal[fragp->fr_fix];
+ unsigned wval = (0x30680000 | count >> 2); /* ba,a,pt %xcc, 1f */
+ if (INSN_BIG_ENDIAN)
+ number_to_chars_bigendian (waddr, wval, 4);
+ else
+ number_to_chars_littleendian (waddr, wval, 4);
+ }
+ fragp->fr_var = count;
+ }
+ }
+}
+
+#ifdef OBJ_ELF
+/* Some special processing for a Sparc ELF file. */
+
+void
+sparc_elf_final_processing ()
+{
+ /* Set the Sparc ELF flag bits. FIXME: There should probably be some
+ sort of BFD interface for this. */
+ if (sparc_arch_size == 64)
+ {
+ switch (sparc_memory_model)
+ {
+ case MM_RMO:
+ elf_elfheader (stdoutput)->e_flags |= EF_SPARCV9_RMO;
+ break;
+ case MM_PSO:
+ elf_elfheader (stdoutput)->e_flags |= EF_SPARCV9_PSO;
+ break;
+ default:
+ break;
+ }
+ }
+ else if (current_architecture >= SPARC_OPCODE_ARCH_V9)
+ elf_elfheader (stdoutput)->e_flags |= EF_SPARC_32PLUS;
+ if (current_architecture == SPARC_OPCODE_ARCH_V9A)
+ elf_elfheader (stdoutput)->e_flags |= EF_SPARC_SUN_US1;
+}
+#endif
+
+/* This is called by emit_expr via TC_CONS_FIX_NEW when creating a
+ reloc for a cons. We could use the definition there, except that
+ we want to handle little endian relocs specially. */
+
+void
+cons_fix_new_sparc (frag, where, nbytes, exp)
+ fragS *frag;
+ int where;
+ unsigned int nbytes;
+ expressionS *exp;
+{
+ bfd_reloc_code_real_type r;
+
+ r = (nbytes == 1 ? BFD_RELOC_8 :
+ (nbytes == 2 ? BFD_RELOC_16 :
+ (nbytes == 4 ? BFD_RELOC_32 : BFD_RELOC_64)));
+
+ if (target_little_endian_data && nbytes == 4
+ && now_seg->flags & SEC_ALLOC)
+ r = BFD_RELOC_SPARC_REV32;
+ fix_new_exp (frag, where, (int) nbytes, exp, 0, r);
+}
+
+#ifdef OBJ_ELF
+int
+elf32_sparc_force_relocation (fixp)
+ struct fix *fixp;
+{
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 1;
+
+ return 0;
+}
+#endif
+
diff --git a/gas/config/tc-sparc.h b/gas/config/tc-sparc.h
new file mode 100644
index 0000000..2a05764
--- /dev/null
+++ b/gas/config/tc-sparc.h
@@ -0,0 +1,164 @@
+/* tc-sparc.h - Macros and type defines for the sparc.
+ Copyright (C) 1989, 90-96, 97, 98, 1999 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with GAS; see the file COPYING. If not, write
+ to the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifndef TC_SPARC
+#define TC_SPARC 1
+
+#ifdef ANSI_PROTOTYPES
+struct frag;
+#endif
+
+/* This is used to set the default value for `target_big_endian'. */
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+#define LOCAL_LABELS_FB 1
+
+#define TARGET_ARCH bfd_arch_sparc
+
+extern const char *sparc_target_format PARAMS ((void));
+#define TARGET_FORMAT sparc_target_format ()
+
+#if 0
+#ifdef TE_SPARCAOUT
+/* Bi-endian support may eventually be unconditional, but until things are
+ working well it's only provided for targets that need it. */
+#define SPARC_BIENDIAN
+#endif
+#endif
+/* Make it unconditional and check if -EL is valid after option parsing */
+#define SPARC_BIENDIAN
+
+#define WORKING_DOT_WORD
+
+#define md_convert_frag(b,s,f) {as_fatal (_("sparc convert_frag\n"));}
+#define md_estimate_size_before_relax(f,s) \
+ (as_fatal(_("estimate_size_before_relax called")),1)
+
+#define LISTING_HEADER "SPARC GAS "
+
+extern int sparc_pic_code;
+
+#define md_do_align(n, fill, len, max, around) \
+if ((n) && (n) <= 10 && !need_pass_2 && !(fill) \
+ && now_seg != data_section && now_seg != bss_section) \
+ { \
+ char *p; \
+ p = frag_var (rs_align_code, 1 << n, 1, (relax_substateT) 1024, \
+ (symbolS *) 0, (offsetT) (n), (char *) 0); \
+ *p = 0x00; \
+ goto around; \
+ }
+
+/* We require .word, et. al., to be aligned correctly. */
+#define md_cons_align(nbytes) sparc_cons_align (nbytes)
+extern void sparc_cons_align PARAMS ((int));
+#define HANDLE_ALIGN(fragp) sparc_handle_align (fragp)
+extern void sparc_handle_align PARAMS ((struct frag *));
+
+#if defined (OBJ_ELF) || defined (OBJ_AOUT)
+
+/* This expression evaluates to false if the relocation is for a local
+ object for which we still want to do the relocation at runtime.
+ True if we are willing to perform this relocation while building
+ the .o file.
+
+ If the reloc is against an externally visible symbol, then the
+ a.out assembler should not do the relocation if generating PIC, and
+ the ELF assembler should never do the relocation. */
+
+#ifdef OBJ_ELF
+#define obj_relocate_extern 0
+#else
+#define obj_relocate_extern (! sparc_pic_code)
+#endif
+
+#define TC_RELOC_RTSYM_LOC_FIXUP(FIX) \
+ (obj_relocate_extern \
+ || (FIX)->fx_addsy == NULL \
+ || (! S_IS_EXTERNAL ((FIX)->fx_addsy) \
+ && ! S_IS_WEAK ((FIX)->fx_addsy) \
+ && S_IS_DEFINED ((FIX)->fx_addsy) \
+ && ! S_IS_COMMON ((FIX)->fx_addsy)))
+#endif
+
+/* I know that "call 0" fails in sparc-coff if this doesn't return 1. I
+ don't know about other relocation types, or other formats, yet. */
+#ifdef OBJ_COFF
+#define TC_FORCE_RELOCATION(FIXP) \
+ ((FIXP)->fx_r_type == BFD_RELOC_32_PCREL_S2 \
+ && ((FIXP)->fx_addsy == 0 \
+ || S_GET_SEGMENT ((FIXP)->fx_addsy) == absolute_section))
+#define RELOC_REQUIRES_SYMBOL
+#endif
+
+#ifdef OBJ_ELF
+#define TC_FORCE_RELOCATION(fixp) elf32_sparc_force_relocation(fixp)
+extern int elf32_sparc_force_relocation PARAMS ((struct fix *));
+#endif
+
+#define MD_APPLY_FIX3
+#define TC_HANDLES_FX_DONE
+
+#ifdef OBJ_ELF
+/* Keep relocations against global symbols. Don't turn them into
+ relocations against sections. This is required for the dynamic
+ linker to operate properly. When generating PIC, we need to keep
+ any non PC relative reloc. */
+#define tc_fix_adjustable(FIX) \
+ (! S_IS_EXTERNAL ((FIX)->fx_addsy) \
+ && ! S_IS_WEAK ((FIX)->fx_addsy) \
+ && (! sparc_pic_code \
+ || (FIX)->fx_pcrel \
+ || ((FIX)->fx_subsy != NULL \
+ && (S_GET_SEGMENT ((FIX)->fx_subsy) \
+ == S_GET_SEGMENT ((FIX)->fx_addsy))) \
+ || strchr (S_GET_NAME ((FIX)->fx_addsy), '\001') != NULL \
+ || strchr (S_GET_NAME ((FIX)->fx_addsy), '\002') != NULL))
+#endif
+
+#ifdef OBJ_AOUT
+/* When generating PIC code, we must not adjust any reloc which will
+ turn into a reloc against the global offset table, nor any reloc
+ which we will need if a symbol is overridden. */
+#define tc_fix_adjustable(FIX) \
+ (! sparc_pic_code \
+ || ((FIX)->fx_pcrel \
+ && ((FIX)->fx_addsy == NULL \
+ || (! S_IS_EXTERNAL ((FIX)->fx_addsy) \
+ && ! S_IS_WEAK ((FIX)->fx_addsy)))) \
+ || (FIX)->fx_r_type == BFD_RELOC_16 \
+ || (FIX)->fx_r_type == BFD_RELOC_32)
+#endif
+
+#define elf_tc_final_processing sparc_elf_final_processing
+extern void sparc_elf_final_processing PARAMS ((void));
+
+#define md_operand(x)
+
+extern void sparc_md_end PARAMS ((void));
+#define md_end() sparc_md_end ()
+
+#endif
+
+#define TC_CONS_FIX_NEW cons_fix_new_sparc
+extern void cons_fix_new_sparc
+ PARAMS ((struct frag *, int, unsigned int, struct expressionS *));
+
+/* end of tc-sparc.h */
diff --git a/gas/config/tc-tahoe.c b/gas/config/tc-tahoe.c
new file mode 100644
index 0000000..2bd63ca
--- /dev/null
+++ b/gas/config/tc-tahoe.c
@@ -0,0 +1,2027 @@
+/* tc-tahoe.c
+ Not part of GAS yet. */
+
+#include "as.h"
+#include "obstack.h"
+
+/* this bit glommed from tahoe-inst.h */
+
+typedef unsigned char byte;
+typedef byte tahoe_opcodeT;
+
+/*
+ * This is part of tahoe-ins-parse.c & friends.
+ * We want to parse a tahoe instruction text into a tree defined here.
+ */
+
+#define TIT_MAX_OPERANDS (4) /* maximum number of operands in one
+ single tahoe instruction */
+
+struct top /* tahoe instruction operand */
+{
+ int top_ndx; /* -1, or index register. eg 7=[R7] */
+ int top_reg; /* -1, or register number. eg 7 = R7 or (R7) */
+ byte top_mode; /* Addressing mode byte. This byte, defines
+ which of the 11 modes opcode is. */
+
+ char top_access; /* Access type wanted for this opperand
+ 'b'branch ' 'no-instruction 'amrvw' */
+ char top_width; /* Operand width expected, one of "bwlq?-:!" */
+
+ char *top_error; /* Say if operand is inappropriate */
+
+ segT seg_of_operand; /* segment as returned by expression()*/
+
+ expressionS exp_of_operand; /* The expression as parsed by expression()*/
+
+ byte top_dispsize; /* Number of bytes in the displacement if we
+ can figure it out */
+};
+
+/* The addressing modes for an operand. These numbers are the acutal values
+ for certain modes, so be carefull if you screw with them. */
+#define TAHOE_DIRECT_REG (0x50)
+#define TAHOE_REG_DEFERRED (0x60)
+
+#define TAHOE_REG_DISP (0xE0)
+#define TAHOE_REG_DISP_DEFERRED (0xF0)
+
+#define TAHOE_IMMEDIATE (0x8F)
+#define TAHOE_IMMEDIATE_BYTE (0x88)
+#define TAHOE_IMMEDIATE_WORD (0x89)
+#define TAHOE_IMMEDIATE_LONGWORD (0x8F)
+#define TAHOE_ABSOLUTE_ADDR (0x9F)
+
+#define TAHOE_DISPLACED_RELATIVE (0xEF)
+#define TAHOE_DISP_REL_DEFERRED (0xFF)
+
+#define TAHOE_AUTO_DEC (0x7E)
+#define TAHOE_AUTO_INC (0x8E)
+#define TAHOE_AUTO_INC_DEFERRED (0x9E)
+/* INDEXED_REG is decided by the existance or lack of a [reg] */
+
+/* These are encoded into top_width when top_access=='b'
+ and it's a psuedo op.*/
+#define TAHOE_WIDTH_ALWAYS_JUMP '-'
+#define TAHOE_WIDTH_CONDITIONAL_JUMP '?'
+#define TAHOE_WIDTH_BIG_REV_JUMP '!'
+#define TAHOE_WIDTH_BIG_NON_REV_JUMP ':'
+
+/* The hex code for certain tahoe commands and modes.
+ This is just for readability. */
+#define TAHOE_JMP (0x71)
+#define TAHOE_PC_REL_LONG (0xEF)
+#define TAHOE_BRB (0x11)
+#define TAHOE_BRW (0x13)
+/* These, when 'ored' with, or added to, a register number,
+ set up the number for the displacement mode. */
+#define TAHOE_PC_OR_BYTE (0xA0)
+#define TAHOE_PC_OR_WORD (0xC0)
+#define TAHOE_PC_OR_LONG (0xE0)
+
+struct tit /* get it out of the sewer, it stands for
+ tahoe instruction tree (Geeze!) */
+{
+ tahoe_opcodeT tit_opcode; /* The opcode. */
+ byte tit_operands; /* How many operands are here. */
+ struct top tit_operand[TIT_MAX_OPERANDS]; /* Operands */
+ char *tit_error; /* "" or fatal error text */
+};
+
+/* end: tahoe-inst.h */
+
+/* tahoe.c - tahoe-specific -
+ Not part of gas yet.
+ */
+
+#include "opcode/tahoe.h"
+
+/* This is the number to put at the beginning of the a.out file */
+long omagic = OMAGIC;
+
+/* These chars start a comment anywhere in a source file (except inside
+ another comment or a quoted string. */
+const char comment_chars[] = "#;";
+
+/* These chars only start a comment at the beginning of a line. */
+const char line_comment_chars[] = "#";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant
+ as in 0f123.456
+ or 0d1.234E-12 (see exp chars above)
+ Note: The Tahoe port doesn't support floating point constants. This is
+ consistant with 'as' If it's needed, I can always add it later. */
+const char FLT_CHARS[] = "df";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c . Ideally it shouldn't have to know about it at all,
+ but nothing is ideal around here.
+ (The tahoe has plenty of room, so the change currently isn't needed.)
+ */
+
+static struct tit t; /* A tahoe instruction after decoding. */
+
+void float_cons ();
+/* A table of pseudo ops (sans .), the function called, and an integer op
+ that the function is called with. */
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"dfloat", float_cons, 'd'},
+ {"ffloat", float_cons, 'f'},
+ {0}
+};
+
+/*
+ * For Tahoe, relative addresses of "just the right length" are pretty easy.
+ * The branch displacement is always the last operand, even in
+ * synthetic instructions.
+ * For Tahoe, we encode the relax_substateTs (in e.g. fr_substate) as:
+ *
+ * 4 3 2 1 0 bit number
+ * ---/ /--+-------+-------+-------+-------+-------+
+ * | what state ? | how long ? |
+ * ---/ /--+-------+-------+-------+-------+-------+
+ *
+ * The "how long" bits are 00=byte, 01=word, 10=long.
+ * This is a Un*x convention.
+ * Not all lengths are legit for a given value of (what state).
+ * The four states are listed below.
+ * The "how long" refers merely to the displacement length.
+ * The address usually has some constant bytes in it as well.
+ *
+
+States for Tahoe address relaxing.
+1. TAHOE_WIDTH_ALWAYS_JUMP (-)
+ Format: "b-"
+ Tahoe opcodes are: (Hex)
+ jr 11
+ jbr 11
+ Simple branch.
+ Always, 1 byte opcode, then displacement/absolute.
+ If word or longword, change opcode to brw or jmp.
+
+
+2. TAHOE_WIDTH_CONDITIONAL_JUMP (?)
+ J<cond> where <cond> is a simple flag test.
+ Format: "b?"
+ Tahoe opcodes are: (Hex)
+ jneq/jnequ 21
+ jeql/jeqlu 31
+ jgtr 41
+ jleq 51
+ jgeq 81
+ jlss 91
+ jgtru a1
+ jlequ b1
+ jvc c1
+ jvs d1
+ jlssu/jcs e1
+ jgequ/jcc f1
+ Always, you complement 4th bit to reverse the condition.
+ Always, 1-byte opcode, then 1-byte displacement.
+
+3. TAHOE_WIDTH_BIG_REV_JUMP (!)
+ Jbc/Jbs where cond tests a memory bit.
+ Format: "rlvlb!"
+ Tahoe opcodes are: (Hex)
+ jbs 0e
+ jbc 1e
+ Always, you complement 4th bit to reverse the condition.
+ Always, 1-byte opcde, longword, longword-address, 1-word-displacement
+
+4. TAHOE_WIDTH_BIG_NON_REV_JUMP (:)
+ JaoblXX/Jbssi
+ Format: "rlmlb:"
+ Tahoe opcodes are: (Hex)
+ aojlss 2f
+ jaoblss 2f
+ aojleq 3f
+ jaobleq 3f
+ jbssi 5f
+ Always, we cannot reverse the sense of the branch; we have a word
+ displacement.
+
+We need to modify the opcode is for class 1, 2 and 3 instructions.
+After relax() we may complement the 4th bit of 2 or 3 to reverse sense of
+branch.
+
+We sometimes store context in the operand literal. This way we can figure out
+after relax() what the original addressing mode was. (Was is pc_rel, or
+pc_rel_disp? That sort of thing.) */
+
+/* These displacements are relative to the START address of the
+ displacement which is at the start of the displacement, not the end of
+ the instruction. The hardware pc_rel is at the end of the instructions.
+ That's why all the displacements have the length of the displacement added
+ to them. (WF + length(word))
+
+ The first letter is Byte, Word.
+ 2nd letter is Forward, Backward. */
+#define BF (1+ 127)
+#define BB (1+-128)
+#define WF (2+ 32767)
+#define WB (2+-32768)
+/* Dont need LF, LB because they always reach. [They are coded as 0.] */
+
+#define C(a,b) ENCODE_RELAX(a,b)
+/* This macro has no side-effects. */
+#define ENCODE_RELAX(what,length) (((what) << 2) + (length))
+#define RELAX_STATE(what) ((what) >> 2)
+#define RELAX_LENGTH(length) ((length) && 3)
+
+#define STATE_ALWAYS_BRANCH (1)
+#define STATE_CONDITIONAL_BRANCH (2)
+#define STATE_BIG_REV_BRANCH (3)
+#define STATE_BIG_NON_REV_BRANCH (4)
+#define STATE_PC_RELATIVE (5)
+
+#define STATE_BYTE (0)
+#define STATE_WORD (1)
+#define STATE_LONG (2)
+#define STATE_UNDF (3) /* Symbol undefined in pass1 */
+
+/* This is the table used by gas to figure out relaxing modes. The fields are
+ forward_branch reach, backward_branch reach, number of bytes it would take,
+ where the next biggest branch is. */
+const relax_typeS md_relax_table[] =
+{
+ {
+ 1, 1, 0, 0
+ }, /* error sentinel 0,0 */
+ {
+ 1, 1, 0, 0
+ }, /* unused 0,1 */
+ {
+ 1, 1, 0, 0
+ }, /* unused 0,2 */
+ {
+ 1, 1, 0, 0
+ }, /* unused 0,3 */
+/* Unconditional branch cases "jrb"
+ The relax part is the actual displacement */
+ {
+ BF, BB, 1, C (1, 1)
+ }, /* brb B`foo 1,0 */
+ {
+ WF, WB, 2, C (1, 2)
+ }, /* brw W`foo 1,1 */
+ {
+ 0, 0, 5, 0
+ }, /* Jmp L`foo 1,2 */
+ {
+ 1, 1, 0, 0
+ }, /* unused 1,3 */
+/* Reversible Conditional Branch. If the branch won't reach, reverse
+ it, and jump over a brw or a jmp that will reach. The relax part is the
+ actual address. */
+ {
+ BF, BB, 1, C (2, 1)
+ }, /* b<cond> B`foo 2,0 */
+ {
+ WF + 2, WB + 2, 4, C (2, 2)
+ }, /* brev over, brw W`foo, over: 2,1 */
+ {
+ 0, 0, 7, 0
+ }, /* brev over, jmp L`foo, over: 2,2 */
+ {
+ 1, 1, 0, 0
+ }, /* unused 2,3 */
+/* Another type of reversable branch. But this only has a word
+ displacement. */
+ {
+ 1, 1, 0, 0
+ }, /* unused 3,0 */
+ {
+ WF, WB, 2, C (3, 2)
+ }, /* jbX W`foo 3,1 */
+ {
+ 0, 0, 8, 0
+ }, /* jrevX over, jmp L`foo, over: 3,2 */
+ {
+ 1, 1, 0, 0
+ }, /* unused 3,3 */
+/* These are the non reversable branches, all of which have a word
+ displacement. If I can't reach, branch over a byte branch, to a
+ jump that will reach. The jumped branch jumps over the reaching
+ branch, to continue with the flow of the program. It's like playing
+ leap frog. */
+ {
+ 1, 1, 0, 0
+ }, /* unused 4,0 */
+ {
+ WF, WB, 2, C (4, 2)
+ }, /* aobl_ W`foo 4,1 */
+ {
+ 0, 0, 10, 0
+ }, /*aobl_ W`hop,br over,hop: jmp L^foo,over 4,2*/
+ {
+ 1, 1, 0, 0
+ }, /* unused 4,3 */
+/* Normal displacement mode, no jumping or anything like that.
+ The relax points to one byte before the address, thats why all
+ the numbers are up by one. */
+ {
+ BF + 1, BB + 1, 2, C (5, 1)
+ }, /* B^"foo" 5,0 */
+ {
+ WF + 1, WB + 1, 3, C (5, 2)
+ }, /* W^"foo" 5,1 */
+ {
+ 0, 0, 5, 0
+ }, /* L^"foo" 5,2 */
+ {
+ 1, 1, 0, 0
+ }, /* unused 5,3 */
+};
+
+#undef C
+#undef BF
+#undef BB
+#undef WF
+#undef WB
+/* End relax stuff */
+
+/* Handle of the OPCODE hash table. NULL means any use before
+ md_begin() will crash. */
+static struct hash_control *op_hash;
+
+/* Init function. Build the hash table. */
+void
+md_begin ()
+{
+ struct tot *tP;
+ char *errorval = 0;
+ int synthetic_too = 1; /* If 0, just use real opcodes. */
+
+ op_hash = hash_new ();
+
+ for (tP = totstrs; *tP->name && !errorval; tP++)
+ errorval = hash_insert (op_hash, tP->name, &tP->detail);
+
+ if (synthetic_too)
+ for (tP = synthetic_totstrs; *tP->name && !errorval; tP++)
+ errorval = hash_insert (op_hash, tP->name, &tP->detail);
+
+ if (errorval)
+ as_fatal (errorval);
+}
+
+CONST char *md_shortopts = "ad:STt:V";
+struct option md_longopts[] = {
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ switch (c)
+ {
+ case 'a':
+ as_warn (_("The -a option doesn't exist. (Despite what the man page says!"));
+ break;
+
+ case 'd':
+ as_warn (_("Displacement length %s ignored!"), arg);
+ break;
+
+ case 'S':
+ as_warn (_("SYMBOL TABLE not implemented"));
+ break;
+
+ case 'T':
+ as_warn (_("TOKEN TRACE not implemented"));
+ break;
+
+ case 't':
+ as_warn (_("I don't need or use temp. file \"%s\"."), arg);
+ break;
+
+ case 'V':
+ as_warn (_("I don't use an interpass file! -V ignored"));
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ fprintf(stream, _("\
+Tahoe options:\n\
+-a ignored\n\
+-d LENGTH ignored\n\
+-J ignored\n\
+-S ignored\n\
+-t FILE ignored\n\
+-T ignored\n\
+-V ignored\n"));
+}
+
+/* The functions in this section take numbers in the machine format, and
+ munges them into Tahoe byte order.
+ They exist primarily for cross assembly purpose. */
+void /* Knows about order of bytes in address. */
+md_number_to_chars (con, value, nbytes)
+ char con[]; /* Return 'nbytes' of chars here. */
+ valueT value; /* The value of the bits. */
+ int nbytes; /* Number of bytes in the output. */
+{
+ number_to_chars_bigendian (con, value, nbytes);
+}
+
+#ifdef comment
+void /* Knows about order of bytes in address. */
+md_number_to_imm (con, value, nbytes)
+ char con[]; /* Return 'nbytes' of chars here. */
+ long int value; /* The value of the bits. */
+ int nbytes; /* Number of bytes in the output. */
+{
+ md_number_to_chars (con, value, nbytes);
+}
+
+#endif /* comment */
+
+void
+tc_apply_fix (fixP, val)
+ fixS *fixP;
+ long val;
+{
+ /* should never be called */
+ know (0);
+}
+
+void /* Knows about order of bytes in address. */
+md_number_to_disp (con, value, nbytes)
+ char con[]; /* Return 'nbytes' of chars here. */
+ long int value; /* The value of the bits. */
+ int nbytes; /* Number of bytes in the output. */
+{
+ md_number_to_chars (con, value, nbytes);
+}
+
+void /* Knows about order of bytes in address. */
+md_number_to_field (con, value, nbytes)
+ char con[]; /* Return 'nbytes' of chars here. */
+ long int value; /* The value of the bits. */
+ int nbytes; /* Number of bytes in the output. */
+{
+ md_number_to_chars (con, value, nbytes);
+}
+
+/* Put the bits in an order that a tahoe will understand, despite the ordering
+ of the native machine.
+ On Tahoe: first 4 bytes are normal unsigned big endian long,
+ next three bytes are symbolnum, in kind of 3 byte big endian (least sig. byte last).
+ The last byte is broken up with bit 7 as pcrel,
+ bits 6 & 5 as length,
+ bit 4 as extern and the last nibble as 'undefined'. */
+
+#if comment
+void
+md_ri_to_chars (ri_p, ri)
+ struct relocation_info *ri_p, ri;
+{
+ byte the_bytes[sizeof (struct relocation_info)];
+ /* The reason I can't just encode these directly into ri_p is that
+ ri_p may point to ri. */
+
+ /* This is easy */
+ md_number_to_chars (the_bytes, ri.r_address, sizeof (ri.r_address));
+
+ /* now the fun stuff */
+ the_bytes[4] = (ri.r_symbolnum >> 16) & 0x0ff;
+ the_bytes[5] = (ri.r_symbolnum >> 8) & 0x0ff;
+ the_bytes[6] = ri.r_symbolnum & 0x0ff;
+ the_bytes[7] = (((ri.r_extern << 4) & 0x10) | ((ri.r_length << 5) & 0x60) |
+ ((ri.r_pcrel << 7) & 0x80)) & 0xf0;
+
+ bcopy (the_bytes, (char *) ri_p, sizeof (struct relocation_info));
+}
+
+#endif /* comment */
+
+/* Put the bits in an order that a tahoe will understand, despite the ordering
+ of the native machine.
+ On Tahoe: first 4 bytes are normal unsigned big endian long,
+ next three bytes are symbolnum, in kind of 3 byte big endian (least sig. byte last).
+ The last byte is broken up with bit 7 as pcrel,
+ bits 6 & 5 as length,
+ bit 4 as extern and the last nibble as 'undefined'. */
+
+void
+tc_aout_fix_to_chars (where, fixP, segment_address_in_file)
+ char *where;
+ fixS *fixP;
+ relax_addressT segment_address_in_file;
+{
+ long r_symbolnum;
+
+ know (fixP->fx_addsy != NULL);
+
+ md_number_to_chars (where,
+ fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file,
+ 4);
+
+ r_symbolnum = (S_IS_DEFINED (fixP->fx_addsy)
+ ? S_GET_TYPE (fixP->fx_addsy)
+ : fixP->fx_addsy->sy_number);
+
+ where[4] = (r_symbolnum >> 16) & 0x0ff;
+ where[5] = (r_symbolnum >> 8) & 0x0ff;
+ where[6] = r_symbolnum & 0x0ff;
+ where[7] = (((is_pcrel (fixP) << 7) & 0x80)
+ | ((((fixP->fx_type == FX_8 || fixP->fx_type == FX_PCREL8
+ ? 0
+ : (fixP->fx_type == FX_16 || fixP->fx_type == FX_PCREL16
+ ? 1
+ : (fixP->fx_type == FX_32 || fixP->fx_type == FX_PCREL32
+ ? 2
+ : 42)))) << 5) & 0x60)
+ | ((!S_IS_DEFINED (fixP->fx_addsy) << 4) & 0x10));
+}
+
+/* Relocate byte stuff */
+
+/* This is for broken word. */
+const int md_short_jump_size = 3;
+
+void
+md_create_short_jump (ptr, from_addr, to_addr, frag, to_symbol)
+ char *ptr;
+ addressT from_addr, to_addr;
+ fragS *frag;
+ symbolS *to_symbol;
+{
+ valueT offset;
+
+ offset = to_addr - (from_addr + 1);
+ *ptr++ = TAHOE_BRW;
+ md_number_to_chars (ptr, offset, 2);
+}
+
+const int md_long_jump_size = 6;
+const int md_reloc_size = 8; /* Size of relocation record */
+
+void
+md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol)
+ char *ptr;
+ addressT from_addr, to_addr;
+ fragS *frag;
+ symbolS *to_symbol;
+{
+ valueT offset;
+
+ offset = to_addr - (from_addr + 4);
+ *ptr++ = TAHOE_JMP;
+ *ptr++ = TAHOE_PC_REL_LONG;
+ md_number_to_chars (ptr, offset, 4);
+}
+
+/*
+ * md_estimate_size_before_relax()
+ *
+ * Called just before relax().
+ * Any symbol that is now undefined will not become defined, so we assumed
+ * that it will be resolved by the linker.
+ * Return the correct fr_subtype in the frag, for relax()
+ * Return the initial "guess for fr_var" to caller. (How big I think this
+ * will be.)
+ * The guess for fr_var is ACTUALLY the growth beyond fr_fix.
+ * Whatever we do to grow fr_fix or fr_var contributes to our returned value.
+ * Although it may not be explicit in the frag, pretend fr_var starts with a
+ * 0 value.
+ */
+int
+md_estimate_size_before_relax (fragP, segment_type)
+ register fragS *fragP;
+ segT segment_type; /* N_DATA or N_TEXT. */
+{
+ register char *p;
+ register int old_fr_fix;
+ /* int pc_rel; FIXME: remove this */
+
+ old_fr_fix = fragP->fr_fix;
+ switch (fragP->fr_subtype)
+ {
+ case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_UNDF):
+ if (S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
+ {
+ /* The symbol was in the same segment as the opcode, and it's
+ a real pc_rel case so it's a relaxable case. */
+ fragP->fr_subtype = ENCODE_RELAX (STATE_PC_RELATIVE, STATE_BYTE);
+ }
+ else
+ {
+ /* This case is still undefined, so asume it's a long word for the
+ linker to fix. */
+ p = fragP->fr_literal + old_fr_fix;
+ *p |= TAHOE_PC_OR_LONG;
+ /* We now know how big it will be, one long word. */
+ fragP->fr_fix += 1 + 4;
+ fix_new (fragP, old_fr_fix + 1, fragP->fr_symbol,
+ fragP->fr_offset, FX_PCREL32, NULL);
+ frag_wane (fragP);
+ }
+ break;
+
+ case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_UNDF):
+ if (S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
+ {
+ fragP->fr_subtype = ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_BYTE);
+ }
+ else
+ {
+ p = fragP->fr_literal + old_fr_fix;
+ *fragP->fr_opcode ^= 0x10; /* Reverse sense of branch. */
+ *p++ = 6;
+ *p++ = TAHOE_JMP;
+ *p++ = TAHOE_PC_REL_LONG;
+ fragP->fr_fix += 1 + 1 + 1 + 4;
+ fix_new (fragP, old_fr_fix + 3, fragP->fr_symbol,
+ fragP->fr_offset, FX_PCREL32, NULL);
+ frag_wane (fragP);
+ }
+ break;
+
+ case ENCODE_RELAX (STATE_BIG_REV_BRANCH, STATE_UNDF):
+ if (S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
+ {
+ fragP->fr_subtype =
+ ENCODE_RELAX (STATE_BIG_REV_BRANCH, STATE_WORD);
+ }
+ else
+ {
+ p = fragP->fr_literal + old_fr_fix;
+ *fragP->fr_opcode ^= 0x10; /* Reverse sense of branch. */
+ *p++ = 0;
+ *p++ = 6;
+ *p++ = TAHOE_JMP;
+ *p++ = TAHOE_PC_REL_LONG;
+ fragP->fr_fix += 2 + 2 + 4;
+ fix_new (fragP, old_fr_fix + 4, fragP->fr_symbol,
+ fragP->fr_offset, FX_PCREL32, NULL);
+ frag_wane (fragP);
+ }
+ break;
+
+ case ENCODE_RELAX (STATE_BIG_NON_REV_BRANCH, STATE_UNDF):
+ if (S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
+ {
+ fragP->fr_subtype = ENCODE_RELAX (STATE_BIG_NON_REV_BRANCH, STATE_WORD);
+ }
+ else
+ {
+ p = fragP->fr_literal + old_fr_fix;
+ *p++ = 2;
+ *p++ = 0;
+ *p++ = TAHOE_BRB;
+ *p++ = 6;
+ *p++ = TAHOE_JMP;
+ *p++ = TAHOE_PC_REL_LONG;
+ fragP->fr_fix += 2 + 2 + 2 + 4;
+ fix_new (fragP, old_fr_fix + 6, fragP->fr_symbol,
+ fragP->fr_offset, FX_PCREL32, NULL);
+ frag_wane (fragP);
+ }
+ break;
+
+ case ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_UNDF):
+ if (S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
+ {
+ fragP->fr_subtype = ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_BYTE);
+ }
+ else
+ {
+ p = fragP->fr_literal + old_fr_fix;
+ *fragP->fr_opcode = TAHOE_JMP;
+ *p++ = TAHOE_PC_REL_LONG;
+ fragP->fr_fix += 1 + 4;
+ fix_new (fragP, old_fr_fix + 1, fragP->fr_symbol,
+ fragP->fr_offset, FX_PCREL32, NULL);
+ frag_wane (fragP);
+ }
+ break;
+
+ default:
+ break;
+ }
+ return (fragP->fr_var + fragP->fr_fix - old_fr_fix);
+} /* md_estimate_size_before_relax() */
+
+/*
+ * md_convert_frag();
+ *
+ * Called after relax() is finished.
+ * In: Address of frag.
+ * fr_type == rs_machine_dependent.
+ * fr_subtype is what the address relaxed to.
+ *
+ * Out: Any fixSs and constants are set up.
+ * Caller will turn frag into a ".space 0".
+ */
+void
+md_convert_frag (headers, seg, fragP)
+ object_headers *headers;
+ segT seg;
+ register fragS *fragP;
+{
+ register char *addressP; /* -> _var to change. */
+ register char *opcodeP; /* -> opcode char(s) to change. */
+ register short int length_code; /* 2=long 1=word 0=byte */
+ register short int extension = 0; /* Size of relaxed address.
+ Added to fr_fix: incl. ALL var chars. */
+ register symbolS *symbolP;
+ register long int where;
+ register long int address_of_var;
+ /* Where, in file space, is _var of *fragP? */
+ register long int target_address;
+ /* Where, in file space, does addr point? */
+
+ know (fragP->fr_type == rs_machine_dependent);
+ length_code = RELAX_LENGTH (fragP->fr_subtype);
+ know (length_code >= 0 && length_code < 3);
+ where = fragP->fr_fix;
+ addressP = fragP->fr_literal + where;
+ opcodeP = fragP->fr_opcode;
+ symbolP = fragP->fr_symbol;
+ know (symbolP);
+ target_address = S_GET_VALUE (symbolP) + fragP->fr_offset;
+ address_of_var = fragP->fr_address + where;
+ switch (fragP->fr_subtype)
+ {
+ case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_BYTE):
+ /* *addressP holds the registers number, plus 0x10, if it's deferred
+ mode. To set up the right mode, just OR the size of this displacement */
+ /* Byte displacement. */
+ *addressP++ |= TAHOE_PC_OR_BYTE;
+ *addressP = target_address - (address_of_var + 2);
+ extension = 2;
+ break;
+
+ case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_WORD):
+ /* Word displacement. */
+ *addressP++ |= TAHOE_PC_OR_WORD;
+ md_number_to_chars (addressP, target_address - (address_of_var + 3), 2);
+ extension = 3;
+ break;
+
+ case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_LONG):
+ /* Long word displacement. */
+ *addressP++ |= TAHOE_PC_OR_LONG;
+ md_number_to_chars (addressP, target_address - (address_of_var + 5), 4);
+ extension = 5;
+ break;
+
+ case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_BYTE):
+ *addressP = target_address - (address_of_var + 1);
+ extension = 1;
+ break;
+
+ case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_WORD):
+ *opcodeP ^= 0x10; /* Reverse sense of test. */
+ *addressP++ = 3; /* Jump over word branch */
+ *addressP++ = TAHOE_BRW;
+ md_number_to_chars (addressP, target_address - (address_of_var + 4), 2);
+ extension = 4;
+ break;
+
+ case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_LONG):
+ *opcodeP ^= 0x10; /* Reverse sense of test. */
+ *addressP++ = 6;
+ *addressP++ = TAHOE_JMP;
+ *addressP++ = TAHOE_PC_REL_LONG;
+ md_number_to_chars (addressP, target_address, 4);
+ extension = 7;
+ break;
+
+ case ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_BYTE):
+ *addressP = target_address - (address_of_var + 1);
+ extension = 1;
+ break;
+
+ case ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_WORD):
+ *opcodeP = TAHOE_BRW;
+ md_number_to_chars (addressP, target_address - (address_of_var + 2), 2);
+ extension = 2;
+ break;
+
+ case ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_LONG):
+ *opcodeP = TAHOE_JMP;
+ *addressP++ = TAHOE_PC_REL_LONG;
+ md_number_to_chars (addressP, target_address - (address_of_var + 5), 4);
+ extension = 5;
+ break;
+
+ case ENCODE_RELAX (STATE_BIG_REV_BRANCH, STATE_WORD):
+ md_number_to_chars (addressP, target_address - (address_of_var + 2), 2);
+ extension = 2;
+ break;
+
+ case ENCODE_RELAX (STATE_BIG_REV_BRANCH, STATE_LONG):
+ *opcodeP ^= 0x10;
+ *addressP++ = 0;
+ *addressP++ = 6;
+ *addressP++ = TAHOE_JMP;
+ *addressP++ = TAHOE_PC_REL_LONG;
+ md_number_to_chars (addressP, target_address, 4);
+ extension = 8;
+ break;
+
+ case ENCODE_RELAX (STATE_BIG_NON_REV_BRANCH, STATE_WORD):
+ md_number_to_chars (addressP, target_address - (address_of_var + 2), 2);
+ extension = 2;
+ break;
+
+ case ENCODE_RELAX (STATE_BIG_NON_REV_BRANCH, STATE_LONG):
+ *addressP++ = 0;
+ *addressP++ = 2;
+ *addressP++ = TAHOE_BRB;
+ *addressP++ = 6;
+ *addressP++ = TAHOE_JMP;
+ *addressP++ = TAHOE_PC_REL_LONG;
+ md_number_to_chars (addressP, target_address, 4);
+ extension = 10;
+ break;
+
+ default:
+ BAD_CASE (fragP->fr_subtype);
+ break;
+ }
+ fragP->fr_fix += extension;
+} /* md_convert_frag */
+
+
+/* This is the stuff for md_assemble. */
+#define FP_REG 13
+#define SP_REG 14
+#define PC_REG 15
+#define BIGGESTREG PC_REG
+
+/*
+ * Parse the string pointed to by START
+ * If it represents a valid register, point START to the character after
+ * the last valid register char, and return the register number (0-15).
+ * If invalid, leave START alone, return -1.
+ * The format has to be exact. I don't do things like eat leading zeros
+ * or the like.
+ * Note: This doesn't check for the next character in the string making
+ * this invalid. Ex: R123 would return 12, it's the callers job to check
+ * what start is point to apon return.
+ *
+ * Valid registers are R1-R15, %1-%15, FP (13), SP (14), PC (15)
+ * Case doesn't matter.
+ */
+int
+tahoe_reg_parse (start)
+ char **start; /* A pointer to the string to parse. */
+{
+ register char *regpoint = *start;
+ register int regnum = -1;
+
+ switch (*regpoint++)
+ {
+ case '%': /* Registers can start with a %,
+ R or r, and then a number. */
+ case 'R':
+ case 'r':
+ if (isdigit (*regpoint))
+ {
+ /* Got the first digit. */
+ regnum = *regpoint++ - '0';
+ if ((regnum == 1) && isdigit (*regpoint))
+ {
+ /* Its a two digit number. */
+ regnum = 10 + (*regpoint++ - '0');
+ if (regnum > BIGGESTREG)
+ { /* Number too big? */
+ regnum = -1;
+ }
+ }
+ }
+ break;
+ case 'F': /* Is it the FP */
+ case 'f':
+ switch (*regpoint++)
+ {
+ case 'p':
+ case 'P':
+ regnum = FP_REG;
+ }
+ break;
+ case 's': /* How about the SP */
+ case 'S':
+ switch (*regpoint++)
+ {
+ case 'p':
+ case 'P':
+ regnum = SP_REG;
+ }
+ break;
+ case 'p': /* OR the PC even */
+ case 'P':
+ switch (*regpoint++)
+ {
+ case 'c':
+ case 'C':
+ regnum = PC_REG;
+ }
+ break;
+ }
+
+ if (regnum != -1)
+ { /* No error, so move string pointer */
+ *start = regpoint;
+ }
+ return regnum; /* Return results */
+} /* tahoe_reg_parse */
+
+/*
+ * This chops up an operand and figures out its modes and stuff.
+ * It's a little touchy about extra characters.
+ * Optex to start with one extra character so it can be overwritten for
+ * the backward part of the parsing.
+ * You can't put a bunch of extra characters in side to
+ * make the command look cute. ie: * foo ( r1 ) [ r0 ]
+ * If you like doing a lot of typing, try COBOL!
+ * Actually, this parser is a little weak all around. It's designed to be
+ * used with compliers, so I emphisise correct decoding of valid code quickly
+ * rather that catching every possable error.
+ * Note: This uses the expression function, so save input_line_pointer before
+ * calling.
+ *
+ * Sperry defines the semantics of address modes (and values)
+ * by a two-letter code, explained here.
+ *
+ * letter 1: access type
+ *
+ * a address calculation - no data access, registers forbidden
+ * b branch displacement
+ * m read - let go of bus - write back "modify"
+ * r read
+ * w write
+ * v bit field address: like 'a' but registers are OK
+ *
+ * letter 2: data type (i.e. width, alignment)
+ *
+ * b byte
+ * w word
+ * l longword
+ * q quadword (Even regs < 14 allowed) (if 12, you get a warning)
+ * - unconditional synthetic jbr operand
+ * ? simple synthetic reversable branch operand
+ * ! complex synthetic reversable branch operand
+ * : complex synthetic non-reversable branch operand
+ *
+ * The '-?!:' letter 2's are not for external consumption. They are used
+ * by GAS for psuedo ops relaxing code.
+ *
+ * After parsing topP has:
+ *
+ * top_ndx: -1, or the index register. eg 7=[R7]
+ * top_reg: -1, or register number. eg 7 = R7 or (R7)
+ * top_mode: The addressing mode byte. This byte, defines which of
+ * the 11 modes opcode is.
+ * top_access: Access type wanted for this opperand 'b'branch ' '
+ * no-instruction 'amrvw'
+ * top_width: Operand width expected, one of "bwlq?-:!"
+ * exp_of_operand: The expression as parsed by expression()
+ * top_dispsize: Number of bytes in the displacement if we can figure it
+ * out and it's relavent.
+ *
+ * Need syntax checks built.
+ */
+
+void
+tip_op (optex, topP)
+ char *optex; /* The users text input, with one leading character */
+ struct top *topP; /* The tahoe instruction with some fields already set:
+ in: access, width
+ out: ndx, reg, mode, error, dispsize */
+
+{
+ int mode = 0; /* This operand's mode. */
+ char segfault = *optex; /* To keep the back parsing from freaking. */
+ char *point = optex + 1; /* Parsing from front to back. */
+ char *end; /* Parsing from back to front. */
+ int reg = -1; /* major register, -1 means absent */
+ int imreg = -1; /* Major register in immediate mode */
+ int ndx = -1; /* index register number, -1 means absent */
+ char dec_inc = ' '; /* Is the SP auto-incremented '+' or
+ auto-decremented '-' or neither ' '. */
+ int immediate = 0; /* 1 if '$' immediate mode */
+ int call_width = 0; /* If the caller casts the displacement */
+ int abs_width = 0; /* The width of the absolute displacment */
+ int com_width = 0; /* Displacement width required by branch */
+ int deferred = 0; /* 1 if '*' deferral is used */
+ byte disp_size = 0; /* How big is this operand. 0 == don't know */
+ char *op_bad = ""; /* Bad operand error */
+
+ char *tp, *temp, c; /* Temporary holders */
+
+ char access = topP->top_access; /* Save on a deref. */
+ char width = topP->top_width;
+
+ int really_none = 0; /* Empty expressions evaluate to 0
+ but I need to know if it's there or not */
+ expressionS *expP; /* -> expression values for this operand */
+
+ /* Does this command restrict the displacement size. */
+ if (access == 'b')
+ com_width = (width == 'b' ? 1 :
+ (width == 'w' ? 2 :
+ (width == 'l' ? 4 : 0)));
+
+ *optex = '\0'; /* This is kind of a back stop for all
+ the searches to fail on if needed.*/
+ if (*point == '*')
+ { /* A dereference? */
+ deferred = 1;
+ point++;
+ }
+
+ /* Force words into a certain mode */
+ /* Bitch, Bitch, Bitch! */
+ /*
+ * Using the ^ operator is ambigous. If I have an absolute label
+ * called 'w' set to, say 2, and I have the expression 'w^1', do I get
+ * 1, forced to be in word displacement mode, or do I get the value of
+ * 'w' or'ed with 1 (3 in this case).
+ * The default is 'w' as an offset, so that's what I use.
+ * Stick with `, it does the same, and isn't ambig.
+ */
+
+ if (*point != '\0' && ((point[1] == '^') || (point[1] == '`')))
+ switch (*point)
+ {
+ case 'b':
+ case 'B':
+ case 'w':
+ case 'W':
+ case 'l':
+ case 'L':
+ if (com_width)
+ as_warn (_("Casting a branch displacement is bad form, and is ignored."));
+ else
+ {
+ c = (isupper (*point) ? tolower (*point) : *point);
+ call_width = ((c == 'b') ? 1 :
+ ((c == 'w') ? 2 : 4));
+ }
+ point += 2;
+ break;
+ }
+
+ /* Setting immediate mode */
+ if (*point == '$')
+ {
+ immediate = 1;
+ point++;
+ }
+
+ /*
+ * I've pulled off all the easy stuff off the front, move to the end and
+ * yank.
+ */
+
+ for (end = point; *end != '\0'; end++) /* Move to the end. */
+ ;
+
+ if (end != point) /* Null string? */
+ end--;
+
+ if (end > point && *end == ' ' && end[-1] != '\'')
+ end--; /* Hop white space */
+
+ /* Is this an index reg. */
+ if ((*end == ']') && (end[-1] != '\''))
+ {
+ temp = end;
+
+ /* Find opening brace. */
+ for (--end; (*end != '[' && end != point); end--)
+ ;
+
+ /* If I found the opening brace, get the index register number. */
+ if (*end == '[')
+ {
+ tp = end + 1; /* tp should point to the start of a reg. */
+ ndx = tahoe_reg_parse (&tp);
+ if (tp != temp)
+ { /* Reg. parse error. */
+ ndx = -1;
+ }
+ else
+ {
+ end--; /* Found it, move past brace. */
+ }
+ if (ndx == -1)
+ {
+ op_bad = _("Couldn't parse the [index] in this operand.");
+ end = point; /* Force all the rest of the tests to fail. */
+ }
+ }
+ else
+ {
+ op_bad = _("Couldn't find the opening '[' for the index of this operand.");
+ end = point; /* Force all the rest of the tests to fail. */
+ }
+ }
+
+ /* Post increment? */
+ if (*end == '+')
+ {
+ dec_inc = '+';
+ /* was: *end--; */
+ end--;
+ }
+
+ /* register in parens? */
+ if ((*end == ')') && (end[-1] != '\''))
+ {
+ temp = end;
+
+ /* Find opening paren. */
+ for (--end; (*end != '(' && end != point); end--)
+ ;
+
+ /* If I found the opening paren, get the register number. */
+ if (*end == '(')
+ {
+ tp = end + 1;
+ reg = tahoe_reg_parse (&tp);
+ if (tp != temp)
+ {
+ /* Not a register, but could be part of the expression. */
+ reg = -1;
+ end = temp; /* Rest the pointer back */
+ }
+ else
+ {
+ end--; /* Found the reg. move before opening paren. */
+ }
+ }
+ else
+ {
+ op_bad = _("Couldn't find the opening '(' for the deref of this operand.");
+ end = point; /* Force all the rest of the tests to fail. */
+ }
+ }
+
+ /* Pre decrement? */
+ if (*end == '-')
+ {
+ if (dec_inc != ' ')
+ {
+ op_bad = _("Operand can't be both pre-inc and post-dec.");
+ end = point;
+ }
+ else
+ {
+ dec_inc = '-';
+ /* was: *end--; */
+ end--;
+ }
+ }
+
+ /*
+ * Everything between point and end is the 'expression', unless it's
+ * a register name.
+ */
+
+ c = end[1];
+ end[1] = '\0';
+
+ tp = point;
+ imreg = tahoe_reg_parse (&point); /* Get the immediate register
+ if it is there.*/
+ if (*point != '\0')
+ {
+ /* If there is junk after point, then the it's not immediate reg. */
+ point = tp;
+ imreg = -1;
+ }
+
+ if (imreg != -1 && reg != -1)
+ op_bad = _("I parsed 2 registers in this operand.");
+
+ /*
+ * Evaluate whats left of the expression to see if it's valid.
+ * Note again: This assumes that the calling expression has saved
+ * input_line_pointer. (Nag, nag, nag!)
+ */
+
+ if (*op_bad == '\0')
+ {
+ /* statement has no syntax goofs yet: lets sniff the expression */
+ input_line_pointer = point;
+ expP = &(topP->exp_of_operand);
+ topP->seg_of_operand = expression (expP);
+ switch (expP->X_op)
+ {
+ case O_absent:
+ /* No expression. For BSD4.2 compatibility, missing expression is
+ absolute 0 */
+ expP->X_op = O_constant;
+ expP->X_add_number = 0;
+ really_none = 1;
+ case O_constant:
+ /* for SEG_ABSOLUTE, we shouldnt need to set X_op_symbol,
+ X_add_symbol to any particular value. */
+ /* But, we will program defensively. Since this situation occurs
+ rarely so it costs us little to do so. */
+ expP->X_add_symbol = NULL;
+ expP->X_op_symbol = NULL;
+ /* How many bytes are needed to express this abs value? */
+ abs_width =
+ ((((expP->X_add_number & 0xFFFFFF80) == 0) ||
+ ((expP->X_add_number & 0xFFFFFF80) == 0xFFFFFF80)) ? 1 :
+ (((expP->X_add_number & 0xFFFF8000) == 0) ||
+ ((expP->X_add_number & 0xFFFF8000) == 0xFFFF8000)) ? 2 : 4);
+
+ case O_symbol:
+ break;
+
+ default:
+ /*
+ * Major bug. We can't handle the case of a operator
+ * expression in a synthetic opcode variable-length
+ * instruction. We don't have a frag type that is smart
+ * enough to relax a operator, and so we just force all
+ * operators to behave like SEG_PASS1s. Clearly, if there is
+ * a demand we can invent a new or modified frag type and
+ * then coding up a frag for this case will be easy.
+ */
+ need_pass_2 = 1;
+ op_bad = _("Can't relocate expression error.");
+ break;
+
+ case O_big:
+ /* This is an error. Tahoe doesn't allow any expressions
+ bigger that a 32 bit long word. Any bigger has to be referenced
+ by address. */
+ op_bad = _("Expression is too large for a 32 bits.");
+ break;
+ }
+ if (*input_line_pointer != '\0')
+ {
+ op_bad = _("Junk at end of expression.");
+ }
+ }
+
+ end[1] = c;
+
+ /* I'm done, so restore optex */
+ *optex = segfault;
+
+
+ /*
+ * At this point in the game, we (in theory) have all the components of
+ * the operand at least parsed. Now it's time to check for syntax/semantic
+ * errors, and build the mode.
+ * This is what I have:
+ * deferred = 1 if '*'
+ * call_width = 0,1,2,4
+ * abs_width = 0,1,2,4
+ * com_width = 0,1,2,4
+ * immediate = 1 if '$'
+ * ndx = -1 or reg num
+ * dec_inc = '-' or '+' or ' '
+ * reg = -1 or reg num
+ * imreg = -1 or reg num
+ * topP->exp_of_operand
+ * really_none
+ */
+ /* Is there a displacement size? */
+ disp_size = (call_width ? call_width :
+ (com_width ? com_width :
+ abs_width ? abs_width : 0));
+
+ if (*op_bad == '\0')
+ {
+ if (imreg != -1)
+ {
+ /* Rn */
+ mode = TAHOE_DIRECT_REG;
+ if (deferred || immediate || (dec_inc != ' ') ||
+ (reg != -1) || !really_none)
+ op_bad = _("Syntax error in direct register mode.");
+ else if (ndx != -1)
+ op_bad = _("You can't index a register in direct register mode.");
+ else if (imreg == SP_REG && access == 'r')
+ op_bad =
+ _("SP can't be the source operand with direct register addressing.");
+ else if (access == 'a')
+ op_bad = _("Can't take the address of a register.");
+ else if (access == 'b')
+ op_bad = _("Direct Register can't be used in a branch.");
+ else if (width == 'q' && ((imreg % 2) || (imreg > 13)))
+ op_bad = _("For quad access, the register must be even and < 14.");
+ else if (call_width)
+ op_bad = _("You can't cast a direct register.");
+
+ if (*op_bad == '\0')
+ {
+ /* No errors, check for warnings */
+ if (width == 'q' && imreg == 12)
+ as_warn (_("Using reg 14 for quadwords can tromp the FP register."));
+
+ reg = imreg;
+ }
+
+ /* We know: imm = -1 */
+ }
+ else if (dec_inc == '-')
+ {
+ /* -(SP) */
+ mode = TAHOE_AUTO_DEC;
+ if (deferred || immediate || !really_none)
+ op_bad = _("Syntax error in auto-dec mode.");
+ else if (ndx != -1)
+ op_bad = _("You can't have an index auto dec mode.");
+ else if (access == 'r')
+ op_bad = _("Auto dec mode cant be used for reading.");
+ else if (reg != SP_REG)
+ op_bad = _("Auto dec only works of the SP register.");
+ else if (access == 'b')
+ op_bad = _("Auto dec can't be used in a branch.");
+ else if (width == 'q')
+ op_bad = _("Auto dec won't work with quadwords.");
+
+ /* We know: imm = -1, dec_inc != '-' */
+ }
+ else if (dec_inc == '+')
+ {
+ if (immediate || !really_none)
+ op_bad = _("Syntax error in one of the auto-inc modes.");
+ else if (deferred)
+ {
+ /* *(SP)+ */
+ mode = TAHOE_AUTO_INC_DEFERRED;
+ if (reg != SP_REG)
+ op_bad = _("Auto inc deferred only works of the SP register.");
+ else if (ndx != -1)
+ op_bad = _("You can't have an index auto inc deferred mode.");
+ else if (access == 'b')
+ op_bad = _("Auto inc can't be used in a branch.");
+ }
+ else
+ {
+ /* (SP)+ */
+ mode = TAHOE_AUTO_INC;
+ if (access == 'm' || access == 'w')
+ op_bad = _("You can't write to an auto inc register.");
+ else if (reg != SP_REG)
+ op_bad = _("Auto inc only works of the SP register.");
+ else if (access == 'b')
+ op_bad = _("Auto inc can't be used in a branch.");
+ else if (width == 'q')
+ op_bad = _("Auto inc won't work with quadwords.");
+ else if (ndx != -1)
+ op_bad = _("You can't have an index in auto inc mode.");
+ }
+
+ /* We know: imm = -1, dec_inc == ' ' */
+ }
+ else if (reg != -1)
+ {
+ if ((ndx != -1) && (reg == SP_REG))
+ op_bad = _("You can't index the sp register.");
+ if (deferred)
+ {
+ /* *<disp>(Rn) */
+ mode = TAHOE_REG_DISP_DEFERRED;
+ if (immediate)
+ op_bad = _("Syntax error in register displaced mode.");
+ }
+ else if (really_none)
+ {
+ /* (Rn) */
+ mode = TAHOE_REG_DEFERRED;
+ /* if reg = SP then cant be indexed */
+ }
+ else
+ {
+ /* <disp>(Rn) */
+ mode = TAHOE_REG_DISP;
+ }
+
+ /* We know: imm = -1, dec_inc == ' ', Reg = -1 */
+ }
+ else
+ {
+ if (really_none)
+ op_bad = _("An offest is needed for this operand.");
+ if (deferred && immediate)
+ {
+ /* *$<ADDR> */
+ mode = TAHOE_ABSOLUTE_ADDR;
+ disp_size = 4;
+ }
+ else if (immediate)
+ {
+ /* $<disp> */
+ mode = TAHOE_IMMEDIATE;
+ if (ndx != -1)
+ op_bad = _("You can't index a register in immediate mode.");
+ if (access == 'a')
+ op_bad = _("Immediate access can't be used as an address.");
+ /* ponder the wisdom of a cast because it doesn't do any good. */
+ }
+ else if (deferred)
+ {
+ /* *<disp> */
+ mode = TAHOE_DISP_REL_DEFERRED;
+ }
+ else
+ {
+ /* <disp> */
+ mode = TAHOE_DISPLACED_RELATIVE;
+ }
+ }
+ }
+
+ /*
+ * At this point, all the errors we can do have be checked for.
+ * We can build the 'top'. */
+
+ topP->top_ndx = ndx;
+ topP->top_reg = reg;
+ topP->top_mode = mode;
+ topP->top_error = op_bad;
+ topP->top_dispsize = disp_size;
+} /* tip_op */
+
+/*
+ * t i p ( )
+ *
+ * This converts a string into a tahoe instruction.
+ * The string must be a bare single instruction in tahoe (with BSD4 frobs)
+ * format.
+ * It provides at most one fatal error message (which stops the scan)
+ * some warning messages as it finds them.
+ * The tahoe instruction is returned in exploded form.
+ *
+ * The exploded instruction is returned to a struct tit of your choice.
+ * #include "tahoe-inst.h" to know what a struct tit is.
+ *
+ */
+
+static void
+tip (titP, instring)
+ struct tit *titP; /* We build an exploded instruction here. */
+ char *instring; /* Text of a vax instruction: we modify. */
+{
+ register struct tot_wot *twP = NULL; /* How to bit-encode this opcode. */
+ register char *p; /* 1/skip whitespace.2/scan vot_how */
+ register char *q; /* */
+ register unsigned char count; /* counts number of operands seen */
+ register struct top *operandp;/* scan operands in struct tit */
+ register char *alloperr = ""; /* error over all operands */
+ register char c; /* Remember char, (we clobber it
+ with '\0' temporarily). */
+ char *save_input_line_pointer;
+
+ if (*instring == ' ')
+ ++instring; /* Skip leading whitespace. */
+ for (p = instring; *p && *p != ' '; p++)
+ ; /* MUST end in end-of-string or
+ exactly 1 space. */
+ /* Scanned up to end of operation-code. */
+ /* Operation-code is ended with whitespace. */
+ if (p == instring)
+ {
+ titP->tit_error = _("No operator");
+ count = 0;
+ titP->tit_opcode = 0;
+ }
+ else
+ {
+ c = *p;
+ *p = '\0';
+ /*
+ * Here with instring pointing to what better be an op-name, and p
+ * pointing to character just past that.
+ * We trust instring points to an op-name, with no whitespace.
+ */
+ twP = (struct tot_wot *) hash_find (op_hash, instring);
+ *p = c; /* Restore char after op-code. */
+ if (twP == 0)
+ {
+ titP->tit_error = _("Unknown operator");
+ count = 0;
+ titP->tit_opcode = 0;
+ }
+ else
+ {
+ /*
+ * We found a match! So lets pick up as many operands as the
+ * instruction wants, and even gripe if there are too many.
+ * We expect comma to seperate each operand.
+ * We let instring track the text, while p tracks a part of the
+ * struct tot.
+ */
+
+ count = 0; /* no operands seen yet */
+ instring = p + (*p != '\0'); /* point past the operation code */
+ /* tip_op() screws with the input_line_pointer, so save it before
+ I jump in */
+ save_input_line_pointer = input_line_pointer;
+ for (p = twP->args, operandp = titP->tit_operand;
+ !*alloperr && *p;
+ operandp++, p += 2)
+ {
+ /*
+ * Here to parse one operand. Leave instring pointing just
+ * past any one ',' that marks the end of this operand.
+ */
+ if (!p[1])
+ as_fatal (_("Compiler bug: ODD number of bytes in arg structure %s."),
+ twP->args);
+ else if (*instring)
+ {
+ for (q = instring; (*q != ',' && *q != '\0'); q++)
+ {
+ if (*q == '\'' && q[1] != '\0') /* Jump quoted characters */
+ q++;
+ }
+ c = *q;
+ /*
+ * Q points to ',' or '\0' that ends argument. C is that
+ * character.
+ */
+ *q = '\0';
+ operandp->top_access = p[0];
+ operandp->top_width = p[1];
+ tip_op (instring - 1, operandp);
+ *q = c; /* Restore input text. */
+ if (*(operandp->top_error))
+ {
+ alloperr = operandp->top_error;
+ }
+ instring = q + (c ? 1 : 0); /* next operand (if any) */
+ count++; /* won another argument, may have an operr */
+ }
+ else
+ alloperr = _("Not enough operands");
+ }
+ /* Restore the pointer. */
+ input_line_pointer = save_input_line_pointer;
+
+ if (!*alloperr)
+ {
+ if (*instring == ' ')
+ instring++; /* Skip whitespace. */
+ if (*instring)
+ alloperr = _("Too many operands");
+ }
+ titP->tit_error = alloperr;
+ }
+ }
+
+ titP->tit_opcode = twP->code; /* The op-code. */
+ titP->tit_operands = count;
+} /* tip */
+
+/* md_assemble() emit frags for 1 instruction */
+void
+md_assemble (instruction_string)
+ char *instruction_string; /* A string: assemble 1 instruction. */
+{
+ char *p;
+ register struct top *operandP;/* An operand. Scans all operands. */
+ /* char c_save; fixme: remove this line *//* What used to live after an expression. */
+ /* struct frag *fragP; fixme: remove this line *//* Fragment of code we just made. */
+ /* register struct top *end_operandP; fixme: remove this line *//* -> slot just after last operand
+ Limit of the for (each operand). */
+ register expressionS *expP; /* -> expression values for this operand */
+
+ /* These refer to an instruction operand expression. */
+ segT to_seg; /* Target segment of the address. */
+
+ register valueT this_add_number;
+ register struct symbol *this_add_symbol; /* +ve (minuend) symbol. */
+
+ /* tahoe_opcodeT opcode_as_number; fixme: remove this line *//* The opcode as a number. */
+ char *opcodeP; /* Where it is in a frag. */
+ /* char *opmodeP; fixme: remove this line *//* Where opcode type is, in a frag. */
+
+ int dispsize; /* From top_dispsize: tahoe_operand_width
+ (in bytes) */
+ int is_undefined; /* 1 if operand expression's
+ segment not known yet. */
+ int pc_rel; /* Is this operand pc relative? */
+
+ /* Decode the operand. */
+ tip (&t, instruction_string);
+
+ /*
+ * Check to see if this operand decode properly.
+ * Notice that we haven't made any frags yet.
+ * If it goofed, then this instruction will wedge in any pass,
+ * and we can safely flush it, without causing interpass symbol phase
+ * errors. That is, without changing label values in different passes.
+ */
+ if (*t.tit_error)
+ {
+ as_warn (_("Ignoring statement due to \"%s\""), t.tit_error);
+ }
+ else
+ {
+ /* We saw no errors in any operands - try to make frag(s) */
+ /* Emit op-code. */
+ /* Remember where it is, in case we want to modify the op-code later. */
+ opcodeP = frag_more (1);
+ *opcodeP = t.tit_opcode;
+ /* Now do each operand. */
+ for (operandP = t.tit_operand;
+ operandP < t.tit_operand + t.tit_operands;
+ operandP++)
+ { /* for each operand */
+ expP = &(operandP->exp_of_operand);
+ if (operandP->top_ndx >= 0)
+ {
+ /* Indexed addressing byte
+ Legality of indexed mode already checked: it is OK */
+ FRAG_APPEND_1_CHAR (0x40 + operandP->top_ndx);
+ } /* if(top_ndx>=0) */
+
+ /* Here to make main operand frag(s). */
+ this_add_number = expP->X_add_number;
+ this_add_symbol = expP->X_add_symbol;
+ to_seg = operandP->seg_of_operand;
+ know (to_seg == SEG_UNKNOWN || \
+ to_seg == SEG_ABSOLUTE || \
+ to_seg == SEG_DATA || \
+ to_seg == SEG_TEXT || \
+ to_seg == SEG_BSS);
+ is_undefined = (to_seg == SEG_UNKNOWN);
+ /* Do we know how big this opperand is? */
+ dispsize = operandP->top_dispsize;
+ pc_rel = 0;
+ /* Deal with the branch possabilities. (Note, this doesn't include
+ jumps.)*/
+ if (operandP->top_access == 'b')
+ {
+ /* Branches must be expressions. A psuedo branch can also jump to
+ an absolute address. */
+ if (to_seg == now_seg || is_undefined)
+ {
+ /* If is_undefined, then it might BECOME now_seg by relax time. */
+ if (dispsize)
+ {
+ /* I know how big the branch is supposed to be (it's a normal
+ branch), so I set up the frag, and let GAS do the rest. */
+ p = frag_more (dispsize);
+ fix_new (frag_now, p - frag_now->fr_literal,
+ this_add_symbol, this_add_number,
+ size_to_fx (dispsize, 1),
+ NULL);
+ }
+ else
+ {
+ /* (to_seg==now_seg || to_seg == SEG_UNKNOWN) && dispsize==0 */
+ /* If we don't know how big it is, then its a synthetic branch,
+ so we set up a simple relax state. */
+ switch (operandP->top_width)
+ {
+ case TAHOE_WIDTH_CONDITIONAL_JUMP:
+ /* Simple (conditional) jump. I may have to reverse the
+ condition of opcodeP, and then jump to my destination.
+ I set 1 byte aside for the branch off set, and could need 6
+ more bytes for the pc_rel jump */
+ frag_var (rs_machine_dependent, 7, 1,
+ ENCODE_RELAX (STATE_CONDITIONAL_BRANCH,
+ is_undefined ? STATE_UNDF : STATE_BYTE),
+ this_add_symbol, this_add_number, opcodeP);
+ break;
+ case TAHOE_WIDTH_ALWAYS_JUMP:
+ /* Simple (unconditional) jump. I may have to convert this to
+ a word branch, or an absolute jump. */
+ frag_var (rs_machine_dependent, 5, 1,
+ ENCODE_RELAX (STATE_ALWAYS_BRANCH,
+ is_undefined ? STATE_UNDF : STATE_BYTE),
+ this_add_symbol, this_add_number, opcodeP);
+ break;
+ /* The smallest size for the next 2 cases is word. */
+ case TAHOE_WIDTH_BIG_REV_JUMP:
+ frag_var (rs_machine_dependent, 8, 2,
+ ENCODE_RELAX (STATE_BIG_REV_BRANCH,
+ is_undefined ? STATE_UNDF : STATE_WORD),
+ this_add_symbol, this_add_number,
+ opcodeP);
+ break;
+ case TAHOE_WIDTH_BIG_NON_REV_JUMP:
+ frag_var (rs_machine_dependent, 10, 2,
+ ENCODE_RELAX (STATE_BIG_NON_REV_BRANCH,
+ is_undefined ? STATE_UNDF : STATE_WORD),
+ this_add_symbol, this_add_number,
+ opcodeP);
+ break;
+ default:
+ as_fatal (_("Compliler bug: Got a case (%d) I wasn't expecting."),
+ operandP->top_width);
+ }
+ }
+ }
+ else
+ {
+ /* to_seg != now_seg && to_seg != seg_unknown (still in branch)
+ In other words, I'm jumping out of my segment so extend the
+ branches to jumps, and let GAS fix them. */
+
+ /* These are "branches" what will always be branches around a jump
+ to the correct addresss in real life.
+ If to_seg is SEG_ABSOLUTE, just encode the branch in,
+ else let GAS fix the address. */
+
+ switch (operandP->top_width)
+ {
+ /* The theory:
+ For SEG_ABSOLUTE, then mode is ABSOLUTE_ADDR, jump
+ to that addresss (not pc_rel).
+ For other segs, address is a long word PC rel jump. */
+ case TAHOE_WIDTH_CONDITIONAL_JUMP:
+ /* b<cond> */
+ /* To reverse the condition in a TAHOE branch,
+ complement bit 4 */
+ *opcodeP ^= 0x10;
+ p = frag_more (7);
+ *p++ = 6;
+ *p++ = TAHOE_JMP;
+ *p++ = (operandP->top_mode ==
+ TAHOE_ABSOLUTE_ADDR ? TAHOE_ABSOLUTE_ADDR :
+ TAHOE_PC_REL_LONG);
+ fix_new (frag_now, p - frag_now->fr_literal,
+ this_add_symbol, this_add_number,
+ (to_seg != SEG_ABSOLUTE) ? FX_PCREL32 : FX_32, NULL);
+ /*
+ * Now (eg) BLEQ 1f
+ * JMP foo
+ * 1:
+ */
+ break;
+ case TAHOE_WIDTH_ALWAYS_JUMP:
+ /* br, just turn it into a jump */
+ *opcodeP = TAHOE_JMP;
+ p = frag_more (5);
+ *p++ = (operandP->top_mode ==
+ TAHOE_ABSOLUTE_ADDR ? TAHOE_ABSOLUTE_ADDR :
+ TAHOE_PC_REL_LONG);
+ fix_new (frag_now, p - frag_now->fr_literal,
+ this_add_symbol, this_add_number,
+ (to_seg != SEG_ABSOLUTE) ? FX_PCREL32 : FX_32, NULL);
+ /* Now (eg) JMP foo */
+ break;
+ case TAHOE_WIDTH_BIG_REV_JUMP:
+ p = frag_more (8);
+ *opcodeP ^= 0x10;
+ *p++ = 0;
+ *p++ = 6;
+ *p++ = TAHOE_JMP;
+ *p++ = (operandP->top_mode ==
+ TAHOE_ABSOLUTE_ADDR ? TAHOE_ABSOLUTE_ADDR :
+ TAHOE_PC_REL_LONG);
+ fix_new (frag_now, p - frag_now->fr_literal,
+ this_add_symbol, this_add_number,
+ (to_seg != SEG_ABSOLUTE) ? FX_PCREL32 : FX_32, NULL);
+ /*
+ * Now (eg) ACBx 1f
+ * JMP foo
+ * 1:
+ */
+ break;
+ case TAHOE_WIDTH_BIG_NON_REV_JUMP:
+ p = frag_more (10);
+ *p++ = 0;
+ *p++ = 2;
+ *p++ = TAHOE_BRB;
+ *p++ = 6;
+ *p++ = TAHOE_JMP;
+ *p++ = (operandP->top_mode ==
+ TAHOE_ABSOLUTE_ADDR ? TAHOE_ABSOLUTE_ADDR :
+ TAHOE_PC_REL_LONG);
+ fix_new (frag_now, p - frag_now->fr_literal,
+ this_add_symbol, this_add_number,
+ (to_seg != SEG_ABSOLUTE) ? FX_PCREL32 : FX_32, NULL);
+ /*
+ * Now (eg) xOBxxx 1f
+ * BRB 2f
+ * 1: JMP @#foo
+ * 2:
+ */
+ break;
+ case 'b':
+ case 'w':
+ as_warn (_("Real branch displacements must be expressions."));
+ break;
+ default:
+ as_fatal (_("Complier error: I got an unknown synthetic branch :%c"),
+ operandP->top_width);
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* It ain't a branch operand. */
+ switch (operandP->top_mode)
+ {
+ /* Auto-foo access, only works for one reg (SP)
+ so the only thing needed is the mode. */
+ case TAHOE_AUTO_DEC:
+ case TAHOE_AUTO_INC:
+ case TAHOE_AUTO_INC_DEFERRED:
+ FRAG_APPEND_1_CHAR (operandP->top_mode);
+ break;
+
+ /* Numbered Register only access. Only thing needed is the
+ mode + Register number */
+ case TAHOE_DIRECT_REG:
+ case TAHOE_REG_DEFERRED:
+ FRAG_APPEND_1_CHAR (operandP->top_mode + operandP->top_reg);
+ break;
+
+ /* An absolute address. It's size is always 5 bytes.
+ (mode_type + 4 byte address). */
+ case TAHOE_ABSOLUTE_ADDR:
+ know ((this_add_symbol == NULL));
+ p = frag_more (5);
+ *p = TAHOE_ABSOLUTE_ADDR;
+ md_number_to_chars (p + 1, this_add_number, 4);
+ break;
+
+ /* Immediate data. If the size isn't known, then it's an address
+ + and offset, which is 4 bytes big. */
+ case TAHOE_IMMEDIATE:
+ if (this_add_symbol != NULL)
+ {
+ p = frag_more (5);
+ *p++ = TAHOE_IMMEDIATE_LONGWORD;
+ fix_new (frag_now, p - frag_now->fr_literal,
+ this_add_symbol, this_add_number,
+ FX_32, NULL);
+ }
+ else
+ {
+ /* It's a integer, and I know it's size. */
+ if ((unsigned) this_add_number < 0x40)
+ {
+ /* Will it fit in a literal? */
+ FRAG_APPEND_1_CHAR ((byte) this_add_number);
+ }
+ else
+ {
+ p = frag_more (dispsize + 1);
+ switch (dispsize)
+ {
+ case 1:
+ *p++ = TAHOE_IMMEDIATE_BYTE;
+ *p = (byte) this_add_number;
+ break;
+ case 2:
+ *p++ = TAHOE_IMMEDIATE_WORD;
+ md_number_to_chars (p, this_add_number, 2);
+ break;
+ case 4:
+ *p++ = TAHOE_IMMEDIATE_LONGWORD;
+ md_number_to_chars (p, this_add_number, 4);
+ break;
+ }
+ }
+ }
+ break;
+
+ /* Distance from the PC. If the size isn't known, we have to relax
+ into it. The difference between this and disp(sp) is that
+ this offset is pc_rel, and disp(sp) isn't.
+ Note the drop through code. */
+
+ case TAHOE_DISPLACED_RELATIVE:
+ case TAHOE_DISP_REL_DEFERRED:
+ operandP->top_reg = PC_REG;
+ pc_rel = 1;
+
+ /* Register, plus a displacement mode. Save the register number,
+ and weather its deffered or not, and relax the size if it isn't
+ known. */
+ case TAHOE_REG_DISP:
+ case TAHOE_REG_DISP_DEFERRED:
+ if (operandP->top_mode == TAHOE_DISP_REL_DEFERRED ||
+ operandP->top_mode == TAHOE_REG_DISP_DEFERRED)
+ operandP->top_reg += 0x10; /* deffered mode is always 0x10 higher
+ than it's non-deffered sibling. */
+
+ /* Is this a value out of this segment?
+ The first part of this conditional is a cludge to make gas
+ produce the same output as 'as' when there is a lable, in
+ the current segment, displaceing a register. It's strange,
+ and no one in their right mind would do it, but it's easy
+ to cludge. */
+ if ((dispsize == 0 && !pc_rel) ||
+ (to_seg != now_seg && !is_undefined && to_seg != SEG_ABSOLUTE))
+ dispsize = 4;
+
+ if (dispsize == 0)
+ {
+ /*
+ * We have a SEG_UNKNOWN symbol, or the size isn't cast.
+ * It might turn out to be in the same segment as
+ * the instruction, permitting relaxation.
+ */
+ p = frag_var (rs_machine_dependent, 5, 2,
+ ENCODE_RELAX (STATE_PC_RELATIVE,
+ is_undefined ? STATE_UNDF : STATE_BYTE),
+ this_add_symbol, this_add_number, 0);
+ *p = operandP->top_reg;
+ }
+ else
+ {
+ /* Either this is an abs, or a cast. */
+ p = frag_more (dispsize + 1);
+ switch (dispsize)
+ {
+ case 1:
+ *p = TAHOE_PC_OR_BYTE + operandP->top_reg;
+ break;
+ case 2:
+ *p = TAHOE_PC_OR_WORD + operandP->top_reg;
+ break;
+ case 4:
+ *p = TAHOE_PC_OR_LONG + operandP->top_reg;
+ break;
+ };
+ fix_new (frag_now, p + 1 - frag_now->fr_literal,
+ this_add_symbol, this_add_number,
+ size_to_fx (dispsize, pc_rel), NULL);
+ }
+ break;
+ default:
+ as_fatal (_("Barf, bad mode %x\n"), operandP->top_mode);
+ }
+ }
+ } /* for(operandP) */
+ } /* if(!need_pass_2 && !goofed) */
+} /* tahoe_assemble() */
+
+
+/* We have no need to default values of symbols. */
+
+/* ARGSUSED */
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ return 0;
+} /* md_undefined_symbol() */
+
+/* Round up a section size to the appropriate boundary. */
+valueT
+md_section_align (segment, size)
+ segT segment;
+ valueT size;
+{
+ return ((size + 7) & ~7); /* Round all sects to multiple of 8 */
+} /* md_section_align() */
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the sparc, they're relative to the address of the offset, plus
+ its size. This gets us to the following instruction.
+ (??? Is this right? FIXME-SOON) */
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ return (((fixP->fx_type == FX_8
+ || fixP->fx_type == FX_PCREL8)
+ ? 1
+ : ((fixP->fx_type == FX_16
+ || fixP->fx_type == FX_PCREL16)
+ ? 2
+ : ((fixP->fx_type == FX_32
+ || fixP->fx_type == FX_PCREL32)
+ ? 4
+ : 0))) + fixP->fx_where + fixP->fx_frag->fr_address);
+} /* md_pcrel_from() */
+
+int
+tc_is_pcrel (fixP)
+ fixS *fixP;
+{
+ /* should never be called */
+ know (0);
+ return (0);
+} /* tc_is_pcrel() */
+
+/* end of tc-tahoe.c */
diff --git a/gas/config/tc-tahoe.h b/gas/config/tc-tahoe.h
new file mode 100644
index 0000000..be8a5be
--- /dev/null
+++ b/gas/config/tc-tahoe.h
@@ -0,0 +1,43 @@
+/* This file is tc-tahoe.h
+
+ Copyright (C) 1987-1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#define TC_TAHOE 1
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+#define NO_LISTING
+
+#define tc_headers_hook(a) {;} /* don't need it. */
+#define tc_crawl_symbol_chain(a) {;} /* don't need it. */
+#define tc_aout_pre_write_hook(a) {;}
+
+#define md_operand(x)
+
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of tc-tahoe.h */
diff --git a/gas/config/tc-tic30.c b/gas/config/tc-tic30.c
new file mode 100644
index 0000000..61ed905
--- /dev/null
+++ b/gas/config/tc-tic30.c
@@ -0,0 +1,1887 @@
+/* tc-c30.c -- Assembly code for the Texas Instruments TMS320C30
+ Copyright (C) 1998 Free Software Foundation.
+ Contributed by Steven Haworth (steve@pm.cse.rmit.edu.au)
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+/*
+ Texas Instruments TMS320C30 machine specific gas.
+ Written by Steven Haworth (steve@pm.cse.rmit.edu.au).
+ Bugs & suggestions are completely welcome. This is free software.
+ Please help us make it better.
+ */
+
+#include "as.h"
+#include "opcode/tic30.h"
+
+/* put here all non-digit non-letter charcters that may occur in an operand */
+static char operand_special_chars[] = "%$-+(,)*._~/<>&^!:[@]";
+static char *ordinal_names[] =
+{"first", "second", "third", "fourth", "fifth"};
+
+const int md_reloc_size = 0;
+
+const char comment_chars[] = ";";
+const char line_comment_chars[] = "*";
+const char line_separator_chars[] = "";
+
+const char *md_shortopts = "";
+struct option md_longopts[] =
+{
+ {NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+const char FLT_CHARS[] = "fFdDxX";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* tables for lexical analysis */
+static char opcode_chars[256];
+static char register_chars[256];
+static char operand_chars[256];
+static char space_chars[256];
+static char identifier_chars[256];
+static char digit_chars[256];
+
+/* lexical macros */
+#define is_opcode_char(x) (opcode_chars[(unsigned char) x])
+#define is_operand_char(x) (operand_chars[(unsigned char) x])
+#define is_register_char(x) (register_chars[(unsigned char) x])
+#define is_space_char(x) (space_chars[(unsigned char) x])
+#define is_identifier_char(x) (identifier_chars[(unsigned char) x])
+#define is_digit_char(x) (digit_chars[(unsigned char) x])
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {0, 0, 0}
+};
+
+#undef USE_STDOUT
+#define USE_STDOUT 1
+
+#ifdef USE_STDARG
+
+#include <stdarg.h>
+
+int
+debug (const char *string,...)
+{
+ if (flag_debug)
+ {
+ va_list argptr;
+ char str[100];
+
+ va_start (argptr, string);
+ vsprintf (str, string, argptr);
+ if (str[0] == '\0')
+ return (0);
+ va_end (argptr);
+ fputs (str, USE_STDOUT ? stdout : stderr);
+ return strlen (str);
+ }
+ else
+ return 0;
+}
+#else
+int
+debug (string, va_alist)
+ const char *string;
+ va_dcl
+{
+ if (flag_debug)
+ {
+ va_list argptr;
+ char str[100];
+ int cnt;
+
+ va_start (argptr, string);
+ cnt = vsprintf (str, string, argptr);
+ if (str[0] == NULL)
+ return (0);
+ va_end (argptr);
+ fputs (str, USE_STDOUT ? stdout : stderr);
+ return (cnt);
+ }
+ else
+ return 0;
+}
+#endif
+
+/* hash table for opcode lookup */
+static struct hash_control *op_hash;
+/* hash table for parallel opcode lookup */
+static struct hash_control *parop_hash;
+/* hash table for register lookup */
+static struct hash_control *reg_hash;
+/* hash table for indirect addressing lookup */
+static struct hash_control *ind_hash;
+
+void
+md_begin ()
+{
+ const char *hash_err;
+ debug ("In md_begin()\n");
+ op_hash = hash_new ();
+ {
+ const template *current_optab = tic30_optab;
+ for (; current_optab < tic30_optab_end; current_optab++)
+ {
+ hash_err = hash_insert (op_hash, current_optab->name, (char *) current_optab);
+ if (hash_err)
+ as_fatal ("Internal Error: Can't Hash %s: %s", current_optab->name, hash_err);
+ }
+ }
+ parop_hash = hash_new ();
+ {
+ const partemplate *current_parop = tic30_paroptab;
+ for (; current_parop < tic30_paroptab_end; current_parop++)
+ {
+ hash_err = hash_insert (parop_hash, current_parop->name, (char *) current_parop);
+ if (hash_err)
+ as_fatal ("Internal Error: Can't Hash %s: %s", current_parop->name, hash_err);
+ }
+ }
+ reg_hash = hash_new ();
+ {
+ const reg *current_reg = tic30_regtab;
+ for (; current_reg < tic30_regtab_end; current_reg++)
+ {
+ hash_err = hash_insert (reg_hash, current_reg->name, (char *) current_reg);
+ if (hash_err)
+ as_fatal ("Internal Error: Can't Hash %s: %s", current_reg->name, hash_err);
+ }
+ }
+ ind_hash = hash_new ();
+ {
+ const ind_addr_type *current_ind = tic30_indaddr_tab;
+ for (; current_ind < tic30_indaddrtab_end; current_ind++)
+ {
+ hash_err = hash_insert (ind_hash, current_ind->syntax, (char *) current_ind);
+ if (hash_err)
+ as_fatal ("Internal Error: Can't Hash %s: %s", current_ind->syntax, hash_err);
+ }
+ }
+ /* fill in lexical tables: opcode_chars, operand_chars, space_chars */
+ {
+ register int c;
+ register char *p;
+
+ for (c = 0; c < 256; c++)
+ {
+ if (islower (c) || isdigit (c))
+ {
+ opcode_chars[c] = c;
+ register_chars[c] = c;
+ }
+ else if (isupper (c))
+ {
+ opcode_chars[c] = tolower (c);
+ register_chars[c] = opcode_chars[c];
+ }
+ else if (c == ')' || c == '(')
+ {
+ register_chars[c] = c;
+ }
+ if (isupper (c) || islower (c) || isdigit (c))
+ operand_chars[c] = c;
+ if (isdigit (c) || c == '-')
+ digit_chars[c] = c;
+ if (isalpha (c) || c == '_' || c == '.' || isdigit (c))
+ identifier_chars[c] = c;
+ if (c == ' ' || c == '\t')
+ space_chars[c] = c;
+ if (c == '_')
+ opcode_chars[c] = c;
+ }
+ for (p = operand_special_chars; *p != '\0'; p++)
+ operand_chars[(unsigned char) *p] = *p;
+ }
+}
+
+/* Address Mode OR values */
+#define AM_Register 0x00000000
+#define AM_Direct 0x00200000
+#define AM_Indirect 0x00400000
+#define AM_Immediate 0x00600000
+#define AM_NotReq 0xFFFFFFFF
+
+/* PC Relative OR values */
+#define PC_Register 0x00000000
+#define PC_Relative 0x02000000
+
+typedef struct
+{
+ unsigned op_type;
+ struct
+ {
+ int resolved;
+ unsigned address;
+ char *label;
+ expressionS direct_expr;
+ }
+ direct;
+ struct
+ {
+ unsigned mod;
+ int ARnum;
+ unsigned char disp;
+ }
+ indirect;
+ struct
+ {
+ unsigned opcode;
+ }
+ reg;
+ struct
+ {
+ int resolved;
+ int decimal_found;
+ float f_number;
+ int s_number;
+ unsigned int u_number;
+ char *label;
+ expressionS imm_expr;
+ }
+ immediate;
+}
+operand;
+
+int tic30_parallel_insn PARAMS ((char *));
+operand *tic30_operand PARAMS ((char *));
+char *tic30_find_parallel_insn PARAMS ((char *, char *));
+
+template *opcode;
+
+struct tic30_insn
+ {
+ template *tm; /* Template of current instruction */
+ unsigned opcode; /* Final opcode */
+ int operands; /* Number of given operands */
+ /* Type of operand given in instruction */
+ operand *operand_type[MAX_OPERANDS];
+ unsigned addressing_mode; /* Final addressing mode of instruction */
+ };
+
+struct tic30_insn insn;
+static int found_parallel_insn;
+
+void
+md_assemble (line)
+ char *line;
+{
+ template *opcode;
+ char *current_posn;
+ char *token_start;
+ char save_char;
+ int count;
+
+ debug ("In md_assemble() with argument %s\n", line);
+ memset (&insn, '\0', sizeof (insn));
+ if (found_parallel_insn)
+ {
+ debug ("Line is second part of parallel instruction\n\n");
+ found_parallel_insn = 0;
+ return;
+ }
+ if ((current_posn = tic30_find_parallel_insn (line, input_line_pointer + 1)) == NULL)
+ current_posn = line;
+ else
+ found_parallel_insn = 1;
+ while (is_space_char (*current_posn))
+ current_posn++;
+ token_start = current_posn;
+ if (!is_opcode_char (*current_posn))
+ {
+ as_bad ("Invalid character %s in opcode", output_invalid (*current_posn));
+ return;
+ }
+ /* Check if instruction is a parallel instruction by seeing if the first
+ character is a q. */
+ if (*token_start == 'q')
+ {
+ if (tic30_parallel_insn (token_start))
+ {
+ if (found_parallel_insn)
+ free (token_start);
+ return;
+ }
+ }
+ while (is_opcode_char (*current_posn))
+ current_posn++;
+ { /* Find instruction */
+ save_char = *current_posn;
+ *current_posn = '\0';
+ opcode = (template *) hash_find (op_hash, token_start);
+ if (opcode)
+ {
+ debug ("Found instruction %s\n", opcode->name);
+ insn.tm = opcode;
+ }
+ else
+ {
+ debug ("Didn't find insn\n");
+ as_bad ("Unknown TMS320C30 instruction: %s", token_start);
+ return;
+ }
+ *current_posn = save_char;
+ }
+ if (*current_posn != END_OF_INSN)
+ { /* Find operands */
+ int paren_not_balanced;
+ int expecting_operand = 0;
+ int this_operand;
+ do
+ {
+ /* skip optional white space before operand */
+ while (!is_operand_char (*current_posn) && *current_posn != END_OF_INSN)
+ {
+ if (!is_space_char (*current_posn))
+ {
+ as_bad ("Invalid character %s before %s operand",
+ output_invalid (*current_posn),
+ ordinal_names[insn.operands]);
+ return;
+ }
+ current_posn++;
+ }
+ token_start = current_posn; /* after white space */
+ paren_not_balanced = 0;
+ while (paren_not_balanced || *current_posn != ',')
+ {
+ if (*current_posn == END_OF_INSN)
+ {
+ if (paren_not_balanced)
+ {
+ as_bad ("Unbalanced parenthesis in %s operand.",
+ ordinal_names[insn.operands]);
+ return;
+ }
+ else
+ break; /* we are done */
+ }
+ else if (!is_operand_char (*current_posn) && !is_space_char (*current_posn))
+ {
+ as_bad ("Invalid character %s in %s operand",
+ output_invalid (*current_posn),
+ ordinal_names[insn.operands]);
+ return;
+ }
+ if (*current_posn == '(')
+ ++paren_not_balanced;
+ if (*current_posn == ')')
+ --paren_not_balanced;
+ current_posn++;
+ }
+ if (current_posn != token_start)
+ { /* yes, we've read in another operand */
+ this_operand = insn.operands++;
+ if (insn.operands > MAX_OPERANDS)
+ {
+ as_bad ("Spurious operands; (%d operands/instruction max)",
+ MAX_OPERANDS);
+ return;
+ }
+ /* now parse operand adding info to 'insn' as we go along */
+ save_char = *current_posn;
+ *current_posn = '\0';
+ insn.operand_type[this_operand] = tic30_operand (token_start);
+ *current_posn = save_char;
+ if (insn.operand_type[this_operand] == NULL)
+ return;
+ }
+ else
+ {
+ if (expecting_operand)
+ {
+ as_bad ("Expecting operand after ','; got nothing");
+ return;
+ }
+ if (*current_posn == ',')
+ {
+ as_bad ("Expecting operand before ','; got nothing");
+ return;
+ }
+ }
+ /* now *current_posn must be either ',' or END_OF_INSN */
+ if (*current_posn == ',')
+ {
+ if (*++current_posn == END_OF_INSN)
+ { /* just skip it, if it's \n complain */
+ as_bad ("Expecting operand after ','; got nothing");
+ return;
+ }
+ expecting_operand = 1;
+ }
+ }
+ while (*current_posn != END_OF_INSN); /* until we get end of insn */
+ }
+ debug ("Number of operands found: %d\n", insn.operands);
+ /* Check that number of operands is correct */
+ if (insn.operands != insn.tm->operands)
+ {
+ int i;
+ int numops = insn.tm->operands;
+ /* If operands are not the same, then see if any of the operands are not
+ required. Then recheck with number of given operands. If they are still not
+ the same, then give an error, otherwise carry on. */
+ for (i = 0; i < insn.tm->operands; i++)
+ if (insn.tm->operand_types[i] & NotReq)
+ numops--;
+ if (insn.operands != numops)
+ {
+ as_bad ("Incorrect number of operands given");
+ return;
+ }
+ }
+ insn.addressing_mode = AM_NotReq;
+ for (count = 0; count < insn.operands; count++)
+ {
+ if (insn.operand_type[count]->op_type & insn.tm->operand_types[count])
+ {
+ debug ("Operand %d matches\n", count + 1);
+ /* If instruction has two operands and has an AddressMode modifier then set
+ addressing mode type for instruction */
+ if (insn.tm->opcode_modifier == AddressMode)
+ {
+ int addr_insn = 0;
+ /* Store instruction uses the second operand for the address mode. */
+ if ((insn.tm->operand_types[1] & (Indirect | Direct)) == (Indirect | Direct))
+ addr_insn = 1;
+ if (insn.operand_type[addr_insn]->op_type & (AllReg))
+ insn.addressing_mode = AM_Register;
+ else if (insn.operand_type[addr_insn]->op_type & Direct)
+ insn.addressing_mode = AM_Direct;
+ else if (insn.operand_type[addr_insn]->op_type & Indirect)
+ insn.addressing_mode = AM_Indirect;
+ else
+ insn.addressing_mode = AM_Immediate;
+ }
+ }
+ else
+ {
+ as_bad ("The %s operand doesn't match", ordinal_names[count]);
+ return;
+ }
+ }
+ /* Now set the addressing mode for 3 operand instructions. */
+ if ((insn.tm->operand_types[0] & op3T1) && (insn.tm->operand_types[1] & op3T2))
+ {
+ /* Set the addressing mode to the values used for 2 operand instructions in the
+ G addressing field of the opcode. */
+ char *p;
+ switch (insn.operand_type[0]->op_type)
+ {
+ case Rn:
+ case ARn:
+ case DPReg:
+ case OtherReg:
+ if (insn.operand_type[1]->op_type & (AllReg))
+ insn.addressing_mode = AM_Register;
+ else if (insn.operand_type[1]->op_type & Indirect)
+ insn.addressing_mode = AM_Direct;
+ else
+ {
+ /* Shouldn't make it to this stage */
+ as_bad ("Incompatible first and second operands in instruction");
+ return;
+ }
+ break;
+ case Indirect:
+ if (insn.operand_type[1]->op_type & (AllReg))
+ insn.addressing_mode = AM_Indirect;
+ else if (insn.operand_type[1]->op_type & Indirect)
+ insn.addressing_mode = AM_Immediate;
+ else
+ {
+ /* Shouldn't make it to this stage */
+ as_bad ("Incompatible first and second operands in instruction");
+ return;
+ }
+ break;
+ }
+ /* Now make up the opcode for the 3 operand instructions. As in parallel
+ instructions, there will be no unresolved values, so they can be fully formed
+ and added to the frag table. */
+ insn.opcode = insn.tm->base_opcode;
+ if (insn.operand_type[0]->op_type & Indirect)
+ {
+ insn.opcode |= (insn.operand_type[0]->indirect.ARnum);
+ insn.opcode |= (insn.operand_type[0]->indirect.mod << 3);
+ }
+ else
+ insn.opcode |= (insn.operand_type[0]->reg.opcode);
+ if (insn.operand_type[1]->op_type & Indirect)
+ {
+ insn.opcode |= (insn.operand_type[1]->indirect.ARnum << 8);
+ insn.opcode |= (insn.operand_type[1]->indirect.mod << 11);
+ }
+ else
+ insn.opcode |= (insn.operand_type[1]->reg.opcode << 8);
+ if (insn.operands == 3)
+ insn.opcode |= (insn.operand_type[2]->reg.opcode << 16);
+ insn.opcode |= insn.addressing_mode;
+ p = frag_more (INSN_SIZE);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else
+ { /* Not a three operand instruction */
+ char *p;
+ int am_insn = -1;
+ insn.opcode = insn.tm->base_opcode;
+ /* Create frag for instruction - all instructions are 4 bytes long. */
+ p = frag_more (INSN_SIZE);
+ if ((insn.operands > 0) && (insn.tm->opcode_modifier == AddressMode))
+ {
+ insn.opcode |= insn.addressing_mode;
+ if (insn.addressing_mode == AM_Indirect)
+ {
+ /* Determine which operand gives the addressing mode */
+ if (insn.operand_type[0]->op_type & Indirect)
+ am_insn = 0;
+ if ((insn.operands > 1) && (insn.operand_type[1]->op_type & Indirect))
+ am_insn = 1;
+ insn.opcode |= (insn.operand_type[am_insn]->indirect.disp);
+ insn.opcode |= (insn.operand_type[am_insn]->indirect.ARnum << 8);
+ insn.opcode |= (insn.operand_type[am_insn]->indirect.mod << 11);
+ if (insn.operands > 1)
+ insn.opcode |= (insn.operand_type[!am_insn]->reg.opcode << 16);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else if (insn.addressing_mode == AM_Register)
+ {
+ insn.opcode |= (insn.operand_type[0]->reg.opcode);
+ if (insn.operands > 1)
+ insn.opcode |= (insn.operand_type[1]->reg.opcode << 16);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else if (insn.addressing_mode == AM_Direct)
+ {
+ if (insn.operand_type[0]->op_type & Direct)
+ am_insn = 0;
+ if ((insn.operands > 1) && (insn.operand_type[1]->op_type & Direct))
+ am_insn = 1;
+ if (insn.operands > 1)
+ insn.opcode |= (insn.operand_type[!am_insn]->reg.opcode << 16);
+ if (insn.operand_type[am_insn]->direct.resolved == 1)
+ {
+ /* Resolved values can be placed straight into instruction word, and output */
+ insn.opcode |= (insn.operand_type[am_insn]->direct.address & 0x0000FFFF);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else
+ { /* Unresolved direct addressing mode instruction */
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ fix_new_exp (frag_now, p + 2 - (frag_now->fr_literal), 2, &insn.operand_type[am_insn]->direct.direct_expr, 0, 0);
+ }
+ }
+ else if (insn.addressing_mode == AM_Immediate)
+ {
+ if (insn.operand_type[0]->immediate.resolved == 1)
+ {
+ char *keeploc;
+ int size;
+ if (insn.operands > 1)
+ insn.opcode |= (insn.operand_type[1]->reg.opcode << 16);
+ switch (insn.tm->imm_arg_type)
+ {
+ case Imm_Float:
+ debug ("Floating point first operand\n");
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ keeploc = input_line_pointer;
+ input_line_pointer = insn.operand_type[0]->immediate.label;
+ if (md_atof ('f', p + 2, &size) != 0)
+ {
+ as_bad ("invalid short form floating point immediate operand");
+ return;
+ }
+ input_line_pointer = keeploc;
+ break;
+ case Imm_UInt:
+ debug ("Unsigned int first operand\n");
+ if (insn.operand_type[0]->immediate.decimal_found)
+ as_warn ("rounding down first operand float to unsigned int");
+ if (insn.operand_type[0]->immediate.u_number > 0xFFFF)
+ as_warn ("only lower 16-bits of first operand are used");
+ insn.opcode |= (insn.operand_type[0]->immediate.u_number & 0x0000FFFFL);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ break;
+ case Imm_SInt:
+ debug ("Int first operand\n");
+ if (insn.operand_type[0]->immediate.decimal_found)
+ as_warn ("rounding down first operand float to signed int");
+ if (insn.operand_type[0]->immediate.s_number < -32768 ||
+ insn.operand_type[0]->immediate.s_number > 32767)
+ {
+ as_bad ("first operand is too large for 16-bit signed int");
+ return;
+ }
+ insn.opcode |= (insn.operand_type[0]->immediate.s_number & 0x0000FFFFL);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ break;
+ }
+ }
+ else
+ { /* Unresolved immediate label */
+ if (insn.operands > 1)
+ insn.opcode |= (insn.operand_type[1]->reg.opcode << 16);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ fix_new_exp (frag_now, p + 2 - (frag_now->fr_literal), 2, &insn.operand_type[0]->immediate.imm_expr, 0, 0);
+ }
+ }
+ }
+ else if (insn.tm->opcode_modifier == PCRel)
+ {
+ /* Conditional Branch and Call instructions */
+ if ((insn.tm->operand_types[0] & (AllReg | Disp)) == (AllReg | Disp))
+ {
+ if (insn.operand_type[0]->op_type & (AllReg))
+ {
+ insn.opcode |= (insn.operand_type[0]->reg.opcode);
+ insn.opcode |= PC_Register;
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else
+ {
+ insn.opcode |= PC_Relative;
+ if (insn.operand_type[0]->immediate.resolved == 1)
+ {
+ insn.opcode |= (insn.operand_type[0]->immediate.s_number & 0x0000FFFF);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else
+ {
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ fix_new_exp (frag_now, p + 2 - (frag_now->fr_literal), 2, &insn.operand_type[0]->immediate.imm_expr, 1, 0);
+ }
+ }
+ }
+ else if ((insn.tm->operand_types[0] & ARn) == ARn)
+ {
+ /* Decrement and Branch instructions */
+ insn.opcode |= ((insn.operand_type[0]->reg.opcode - 0x08) << 22);
+ if (insn.operand_type[1]->op_type & (AllReg))
+ {
+ insn.opcode |= (insn.operand_type[1]->reg.opcode);
+ insn.opcode |= PC_Register;
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else if (insn.operand_type[1]->immediate.resolved == 1)
+ {
+ if (insn.operand_type[0]->immediate.decimal_found)
+ {
+ as_bad ("first operand is floating point");
+ return;
+ }
+ if (insn.operand_type[0]->immediate.s_number < -32768 ||
+ insn.operand_type[0]->immediate.s_number > 32767)
+ {
+ as_bad ("first operand is too large for 16-bit signed int");
+ return;
+ }
+ insn.opcode |= (insn.operand_type[1]->immediate.s_number);
+ insn.opcode |= PC_Relative;
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else
+ {
+ insn.opcode |= PC_Relative;
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ fix_new_exp (frag_now, p + 2 - frag_now->fr_literal, 2, &insn.operand_type[1]->immediate.imm_expr, 1, 0);
+ }
+ }
+ }
+ else if (insn.tm->operand_types[0] == IVector)
+ {
+ /* Trap instructions */
+ if (insn.operand_type[0]->op_type & IVector)
+ insn.opcode |= (insn.operand_type[0]->immediate.u_number);
+ else
+ { /* Shouldn't get here */
+ as_bad ("interrupt vector for trap instruction out of range");
+ return;
+ }
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else if (insn.tm->opcode_modifier == StackOp || insn.tm->opcode_modifier == Rotate)
+ {
+ /* Push, Pop and Rotate instructions */
+ insn.opcode |= (insn.operand_type[0]->reg.opcode << 16);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else if ((insn.tm->operand_types[0] & (Abs24 | Direct)) == (Abs24 | Direct))
+ {
+ /* LDP Instruction needs to be tested for before the next section */
+ if (insn.operand_type[0]->op_type & Direct)
+ {
+ if (insn.operand_type[0]->direct.resolved == 1)
+ {
+ /* Direct addressing uses lower 8 bits of direct address */
+ insn.opcode |= (insn.operand_type[0]->direct.address & 0x00FF0000) >> 16;
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else
+ {
+ fixS *fix;
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ fix = fix_new_exp (frag_now, p + 3 - (frag_now->fr_literal), 1, &insn.operand_type[0]->direct.direct_expr, 0, 0);
+ /* Ensure that the assembler doesn't complain about fitting a 24-bit
+ address into 8 bits. */
+ fix->fx_no_overflow = 1;
+ }
+ }
+ else
+ {
+ if (insn.operand_type[0]->immediate.resolved == 1)
+ {
+ /* Immediate addressing uses upper 8 bits of address */
+ if (insn.operand_type[0]->immediate.u_number > 0x00FFFFFF)
+ {
+ as_bad ("LDP instruction needs a 24-bit operand");
+ return;
+ }
+ insn.opcode |= ((insn.operand_type[0]->immediate.u_number & 0x00FF0000) >> 16);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else
+ {
+ fixS *fix;
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ fix = fix_new_exp (frag_now, p + 3 - (frag_now->fr_literal), 1, &insn.operand_type[0]->immediate.imm_expr, 0, 0);
+ fix->fx_no_overflow = 1;
+ }
+ }
+ }
+ else if (insn.tm->operand_types[0] & (Imm24))
+ {
+ /* Unconditional Branch and Call instructions */
+ if (insn.operand_type[0]->immediate.resolved == 1)
+ {
+ if (insn.operand_type[0]->immediate.u_number > 0x00FFFFFF)
+ as_warn ("first operand is too large for a 24-bit displacement");
+ insn.opcode |= (insn.operand_type[0]->immediate.u_number & 0x00FFFFFF);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else
+ {
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ fix_new_exp (frag_now, p + 1 - (frag_now->fr_literal), 3, &insn.operand_type[0]->immediate.imm_expr, 0, 0);
+ }
+ }
+ else if (insn.tm->operand_types[0] & NotReq)
+ {
+ /* Check for NOP instruction without arguments. */
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else if (insn.tm->operands == 0)
+ {
+ /* Check for instructions without operands. */
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ }
+ debug ("Addressing mode: %08X\n", insn.addressing_mode);
+ {
+ int i;
+ for (i = 0; i < insn.operands; i++)
+ {
+ if (insn.operand_type[i]->immediate.label)
+ free (insn.operand_type[i]->immediate.label);
+ free (insn.operand_type[i]);
+ }
+ }
+ debug ("Final opcode: %08X\n", insn.opcode);
+ debug ("\n");
+}
+
+struct tic30_par_insn
+{
+ partemplate *tm; /* Template of current parallel instruction */
+ int operands[2]; /* Number of given operands for each insn */
+ /* Type of operand given in instruction */
+ operand *operand_type[2][MAX_OPERANDS];
+ int swap_operands; /* Whether to swap operands around. */
+ unsigned p_field; /* Value of p field in multiply add/sub instructions */
+ unsigned opcode; /* Final opcode */
+};
+
+struct tic30_par_insn p_insn;
+
+int
+tic30_parallel_insn (char *token)
+{
+ static partemplate *p_opcode;
+ char *current_posn = token;
+ char *token_start;
+ char save_char;
+
+ debug ("In tic30_parallel_insn with %s\n", token);
+ memset (&p_insn, '\0', sizeof (p_insn));
+ while (is_opcode_char (*current_posn))
+ current_posn++;
+ { /* Find instruction */
+ save_char = *current_posn;
+ *current_posn = '\0';
+ p_opcode = (partemplate *) hash_find (parop_hash, token);
+ if (p_opcode)
+ {
+ debug ("Found instruction %s\n", p_opcode->name);
+ p_insn.tm = p_opcode;
+ }
+ else
+ {
+ char first_opcode[6] =
+ {0};
+ char second_opcode[6] =
+ {0};
+ int i;
+ int current_opcode = -1;
+ int char_ptr = 0;
+
+ for (i = 0; i < strlen (token); i++)
+ {
+ char ch = *(token + i);
+ if (ch == '_' && current_opcode == -1)
+ {
+ current_opcode = 0;
+ continue;
+ }
+ if (ch == '_' && current_opcode == 0)
+ {
+ current_opcode = 1;
+ char_ptr = 0;
+ continue;
+ }
+ switch (current_opcode)
+ {
+ case 0:
+ first_opcode[char_ptr++] = ch;
+ break;
+ case 1:
+ second_opcode[char_ptr++] = ch;
+ break;
+ }
+ }
+ debug ("first_opcode = %s\n", first_opcode);
+ debug ("second_opcode = %s\n", second_opcode);
+ sprintf (token, "q_%s_%s", second_opcode, first_opcode);
+ p_opcode = (partemplate *) hash_find (parop_hash, token);
+ if (p_opcode)
+ {
+ debug ("Found instruction %s\n", p_opcode->name);
+ p_insn.tm = p_opcode;
+ p_insn.swap_operands = 1;
+ }
+ else
+ return 0;
+ }
+ *current_posn = save_char;
+ }
+ { /* Find operands */
+ int paren_not_balanced;
+ int expecting_operand = 0;
+ int found_separator = 0;
+ do
+ {
+ /* skip optional white space before operand */
+ while (!is_operand_char (*current_posn) && *current_posn != END_OF_INSN)
+ {
+ if (!is_space_char (*current_posn) && *current_posn != PARALLEL_SEPARATOR)
+ {
+ as_bad ("Invalid character %s before %s operand",
+ output_invalid (*current_posn),
+ ordinal_names[insn.operands]);
+ return 1;
+ }
+ if (*current_posn == PARALLEL_SEPARATOR)
+ found_separator = 1;
+ current_posn++;
+ }
+ token_start = current_posn; /* after white space */
+ paren_not_balanced = 0;
+ while (paren_not_balanced || *current_posn != ',')
+ {
+ if (*current_posn == END_OF_INSN)
+ {
+ if (paren_not_balanced)
+ {
+ as_bad ("Unbalanced parenthesis in %s operand.",
+ ordinal_names[insn.operands]);
+ return 1;
+ }
+ else
+ break; /* we are done */
+ }
+ else if (*current_posn == PARALLEL_SEPARATOR)
+ {
+ while (is_space_char (*(current_posn - 1)))
+ current_posn--;
+ break;
+ }
+ else if (!is_operand_char (*current_posn) && !is_space_char (*current_posn))
+ {
+ as_bad ("Invalid character %s in %s operand",
+ output_invalid (*current_posn),
+ ordinal_names[insn.operands]);
+ return 1;
+ }
+ if (*current_posn == '(')
+ ++paren_not_balanced;
+ if (*current_posn == ')')
+ --paren_not_balanced;
+ current_posn++;
+ }
+ if (current_posn != token_start)
+ { /* yes, we've read in another operand */
+ p_insn.operands[found_separator]++;
+ if (p_insn.operands[found_separator] > MAX_OPERANDS)
+ {
+ as_bad ("Spurious operands; (%d operands/instruction max)",
+ MAX_OPERANDS);
+ return 1;
+ }
+ /* now parse operand adding info to 'insn' as we go along */
+ save_char = *current_posn;
+ *current_posn = '\0';
+ p_insn.operand_type[found_separator][p_insn.operands[found_separator] - 1] =
+ tic30_operand (token_start);
+ *current_posn = save_char;
+ if (!p_insn.operand_type[found_separator][p_insn.operands[found_separator] - 1])
+ return 1;
+ }
+ else
+ {
+ if (expecting_operand)
+ {
+ as_bad ("Expecting operand after ','; got nothing");
+ return 1;
+ }
+ if (*current_posn == ',')
+ {
+ as_bad ("Expecting operand before ','; got nothing");
+ return 1;
+ }
+ }
+ /* now *current_posn must be either ',' or END_OF_INSN */
+ if (*current_posn == ',')
+ {
+ if (*++current_posn == END_OF_INSN)
+ { /* just skip it, if it's \n complain */
+ as_bad ("Expecting operand after ','; got nothing");
+ return 1;
+ }
+ expecting_operand = 1;
+ }
+ }
+ while (*current_posn != END_OF_INSN); /* until we get end of insn */
+ }
+ if (p_insn.swap_operands)
+ {
+ int temp_num, i;
+ operand *temp_op;
+
+ temp_num = p_insn.operands[0];
+ p_insn.operands[0] = p_insn.operands[1];
+ p_insn.operands[1] = temp_num;
+ for (i = 0; i < MAX_OPERANDS; i++)
+ {
+ temp_op = p_insn.operand_type[0][i];
+ p_insn.operand_type[0][i] = p_insn.operand_type[1][i];
+ p_insn.operand_type[1][i] = temp_op;
+ }
+ }
+ if (p_insn.operands[0] != p_insn.tm->operands_1)
+ {
+ as_bad ("incorrect number of operands given in the first instruction");
+ return 1;
+ }
+ if (p_insn.operands[1] != p_insn.tm->operands_2)
+ {
+ as_bad ("incorrect number of operands given in the second instruction");
+ return 1;
+ }
+ debug ("Number of operands in first insn: %d\n", p_insn.operands[0]);
+ debug ("Number of operands in second insn: %d\n", p_insn.operands[1]);
+ { /* Now check if operands are correct */
+ int count;
+ int num_rn = 0;
+ int num_ind = 0;
+ for (count = 0; count < 2; count++)
+ {
+ int i;
+ for (i = 0; i < p_insn.operands[count]; i++)
+ {
+ if ((p_insn.operand_type[count][i]->op_type &
+ p_insn.tm->operand_types[count][i]) == 0)
+ {
+ as_bad ("%s instruction, operand %d doesn't match", ordinal_names[count], i + 1);
+ return 1;
+ }
+ /* Get number of R register and indirect reference contained within the first
+ two operands of each instruction. This is required for the multiply
+ parallel instructions which require two R registers and two indirect
+ references, but not in any particular place. */
+ if ((p_insn.operand_type[count][i]->op_type & Rn) && i < 2)
+ num_rn++;
+ else if ((p_insn.operand_type[count][i]->op_type & Indirect) && i < 2)
+ num_ind++;
+ }
+ }
+ if ((p_insn.tm->operand_types[0][0] & (Indirect | Rn)) == (Indirect | Rn))
+ {
+ /* Check for the multiply instructions */
+ if (num_rn != 2)
+ {
+ as_bad ("incorrect format for multiply parallel instruction");
+ return 1;
+ }
+ if (num_ind != 2)
+ { /* Shouldn't get here */
+ as_bad ("incorrect format for multiply parallel instruction");
+ return 1;
+ }
+ if ((p_insn.operand_type[0][2]->reg.opcode != 0x00) &&
+ (p_insn.operand_type[0][2]->reg.opcode != 0x01))
+ {
+ as_bad ("destination for multiply can only be R0 or R1");
+ return 1;
+ }
+ if ((p_insn.operand_type[1][2]->reg.opcode != 0x02) &&
+ (p_insn.operand_type[1][2]->reg.opcode != 0x03))
+ {
+ as_bad ("destination for add/subtract can only be R2 or R3");
+ return 1;
+ }
+ /* Now determine the P field for the instruction */
+ if (p_insn.operand_type[0][0]->op_type & Indirect)
+ {
+ if (p_insn.operand_type[0][1]->op_type & Indirect)
+ p_insn.p_field = 0x00000000; /* Ind * Ind, Rn +/- Rn */
+ else if (p_insn.operand_type[1][0]->op_type & Indirect)
+ p_insn.p_field = 0x01000000; /* Ind * Rn, Ind +/- Rn */
+ else
+ p_insn.p_field = 0x03000000; /* Ind * Rn, Rn +/- Ind */
+ }
+ else
+ {
+ if (p_insn.operand_type[0][1]->op_type & Rn)
+ p_insn.p_field = 0x02000000; /* Rn * Rn, Ind +/- Ind */
+ else if (p_insn.operand_type[1][0]->op_type & Indirect)
+ {
+ operand *temp;
+ p_insn.p_field = 0x01000000; /* Rn * Ind, Ind +/- Rn */
+ /* Need to swap the two multiply operands around so that everything is in
+ its place for the opcode makeup ie so Ind * Rn, Ind +/- Rn */
+ temp = p_insn.operand_type[0][0];
+ p_insn.operand_type[0][0] = p_insn.operand_type[0][1];
+ p_insn.operand_type[0][1] = temp;
+ }
+ else
+ {
+ operand *temp;
+ p_insn.p_field = 0x03000000; /* Rn * Ind, Rn +/- Ind */
+ temp = p_insn.operand_type[0][0];
+ p_insn.operand_type[0][0] = p_insn.operand_type[0][1];
+ p_insn.operand_type[0][1] = temp;
+ }
+ }
+ }
+ }
+ debug ("P field: %08X\n", p_insn.p_field);
+ /* Finalise opcode. This is easier for parallel instructions as they have to be
+ fully resolved, there are no memory addresses allowed, except through indirect
+ addressing, so there are no labels to resolve. */
+ {
+ p_insn.opcode = p_insn.tm->base_opcode;
+ switch (p_insn.tm->oporder)
+ {
+ case OO_4op1:
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 3);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum << 8);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 11);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 16);
+ p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 22);
+ break;
+ case OO_4op2:
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 3);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.ARnum << 8);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.mod << 11);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->reg.opcode << 19);
+ p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 22);
+ if (p_insn.operand_type[1][1]->reg.opcode == p_insn.operand_type[0][1]->reg.opcode)
+ as_warn ("loading the same register in parallel operation");
+ break;
+ case OO_4op3:
+ p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.ARnum);
+ p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.mod << 3);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum << 8);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 11);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 16);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->reg.opcode << 22);
+ break;
+ case OO_5op1:
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 3);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum << 8);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 11);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 16);
+ p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 19);
+ p_insn.opcode |= (p_insn.operand_type[0][2]->reg.opcode << 22);
+ break;
+ case OO_5op2:
+ p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.ARnum);
+ p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.mod << 3);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum << 8);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 11);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 16);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->reg.opcode << 19);
+ p_insn.opcode |= (p_insn.operand_type[0][2]->reg.opcode << 22);
+ break;
+ case OO_PField:
+ p_insn.opcode |= p_insn.p_field;
+ if (p_insn.operand_type[0][2]->reg.opcode == 0x01)
+ p_insn.opcode |= 0x00800000;
+ if (p_insn.operand_type[1][2]->reg.opcode == 0x03)
+ p_insn.opcode |= 0x00400000;
+ switch (p_insn.p_field)
+ {
+ case 0x00000000:
+ p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.ARnum);
+ p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.mod << 3);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum << 8);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 11);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->reg.opcode << 16);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 19);
+ break;
+ case 0x01000000:
+ p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.ARnum);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.mod << 3);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum << 8);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 11);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->reg.opcode << 16);
+ p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 19);
+ break;
+ case 0x02000000:
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 3);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.ARnum << 8);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.mod << 11);
+ p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 16);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->reg.opcode << 19);
+ break;
+ case 0x03000000:
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 3);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum << 8);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 11);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 16);
+ p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 19);
+ break;
+ }
+ break;
+ }
+ } /* Opcode is finalised at this point for all parallel instructions. */
+ { /* Output opcode */
+ char *p;
+ p = frag_more (INSN_SIZE);
+ md_number_to_chars (p, (valueT) p_insn.opcode, INSN_SIZE);
+ }
+ {
+ int i, j;
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < p_insn.operands[i]; j++)
+ free (p_insn.operand_type[i][j]);
+ }
+ debug ("Final opcode: %08X\n", p_insn.opcode);
+ debug ("\n");
+ return 1;
+}
+
+operand *
+tic30_operand (token)
+ char *token;
+{
+ int count;
+ char ind_buffer[strlen (token)];
+ operand *current_op;
+
+ debug ("In tic30_operand with %s\n", token);
+ current_op = (operand *) malloc (sizeof (operand));
+ memset (current_op, '\0', sizeof (operand));
+ if (*token == DIRECT_REFERENCE)
+ {
+ char *token_posn = token + 1;
+ int direct_label = 0;
+ debug ("Found direct reference\n");
+ while (*token_posn)
+ {
+ if (!is_digit_char (*token_posn))
+ direct_label = 1;
+ token_posn++;
+ }
+ if (direct_label)
+ {
+ char *save_input_line_pointer;
+ segT retval;
+ debug ("Direct reference is a label\n");
+ current_op->direct.label = token + 1;
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = token + 1;
+ debug ("Current input_line_pointer: %s\n", input_line_pointer);
+ retval = expression (&current_op->direct.direct_expr);
+ debug ("Expression type: %d\n", current_op->direct.direct_expr.X_op);
+ debug ("Expression addnum: %d\n", current_op->direct.direct_expr.X_add_number);
+ debug ("Segment: %d\n", retval);
+ input_line_pointer = save_input_line_pointer;
+ if (current_op->direct.direct_expr.X_op == O_constant)
+ {
+ current_op->direct.address = current_op->direct.direct_expr.X_add_number;
+ current_op->direct.resolved = 1;
+ }
+ }
+ else
+ {
+ debug ("Direct reference is a number\n");
+ current_op->direct.address = atoi (token + 1);
+ current_op->direct.resolved = 1;
+ }
+ current_op->op_type = Direct;
+ }
+ else if (*token == INDIRECT_REFERENCE)
+ { /* Indirect reference operand */
+ int found_ar = 0;
+ int found_disp = 0;
+ int ar_number = -1;
+ int disp_number = 0;
+ int buffer_posn = 1;
+ ind_addr_type *ind_addr_op;
+ debug ("Found indirect reference\n");
+ ind_buffer[0] = *token;
+ for (count = 1; count < strlen (token); count++)
+ { /* Strip operand */
+ ind_buffer[buffer_posn] = tolower (*(token + count));
+ if ((*(token + count - 1) == 'a' || *(token + count - 1) == 'A') &&
+ (*(token + count) == 'r' || *(token + count) == 'R'))
+ {
+ /* AR reference is found, so get its number and remove it from the buffer
+ so it can pass through hash_find() */
+ if (found_ar)
+ {
+ as_bad ("More than one AR register found in indirect reference");
+ return NULL;
+ }
+ if (*(token + count + 1) < '0' || *(token + count + 1) > '7')
+ {
+ as_bad ("Illegal AR register in indirect reference");
+ return NULL;
+ }
+ ar_number = *(token + count + 1) - '0';
+ found_ar = 1;
+ count++;
+ }
+ if (*(token + count) == '(')
+ {
+ /* Parenthesis found, so check if a displacement value is inside. If so, get
+ the value and remove it from the buffer. */
+ if (is_digit_char (*(token + count + 1)))
+ {
+ char disp[10];
+ int disp_posn = 0;
+
+ if (found_disp)
+ {
+ as_bad ("More than one displacement found in indirect reference");
+ return NULL;
+ }
+ count++;
+ while (*(token + count) != ')')
+ {
+ if (!is_digit_char (*(token + count)))
+ {
+ as_bad ("Invalid displacement in indirect reference");
+ return NULL;
+ }
+ disp[disp_posn++] = *(token + (count++));
+ }
+ disp[disp_posn] = '\0';
+ disp_number = atoi (disp);
+ count--;
+ found_disp = 1;
+ }
+ }
+ buffer_posn++;
+ }
+ ind_buffer[buffer_posn] = '\0';
+ if (!found_ar)
+ {
+ as_bad ("AR register not found in indirect reference");
+ return NULL;
+ }
+ ind_addr_op = (ind_addr_type *) hash_find (ind_hash, ind_buffer);
+ if (ind_addr_op)
+ {
+ debug ("Found indirect reference: %s\n", ind_addr_op->syntax);
+ if (ind_addr_op->displacement == IMPLIED_DISP)
+ {
+ found_disp = 1;
+ disp_number = 1;
+ }
+ else if ((ind_addr_op->displacement == DISP_REQUIRED) && !found_disp)
+ {
+ /* Maybe an implied displacement of 1 again */
+ as_bad ("required displacement wasn't given in indirect reference");
+ return 0;
+ }
+ }
+ else
+ {
+ as_bad ("illegal indirect reference");
+ return NULL;
+ }
+ if (found_disp && (disp_number < 0 || disp_number > 255))
+ {
+ as_bad ("displacement must be an unsigned 8-bit number");
+ return NULL;
+ }
+ current_op->indirect.mod = ind_addr_op->modfield;
+ current_op->indirect.disp = disp_number;
+ current_op->indirect.ARnum = ar_number;
+ current_op->op_type = Indirect;
+ }
+ else
+ {
+ reg *regop = (reg *) hash_find (reg_hash, token);
+ if (regop)
+ {
+ debug ("Found register operand: %s\n", regop->name);
+ if (regop->regtype == REG_ARn)
+ current_op->op_type = ARn;
+ else if (regop->regtype == REG_Rn)
+ current_op->op_type = Rn;
+ else if (regop->regtype == REG_DP)
+ current_op->op_type = DPReg;
+ else
+ current_op->op_type = OtherReg;
+ current_op->reg.opcode = regop->opcode;
+ }
+ else
+ {
+ if (!is_digit_char (*token) || *(token + 1) == 'x' || strchr (token, 'h'))
+ {
+ char *save_input_line_pointer;
+ segT retval;
+ debug ("Probably a label: %s\n", token);
+ current_op->immediate.label = (char *) malloc (strlen (token) + 1);
+ strcpy (current_op->immediate.label, token);
+ current_op->immediate.label[strlen (token)] = '\0';
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = token;
+ debug ("Current input_line_pointer: %s\n", input_line_pointer);
+ retval = expression (&current_op->immediate.imm_expr);
+ debug ("Expression type: %d\n", current_op->immediate.imm_expr.X_op);
+ debug ("Expression addnum: %d\n", current_op->immediate.imm_expr.X_add_number);
+ debug ("Segment: %d\n", retval);
+ input_line_pointer = save_input_line_pointer;
+ if (current_op->immediate.imm_expr.X_op == O_constant)
+ {
+ current_op->immediate.s_number = current_op->immediate.imm_expr.X_add_number;
+ current_op->immediate.u_number = (unsigned int) current_op->immediate.imm_expr.X_add_number;
+ current_op->immediate.resolved = 1;
+ }
+ }
+ else
+ {
+ unsigned count;
+ debug ("Found a number or displacement\n");
+ for (count = 0; count < strlen (token); count++)
+ if (*(token + count) == '.')
+ current_op->immediate.decimal_found = 1;
+ current_op->immediate.label = (char *) malloc (strlen (token) + 1);
+ strcpy (current_op->immediate.label, token);
+ current_op->immediate.label[strlen (token)] = '\0';
+ current_op->immediate.f_number = (float) atof (token);
+ current_op->immediate.s_number = (int) atoi (token);
+ current_op->immediate.u_number = (unsigned int) atoi (token);
+ current_op->immediate.resolved = 1;
+ }
+ current_op->op_type = Disp | Abs24 | Imm16 | Imm24;
+ if (current_op->immediate.u_number >= 0 && current_op->immediate.u_number <= 31)
+ current_op->op_type |= IVector;
+ }
+ }
+ return current_op;
+}
+
+/* next_line points to the next line after the current instruction (current_line).
+ Search for the parallel bars, and if found, merge two lines into internal syntax
+ for a parallel instruction:
+ q_[INSN1]_[INSN2] [OPERANDS1] | [OPERANDS2]
+ By this stage, all comments are scrubbed, and only the bare lines are given.
+ */
+
+#define NONE 0
+#define START_OPCODE 1
+#define END_OPCODE 2
+#define START_OPERANDS 3
+#define END_OPERANDS 4
+
+char *
+tic30_find_parallel_insn (current_line, next_line)
+ char *current_line;
+ char *next_line;
+{
+ int found_parallel = 0;
+ char first_opcode[256];
+ char second_opcode[256];
+ char first_operands[256];
+ char second_operands[256];
+ char *parallel_insn;
+
+ debug ("In tic30_find_parallel_insn()\n");
+ while (!is_end_of_line[(int) *next_line])
+ {
+ if (*next_line == PARALLEL_SEPARATOR && *(next_line + 1) == PARALLEL_SEPARATOR)
+ {
+ found_parallel = 1;
+ next_line++;
+ break;
+ }
+ next_line++;
+ }
+ if (!found_parallel)
+ return NULL;
+ debug ("Found a parallel instruction\n");
+ {
+ int i;
+ char *opcode, *operands, *line;
+
+ for (i = 0; i < 2; i++)
+ {
+ if (i == 0)
+ {
+ opcode = &first_opcode[0];
+ operands = &first_operands[0];
+ line = current_line;
+ }
+ else
+ {
+ opcode = &second_opcode[0];
+ operands = &second_operands[0];
+ line = next_line;
+ }
+ {
+ int search_status = NONE;
+ int char_ptr = 0;
+ char c;
+
+ while (!is_end_of_line[(int) (c = *line)] && *line)
+ {
+ if (is_opcode_char (c) && search_status == NONE)
+ {
+ opcode[char_ptr++] = tolower (c);
+ search_status = START_OPCODE;
+ }
+ else if (is_opcode_char (c) && search_status == START_OPCODE)
+ {
+ opcode[char_ptr++] = tolower (c);
+ }
+ else if (!is_opcode_char (c) && search_status == START_OPCODE)
+ {
+ opcode[char_ptr] = '\0';
+ char_ptr = 0;
+ search_status = END_OPCODE;
+ }
+ else if (is_operand_char (c) && search_status == START_OPERANDS)
+ {
+ operands[char_ptr++] = c;
+ }
+ if (is_operand_char (c) && search_status == END_OPCODE)
+ {
+ operands[char_ptr++] = c;
+ search_status = START_OPERANDS;
+ }
+ line++;
+ }
+ if (search_status != START_OPERANDS)
+ return NULL;
+ operands[char_ptr] = '\0';
+ }
+ }
+ }
+ parallel_insn = (char *) malloc (strlen (first_opcode) + strlen (first_operands) +
+ strlen (second_opcode) + strlen (second_operands) + 8);
+ sprintf (parallel_insn, "q_%s_%s %s | %s", first_opcode, second_opcode, first_operands, second_operands);
+ debug ("parallel insn = %s\n", parallel_insn);
+ return parallel_insn;
+}
+
+#undef NONE
+#undef START_OPCODE
+#undef END_OPCODE
+#undef START_OPERANDS
+#undef END_OPERANDS
+
+/* In order to get gas to ignore any | chars at the start of a line,
+ this function returns true if a | is found in a line. */
+
+int
+tic30_unrecognized_line (c)
+ int c;
+{
+ debug ("In tc_unrecognized_line\n");
+ return (c == PARALLEL_SEPARATOR);
+}
+
+int
+md_estimate_size_before_relax (fragP, segment)
+ fragS *fragP;
+ segT segment;
+{
+ debug ("In md_estimate_size_before_relax()\n");
+ return 0;
+}
+
+void
+md_convert_frag (abfd, sec, fragP)
+ bfd *abfd;
+ segT sec;
+ register fragS *fragP;
+{
+ debug ("In md_convert_frag()\n");
+}
+
+int
+md_apply_fix (fixP, valP)
+ fixS *fixP;
+ valueT *valP;
+{
+ valueT value = *valP;
+
+ debug ("In md_apply_fix() with value = %ld\n", (long) value);
+ debug ("Values in fixP\n");
+ debug ("fx_size = %d\n", fixP->fx_size);
+ debug ("fx_pcrel = %d\n", fixP->fx_pcrel);
+ debug ("fx_where = %d\n", fixP->fx_where);
+ debug ("fx_offset = %d\n", (int) fixP->fx_offset);
+ {
+ char *buf = fixP->fx_frag->fr_literal + fixP->fx_where;
+ value /= INSN_SIZE;
+ if (fixP->fx_size == 1)
+ { /* Special fix for LDP instruction. */
+ value = (value & 0x00FF0000) >> 16;
+ }
+ debug ("new value = %ld\n", (long) value);
+ md_number_to_chars (buf, value, fixP->fx_size);
+ }
+ return 1;
+}
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ int i;
+
+ debug ("In md_parse_option()\n");
+ for (i = 0; i < c; i++)
+ {
+ printf ("%c\n", arg[c]);
+ }
+ return 0;
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ debug ("In md_show_usage()\n");
+}
+
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ debug ("In md_undefined_symbol()\n");
+ return (symbolS *) 0;
+}
+
+valueT
+md_section_align (segment, size)
+ segT segment;
+ valueT size;
+{
+ debug ("In md_section_align() segment = %d and size = %d\n", segment, size);
+ size = (size + 3) / 4;
+ size *= 4;
+ debug ("New size value = %d\n", size);
+ return size;
+}
+
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ int offset;
+
+ debug ("In md_pcrel_from()\n");
+ debug ("fx_where = %d\n", fixP->fx_where);
+ debug ("fx_size = %d\n", fixP->fx_size);
+ /* Find the opcode that represents the current instruction in the fr_literal
+ storage area, and check bit 21. Bit 21 contains whether the current instruction
+ is a delayed one or not, and then set the offset value appropriately. */
+ if (fixP->fx_frag->fr_literal[fixP->fx_where - fixP->fx_size + 1] & 0x20)
+ offset = 3;
+ else
+ offset = 1;
+ debug ("offset = %d\n", offset);
+ /* PC Relative instructions have a format:
+ displacement = Label - (PC + offset)
+ This function returns PC + offset where:
+ fx_where - fx_size = PC
+ INSN_SIZE * offset = offset number of instructions
+ */
+ return fixP->fx_where - fixP->fx_size + (INSN_SIZE * offset);
+}
+
+char *
+md_atof (what_statement_type, literalP, sizeP)
+ int what_statement_type;
+ char *literalP;
+ int *sizeP;
+{
+ int prec;
+ char *token;
+ char keepval;
+ unsigned long value;
+ /* char *atof_ieee (); */
+ float float_value;
+ debug ("In md_atof()\n");
+ debug ("precision = %c\n", what_statement_type);
+ debug ("literal = %s\n", literalP);
+ debug ("line = ");
+ token = input_line_pointer;
+ while (!is_end_of_line[(unsigned) *input_line_pointer] && (*input_line_pointer) && (*input_line_pointer != ','))
+ {
+ debug ("%c", *input_line_pointer);
+ input_line_pointer++;
+ }
+ keepval = *input_line_pointer;
+ *input_line_pointer = '\0';
+ debug ("\n");
+ float_value = (float) atof (token);
+ *input_line_pointer = keepval;
+ debug ("float_value = %f\n", float_value);
+ switch (what_statement_type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ default:
+ *sizeP = 0;
+ return "Bad call to MD_ATOF()";
+ }
+ if (float_value == 0.0)
+ {
+ value = (prec == 2) ? 0x00008000L : 0x80000000L;
+ }
+ else
+ {
+ unsigned long exp, sign, mant, tmsfloat;
+ tmsfloat = *((long *) &float_value);
+ sign = tmsfloat & 0x80000000;
+ mant = tmsfloat & 0x007FFFFF;
+ exp = tmsfloat & 0x7F800000;
+ exp <<= 1;
+ if (exp == 0xFF000000)
+ {
+ if (mant == 0)
+ value = 0x7F7FFFFF;
+ else if (sign == 0)
+ value = 0x7F7FFFFF;
+ else
+ value = 0x7F800000;
+ }
+ else
+ {
+ exp -= 0x7F000000;
+ if (sign)
+ {
+ mant = mant & 0x007FFFFF;
+ mant = -mant;
+ mant = mant & 0x00FFFFFF;
+ if (mant == 0)
+ {
+ mant |= 0x00800000;
+ exp = (long) exp - 0x01000000;
+ }
+ }
+ tmsfloat = exp | mant;
+ value = tmsfloat;
+ }
+ if (prec == 2)
+ {
+ long exp, mant;
+
+ if (tmsfloat == 0x80000000)
+ {
+ value = 0x8000;
+ }
+ else
+ {
+ value = 0;
+ exp = (tmsfloat & 0xFF000000);
+ exp >>= 24;
+ mant = tmsfloat & 0x007FFFFF;
+ if (tmsfloat & 0x00800000)
+ {
+ mant |= 0xFF000000;
+ mant += 0x00000800;
+ mant >>= 12;
+ mant |= 0x00000800;
+ mant &= 0x0FFF;
+ if (exp > 7)
+ value = 0x7800;
+ }
+ else
+ {
+ mant |= 0x00800000;
+ mant += 0x00000800;
+ exp += (mant >> 24);
+ mant >>= 12;
+ mant &= 0x07FF;
+ if (exp > 7)
+ value = 0x77FF;
+ }
+ if (exp < -8)
+ value = 0x8000;
+ if (value == 0)
+ {
+ mant = (exp << 12) | mant;
+ value = mant & 0xFFFF;
+ }
+ }
+ }
+ }
+ md_number_to_chars (literalP, value, prec);
+ *sizeP = prec;
+ return 0;
+}
+
+void
+md_number_to_chars (buf, val, n)
+ char *buf;
+ valueT val;
+ int n;
+{
+ debug ("In md_number_to_chars()\n");
+ number_to_chars_bigendian (buf, val, n);
+ /* number_to_chars_littleendian(buf,val,n); */
+}
+
+#define F(SZ,PCREL) (((SZ) << 1) + (PCREL))
+#define MAP(SZ,PCREL,TYPE) case F(SZ,PCREL): code = (TYPE); break
+
+arelent *
+tc_gen_reloc (section, fixP)
+ asection *section;
+ fixS *fixP;
+{
+ arelent *rel;
+ bfd_reloc_code_real_type code = 0;
+
+ debug ("In tc_gen_reloc()\n");
+ debug ("fixP.size = %d\n", fixP->fx_size);
+ debug ("fixP.pcrel = %d\n", fixP->fx_pcrel);
+ debug ("addsy.name = %s\n", S_GET_NAME (fixP->fx_addsy));
+ switch (F (fixP->fx_size, fixP->fx_pcrel))
+ {
+ MAP (1, 0, BFD_RELOC_TIC30_LDP);
+ MAP (2, 0, BFD_RELOC_16);
+ MAP (3, 0, BFD_RELOC_24);
+ MAP (2, 1, BFD_RELOC_16_PCREL);
+ MAP (4, 0, BFD_RELOC_32);
+ default:
+ as_bad ("Can not do %d byte %srelocation", fixP->fx_size,
+ fixP->fx_pcrel ? "pc-relative " : "");
+ }
+#undef MAP
+#undef F
+
+ rel = (arelent *) xmalloc (sizeof (arelent));
+ assert (rel != 0);
+ rel->sym_ptr_ptr = &fixP->fx_addsy->bsym;
+ rel->address = fixP->fx_frag->fr_address + fixP->fx_where;
+ if (fixP->fx_pcrel)
+ rel->addend = fixP->fx_addnumber;
+ else
+ rel->addend = 0;
+ rel->howto = bfd_reloc_type_lookup (stdoutput, code);
+ if (!rel->howto)
+ {
+ const char *name;
+ name = S_GET_NAME (fixP->fx_addsy);
+ if (name == NULL)
+ name = "<unknown>";
+ as_fatal ("Cannot generate relocation type for symbol %s, code %s", name, bfd_get_reloc_code_name (code));
+ }
+ return rel;
+}
+
+void
+tc_aout_pre_write_hook ()
+{
+ debug ("In tc_aout_pre_write_hook()\n");
+}
+
+void
+md_operand (expressionP)
+ expressionS *expressionP;
+{
+ debug ("In md_operand()\n");
+}
+
+char output_invalid_buf[8];
+
+char *
+output_invalid (c)
+ char c;
+{
+ if (isprint (c))
+ sprintf (output_invalid_buf, "'%c'", c);
+ else
+ sprintf (output_invalid_buf, "(0x%x)", (unsigned) c);
+ return output_invalid_buf;
+}
diff --git a/gas/config/tc-tic30.h b/gas/config/tc-tic30.h
new file mode 100644
index 0000000..d172d2e
--- /dev/null
+++ b/gas/config/tc-tic30.h
@@ -0,0 +1,55 @@
+/* tc-tic30.h -- Header file for tc-tic30.c
+ Copyright (C) 1998 Free Software Foundation.
+ Contributed by Steven Haworth (steve@pm.cse.rmit.edu.au)
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifndef _TC_TIC30_H_
+#define _TC_TIC30_H_
+
+#define TC_TIC30 1
+
+#ifdef OBJ_AOUT
+#define TARGET_FORMAT "a.out-tic30"
+#endif
+
+#define TARGET_ARCH bfd_arch_tic30
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+#define WORKING_DOT_WORD
+
+char *output_invalid PARAMS ((int c));
+
+#define END_OF_INSN '\0'
+#define MAX_OPERANDS 6
+#define DIRECT_REFERENCE '@'
+#define INDIRECT_REFERENCE '*'
+#define PARALLEL_SEPARATOR '|'
+#define INSN_SIZE 4
+
+/* Define this to 1 if you want the debug output to be on stdout,
+ otherwise stderr will be used. If stderr is used, there will be a
+ better synchronisation with the as_bad outputs, but you can't
+ capture the output. */
+#define USE_STDOUT 0
+
+#define tc_unrecognized_line tic30_unrecognized_line
+
+extern int tic30_unrecognized_line PARAMS ((int));
+
+#endif
diff --git a/gas/config/tc-tic80.c b/gas/config/tc-tic80.c
new file mode 100644
index 0000000..f31dba3
--- /dev/null
+++ b/gas/config/tc-tic80.c
@@ -0,0 +1,1061 @@
+/* tc-tic80.c -- Assemble for the TI TMS320C80 (MV)
+ Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#include "as.h"
+#include "opcode/tic80.h"
+
+#define internal_error(what) \
+ as_fatal(_("internal error:%s:%d: %s\n"),__FILE__,__LINE__,what)
+#define internal_error_a(what,arg) \
+ as_fatal(_("internal error:%s:%d: %s %d\n"),__FILE__,__LINE__,what,arg)
+
+
+/* Generic assembler global variables which must be defined by all targets. */
+
+/* Characters which always start a comment. */
+const char comment_chars[] = ";";
+
+/* Characters which start a comment at the beginning of a line. */
+const char line_comment_chars[] = ";*#";
+
+/* Characters which may be used to separate multiple commands on a single
+ line. The semicolon is such a character by default and should not be
+ explicitly listed. */
+const char line_separator_chars[] = "";
+
+/* Characters which are used to indicate an exponent in a floating
+ point number. */
+const char EXP_CHARS[] = "eE";
+
+/* Characters which mean that a number is a floating point constant,
+ as in 0f1.0. */
+const char FLT_CHARS[] = "fF";
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ integer arg to pass to the function */
+
+extern void obj_coff_section ();
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "align", s_align_bytes, 4 }, /* Do byte alignment, default is a 4 byte boundary */
+ { "word", cons, 4 }, /* FIXME: Should this be machine independent? */
+ { "bss", s_lcomm_bytes, 1 },
+ { "sect", obj_coff_section, 0}, /* For compatibility with TI tools */
+ { "section", obj_coff_section, 0}, /* Standard COFF .section pseudo-op */
+ { NULL, NULL, 0 }
+};
+
+/* Opcode hash table. */
+static struct hash_control *tic80_hash;
+
+static struct tic80_opcode * find_opcode PARAMS ((struct tic80_opcode *, expressionS []));
+static void build_insn PARAMS ((struct tic80_opcode *, expressionS *));
+static int get_operands PARAMS ((expressionS exp[]));
+static int const_overflow PARAMS ((unsigned long num, int bits, int flags));
+
+/* Replace short PC relative instructions with long form when necessary. Currently
+ this is off by default or when given the -no-relax option. Turning it on by using
+ the -relax option forces all PC relative instructions to use the long form, which
+ is why it is currently not the default. */
+static int tic80_relax = 0;
+
+
+int
+md_estimate_size_before_relax (fragP, segment_type)
+ fragS *fragP;
+ segT segment_type;
+{
+ internal_error (_("Relaxation is a luxury we can't afford"));
+ return (-1);
+}
+
+/* We have no need to default values of symbols. */
+
+/* ARGSUSED */
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ return 0;
+}
+
+/* Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP . An error message is returned, or NULL on OK.
+ */
+
+#define MAX_LITTLENUMS 4
+
+char *
+md_atof (type, litP, sizeP)
+ int type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+ char *atof_ieee ();
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("bad call to md_atof ()");
+ }
+
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ {
+ input_line_pointer = t;
+ }
+
+ *sizeP = prec * sizeof (LITTLENUM_TYPE);
+
+ for (wordP = words; prec--;)
+ {
+ md_number_to_chars (litP, (valueT) (*wordP++), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ return (NULL);
+}
+
+/* Check to see if the constant value in NUM will fit in a field of
+ width BITS if it has flags FLAGS. */
+
+static int
+const_overflow (num, bits, flags)
+ unsigned long num;
+ int bits;
+ int flags;
+{
+ long min, max;
+ int retval = 0;
+
+ /* Only need to check fields less than 32 bits wide */
+ if (bits < 32)
+ if (flags & TIC80_OPERAND_SIGNED)
+ {
+ max = (1 << (bits - 1)) - 1;
+ min = - (1 << (bits - 1));
+ retval = ((long) num > max) || ((long) num < min);
+ }
+ else
+ {
+ max = (1 << bits) - 1;
+ min = 0;
+ retval = (num > max) || (num < min);
+ }
+ return (retval);
+}
+
+/* get_operands() parses a string of operands and fills in a passed array of
+ expressions in EXP.
+
+ Note that we use O_absent expressions to record additional information
+ about the previous non-O_absent expression, such as ":m" or ":s"
+ modifiers or register numbers enclosed in parens like "(r10)".
+
+ Returns the number of expressions that were placed in EXP.
+
+ */
+
+static int
+get_operands (exp)
+ expressionS exp[];
+{
+ char *p = input_line_pointer;
+ int numexp = 0;
+ int mflag = 0;
+ int sflag = 0;
+ int parens = 0;
+
+ while (*p)
+ {
+ /* Skip leading whitespace */
+ while (*p == ' ' || *p == '\t' || *p == ',')
+ {
+ p++;
+ }
+
+ /* Check to see if we have any operands left to parse */
+ if (*p == 0 || *p == '\n' || *p == '\r')
+ {
+ break;
+ }
+
+ /* Notice scaling or direct memory operand modifiers and save them in
+ an O_absent expression after the expression that they modify. */
+
+ if (*p == ':')
+ {
+ p++;
+ exp[numexp].X_op = O_absent;
+ if (*p == 'm')
+ {
+ p++;
+ /* This is a ":m" modifier */
+ exp[numexp].X_add_number = TIC80_OPERAND_M_SI | TIC80_OPERAND_M_LI;
+ }
+ else if (*p == 's')
+ {
+ p++;
+ /* This is a ":s" modifier */
+ exp[numexp].X_add_number = TIC80_OPERAND_SCALED;
+ }
+ else
+ {
+ as_bad (_("':' not followed by 'm' or 's'"));
+ }
+ numexp++;
+ continue;
+ }
+
+ /* Handle leading '(' on operands that use them, by recording that we
+ have entered a paren nesting level and then continuing. We complain
+ about multiple nesting. */
+
+ if (*p == '(')
+ {
+ if (++parens != 1)
+ {
+ as_bad (_("paren nesting"));
+ }
+ p++;
+ continue;
+ }
+
+ /* Handle trailing ')' on operands that use them, by reducing the
+ nesting level and then continuing. We complain if there were too
+ many closures. */
+
+ if (*p == ')')
+ {
+ /* Record that we have left a paren group and continue */
+ if (--parens < 0)
+ {
+ as_bad (_("mismatched parenthesis"));
+ }
+ p++;
+ continue;
+ }
+
+ /* Begin operand parsing at the current scan point. */
+
+ input_line_pointer = p;
+ expression (&exp[numexp]);
+
+ if (exp[numexp].X_op == O_illegal)
+ {
+ as_bad (_("illegal operand"));
+ }
+ else if (exp[numexp].X_op == O_absent)
+ {
+ as_bad (_("missing operand"));
+ }
+
+ numexp++;
+ p = input_line_pointer;
+ }
+
+ if (parens)
+ {
+ exp[numexp].X_op = O_absent;
+ exp[numexp++].X_add_number = TIC80_OPERAND_PARENS;
+ }
+
+ /* Mark the end of the valid operands with an illegal expression. */
+ exp[numexp].X_op = O_illegal;
+
+ return (numexp);
+}
+
+/* find_opcode() gets a pointer to the entry in the opcode table that
+ matches the instruction being assembled, or returns NULL if no such match
+ is found.
+
+ First it parses all the operands and save them as expressions. Note that
+ we use O_absent expressions to record additional information about the
+ previous non-O_absent expression, such as ":m" or ":s" modifiers or
+ register numbers enclosed in parens like "(r10)".
+
+ It then looks at all opcodes with the same name and uses the operands to
+ choose the correct opcode. */
+
+static struct tic80_opcode *
+find_opcode (opcode, myops)
+ struct tic80_opcode *opcode;
+ expressionS myops[];
+{
+ int numexp; /* Number of expressions from parsing operands */
+ int expi; /* Index of current expression to match */
+ int opi; /* Index of current operand to match */
+ int match = 0; /* Set to 1 when an operand match is found */
+ struct tic80_opcode *opc = opcode; /* Pointer to current opcode table entry */
+ const struct tic80_opcode *end; /* Pointer to end of opcode table */
+
+ /* First parse all the operands so we only have to do it once. There may
+ be more expressions generated than there are operands. */
+
+ numexp = get_operands (myops);
+
+ /* For each opcode with the same name, try to match it against the parsed
+ operands. */
+
+ end = tic80_opcodes + tic80_num_opcodes;
+ while (!match && (opc < end) && (strcmp (opc -> name, opcode -> name) == 0))
+ {
+ /* Start off assuming a match. If we find a mismatch, then this is
+ reset and the operand/expr matching loop terminates with match
+ equal to zero, which allows us to try the next opcode. */
+
+ match = 1;
+
+ /* For each expression, try to match it against the current operand
+ for the current opcode. Upon any mismatch, we abandon further
+ matching for the current opcode table entry. */
+
+ for (expi = 0, opi = -1; (expi < numexp) && match; expi++)
+ {
+ int bits, flags, X_op, num;
+
+ X_op = myops[expi].X_op;
+ num = myops[expi].X_add_number;
+
+ /* The O_absent expressions apply to the same operand as the most
+ recent non O_absent expression. So only increment the operand
+ index when the current expression is not one of these special
+ expressions. */
+
+ if (X_op != O_absent)
+ {
+ opi++;
+ }
+
+ flags = tic80_operands[opc -> operands[opi]].flags;
+ bits = tic80_operands[opc -> operands[opi]].bits;
+
+ switch (X_op)
+ {
+ case O_register:
+ /* Also check that registers that are supposed to be even actually
+ are even. */
+ if (((flags & TIC80_OPERAND_GPR) != (num & TIC80_OPERAND_GPR)) ||
+ ((flags & TIC80_OPERAND_FPA) != (num & TIC80_OPERAND_FPA)) ||
+ ((flags & TIC80_OPERAND_CR) != (num & TIC80_OPERAND_CR)) ||
+ ((flags & TIC80_OPERAND_EVEN) && (num & 1)) ||
+ const_overflow (num & ~TIC80_OPERAND_MASK, bits, flags))
+ {
+ match = 0;
+ }
+ break;
+ case O_constant:
+ if ((flags & TIC80_OPERAND_ENDMASK) && (num == 32))
+ {
+ /* Endmask values of 0 and 32 give identical results */
+ num = 0;
+ }
+ if ((flags & (TIC80_OPERAND_FPA | TIC80_OPERAND_GPR)) ||
+ const_overflow (num, bits, flags))
+ {
+ match = 0;
+ }
+ break;
+ case O_symbol:
+ if ((bits < 32) && (flags & TIC80_OPERAND_PCREL) && !tic80_relax)
+ {
+ /* The default is to prefer the short form of PC relative relocations.
+ This is the only form that the TI assembler supports.
+ If the -relax option is given, we never use the short forms.
+ FIXME: Should be able to choose "best-fit". */
+ }
+ else if ((bits == 32) /* && (flags & TIC80_OPERAND_BASEREL) */)
+ {
+ /* The default is to prefer the long form of base relative relocations.
+ This is the only form that the TI assembler supports.
+ If the -no-relax option is given, we always use the long form of
+ PC relative relocations.
+ FIXME: Should be able to choose "best-fit". */
+ }
+ else
+ {
+ /* Symbols that don't match one of the above cases are
+ rejected as an operand. */
+ match = 0;
+ }
+ break;
+ case O_absent:
+ /* If this is an O_absent expression, then it may be an expression that
+ supplies additional information about the operand, such as ":m" or
+ ":s" modifiers. Check to see that the operand matches this requirement. */
+ if (!((num & TIC80_OPERAND_M_SI) && (flags & TIC80_OPERAND_M_SI) ||
+ (num & TIC80_OPERAND_M_LI) && (flags & TIC80_OPERAND_M_LI) ||
+ (num & TIC80_OPERAND_SCALED) && (flags & TIC80_OPERAND_SCALED)))
+ {
+ match = 0;
+ }
+ break;
+ case O_big:
+ if ((num > 0) || !(flags & TIC80_OPERAND_FLOAT))
+ {
+ match = 0;
+ }
+ break;
+ case O_illegal:
+ case O_symbol_rva:
+ case O_uminus:
+ case O_bit_not:
+ case O_logical_not:
+ case O_multiply:
+ case O_divide:
+ case O_modulus:
+ case O_left_shift:
+ case O_right_shift:
+ case O_bit_inclusive_or:
+ case O_bit_or_not:
+ case O_bit_exclusive_or:
+ case O_bit_and:
+ case O_add:
+ case O_subtract:
+ case O_eq:
+ case O_ne:
+ case O_lt:
+ case O_le:
+ case O_ge:
+ case O_gt:
+ case O_logical_and:
+ case O_logical_or:
+ case O_max:
+ default:
+ internal_error_a (_("unhandled expression type"), X_op);
+ }
+ }
+ if (!match)
+ {
+ opc++;
+ }
+ }
+
+ return (match ? opc : NULL);
+
+#if 0
+
+ /* Now search the opcode table table for one with operands that
+ matches what we've got. */
+
+ while (!match)
+ {
+ match = 1;
+ for (i = 0; opcode -> operands[i]; i++)
+ {
+ int flags = tic80_operands[opcode->operands[i]].flags;
+ int X_op = myops[i].X_op;
+ int num = myops[i].X_add_number;
+
+ if (X_op == 0)
+ {
+ match = 0;
+ break;
+ }
+
+ if (flags & (TIC80_OPERAND_GPR | TIC80_OPERAND_FPA | TIC80_OPERAND_CR))
+ {
+ if ((X_op != O_register) ||
+ ((flags & TIC80_OPERAND_GPR) != (num & TIC80_OPERAND_GPR)) ||
+ ((flags & TIC80_OPERAND_FPA) != (num & TIC80_OPERAND_FPA)) ||
+ ((flags & TIC80_OPERAND_CR) != (num & TIC80_OPERAND_CR)))
+ {
+ match=0;
+ break;
+ }
+ }
+
+ if (((flags & TIC80_OPERAND_MINUS) && ((X_op != O_absent) || (num != TIC80_OPERAND_MINUS))) ||
+ ((flags & TIC80_OPERAND_PLUS) && ((X_op != O_absent) || (num != TIC80_OPERAND_PLUS))) ||
+ ((flags & TIC80_OPERAND_ATMINUS) && ((X_op != O_absent) || (num != TIC80_OPERAND_ATMINUS))) ||
+ ((flags & TIC80_OPERAND_ATPAR) && ((X_op != O_absent) || (num != TIC80_OPERAND_ATPAR))) ||
+ ((flags & TIC80_OPERAND_ATSIGN) && ((X_op != O_absent) || (num != TIC80_OPERAND_ATSIGN))))
+ {
+ match=0;
+ break;
+ }
+ }
+ /* we're only done if the operands matched so far AND there
+ are no more to check */
+ if (match && myops[i].X_op==0)
+ break;
+ else
+ match = 0;
+
+ next_opcode = opcode+1;
+ if (next_opcode->opcode == 0)
+ break;
+ if (strcmp(next_opcode->name, opcode->name))
+ break;
+ opcode = next_opcode;
+ }
+
+ if (!match)
+ {
+ as_bad (_("bad opcode or operands"));
+ return (0);
+ }
+
+ /* Check that all registers that are required to be even are. */
+ /* Also, if any operands were marked as registers, but were really symbols */
+ /* fix that here. */
+ for (i=0; opcode->operands[i]; i++)
+ {
+ if ((tic80_operands[opcode->operands[i]].flags & TIC80_OPERAND_EVEN) &&
+ (myops[i].X_add_number & 1))
+ as_fatal (_("Register number must be EVEN"));
+ if (myops[i].X_op == O_register)
+ {
+ if (!(tic80_operands[opcode->operands[i]].flags & TIC80_OPERAND_REG))
+ {
+ myops[i].X_op = O_symbol;
+ myops[i].X_add_symbol = symbol_find_or_make ((char *)myops[i].X_op_symbol);
+ myops[i].X_add_number = 0;
+ myops[i].X_op_symbol = NULL;
+ }
+ }
+ }
+
+#endif
+}
+
+/* build_insn takes a pointer to the opcode entry in the opcode table
+ and the array of operand expressions and writes out the instruction.
+
+ Note that the opcode word and extended word may be written to different
+ frags, with the opcode at the end of one frag and the extension at the
+ beginning of the next. */
+
+static void
+build_insn (opcode, opers)
+ struct tic80_opcode *opcode;
+ expressionS *opers;
+{
+ int expi; /* Index of current expression to match */
+ int opi; /* Index of current operand to match */
+ unsigned long insn[2]; /* Instruction and long immediate (if any) */
+ char *f; /* Pointer to frag location for insn[0] */
+ fragS *ffrag; /* Frag containing location f */
+ char *fx = NULL; /* Pointer to frag location for insn[1] */
+ fragS *fxfrag; /* Frag containing location fx */
+
+ /* Start with the raw opcode bits from the opcode table. */
+ insn[0] = opcode -> opcode;
+
+ /* We are going to insert at least one 32 bit opcode so get the
+ frag now. */
+
+ f = frag_more (4);
+ ffrag = frag_now;
+
+ /* For each operand expression, insert the appropriate bits into the
+ instruction . */
+ for (expi = 0, opi = -1; opers[expi].X_op != O_illegal; expi++)
+ {
+ int bits, shift, flags, X_op, num;
+
+ X_op = opers[expi].X_op;
+ num = opers[expi].X_add_number;
+
+ /* The O_absent expressions apply to the same operand as the most
+ recent non O_absent expression. So only increment the operand
+ index when the current expression is not one of these special
+ expressions. */
+
+ if (X_op != O_absent)
+ {
+ opi++;
+ }
+
+ flags = tic80_operands[opcode -> operands[opi]].flags;
+ bits = tic80_operands[opcode -> operands[opi]].bits;
+ shift = tic80_operands[opcode -> operands[opi]].shift;
+
+ switch (X_op)
+ {
+ case O_register:
+ num &= ~TIC80_OPERAND_MASK;
+ insn[0] = insn[0] | (num << shift);
+ break;
+ case O_constant:
+ if ((flags & TIC80_OPERAND_ENDMASK) && (num == 32))
+ {
+ /* Endmask values of 0 and 32 give identical results */
+ num = 0;
+ }
+ else if ((flags & TIC80_OPERAND_BITNUM))
+ {
+ /* BITNUM values are stored in one's complement form */
+ num = (~num & 0x1F);
+ }
+ /* Mask off upper bits, just it case it is signed and is negative */
+ if (bits < 32)
+ {
+ num &= (1 << bits) - 1;
+ insn[0] = insn[0] | (num << shift);
+ }
+ else
+ {
+ fx = frag_more (4);
+ fxfrag = frag_now;
+ insn[1] = num;
+ }
+ break;
+ case O_symbol:
+ if (bits == 32)
+ {
+ fx = frag_more (4);
+ fxfrag = frag_now;
+ insn[1] = 0;
+ if (flags & TIC80_OPERAND_PCREL)
+ {
+ fix_new_exp (fxfrag,
+ fx - (fxfrag -> fr_literal),
+ 4,
+ &opers[expi],
+ 1,
+ R_MPPCR);
+ }
+ else
+ {
+ fix_new_exp (fxfrag,
+ fx - (fxfrag -> fr_literal),
+ 4,
+ &opers[expi],
+ 0,
+ R_RELLONGX);
+ }
+ }
+ else if (flags & TIC80_OPERAND_PCREL)
+ {
+ fix_new_exp (ffrag,
+ f - (ffrag -> fr_literal),
+ 4, /* FIXME! how is this used? */
+ &opers[expi],
+ 1,
+ R_MPPCR15W);
+ }
+ else
+ {
+ internal_error (_("symbol reloc that is not PC relative or 32 bits"));
+ }
+ break;
+ case O_absent:
+ /* Each O_absent expression can indicate exactly one possible modifier. */
+ if ((num & TIC80_OPERAND_M_SI) && (flags & TIC80_OPERAND_M_SI))
+ {
+ insn[0] = insn[0] | (1 << 17);
+ }
+ else if ((num & TIC80_OPERAND_M_LI) && (flags & TIC80_OPERAND_M_LI))
+ {
+ insn[0] = insn[0] | (1 << 15);
+ }
+ else if ((num & TIC80_OPERAND_SCALED) && (flags & TIC80_OPERAND_SCALED))
+ {
+ insn[0] = insn[0] | (1 << 11);
+ }
+ else if ((num & TIC80_OPERAND_PARENS) && (flags & TIC80_OPERAND_PARENS))
+ {
+ /* No code to generate, just accept and discard this expression */
+ }
+ else
+ {
+ internal_error_a (_("unhandled operand modifier"), opers[expi].X_add_number);
+ }
+ break;
+ case O_big:
+ fx = frag_more (4);
+ fxfrag = frag_now;
+ {
+ int precision = 2;
+ long exponent_bits = 8L;
+ LITTLENUM_TYPE words[2];
+ /* Value is still in generic_floating_point_number */
+ gen_to_words (words, precision, exponent_bits);
+ insn[1] = (words[0] << 16) | words[1];
+ }
+ break;
+ case O_illegal:
+ case O_symbol_rva:
+ case O_uminus:
+ case O_bit_not:
+ case O_logical_not:
+ case O_multiply:
+ case O_divide:
+ case O_modulus:
+ case O_left_shift:
+ case O_right_shift:
+ case O_bit_inclusive_or:
+ case O_bit_or_not:
+ case O_bit_exclusive_or:
+ case O_bit_and:
+ case O_add:
+ case O_subtract:
+ case O_eq:
+ case O_ne:
+ case O_lt:
+ case O_le:
+ case O_ge:
+ case O_gt:
+ case O_logical_and:
+ case O_logical_or:
+ case O_max:
+ default:
+ internal_error_a (_("unhandled expression"), X_op);
+ break;
+ }
+ }
+
+ /* Write out the instruction, either 4 or 8 bytes. */
+
+ md_number_to_chars (f, insn[0], 4);
+ if (fx != NULL)
+ {
+ md_number_to_chars (fx, insn[1], 4);
+ }
+}
+
+/* This is the main entry point for the machine-dependent assembler. Gas
+ calls this function for each input line which does not contain a
+ pseudoop.
+
+ STR points to a NULL terminated machine dependent instruction. This
+ function is supposed to emit the frags/bytes it assembles to. */
+
+void
+md_assemble (str)
+ char *str;
+{
+ char *scan;
+ unsigned char *input_line_save;
+ struct tic80_opcode *opcode;
+ expressionS myops[16];
+ unsigned long insn;
+
+ /* Ensure there is something there to assemble. */
+ assert (str);
+
+ /* Drop any leading whitespace. */
+ while (isspace (*str))
+ {
+ str++;
+ }
+
+ /* Isolate the mnemonic from the rest of the string by finding the first
+ whitespace character and zapping it to a null byte. */
+ for (scan = str; *scan != '\000' && !isspace (*scan); scan++) {;}
+ if (*scan != '\000')
+ {
+ *scan++ = '\000';
+ }
+
+ /* Try to find this mnemonic in the hash table */
+ if ((opcode = (struct tic80_opcode *) hash_find (tic80_hash, str)) == NULL)
+ {
+ as_bad (_("Invalid mnemonic: '%s'"), str);
+ return;
+ }
+
+ str = scan;
+ while (isspace (*scan))
+ {
+ scan++;
+ }
+
+ input_line_save = input_line_pointer;
+ input_line_pointer = str;
+
+ opcode = find_opcode (opcode, myops);
+ if (opcode == NULL)
+ {
+ as_bad (_("Invalid operands: '%s'"), input_line_save);
+ }
+
+ input_line_pointer = input_line_save;
+ build_insn (opcode, myops);
+}
+
+/* This function is called once at the start of assembly, after the command
+ line arguments have been parsed and all the machine independent
+ initializations have been completed.
+
+ It should set up all the tables, etc., that the machine dependent part of
+ the assembler will need. */
+
+void
+md_begin ()
+{
+ char *prev_name = "";
+ register const struct tic80_opcode *op;
+ register const struct tic80_opcode *op_end;
+ const struct predefined_symbol *pdsp;
+ extern int coff_flags; /* Defined in obj-coff.c */
+
+ /* Set F_AR32WR in coff_flags, which will end up in the file header
+ f_flags field. */
+
+ coff_flags |= F_AR32WR; /* TIc80 is 32 bit little endian */
+
+ /* Insert unique names into hash table. The TIc80 instruction set
+ has many identical opcode names that have different opcodes based
+ on the operands. This hash table then provides a quick index to
+ the first opcode with a particular name in the opcode table. */
+
+ tic80_hash = hash_new ();
+ op_end = tic80_opcodes + tic80_num_opcodes;
+ for (op = tic80_opcodes; op < op_end; op++)
+ {
+ if (strcmp (prev_name, op -> name) != 0)
+ {
+ prev_name = (char *) op -> name;
+ hash_insert (tic80_hash, op -> name, (char *) op);
+ }
+ }
+
+ /* Insert the predefined symbols into the symbol table. We use symbol_create
+ rather than symbol_new so that these symbols don't end up in the object
+ files' symbol table. Note that the values of the predefined symbols include
+ some upper bits that distinguish the type of the symbol (register, bitnum,
+ condition code, etc) and these bits must be masked away before actually
+ inserting the values into the instruction stream. For registers we put
+ these bits in the symbol table since we use them later and there is no
+ question that they aren't part of the register number. For constants we
+ can't do that since the constant can be any value, so they are masked off
+ before putting them into the symbol table. */
+
+ pdsp = NULL;
+ while ((pdsp = tic80_next_predefined_symbol (pdsp)) != NULL)
+ {
+ segT segment;
+ valueT valu;
+ int symtype;
+
+ symtype = PDS_VALUE (pdsp) & TIC80_OPERAND_MASK;
+ switch (symtype)
+ {
+ case TIC80_OPERAND_GPR:
+ case TIC80_OPERAND_FPA:
+ case TIC80_OPERAND_CR:
+ segment = reg_section;
+ valu = PDS_VALUE (pdsp);
+ break;
+ case TIC80_OPERAND_CC:
+ case TIC80_OPERAND_BITNUM:
+ segment = absolute_section;
+ valu = PDS_VALUE (pdsp) & ~TIC80_OPERAND_MASK;
+ break;
+ default:
+ internal_error_a (_("unhandled predefined symbol bits"), symtype);
+ break;
+ }
+ symbol_table_insert (symbol_create (PDS_NAME (pdsp), segment, valu,
+ &zero_address_frag));
+ }
+}
+
+
+
+/* The assembler adds md_shortopts to the string passed to getopt. */
+
+CONST char *md_shortopts = "";
+
+/* The assembler adds md_longopts to the machine independent long options
+ that are passed to getopt. */
+
+struct option md_longopts[] = {
+
+#define OPTION_RELAX (OPTION_MD_BASE)
+ {"relax", no_argument, NULL, OPTION_RELAX},
+
+#define OPTION_NO_RELAX (OPTION_RELAX + 1)
+ {"no-relax", no_argument, NULL, OPTION_NO_RELAX},
+
+ {NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof(md_longopts);
+
+/* The md_parse_option function will be called whenever getopt returns an
+ unrecognized code, presumably indicating a special code value which
+ appears in md_longopts for machine specific command line options. */
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ switch (c)
+ {
+ case OPTION_RELAX:
+ tic80_relax = 1;
+ break;
+ case OPTION_NO_RELAX:
+ tic80_relax = 0;
+ break;
+ default:
+ return (0);
+ }
+ return (1);
+}
+
+/* The md_show_usage function will be called whenever a usage message is
+ printed. It should print a description of the machine specific options
+ found in md_longopts. */
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ fprintf (stream, "\
+TIc80 options:\n\
+-relax alter PC relative branch instructions to use long form when needed\n\
+-no-relax always use short PC relative branch instructions, error on overflow\n");
+}
+
+
+/* Attempt to simplify or even eliminate a fixup. The return value is
+ ignored; perhaps it was once meaningful, but now it is historical.
+ To indicate that a fixup has been eliminated, set fixP->fx_done.
+ */
+
+void
+md_apply_fix (fixP, val)
+ fixS *fixP;
+ long val;
+{
+ char *dest = fixP -> fx_frag -> fr_literal + fixP -> fx_where;
+ int overflow;
+
+ switch (fixP -> fx_r_type)
+ {
+ case R_RELLONGX:
+ md_number_to_chars (dest, (valueT) val, 4);
+ break;
+ case R_MPPCR:
+ val >>= 2;
+ val += 1; /* Target address computed from inst start */
+ md_number_to_chars (dest, (valueT) val, 4);
+ break;
+ case R_MPPCR15W:
+ overflow = (val < -65536L) || (val > 65532L);
+ if (overflow)
+ {
+ as_bad_where (fixP -> fx_file, fixP -> fx_line,
+ _("PC offset 0x%lx outside range 0x%lx-0x%lx"),
+ val, -65536L, 65532L);
+ }
+ else
+ {
+ val >>= 2;
+ *dest++ = val & 0xFF;
+ val >>= 8;
+ *dest = (*dest & 0x80) | (val & 0x7F);
+ }
+ break;
+ case R_ABS:
+ md_number_to_chars (dest, (valueT) val, fixP -> fx_size);
+ break;
+ default:
+ internal_error_a (_("unhandled relocation type in fixup"), fixP -> fx_r_type);
+ break;
+ }
+}
+
+
+/* Functions concerning relocs. */
+
+/* The location from which a PC relative jump should be calculated,
+ given a PC relative reloc.
+
+ For the TIc80, this is the address of the 32 bit opcode containing
+ the PC relative field. */
+
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ return (fixP -> fx_frag -> fr_address + fixP -> fx_where) ;
+}
+
+/*
+ * Called after relax() is finished.
+ * In: Address of frag.
+ * fr_type == rs_machine_dependent.
+ * fr_subtype is what the address relaxed to.
+ *
+ * Out: Any fixSs and constants are set up.
+ * Caller will turn frag into a ".space 0".
+ */
+
+void
+md_convert_frag (headers, seg, fragP)
+ object_headers *headers;
+ segT seg;
+ fragS *fragP;
+{
+ internal_error (_("md_convert_frag() not implemented yet"));
+ abort ();
+}
+
+
+/*ARGSUSED*/
+void
+tc_coff_symbol_emit_hook (ignore)
+ symbolS *ignore;
+{
+}
+
+#if defined OBJ_COFF
+
+short
+tc_coff_fix2rtype (fixP)
+ fixS *fixP;
+{
+ return (fixP -> fx_r_type);
+}
+
+#endif /* OBJ_COFF */
+
+/* end of tc-tic80.c */
diff --git a/gas/config/tc-tic80.h b/gas/config/tc-tic80.h
new file mode 100644
index 0000000..06ff249
--- /dev/null
+++ b/gas/config/tc-tic80.h
@@ -0,0 +1,63 @@
+/* This file is tc-tic80.h
+ Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#define TC_TIC80
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define TARGET_ARCH bfd_arch_tic80
+#define TARGET_FORMAT "coff-tic80"
+#define BFD_ARCH TARGET_ARCH
+
+/* We need the extra field in the fixup struct to put the relocation in. */
+
+#define NEED_FX_R_TYPE
+
+/* Define md_number_to_chars as the appropriate standard big endian or
+ little endian function. Should we someday support endianness as a
+ runtime decision, this will need to change. */
+
+#define md_number_to_chars number_to_chars_littleendian
+
+/* Define away the call to md_operand in the expression parsing code.
+ This is called whenever the expression parser can't parse the input
+ and gives the assembler backend a chance to deal with it instead. */
+
+#define md_operand(x)
+
+#ifdef OBJ_COFF
+
+/* COFF specific definitions. */
+
+#define COFF_MAGIC TIC80_ARCH_MAGIC
+
+/* Whether a reloc should be output. */
+
+#define TC_COUNT_RELOC(fixp) ((fixp) -> fx_addsy != NULL)
+
+/* This macro translates between an internal fix and an coff reloc type */
+
+#define TC_COFF_FIX2RTYPE(fixP) tc_coff_fix2rtype(fixP)
+
+extern short tc_coff_fix2rtype ();
+
+#endif /* OBJ_COFF */
+
+/* end of tc-tic80.h */
diff --git a/gas/config/tc-v850.c b/gas/config/tc-v850.c
new file mode 100644
index 0000000..c8ab145
--- /dev/null
+++ b/gas/config/tc-v850.c
@@ -0,0 +1,2478 @@
+/* tc-v850.c -- Assembler code for the NEC V850
+ Copyright (C) 1996, 1997, 1998 Free Software Foundation.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "as.h"
+#include "subsegs.h"
+#include "opcode/v850.h"
+
+#define AREA_ZDA 0
+#define AREA_SDA 1
+#define AREA_TDA 2
+
+/* sign-extend a 16-bit number */
+#define SEXT16(x) ((((x) & 0xffff) ^ (~ 0x7fff)) + 0x8000)
+
+/* Temporarily holds the reloc in a cons expression. */
+static bfd_reloc_code_real_type hold_cons_reloc;
+
+/* Set to TRUE if we want to be pedantic about signed overflows. */
+static boolean warn_signed_overflows = FALSE;
+static boolean warn_unsigned_overflows = FALSE;
+
+/* Indicates the target BFD machine number. */
+static int machine = -1;
+
+/* Indicates the target processor(s) for the assemble. */
+static unsigned int processor_mask = -1;
+
+
+/* Structure to hold information about predefined registers. */
+struct reg_name
+{
+ const char * name;
+ int value;
+};
+
+/* Generic assembler global variables which must be defined by all targets. */
+
+/* Characters which always start a comment. */
+const char comment_chars[] = "#";
+
+/* Characters which start a comment at the beginning of a line. */
+const char line_comment_chars[] = ";#";
+
+/* Characters which may be used to separate multiple commands on a
+ single line. */
+const char line_separator_chars[] = ";";
+
+/* Characters which are used to indicate an exponent in a floating
+ point number. */
+const char EXP_CHARS[] = "eE";
+
+/* Characters which mean that a number is a floating point constant,
+ as in 0d1.0. */
+const char FLT_CHARS[] = "dD";
+
+
+const relax_typeS md_relax_table[] =
+{
+ /* Conditional branches. */
+ {0xff, -0x100, 2, 1},
+ {0x1fffff, -0x200000, 6, 0},
+ /* Unconditional branches. */
+ {0xff, -0x100, 2, 3},
+ {0x1fffff, -0x200000, 4, 0},
+};
+
+
+static segT sdata_section = NULL;
+static segT tdata_section = NULL;
+static segT zdata_section = NULL;
+static segT sbss_section = NULL;
+static segT tbss_section = NULL;
+static segT zbss_section = NULL;
+static segT rosdata_section = NULL;
+static segT rozdata_section = NULL;
+static segT scommon_section = NULL;
+static segT tcommon_section = NULL;
+static segT zcommon_section = NULL;
+static segT call_table_data_section = NULL;
+static segT call_table_text_section = NULL;
+
+/* fixups */
+#define MAX_INSN_FIXUPS (5)
+struct v850_fixup
+{
+ expressionS exp;
+ int opindex;
+ bfd_reloc_code_real_type reloc;
+};
+
+struct v850_fixup fixups [MAX_INSN_FIXUPS];
+static int fc;
+
+
+void
+v850_sdata (int ignore)
+{
+ obj_elf_section_change_hook();
+
+ subseg_set (sdata_section, (subsegT) get_absolute_expression ());
+
+ demand_empty_rest_of_line ();
+}
+
+void
+v850_tdata (int ignore)
+{
+ obj_elf_section_change_hook();
+
+ subseg_set (tdata_section, (subsegT) get_absolute_expression ());
+
+ demand_empty_rest_of_line ();
+}
+
+void
+v850_zdata (int ignore)
+{
+ obj_elf_section_change_hook();
+
+ subseg_set (zdata_section, (subsegT) get_absolute_expression ());
+
+ demand_empty_rest_of_line ();
+}
+
+void
+v850_sbss (int ignore)
+{
+ obj_elf_section_change_hook();
+
+ subseg_set (sbss_section, (subsegT) get_absolute_expression ());
+
+ demand_empty_rest_of_line ();
+}
+
+void
+v850_tbss (int ignore)
+{
+ obj_elf_section_change_hook();
+
+ subseg_set (tbss_section, (subsegT) get_absolute_expression ());
+
+ demand_empty_rest_of_line ();
+}
+
+void
+v850_zbss (int ignore)
+{
+ obj_elf_section_change_hook();
+
+ subseg_set (zbss_section, (subsegT) get_absolute_expression ());
+
+ demand_empty_rest_of_line ();
+}
+
+void
+v850_rosdata (int ignore)
+{
+ obj_elf_section_change_hook();
+
+ subseg_set (rosdata_section, (subsegT) get_absolute_expression ());
+
+ demand_empty_rest_of_line ();
+}
+
+void
+v850_rozdata (int ignore)
+{
+ obj_elf_section_change_hook();
+
+ subseg_set (rozdata_section, (subsegT) get_absolute_expression ());
+
+ demand_empty_rest_of_line ();
+}
+
+void
+v850_call_table_data (int ignore)
+{
+ obj_elf_section_change_hook();
+
+ subseg_set (call_table_data_section, (subsegT) get_absolute_expression ());
+
+ demand_empty_rest_of_line ();
+}
+
+void
+v850_call_table_text (int ignore)
+{
+ obj_elf_section_change_hook();
+
+ subseg_set (call_table_text_section, (subsegT) get_absolute_expression ());
+
+ demand_empty_rest_of_line ();
+}
+
+void
+v850_bss (int ignore)
+{
+ register int temp = get_absolute_expression ();
+
+ obj_elf_section_change_hook();
+
+ subseg_set (bss_section, (subsegT) temp);
+
+ demand_empty_rest_of_line ();
+}
+
+void
+v850_offset (int ignore)
+{
+ int temp = get_absolute_expression ();
+
+ temp -= frag_now_fix();
+
+ if (temp > 0)
+ (void) frag_more (temp);
+
+ demand_empty_rest_of_line ();
+}
+
+/* Copied from obj_elf_common() in gas/config/obj-elf.c */
+static void
+v850_comm (area)
+ int area;
+{
+ char * name;
+ char c;
+ char * p;
+ int temp;
+ int size;
+ symbolS * symbolP;
+ int have_align;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ /* just after name is now '\0' */
+ p = input_line_pointer;
+ *p = c;
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("Expected comma after symbol-name"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ input_line_pointer ++; /* skip ',' */
+
+ if ((temp = get_absolute_expression ()) < 0)
+ {
+ /* xgettext:c-format */
+ as_bad (_(".COMMon length (%d.) < 0! Ignored."), temp);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ size = temp;
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+
+ if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
+ {
+ as_bad (_("Ignoring attempt to re-define symbol"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (S_GET_VALUE (symbolP) != 0)
+ {
+ if (S_GET_VALUE (symbolP) != size)
+ {
+ /* xgettext:c-format */
+ as_warn (_("Length of .comm \"%s\" is already %ld. Not changed to %d."),
+ S_GET_NAME (symbolP), (long) S_GET_VALUE (symbolP), size);
+ }
+ }
+
+ know (symbolP->sy_frag == & zero_address_frag);
+
+ if (*input_line_pointer != ',')
+ have_align = 0;
+ else
+ {
+ have_align = 1;
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ }
+
+ if (! have_align || *input_line_pointer != '"')
+ {
+ if (! have_align)
+ temp = 0;
+ else
+ {
+ temp = get_absolute_expression ();
+
+ if (temp < 0)
+ {
+ temp = 0;
+ as_warn (_("Common alignment negative; 0 assumed"));
+ }
+ }
+
+ if (symbolP->local)
+ {
+ segT old_sec;
+ int old_subsec;
+ char * pfrag;
+ int align;
+ flagword applicable;
+
+ old_sec = now_seg;
+ old_subsec = now_subseg;
+
+ applicable = bfd_applicable_section_flags (stdoutput);
+
+ applicable &= SEC_ALLOC;
+
+ switch (area)
+ {
+ case AREA_SDA:
+ if (sbss_section == NULL)
+ {
+ sbss_section = subseg_new (".sbss", 0);
+
+ bfd_set_section_flags (stdoutput, sbss_section, applicable);
+
+ seg_info (sbss_section)->bss = 1;
+ }
+ break;
+
+ case AREA_ZDA:
+ if (zbss_section == NULL)
+ {
+ zbss_section = subseg_new (".zbss", 0);
+
+ bfd_set_section_flags (stdoutput, sbss_section, applicable);
+
+ seg_info (zbss_section)->bss = 1;
+ }
+ break;
+
+ case AREA_TDA:
+ if (tbss_section == NULL)
+ {
+ tbss_section = subseg_new (".tbss", 0);
+
+ bfd_set_section_flags (stdoutput, tbss_section, applicable);
+
+ seg_info (tbss_section)->bss = 1;
+ }
+ break;
+ }
+
+ if (temp)
+ {
+ /* convert to a power of 2 alignment */
+ for (align = 0; (temp & 1) == 0; temp >>= 1, ++align)
+ ;
+
+ if (temp != 1)
+ {
+ as_bad (_("Common alignment not a power of 2"));
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ else
+ align = 0;
+
+ switch (area)
+ {
+ case AREA_SDA:
+ record_alignment (sbss_section, align);
+ obj_elf_section_change_hook();
+ subseg_set (sbss_section, 0);
+ break;
+
+ case AREA_ZDA:
+ record_alignment (zbss_section, align);
+ obj_elf_section_change_hook();
+ subseg_set (zbss_section, 0);
+ break;
+
+ case AREA_TDA:
+ record_alignment (tbss_section, align);
+ obj_elf_section_change_hook();
+ subseg_set (tbss_section, 0);
+ break;
+
+ default:
+ abort();
+ }
+
+ if (align)
+ frag_align (align, 0, 0);
+
+ switch (area)
+ {
+ case AREA_SDA:
+ if (S_GET_SEGMENT (symbolP) == sbss_section)
+ symbolP->sy_frag->fr_symbol = 0;
+ break;
+
+ case AREA_ZDA:
+ if (S_GET_SEGMENT (symbolP) == zbss_section)
+ symbolP->sy_frag->fr_symbol = 0;
+ break;
+
+ case AREA_TDA:
+ if (S_GET_SEGMENT (symbolP) == tbss_section)
+ symbolP->sy_frag->fr_symbol = 0;
+ break;
+
+ default:
+ abort();
+ }
+
+ symbolP->sy_frag = frag_now;
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
+ (offsetT) size, (char *) 0);
+ *pfrag = 0;
+ S_SET_SIZE (symbolP, size);
+
+ switch (area)
+ {
+ case AREA_SDA:
+ S_SET_SEGMENT (symbolP, sbss_section);
+ break;
+
+ case AREA_ZDA:
+ S_SET_SEGMENT (symbolP, zbss_section);
+ break;
+
+ case AREA_TDA:
+ S_SET_SEGMENT (symbolP, tbss_section);
+ break;
+
+ default:
+ abort();
+ }
+
+ S_CLEAR_EXTERNAL (symbolP);
+ obj_elf_section_change_hook();
+ subseg_set (old_sec, old_subsec);
+ }
+ else
+ {
+ allocate_common:
+ S_SET_VALUE (symbolP, (valueT) size);
+ S_SET_ALIGN (symbolP, temp);
+ S_SET_EXTERNAL (symbolP);
+
+ switch (area)
+ {
+ case AREA_SDA:
+ if (scommon_section == NULL)
+ {
+ flagword applicable;
+
+ applicable = bfd_applicable_section_flags (stdoutput);
+
+ scommon_section = subseg_new (".scommon", 0);
+
+ bfd_set_section_flags (stdoutput, scommon_section, applicable
+ & (SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA
+ | SEC_HAS_CONTENTS) | SEC_IS_COMMON);
+ }
+ S_SET_SEGMENT (symbolP, scommon_section);
+ break;
+
+ case AREA_ZDA:
+ if (zcommon_section == NULL)
+ {
+ flagword applicable;
+
+ applicable = bfd_applicable_section_flags (stdoutput);
+
+ zcommon_section = subseg_new (".zcommon", 0);
+
+ bfd_set_section_flags (stdoutput, zcommon_section, applicable
+ & (SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA
+ | SEC_HAS_CONTENTS) | SEC_IS_COMMON);
+ }
+ S_SET_SEGMENT (symbolP, zcommon_section);
+ break;
+
+ case AREA_TDA:
+ if (tcommon_section == NULL)
+ {
+ flagword applicable;
+
+ applicable = bfd_applicable_section_flags (stdoutput);
+
+ tcommon_section = subseg_new (".tcommon", 0);
+
+ bfd_set_section_flags (stdoutput, tcommon_section, applicable
+ & (SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA
+ | SEC_HAS_CONTENTS) | SEC_IS_COMMON);
+ }
+ S_SET_SEGMENT (symbolP, tcommon_section);
+ break;
+
+ default:
+ abort();
+ }
+ }
+ }
+ else
+ {
+ input_line_pointer++;
+ /* @@ Some use the dot, some don't. Can we get some consistency?? */
+ if (*input_line_pointer == '.')
+ input_line_pointer++;
+ /* @@ Some say data, some say bss. */
+ if (strncmp (input_line_pointer, "bss\"", 4)
+ && strncmp (input_line_pointer, "data\"", 5))
+ {
+ while (*--input_line_pointer != '"')
+ ;
+ input_line_pointer--;
+ goto bad_common_segment;
+ }
+ while (*input_line_pointer++ != '"')
+ ;
+ goto allocate_common;
+ }
+
+ symbolP->bsym->flags |= BSF_OBJECT;
+
+ demand_empty_rest_of_line ();
+ return;
+
+ {
+ bad_common_segment:
+ p = input_line_pointer;
+ while (*p && *p != '\n')
+ p++;
+ c = *p;
+ *p = '\0';
+ as_bad (_("bad .common segment %s"), input_line_pointer + 1);
+ *p = c;
+ input_line_pointer = p;
+ ignore_rest_of_line ();
+ return;
+ }
+}
+
+void
+set_machine (int number)
+{
+ machine = number;
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH, machine);
+
+ switch (machine)
+ {
+ case 0: processor_mask = PROCESSOR_V850; break;
+ case bfd_mach_v850e: processor_mask = PROCESSOR_V850E; break;
+ case bfd_mach_v850ea: processor_mask = PROCESSOR_V850EA; break;
+ }
+}
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"sdata", v850_sdata, 0},
+ {"tdata", v850_tdata, 0},
+ {"zdata", v850_zdata, 0},
+ {"sbss", v850_sbss, 0},
+ {"tbss", v850_tbss, 0},
+ {"zbss", v850_zbss, 0},
+ {"rosdata", v850_rosdata, 0},
+ {"rozdata", v850_rozdata, 0},
+ {"bss", v850_bss, 0},
+ {"offset", v850_offset, 0},
+ {"word", cons, 4},
+ {"zcomm", v850_comm, AREA_ZDA},
+ {"scomm", v850_comm, AREA_SDA},
+ {"tcomm", v850_comm, AREA_TDA},
+ {"v850", set_machine, 0},
+ {"call_table_data", v850_call_table_data, 0},
+ {"call_table_text", v850_call_table_text, 0},
+ {"v850e", set_machine, bfd_mach_v850e},
+ {"v850ea", set_machine, bfd_mach_v850ea},
+ { NULL, NULL, 0}
+};
+
+/* Opcode hash table. */
+static struct hash_control *v850_hash;
+
+/* This table is sorted. Suitable for searching by a binary search. */
+static const struct reg_name pre_defined_registers[] =
+{
+ { "ep", 30 }, /* ep - element ptr */
+ { "gp", 4 }, /* gp - global ptr */
+ { "hp", 2 }, /* hp - handler stack ptr */
+ { "lp", 31 }, /* lp - link ptr */
+ { "r0", 0 },
+ { "r1", 1 },
+ { "r10", 10 },
+ { "r11", 11 },
+ { "r12", 12 },
+ { "r13", 13 },
+ { "r14", 14 },
+ { "r15", 15 },
+ { "r16", 16 },
+ { "r17", 17 },
+ { "r18", 18 },
+ { "r19", 19 },
+ { "r2", 2 },
+ { "r20", 20 },
+ { "r21", 21 },
+ { "r22", 22 },
+ { "r23", 23 },
+ { "r24", 24 },
+ { "r25", 25 },
+ { "r26", 26 },
+ { "r27", 27 },
+ { "r28", 28 },
+ { "r29", 29 },
+ { "r3", 3 },
+ { "r30", 30 },
+ { "r31", 31 },
+ { "r4", 4 },
+ { "r5", 5 },
+ { "r6", 6 },
+ { "r7", 7 },
+ { "r8", 8 },
+ { "r9", 9 },
+ { "sp", 3 }, /* sp - stack ptr */
+ { "tp", 5 }, /* tp - text ptr */
+ { "zero", 0 },
+};
+#define REG_NAME_CNT (sizeof (pre_defined_registers) / sizeof (struct reg_name))
+
+
+static const struct reg_name system_registers[] =
+{
+ { "ctbp", 20 },
+ { "ctpc", 16 },
+ { "ctpsw", 17 },
+ { "dbpc", 18 },
+ { "dbpsw", 19 },
+ { "ecr", 4 },
+ { "eipc", 0 },
+ { "eipsw", 1 },
+ { "fepc", 2 },
+ { "fepsw", 3 },
+ { "psw", 5 },
+};
+#define SYSREG_NAME_CNT (sizeof (system_registers) / sizeof (struct reg_name))
+
+static const struct reg_name system_list_registers[] =
+{
+ {"PS", 5 },
+ {"SR", 0 + 1}
+};
+#define SYSREGLIST_NAME_CNT (sizeof (system_list_registers) / sizeof (struct reg_name))
+
+static const struct reg_name cc_names[] =
+{
+ { "c", 0x1 },
+ { "e", 0x2 },
+ { "ge", 0xe },
+ { "gt", 0xf },
+ { "h", 0xb },
+ { "l", 0x1 },
+ { "le", 0x7 },
+ { "lt", 0x6 },
+ { "n", 0x4 },
+ { "nc", 0x9 },
+ { "ne", 0xa },
+ { "nh", 0x3 },
+ { "nl", 0x9 },
+ { "ns", 0xc },
+ { "nv", 0x8 },
+ { "nz", 0xa },
+ { "p", 0xc },
+ { "s", 0x4 },
+ { "sa", 0xd },
+ { "t", 0x5 },
+ { "v", 0x0 },
+ { "z", 0x2 },
+};
+#define CC_NAME_CNT (sizeof (cc_names) / sizeof (struct reg_name))
+
+/* reg_name_search does a binary search of the given register table
+ to see if "name" is a valid regiter name. Returns the register
+ number from the array on success, or -1 on failure. */
+
+static int
+reg_name_search (regs, regcount, name, accept_numbers)
+ const struct reg_name * regs;
+ int regcount;
+ const char * name;
+ boolean accept_numbers;
+{
+ int middle, low, high;
+ int cmp;
+ symbolS * symbolP;
+
+ /* If the register name is a symbol, then evaluate it. */
+ if ((symbolP = symbol_find (name)) != NULL)
+ {
+ /* If the symbol is an alias for another name then use that.
+ If the symbol is an alias for a number, then return the number. */
+ if (symbolP->sy_value.X_op == O_symbol)
+ {
+ name = S_GET_NAME (symbolP->sy_value.X_add_symbol);
+ }
+ else if (accept_numbers)
+ {
+ int reg = S_GET_VALUE (symbolP);
+
+ if (reg >= 0 && reg <= 31)
+ return reg;
+ }
+
+ /* Otherwise drop through and try parsing name normally. */
+ }
+
+ low = 0;
+ high = regcount - 1;
+
+ do
+ {
+ middle = (low + high) / 2;
+ cmp = strcasecmp (name, regs[middle].name);
+ if (cmp < 0)
+ high = middle - 1;
+ else if (cmp > 0)
+ low = middle + 1;
+ else
+ return regs[middle].value;
+ }
+ while (low <= high);
+ return -1;
+}
+
+
+/* Summary of register_name().
+ *
+ * in: Input_line_pointer points to 1st char of operand.
+ *
+ * out: A expressionS.
+ * The operand may have been a register: in this case, X_op == O_register,
+ * X_add_number is set to the register number, and truth is returned.
+ * Input_line_pointer->(next non-blank) char after operand, or is in
+ * its original state.
+ */
+static boolean
+register_name (expressionP)
+ expressionS * expressionP;
+{
+ int reg_number;
+ char * name;
+ char * start;
+ char c;
+
+ /* Find the spelling of the operand */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+
+ reg_number = reg_name_search (pre_defined_registers, REG_NAME_CNT,
+ name, FALSE);
+
+ * input_line_pointer = c; /* put back the delimiting char */
+
+ /* look to see if it's in the register table */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ /* make the rest nice */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+
+ return true;
+ }
+ else
+ {
+ /* reset the line as if we had not done anything */
+ input_line_pointer = start;
+
+ return false;
+ }
+}
+
+/* Summary of system_register_name().
+ *
+ * in: Input_line_pointer points to 1st char of operand.
+ * expressionP points to an expression structure to be filled in.
+ * accept_numbers is true iff numerical register names may be used.
+ * accept_list_names is true iff the special names PS and SR may be
+ * accepted.
+ *
+ * out: A expressionS structure in expressionP.
+ * The operand may have been a register: in this case, X_op == O_register,
+ * X_add_number is set to the register number, and truth is returned.
+ * Input_line_pointer->(next non-blank) char after operand, or is in
+ * its original state.
+ */
+static boolean
+system_register_name (expressionP, accept_numbers, accept_list_names)
+ expressionS * expressionP;
+ boolean accept_numbers;
+ boolean accept_list_names;
+{
+ int reg_number;
+ char * name;
+ char * start;
+ char c;
+
+ /* Find the spelling of the operand */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (system_registers, SYSREG_NAME_CNT, name,
+ accept_numbers);
+
+ * input_line_pointer = c; /* put back the delimiting char */
+
+ if (reg_number < 0
+ && accept_numbers)
+ {
+ input_line_pointer = start; /* reset input_line pointer */
+
+ if (isdigit (* input_line_pointer))
+ {
+ reg_number = strtol (input_line_pointer, & input_line_pointer, 10);
+
+ /* Make sure that the register number is allowable. */
+ if ( reg_number < 0
+ || reg_number > 5
+ && reg_number < 16
+ || reg_number > 20
+ )
+ {
+ reg_number = -1;
+ }
+ }
+ else if (accept_list_names)
+ {
+ c = get_symbol_end ();
+ reg_number = reg_name_search (system_list_registers,
+ SYSREGLIST_NAME_CNT, name, FALSE);
+
+ * input_line_pointer = c; /* put back the delimiting char */
+ }
+ }
+
+ /* look to see if it's in the register table */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ /* make the rest nice */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+
+ return true;
+ }
+ else
+ {
+ /* reset the line as if we had not done anything */
+ input_line_pointer = start;
+
+ return false;
+ }
+}
+
+/* Summary of cc_name().
+ *
+ * in: Input_line_pointer points to 1st char of operand.
+ *
+ * out: A expressionS.
+ * The operand may have been a register: in this case, X_op == O_register,
+ * X_add_number is set to the register number, and truth is returned.
+ * Input_line_pointer->(next non-blank) char after operand, or is in
+ * its original state.
+ */
+static boolean
+cc_name (expressionP)
+ expressionS * expressionP;
+{
+ int reg_number;
+ char * name;
+ char * start;
+ char c;
+
+ /* Find the spelling of the operand */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (cc_names, CC_NAME_CNT, name, FALSE);
+
+ * input_line_pointer = c; /* put back the delimiting char */
+
+ /* look to see if it's in the register table */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_constant;
+ expressionP->X_add_number = reg_number;
+
+ /* make the rest nice */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+
+ return true;
+ }
+ else
+ {
+ /* reset the line as if we had not done anything */
+ input_line_pointer = start;
+
+ return false;
+ }
+}
+
+static void
+skip_white_space (void)
+{
+ while ( * input_line_pointer == ' '
+ || * input_line_pointer == '\t')
+ ++ input_line_pointer;
+}
+
+/* Summary of parse_register_list ().
+ *
+ * in: Input_line_pointer points to 1st char of a list of registers.
+ * insn is the partially constructed instruction.
+ * operand is the operand being inserted.
+ *
+ * out: NULL if the parse completed successfully, otherwise a
+ * pointer to an error message is returned. If the parse
+ * completes the correct bit fields in the instruction
+ * will be filled in.
+ *
+ * Parses register lists with the syntax:
+ *
+ * { rX }
+ * { rX, rY }
+ * { rX - rY }
+ * { rX - rY, rZ }
+ * etc
+ *
+ * and also parses constant epxressions whoes bits indicate the
+ * registers in the lists. The LSB in the expression refers to
+ * the lowest numbered permissable register in the register list,
+ * and so on upwards. System registers are considered to be very
+ * high numbers.
+ *
+ */
+static char *
+parse_register_list
+(
+ unsigned long * insn,
+ const struct v850_operand * operand
+)
+{
+ static int type1_regs[ 32 ] = { 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 29, 28, 23, 22, 21, 20, 27, 26, 25, 24 };
+ static int type2_regs[ 32 ] = { 19, 18, 17, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 29, 28, 23, 22, 21, 20, 27, 26, 25, 24 };
+ static int type3_regs[ 32 ] = { 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 15, 13, 12, 7, 6, 5, 4, 11, 10, 9, 8 };
+ int * regs;
+ expressionS exp;
+
+
+ /* Select a register array to parse. */
+ switch (operand->shift)
+ {
+ case 0xffe00001: regs = type1_regs; break;
+ case 0xfff8000f: regs = type2_regs; break;
+ case 0xfff8001f: regs = type3_regs; break;
+ default:
+ as_bad (_("unknown operand shift: %x\n"), operand->shift);
+ return _("internal failure in parse_register_list");
+ }
+
+ skip_white_space ();
+
+ /* If the expression starts with a curly brace it is a register list.
+ Otherwise it is a constant expression, whoes bits indicate which
+ registers are to be included in the list. */
+
+ if (* input_line_pointer != '{')
+ {
+ int bits;
+ int reg;
+ int i;
+
+ expression (& exp);
+
+ if (exp.X_op != O_constant)
+ return _("constant expression or register list expected");
+
+ if (regs == type1_regs)
+ {
+ if (exp.X_add_number & 0xFFFFF000)
+ return _("high bits set in register list expression");
+
+ for (reg = 20; reg < 32; reg ++)
+ if (exp.X_add_number & (1 << (reg - 20)))
+ {
+ for (i = 0; i < 32; i++)
+ if (regs[i] == reg)
+ * insn |= (1 << i);
+ }
+ }
+ else if (regs == type2_regs)
+ {
+ if (exp.X_add_number & 0xFFFE0000)
+ return _("high bits set in register list expression");
+
+ for (reg = 1; reg < 16; reg ++)
+ if (exp.X_add_number & (1 << (reg - 1)))
+ {
+ for (i = 0; i < 32; i++)
+ if (regs[i] == reg)
+ * insn |= (1 << i);
+ }
+
+ if (exp.X_add_number & (1 << 15))
+ * insn |= (1 << 3);
+
+ if (exp.X_add_number & (1 << 16))
+ * insn |= (1 << 19);
+ }
+ else /* regs == type3_regs */
+ {
+ if (exp.X_add_number & 0xFFFE0000)
+ return _("high bits set in register list expression");
+
+ for (reg = 16; reg < 32; reg ++)
+ if (exp.X_add_number & (1 << (reg - 16)))
+ {
+ for (i = 0; i < 32; i++)
+ if (regs[i] == reg)
+ * insn |= (1 << i);
+ }
+
+ if (exp.X_add_number & (1 << 16))
+ * insn |= (1 << 19);
+ }
+
+ return NULL;
+ }
+
+ input_line_pointer ++;
+
+ /* Parse the register list until a terminator (closing curly brace or
+ new-line) is found. */
+ for (;;)
+ {
+ if (register_name (& exp))
+ {
+ int i;
+
+ /* Locate the given register in the list, and if it is there,
+ insert the corresponding bit into the instruction. */
+ for (i = 0; i < 32; i++)
+ {
+ if (regs[ i ] == exp.X_add_number)
+ {
+ * insn |= (1 << i);
+ break;
+ }
+ }
+
+ if (i == 32)
+ {
+ return _("illegal register included in list");
+ }
+ }
+ else if (system_register_name (& exp, true, true))
+ {
+ if (regs == type1_regs)
+ {
+ return _("system registers cannot be included in list");
+ }
+ else if (exp.X_add_number == 5)
+ {
+ if (regs == type2_regs)
+ return _("PSW cannot be included in list");
+ else
+ * insn |= 0x8;
+ }
+ else if (exp.X_add_number < 4)
+ * insn |= 0x80000;
+ else
+ return _("High value system registers cannot be included in list");
+ }
+ else if (* input_line_pointer == '}')
+ {
+ input_line_pointer ++;
+ break;
+ }
+ else if (* input_line_pointer == ',')
+ {
+ input_line_pointer ++;
+ continue;
+ }
+ else if (* input_line_pointer == '-')
+ {
+ /* We have encountered a range of registers: rX - rY */
+ int j;
+ expressionS exp2;
+
+ /* Skip the dash. */
+ ++ input_line_pointer;
+
+ /* Get the second register in the range. */
+ if (! register_name (& exp2))
+ {
+ return _("second register should follow dash in register list");
+ exp2.X_add_number = exp.X_add_number;
+ }
+
+ /* Add the rest of the registers in the range. */
+ for (j = exp.X_add_number + 1; j <= exp2.X_add_number; j++)
+ {
+ int i;
+
+ /* Locate the given register in the list, and if it is there,
+ insert the corresponding bit into the instruction. */
+ for (i = 0; i < 32; i++)
+ {
+ if (regs[ i ] == j)
+ {
+ * insn |= (1 << i);
+ break;
+ }
+ }
+
+ if (i == 32)
+ return _("illegal register included in list");
+ }
+ }
+ else
+ {
+ break;
+ }
+
+ skip_white_space ();
+ }
+
+ return NULL;
+}
+
+CONST char * md_shortopts = "m:";
+
+struct option md_longopts[] =
+{
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof md_longopts;
+
+
+void
+md_show_usage (stream)
+ FILE * stream;
+{
+ fprintf (stream, _(" V850 options:\n"));
+ fprintf (stream, _(" -mwarn-signed-overflow Warn if signed immediate values overflow\n"));
+ fprintf (stream, _(" -mwarn-unsigned-overflow Warn if unsigned immediate values overflow\n"));
+ fprintf (stream, _(" -mv850 The code is targeted at the v850\n"));
+ fprintf (stream, _(" -mv850e The code is targeted at the v850e\n"));
+ fprintf (stream, _(" -mv850ea The code is targeted at the v850ea\n"));
+ fprintf (stream, _(" -mv850any The code is generic, despite any processor specific instructions\n"));
+}
+
+int
+md_parse_option (c, arg)
+ int c;
+ char * arg;
+{
+ if (c != 'm')
+ {
+ /* xgettext:c-format */
+ fprintf (stderr, _("unknown command line option: -%c%s\n"), c, arg);
+ return 0;
+ }
+
+ if (strcmp (arg, "warn-signed-overflow") == 0)
+ {
+ warn_signed_overflows = TRUE;
+ }
+ else if (strcmp (arg, "warn-unsigned-overflow") == 0)
+ {
+ warn_unsigned_overflows = TRUE;
+ }
+ else if (strcmp (arg, "v850") == 0)
+ {
+ machine = 0;
+ processor_mask = PROCESSOR_V850;
+ }
+ else if (strcmp (arg, "v850e") == 0)
+ {
+ machine = bfd_mach_v850e;
+ processor_mask = PROCESSOR_V850E;
+ }
+ else if (strcmp (arg, "v850ea") == 0)
+ {
+ machine = bfd_mach_v850ea;
+ processor_mask = PROCESSOR_V850EA;
+ }
+ else if (strcmp (arg, "v850any") == 0)
+ {
+ machine = 0; /* Tell the world that this is for any v850 chip. */
+ processor_mask = PROCESSOR_V850EA; /* But support instructions for the extended versions. */
+ }
+ else
+ {
+ /* xgettext:c-format */
+ fprintf (stderr, _("unknown command line option: -%c%s\n"), c, arg);
+ return 0;
+ }
+
+ return 1;
+}
+
+symbolS *
+md_undefined_symbol (name)
+ char * name;
+{
+ return 0;
+}
+
+char *
+md_atof (type, litp, sizep)
+ int type;
+ char * litp;
+ int * sizep;
+{
+ int prec;
+ LITTLENUM_TYPE words[4];
+ char * t;
+ int i;
+
+ switch (type)
+ {
+ case 'f':
+ prec = 2;
+ break;
+
+ case 'd':
+ prec = 4;
+ break;
+
+ default:
+ *sizep = 0;
+ return _("bad call to md_atof");
+ }
+
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+
+ *sizep = prec * 2;
+
+ for (i = prec - 1; i >= 0; i--)
+ {
+ md_number_to_chars (litp, (valueT) words[i], 2);
+ litp += 2;
+ }
+
+ return NULL;
+}
+
+
+/* Very gross. */
+void
+md_convert_frag (abfd, sec, fragP)
+ bfd * abfd;
+ asection * sec;
+ fragS * fragP;
+{
+ subseg_change (sec, 0);
+
+ /* In range conditional or unconditional branch. */
+ if (fragP->fr_subtype == 0 || fragP->fr_subtype == 2)
+ {
+ fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_UNUSED + (int)fragP->fr_opcode);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 2;
+ }
+ /* Out of range conditional branch. Emit a branch around a jump. */
+ else if (fragP->fr_subtype == 1)
+ {
+ unsigned char *buffer =
+ (unsigned char *) (fragP->fr_fix + fragP->fr_literal);
+
+ /* Reverse the condition of the first branch. */
+ buffer[0] ^= 0x08;
+ /* Mask off all the displacement bits. */
+ buffer[0] &= 0x8f;
+ buffer[1] &= 0x07;
+ /* Now set the displacement bits so that we branch
+ around the unconditional branch. */
+ buffer[0] |= 0x30;
+
+ /* Now create the unconditional branch + fixup to the final
+ target. */
+ md_number_to_chars (buffer + 2, 0x00000780, 4);
+ fix_new (fragP, fragP->fr_fix + 2, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_UNUSED +
+ (int) fragP->fr_opcode + 1);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 6;
+ }
+ /* Out of range unconditional branch. Emit a jump. */
+ else if (fragP->fr_subtype == 3)
+ {
+ md_number_to_chars (fragP->fr_fix + fragP->fr_literal, 0x00000780, 4);
+ fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_UNUSED +
+ (int) fragP->fr_opcode + 1);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 4;
+ }
+ else
+ abort ();
+}
+
+valueT
+md_section_align (seg, addr)
+ asection * seg;
+ valueT addr;
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+void
+md_begin ()
+{
+ char * prev_name = "";
+ register const struct v850_opcode * op;
+ flagword applicable;
+
+ if (strncmp (TARGET_CPU, "v850ea", 6) == 0)
+ {
+ if (machine == -1)
+ machine = bfd_mach_v850ea;
+
+ if (processor_mask == -1)
+ processor_mask = PROCESSOR_V850EA;
+ }
+ else if (strncmp (TARGET_CPU, "v850e", 5) == 0)
+ {
+ if (machine == -1)
+ machine = bfd_mach_v850e;
+
+ if (processor_mask == -1)
+ processor_mask = PROCESSOR_V850E;
+ }
+ else
+ if (strncmp (TARGET_CPU, "v850", 4) == 0)
+ {
+ if (machine == -1)
+ machine = 0;
+
+ if (processor_mask == -1)
+ processor_mask = PROCESSOR_V850;
+ }
+ else
+ /* xgettext:c-format */
+ as_bad (_("Unable to determine default target processor from string: %s"),
+ TARGET_CPU);
+
+ v850_hash = hash_new();
+
+ /* Insert unique names into hash table. The V850 instruction set
+ has many identical opcode names that have different opcodes based
+ on the operands. This hash table then provides a quick index to
+ the first opcode with a particular name in the opcode table. */
+
+ op = v850_opcodes;
+ while (op->name)
+ {
+ if (strcmp (prev_name, op->name))
+ {
+ prev_name = (char *) op->name;
+ hash_insert (v850_hash, op->name, (char *) op);
+ }
+ op++;
+ }
+
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH, machine);
+
+ applicable = bfd_applicable_section_flags (stdoutput);
+
+ call_table_data_section = subseg_new (".call_table_data", 0);
+ bfd_set_section_flags (stdoutput, call_table_data_section,
+ applicable & (SEC_ALLOC | SEC_LOAD | SEC_RELOC
+ | SEC_DATA | SEC_HAS_CONTENTS));
+
+ call_table_text_section = subseg_new (".call_table_text", 0);
+ bfd_set_section_flags (stdoutput, call_table_text_section,
+ applicable & (SEC_ALLOC | SEC_LOAD | SEC_READONLY
+ | SEC_CODE));
+
+ /* Restore text section as the current default. */
+ subseg_set (text_section, 0);
+}
+
+
+static bfd_reloc_code_real_type
+handle_ctoff (const struct v850_operand * operand)
+{
+ if (operand == NULL)
+ return BFD_RELOC_V850_CALLT_16_16_OFFSET;
+
+ if ( operand->bits != 6
+ || operand->shift != 0)
+ {
+ as_bad (_("ctoff() relocation used on an instruction which does not support it"));
+ return BFD_RELOC_64; /* Used to indicate an error condition. */
+ }
+
+ return BFD_RELOC_V850_CALLT_6_7_OFFSET;
+}
+
+static bfd_reloc_code_real_type
+handle_sdaoff (const struct v850_operand * operand)
+{
+ if (operand == NULL) return BFD_RELOC_V850_SDA_16_16_OFFSET;
+ if (operand->bits == 15 && operand->shift == 17) return BFD_RELOC_V850_SDA_15_16_OFFSET;
+ if (operand->bits == -1) return BFD_RELOC_V850_SDA_16_16_SPLIT_OFFSET;
+
+ if ( operand->bits != 16
+ || operand->shift != 16)
+ {
+ as_bad (_("sdaoff() relocation used on an instruction which does not support it"));
+ return BFD_RELOC_64; /* Used to indicate an error condition. */
+ }
+
+ return BFD_RELOC_V850_SDA_16_16_OFFSET;
+}
+
+static bfd_reloc_code_real_type
+handle_zdaoff (const struct v850_operand * operand)
+{
+ if (operand == NULL) return BFD_RELOC_V850_ZDA_16_16_OFFSET;
+ if (operand->bits == 15 && operand->shift == 17) return BFD_RELOC_V850_ZDA_15_16_OFFSET;
+ if (operand->bits == -1) return BFD_RELOC_V850_ZDA_16_16_SPLIT_OFFSET;
+
+ if ( operand->bits != 16
+ || operand->shift != 16)
+ {
+ as_bad (_("zdaoff() relocation used on an instruction which does not support it"));
+ return BFD_RELOC_64; /* Used to indicate an error condition. */
+ }
+
+ return BFD_RELOC_V850_ZDA_16_16_OFFSET;
+}
+
+static bfd_reloc_code_real_type
+handle_tdaoff (const struct v850_operand * operand)
+{
+ if (operand == NULL) return BFD_RELOC_V850_TDA_7_7_OFFSET; /* data item, not an instruction. */
+ if (operand->bits == 6 && operand->shift == 1) return BFD_RELOC_V850_TDA_6_8_OFFSET; /* sld.w/sst.w, operand: D8_6 */
+ if (operand->bits == 4 && operand->insert != NULL) return BFD_RELOC_V850_TDA_4_5_OFFSET; /* sld.hu, operand: D5-4 */
+ if (operand->bits == 4 && operand->insert == NULL) return BFD_RELOC_V850_TDA_4_4_OFFSET; /* sld.bu, operand: D4 */
+ if (operand->bits == 16 && operand->shift == 16) return BFD_RELOC_V850_TDA_16_16_OFFSET; /* set1 & chums, operands: D16 */
+
+ if (operand->bits != 7)
+ {
+ as_bad (_("tdaoff() relocation used on an instruction which does not support it"));
+ return BFD_RELOC_64; /* Used to indicate an error condition. */
+ }
+
+ return operand->insert != NULL
+ ? BFD_RELOC_V850_TDA_7_8_OFFSET /* sld.h/sst.h, operand: D8_7 */
+ : BFD_RELOC_V850_TDA_7_7_OFFSET; /* sld.b/sst.b, opreand: D7 */
+}
+
+/* Warning: The code in this function relies upon the definitions
+ in the v850_operands[] array (defined in opcodes/v850-opc.c)
+ matching the hard coded values contained herein. */
+
+static bfd_reloc_code_real_type
+v850_reloc_prefix (const struct v850_operand * operand)
+{
+ boolean paren_skipped = false;
+
+
+ /* Skip leading opening parenthesis. */
+ if (* input_line_pointer == '(')
+ {
+ ++ input_line_pointer;
+ paren_skipped = true;
+ }
+
+#define CHECK_(name, reloc) \
+ if (strncmp (input_line_pointer, name##"(", strlen (name) + 1) == 0) \
+ { \
+ input_line_pointer += strlen (name); \
+ return reloc; \
+ }
+
+ CHECK_ ("hi0", BFD_RELOC_HI16);
+ CHECK_ ("hi", BFD_RELOC_HI16_S);
+ CHECK_ ("lo", BFD_RELOC_LO16);
+ CHECK_ ("sdaoff", handle_sdaoff (operand));
+ CHECK_ ("zdaoff", handle_zdaoff (operand));
+ CHECK_ ("tdaoff", handle_tdaoff (operand));
+ CHECK_ ("hilo", BFD_RELOC_32);
+ CHECK_ ("ctoff", handle_ctoff (operand));
+
+ /* Restore skipped parenthesis. */
+ if (paren_skipped)
+ -- input_line_pointer;
+
+ return BFD_RELOC_UNUSED;
+}
+
+/* Insert an operand value into an instruction. */
+
+static unsigned long
+v850_insert_operand (insn, operand, val, file, line, str)
+ unsigned long insn;
+ const struct v850_operand * operand;
+ offsetT val;
+ char * file;
+ unsigned int line;
+ char * str;
+{
+ if (operand->insert)
+ {
+ const char * message = NULL;
+
+ insn = operand->insert (insn, val, & message);
+ if (message != NULL)
+ {
+ if ((operand->flags & V850_OPERAND_SIGNED)
+ && ! warn_signed_overflows
+ && strstr (message, "out of range") != NULL)
+ {
+ /* skip warning... */
+ }
+ else if ((operand->flags & V850_OPERAND_SIGNED) == 0
+ && ! warn_unsigned_overflows
+ && strstr (message, "out of range") != NULL)
+ {
+ /* skip warning... */
+ }
+ else if (str)
+ {
+ if (file == (char *) NULL)
+ as_warn ("%s: %s", str, message);
+ else
+ as_warn_where (file, line, "%s: %s", str, message);
+ }
+ else
+ {
+ if (file == (char *) NULL)
+ as_warn (message);
+ else
+ as_warn_where (file, line, message);
+ }
+ }
+ }
+ else
+ {
+ if (operand->bits != 32)
+ {
+ long min, max;
+ offsetT test;
+
+ if ((operand->flags & V850_OPERAND_SIGNED) != 0)
+ {
+ if (! warn_signed_overflows)
+ max = (1 << operand->bits) - 1;
+ else
+ max = (1 << (operand->bits - 1)) - 1;
+
+ min = - (1 << (operand->bits - 1));
+ }
+ else
+ {
+ max = (1 << operand->bits) - 1;
+
+ if (! warn_unsigned_overflows)
+ min = - (1 << (operand->bits - 1));
+ else
+ min = 0;
+ }
+
+ if (val < (offsetT) min || val > (offsetT) max)
+ {
+ /* xgettext:c-format */
+ const char * err = _("operand out of range (%s not between %ld and %ld)");
+ char buf[100];
+
+ /* Restore min and mix to expected values for decimal ranges. */
+ if ((operand->flags & V850_OPERAND_SIGNED)
+ && ! warn_signed_overflows)
+ max = (1 << (operand->bits - 1)) - 1;
+
+ if (! (operand->flags & V850_OPERAND_SIGNED)
+ && ! warn_unsigned_overflows)
+ min = 0;
+
+ if (str)
+ {
+ sprintf (buf, "%s: ", str);
+
+ sprint_value (buf + strlen (buf), val);
+ }
+ else
+ sprint_value (buf, val);
+
+ if (file == (char *) NULL)
+ as_warn (err, buf, min, max);
+ else
+ as_warn_where (file, line, err, buf, min, max);
+ }
+ }
+
+ insn |= (((long) val & ((1 << operand->bits) - 1)) << operand->shift);
+ }
+
+ return insn;
+}
+
+
+static char copy_of_instruction [128];
+
+void
+md_assemble (str)
+ char * str;
+{
+ char * s;
+ char * start_of_operands;
+ struct v850_opcode * opcode;
+ struct v850_opcode * next_opcode;
+ const unsigned char * opindex_ptr;
+ int next_opindex;
+ int relaxable;
+ unsigned long insn;
+ unsigned long insn_size;
+ char * f;
+ int i;
+ int match;
+ boolean extra_data_after_insn = false;
+ unsigned extra_data_len;
+ unsigned long extra_data;
+ char * saved_input_line_pointer;
+
+
+ strncpy (copy_of_instruction, str, sizeof (copy_of_instruction) - 1);
+
+ /* Get the opcode. */
+ for (s = str; *s != '\0' && ! isspace (*s); s++)
+ continue;
+
+ if (*s != '\0')
+ *s++ = '\0';
+
+ /* find the first opcode with the proper name */
+ opcode = (struct v850_opcode *) hash_find (v850_hash, str);
+ if (opcode == NULL)
+ {
+ /* xgettext:c-format */
+ as_bad (_("Unrecognized opcode: `%s'"), str);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ str = s;
+ while (isspace (* str))
+ ++ str;
+
+ start_of_operands = str;
+
+ saved_input_line_pointer = input_line_pointer;
+
+ for (;;)
+ {
+ const char * errmsg = NULL;
+
+ match = 0;
+
+ if ((opcode->processors & processor_mask) == 0)
+ {
+ errmsg = _("Target processor does not support this instruction.");
+ goto error;
+ }
+
+ relaxable = 0;
+ fc = 0;
+ next_opindex = 0;
+ insn = opcode->opcode;
+ extra_data_after_insn = false;
+
+ input_line_pointer = str = start_of_operands;
+
+ for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr ++)
+ {
+ const struct v850_operand * operand;
+ char * hold;
+ expressionS ex;
+ bfd_reloc_code_real_type reloc;
+
+ if (next_opindex == 0)
+ {
+ operand = & v850_operands[ * opindex_ptr ];
+ }
+ else
+ {
+ operand = & v850_operands[ next_opindex ];
+ next_opindex = 0;
+ }
+
+ errmsg = NULL;
+
+ while (*str == ' ' || *str == ',' || *str == '[' || *str == ']')
+ ++ str;
+
+ if (operand->flags & V850_OPERAND_RELAX)
+ relaxable = 1;
+
+ /* Gather the operand. */
+ hold = input_line_pointer;
+ input_line_pointer = str;
+
+ /* lo(), hi(), hi0(), etc... */
+ if ((reloc = v850_reloc_prefix (operand)) != BFD_RELOC_UNUSED)
+ {
+ /* This is a fake reloc, used to indicate an error condition. */
+ if (reloc == BFD_RELOC_64)
+ {
+ match = 1;
+ goto error;
+ }
+
+ expression (& ex);
+
+ if (ex.X_op == O_constant)
+ {
+ switch (reloc)
+ {
+ case BFD_RELOC_V850_ZDA_16_16_OFFSET:
+ /* To cope with "not1 7, zdaoff(0xfffff006)[r0]"
+ and the like. */
+ /* Fall through. */
+
+ case BFD_RELOC_LO16:
+ {
+ /* Truncate, then sign extend the value. */
+ ex.X_add_number = SEXT16 (ex.X_add_number);
+ break;
+ }
+
+ case BFD_RELOC_HI16:
+ {
+ /* Truncate, then sign extend the value. */
+ ex.X_add_number = SEXT16 (ex.X_add_number >> 16);
+ break;
+ }
+
+ case BFD_RELOC_HI16_S:
+ {
+ /* Truncate, then sign extend the value. */
+ int temp = (ex.X_add_number >> 16) & 0xffff;
+
+ temp += (ex.X_add_number >> 15) & 1;
+
+ ex.X_add_number = SEXT16 (temp);
+ break;
+ }
+
+ case BFD_RELOC_32:
+ if ((operand->flags & V850E_IMMEDIATE32) == 0)
+ {
+ errmsg = _("immediate operand is too large");
+ goto error;
+ }
+
+ extra_data_after_insn = true;
+ extra_data_len = 4;
+ extra_data = ex.X_add_number;
+ ex.X_add_number = 0;
+ break;
+
+ default:
+ fprintf (stderr, "reloc: %d\n", reloc);
+ as_bad (_("AAARG -> unhandled constant reloc"));
+ break;
+ }
+
+ if (fc > MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ fixups[ fc ].exp = ex;
+ fixups[ fc ].opindex = * opindex_ptr;
+ fixups[ fc ].reloc = reloc;
+ fc++;
+ }
+ else
+ {
+ if (reloc == BFD_RELOC_32)
+ {
+ if ((operand->flags & V850E_IMMEDIATE32) == 0)
+ {
+ errmsg = _("immediate operand is too large");
+ goto error;
+ }
+
+ extra_data_after_insn = true;
+ extra_data_len = 4;
+ extra_data = ex.X_add_number;
+ }
+
+ if (fc > MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ fixups[ fc ].exp = ex;
+ fixups[ fc ].opindex = * opindex_ptr;
+ fixups[ fc ].reloc = reloc;
+ fc++;
+ }
+ }
+ else
+ {
+ errmsg = NULL;
+
+ if ((operand->flags & V850_OPERAND_REG) != 0)
+ {
+ if (!register_name (& ex))
+ {
+ errmsg = _("invalid register name");
+ }
+ else if ((operand->flags & V850_NOT_R0)
+ && ex.X_add_number == 0)
+ {
+ errmsg = _("register r0 cannot be used here");
+
+ /* Force an error message to be generated by
+ skipping over any following potential matches
+ for this opcode. */
+ opcode += 3;
+ }
+ }
+ else if ((operand->flags & V850_OPERAND_SRG) != 0)
+ {
+ if (!system_register_name (& ex, true, false))
+ {
+ errmsg = _("invalid system register name");
+ }
+ }
+ else if ((operand->flags & V850_OPERAND_EP) != 0)
+ {
+ char * start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcmp (start, "ep") != 0 && strcmp (start, "r30") != 0)
+ {
+ /* Put things back the way we found them. */
+ *input_line_pointer = c;
+ input_line_pointer = start;
+ errmsg = _("expected EP register");
+ goto error;
+ }
+
+ *input_line_pointer = c;
+ str = input_line_pointer;
+ input_line_pointer = hold;
+
+ while (*str == ' ' || *str == ',' || *str == '[' || *str == ']')
+ ++ str;
+ continue;
+ }
+ else if ((operand->flags & V850_OPERAND_CC) != 0)
+ {
+ if (!cc_name (& ex))
+ {
+ errmsg = _("invalid condition code name");
+ }
+ }
+ else if (operand->flags & V850E_PUSH_POP)
+ {
+ errmsg = parse_register_list (& insn, operand);
+
+ /* The parse_register_list() function has already done
+ everything, so fake a dummy expression. */
+ ex.X_op = O_constant;
+ ex.X_add_number = 0;
+ }
+ else if (operand->flags & V850E_IMMEDIATE16)
+ {
+ expression (& ex);
+
+ if (ex.X_op != O_constant)
+ errmsg = _("constant expression expected");
+ else if (ex.X_add_number & 0xffff0000)
+ {
+ if (ex.X_add_number & 0xffff)
+ errmsg = _("constant too big to fit into instruction");
+ else if ((insn & 0x001fffc0) == 0x00130780)
+ ex.X_add_number >>= 16;
+ else
+ errmsg = _("constant too big to fit into instruction");
+ }
+
+ extra_data_after_insn = true;
+ extra_data_len = 2;
+ extra_data = ex.X_add_number;
+ ex.X_add_number = 0;
+ }
+ else if (operand->flags & V850E_IMMEDIATE32)
+ {
+ expression (& ex);
+
+ if (ex.X_op != O_constant)
+ errmsg = _("constant expression expected");
+
+ extra_data_after_insn = true;
+ extra_data_len = 4;
+ extra_data = ex.X_add_number;
+ ex.X_add_number = 0;
+ }
+ else if (register_name (& ex)
+ && (operand->flags & V850_OPERAND_REG) == 0)
+ {
+ char c;
+ int exists = 0;
+
+ /* It is possible that an alias has been defined that
+ matches a register name. For example the code may
+ include a ".set ZERO, 0" directive, which matches
+ the register name "zero". Attempt to reparse the
+ field as an expression, and only complain if we
+ cannot generate a constant. */
+
+ input_line_pointer = str;
+
+ c = get_symbol_end ();
+
+ if (symbol_find (str) != NULL)
+ exists = 1;
+
+ * input_line_pointer = c;
+ input_line_pointer = str;
+
+ expression (& ex);
+
+ if (ex.X_op != O_constant)
+ {
+ /* If this register is actually occuring too early on
+ the parsing of the instruction, (because another
+ field is missing) then report this. */
+ if (opindex_ptr[1] != 0
+ && (v850_operands [opindex_ptr [1]].flags & V850_OPERAND_REG))
+ errmsg = _("syntax error: value is missing before the register name");
+ else
+ errmsg = _("syntax error: register not expected");
+
+ /* If we created a symbol in the process of this test then
+ delete it now, so that it will not be output with the real
+ symbols... */
+ if (exists == 0
+ && ex.X_op == O_symbol)
+ symbol_remove (ex.X_add_symbol,
+ & symbol_rootP, & symbol_lastP);
+ }
+ }
+ else if (system_register_name (& ex, false, false)
+ && (operand->flags & V850_OPERAND_SRG) == 0)
+ {
+ errmsg = _("syntax error: system register not expected");
+ }
+ else if (cc_name (&ex)
+ && (operand->flags & V850_OPERAND_CC) == 0)
+ {
+ errmsg = _("syntax error: condition code not expected");
+ }
+ else
+ {
+ expression (& ex);
+ /* Special case:
+ If we are assembling a MOV instruction (or a CALLT.... :-)
+ and the immediate value does not fit into the bits
+ available then create a fake error so that the next MOV
+ instruction will be selected. This one has a 32 bit
+ immediate field. */
+
+ if (((insn & 0x07e0) == 0x0200)
+ && ex.X_op == O_constant
+ && (ex.X_add_number < (- (1 << (operand->bits - 1)))
+ || ex.X_add_number > ((1 << operand->bits) - 1)))
+ errmsg = _("immediate operand is too large");
+ }
+
+ if (errmsg)
+ goto error;
+
+/* fprintf (stderr, " insn: %x, operand %d, op: %d, add_number: %d\n",
+ insn, opindex_ptr - opcode->operands, ex.X_op, ex.X_add_number); */
+
+ switch (ex.X_op)
+ {
+ case O_illegal:
+ errmsg = _("illegal operand");
+ goto error;
+ case O_absent:
+ errmsg = _("missing operand");
+ goto error;
+ case O_register:
+ if ((operand->flags & (V850_OPERAND_REG | V850_OPERAND_SRG)) == 0)
+ {
+ errmsg = _("invalid operand");
+ goto error;
+ }
+ insn = v850_insert_operand (insn, operand, ex.X_add_number,
+ (char *) NULL, 0,
+ copy_of_instruction);
+ break;
+
+ case O_constant:
+ insn = v850_insert_operand (insn, operand, ex.X_add_number,
+ (char *) NULL, 0,
+ copy_of_instruction);
+ break;
+
+ default:
+ /* We need to generate a fixup for this expression. */
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ fixups[ fc ].exp = ex;
+ fixups[ fc ].opindex = * opindex_ptr;
+ fixups[ fc ].reloc = BFD_RELOC_UNUSED;
+ ++fc;
+ break;
+ }
+ }
+
+ str = input_line_pointer;
+ input_line_pointer = hold;
+
+ while (*str == ' ' || *str == ',' || *str == '[' || *str == ']'
+ || *str == ')')
+ ++str;
+ }
+ match = 1;
+
+ error:
+ if (match == 0)
+ {
+ next_opcode = opcode + 1;
+ if (next_opcode->name != NULL
+ && strcmp (next_opcode->name, opcode->name) == 0)
+ {
+ opcode = next_opcode;
+
+ /* Skip versions that are not supported by the target
+ processor. */
+ if ((opcode->processors & processor_mask) == 0)
+ goto error;
+
+ continue;
+ }
+
+ as_bad ("%s: %s", copy_of_instruction, errmsg);
+
+ if (* input_line_pointer == ']')
+ ++ input_line_pointer;
+
+ ignore_rest_of_line ();
+ input_line_pointer = saved_input_line_pointer;
+ return;
+ }
+ break;
+ }
+
+ while (isspace (*str))
+ ++str;
+
+ if (*str != '\0')
+ /* xgettext:c-format */
+ as_bad (_("junk at end of line: `%s'"), str);
+
+ input_line_pointer = str;
+
+ /* Write out the instruction. */
+
+ if (relaxable && fc > 0)
+ {
+ insn_size = 2;
+ fc = 0;
+
+ if (!strcmp (opcode->name, "br"))
+ {
+ f = frag_var (rs_machine_dependent, 4, 2, 2,
+ fixups[0].exp.X_add_symbol,
+ fixups[0].exp.X_add_number,
+ (char *)fixups[0].opindex);
+ md_number_to_chars (f, insn, insn_size);
+ md_number_to_chars (f + 2, 0, 2);
+ }
+ else
+ {
+ f = frag_var (rs_machine_dependent, 6, 4, 0,
+ fixups[0].exp.X_add_symbol,
+ fixups[0].exp.X_add_number,
+ (char *)fixups[0].opindex);
+ md_number_to_chars (f, insn, insn_size);
+ md_number_to_chars (f + 2, 0, 4);
+ }
+ }
+ else
+ {
+ /* Four byte insns have an opcode with the two high bits on. */
+ if ((insn & 0x0600) == 0x0600)
+ insn_size = 4;
+ else
+ insn_size = 2;
+
+ /* Special case: 32 bit MOV */
+ if ((insn & 0xffe0) == 0x0620)
+ insn_size = 2;
+
+ f = frag_more (insn_size);
+
+ md_number_to_chars (f, insn, insn_size);
+
+ if (extra_data_after_insn)
+ {
+ f = frag_more (extra_data_len);
+
+ md_number_to_chars (f, extra_data, extra_data_len);
+
+ extra_data_after_insn = false;
+ }
+ }
+
+ /* Create any fixups. At this point we do not use a
+ bfd_reloc_code_real_type, but instead just use the
+ BFD_RELOC_UNUSED plus the operand index. This lets us easily
+ handle fixups for any operand type, although that is admittedly
+ not a very exciting feature. We pick a BFD reloc type in
+ md_apply_fix. */
+ for (i = 0; i < fc; i++)
+ {
+ const struct v850_operand * operand;
+ bfd_reloc_code_real_type reloc;
+
+ operand = & v850_operands[ fixups[i].opindex ];
+
+ reloc = fixups[i].reloc;
+
+ if (reloc != BFD_RELOC_UNUSED)
+ {
+ reloc_howto_type * reloc_howto = bfd_reloc_type_lookup (stdoutput,
+ reloc);
+ int size;
+ int address;
+ fixS * fixP;
+
+ if (!reloc_howto)
+ abort();
+
+ size = bfd_get_reloc_size (reloc_howto);
+
+ /* XXX This will abort on an R_V850_8 reloc -
+ is this reloc actually used ? */
+ if (size != 2 && size != 4)
+ abort ();
+
+ address = (f - frag_now->fr_literal) + insn_size - size;
+
+ if (reloc == BFD_RELOC_32)
+ {
+ address += 2;
+ }
+
+ fixP = fix_new_exp (frag_now, address, size,
+ & fixups[i].exp,
+ reloc_howto->pc_relative,
+ reloc);
+
+ switch (reloc)
+ {
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_HI16:
+ case BFD_RELOC_HI16_S:
+ fixP->fx_no_overflow = 1;
+ break;
+ }
+ }
+ else
+ {
+ fix_new_exp (
+ frag_now,
+ f - frag_now->fr_literal, 4,
+ & fixups[i].exp,
+ 1 /* FIXME: V850_OPERAND_RELATIVE ??? */,
+ (bfd_reloc_code_real_type) (fixups[i].opindex
+ + (int) BFD_RELOC_UNUSED)
+ );
+ }
+ }
+
+ input_line_pointer = saved_input_line_pointer;
+}
+
+
+/* If while processing a fixup, a reloc really needs to be created */
+/* then it is done here. */
+
+arelent *
+tc_gen_reloc (seg, fixp)
+ asection * seg;
+ fixS * fixp;
+{
+ arelent * reloc;
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = & fixp->fx_addsy->bsym;
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ /* xgettext:c-format */
+ _("reloc %d not supported by object file format"),
+ (int) fixp->fx_r_type);
+
+ xfree (reloc);
+
+ return NULL;
+ }
+
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT)
+ reloc->addend = fixp->fx_offset;
+ else
+ reloc->addend = fixp->fx_addnumber;
+
+ return reloc;
+}
+
+/* Assume everything will fit in two bytes, then expand as necessary. */
+int
+md_estimate_size_before_relax (fragp, seg)
+ fragS * fragp;
+ asection * seg;
+{
+ if (fragp->fr_subtype == 0)
+ fragp->fr_var = 4;
+ else if (fragp->fr_subtype == 2)
+ fragp->fr_var = 2;
+ else
+ abort ();
+ return 2;
+}
+
+long
+v850_pcrel_from_section (fixp, section)
+ fixS * fixp;
+ segT section;
+{
+ /* If the symbol is undefined, or in a section other than our own,
+ then let the linker figure it out. */
+ if (fixp->fx_addsy != (symbolS *) NULL
+ && (! S_IS_DEFINED (fixp->fx_addsy)
+ || (S_GET_SEGMENT (fixp->fx_addsy) != section)))
+ {
+ /* The symbol is undefined/not in our section.
+ Let the linker figure it out. */
+ return 0;
+ }
+
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+}
+
+int
+md_apply_fix3 (fixp, valuep, seg)
+ fixS * fixp;
+ valueT * valuep;
+ segT seg;
+{
+ valueT value;
+ char * where;
+
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ {
+ fixp->fx_done = 0;
+ return 1;
+ }
+
+ if (fixp->fx_addsy == (symbolS *) NULL)
+ {
+ value = * valuep;
+ fixp->fx_done = 1;
+ }
+ else if (fixp->fx_pcrel)
+ value = * valuep;
+ else
+ {
+ value = fixp->fx_offset;
+ if (fixp->fx_subsy != (symbolS *) NULL)
+ {
+ if (S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
+ value -= S_GET_VALUE (fixp->fx_subsy);
+ else
+ {
+ /* We don't actually support subtracting a symbol. */
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("expression too complex"));
+ }
+ }
+ }
+
+ if ((int) fixp->fx_r_type >= (int) BFD_RELOC_UNUSED)
+ {
+ int opindex;
+ const struct v850_operand * operand;
+ unsigned long insn;
+
+ opindex = (int) fixp->fx_r_type - (int) BFD_RELOC_UNUSED;
+ operand = & v850_operands[ opindex ];
+
+ /* Fetch the instruction, insert the fully resolved operand
+ value, and stuff the instruction back again.
+
+ Note the instruction has been stored in little endian
+ format! */
+ where = fixp->fx_frag->fr_literal + fixp->fx_where;
+
+ insn = bfd_getl32 ((unsigned char *) where);
+ insn = v850_insert_operand (insn, operand, (offsetT) value,
+ fixp->fx_file, fixp->fx_line, NULL);
+ bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
+
+ if (fixp->fx_done)
+ {
+ /* Nothing else to do here. */
+ return 1;
+ }
+
+ /* Determine a BFD reloc value based on the operand information.
+ We are only prepared to turn a few of the operands into relocs. */
+
+ if (operand->bits == 22)
+ fixp->fx_r_type = BFD_RELOC_V850_22_PCREL;
+ else if (operand->bits == 9)
+ fixp->fx_r_type = BFD_RELOC_V850_9_PCREL;
+ else
+ {
+ /* fprintf (stderr, "bits: %d, insn: %x\n", operand->bits, insn); */
+
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("unresolved expression that must be resolved"));
+ fixp->fx_done = 1;
+ return 1;
+ }
+ }
+ else if (fixp->fx_done)
+ {
+ /* We still have to insert the value into memory! */
+ where = fixp->fx_frag->fr_literal + fixp->fx_where;
+
+ if (fixp->fx_size == 1)
+ * where = value & 0xff;
+ else if (fixp->fx_size == 2)
+ bfd_putl16 (value & 0xffff, (unsigned char *) where);
+ else if (fixp->fx_size == 4)
+ bfd_putl32 (value, (unsigned char *) where);
+ }
+
+ fixp->fx_addnumber = value;
+ return 1;
+}
+
+
+/* Parse a cons expression. We have to handle hi(), lo(), etc
+ on the v850. */
+void
+parse_cons_expression_v850 (exp)
+ expressionS *exp;
+{
+ /* See if there's a reloc prefix like hi() we have to handle. */
+ hold_cons_reloc = v850_reloc_prefix (NULL);
+
+ /* Do normal expression parsing. */
+ expression (exp);
+}
+
+/* Create a fixup for a cons expression. If parse_cons_expression_v850
+ found a reloc prefix, then we use that reloc, else we choose an
+ appropriate one based on the size of the expression. */
+void
+cons_fix_new_v850 (frag, where, size, exp)
+ fragS *frag;
+ int where;
+ int size;
+ expressionS *exp;
+{
+ if (hold_cons_reloc == BFD_RELOC_UNUSED)
+ {
+ if (size == 4)
+ hold_cons_reloc = BFD_RELOC_32;
+ if (size == 2)
+ hold_cons_reloc = BFD_RELOC_16;
+ if (size == 1)
+ hold_cons_reloc = BFD_RELOC_8;
+ }
+
+ if (exp != NULL)
+ fix_new_exp (frag, where, size, exp, 0, hold_cons_reloc);
+ else
+ fix_new (frag, where, size, NULL, 0, 0, hold_cons_reloc);
+}
+boolean
+v850_fix_adjustable (fixP)
+ fixS *fixP;
+{
+
+ if (fixP->fx_addsy == NULL)
+ return 1;
+
+ /* Prevent all adjustments to global symbols. */
+ if (S_IS_EXTERN (fixP->fx_addsy))
+ return 0;
+ if (S_IS_WEAK (fixP->fx_addsy))
+ return 0;
+ /* Don't adjust function names */
+ if (S_IS_FUNCTION (fixP->fx_addsy))
+ return 0;
+
+ /* We need the symbol name for the VTABLE entries */
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ return 1;
+}
+
+int
+v850_force_relocation (fixp)
+ struct fix *fixp;
+{
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 1;
+
+ return 0;
+}
diff --git a/gas/config/tc-v850.h b/gas/config/tc-v850.h
new file mode 100644
index 0000000..fad4d1c
--- /dev/null
+++ b/gas/config/tc-v850.h
@@ -0,0 +1,85 @@
+/* tc-v850.h -- Header file for tc-v850.c.
+ Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#define TC_V850
+
+#include <elf/v850.h>
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#ifndef BFD_ASSEMBLER
+ #error V850 support requires BFD_ASSEMBLER
+#endif
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_v850
+
+/* The target BFD format. */
+#define TARGET_FORMAT "elf32-v850"
+
+#define MD_APPLY_FIX3
+#define md_operand(x)
+
+#define obj_fix_adjustable(fixP) v850_fix_adjustable(fixP)
+#define TC_FORCE_RELOCATION(fixp) v850_force_relocation(fixp)
+extern int v850_force_relocation PARAMS ((struct fix *));
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+#define DIFF_EXPR_OK /* foo-. gets turned into PC relative relocs */
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+#define md_number_to_chars number_to_chars_littleendian
+
+/* We need to handle lo(), hi(), etc etc in .hword, .word, etc
+ directives, so we have to parse "cons" expressions ourselves. */
+#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) parse_cons_expression_v850 (EXP)
+#define TC_CONS_FIX_NEW cons_fix_new_v850
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+
+/* This section must be in the small data area (pointed to by GP). */
+#define SHF_V850_GPREL 0x10000000
+/* This section must be in the tiny data area (pointed to by EP). */
+#define SHF_V850_EPREL 0x20000000
+/* This section must be in the zero data area (pointed to by R0). */
+#define SHF_V850_R0REL 0x40000000
+
+#define ELF_TC_SPECIAL_SECTIONS \
+ { ".sdata", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_V850_GPREL }, \
+ { ".rosdata", SHT_PROGBITS, SHF_ALLOC + SHF_V850_GPREL }, \
+ { ".sbss", SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_V850_GPREL }, \
+ { ".scommon", SHT_V850_SCOMMON, SHF_ALLOC + SHF_WRITE + SHF_V850_GPREL }, \
+ { ".tdata", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_V850_EPREL }, \
+ { ".tbss", SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_V850_EPREL }, \
+ { ".tcommon", SHT_V850_TCOMMON, SHF_ALLOC + SHF_WRITE + SHF_V850_R0REL }, \
+ { ".zdata", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_V850_R0REL }, \
+ { ".rozdata", SHT_PROGBITS, SHF_ALLOC + SHF_V850_R0REL }, \
+ { ".zbss", SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_V850_R0REL }, \
+ { ".zcommon", SHT_V850_ZCOMMON, SHF_ALLOC + SHF_WRITE + SHF_V850_R0REL }, \
+ { ".call_table_data", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE }, \
+ { ".call_table_text", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_EXECINSTR },
+
+#define MD_PCREL_FROM_SECTION(fixP,section) v850_pcrel_from_section (fixP, section)
+extern long v850_pcrel_from_section ();
diff --git a/gas/config/tc-vax.c b/gas/config/tc-vax.c
new file mode 100644
index 0000000..24e4a9b
--- /dev/null
+++ b/gas/config/tc-vax.c
@@ -0,0 +1,3242 @@
+/* tc-vax.c - vax-specific -
+ Copyright (C) 1987, 91, 92, 93, 94, 95, 1998 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#include "as.h"
+
+#include "vax-inst.h"
+#include "obstack.h" /* For FRAG_APPEND_1_CHAR macro in "frags.h" */
+
+/* These chars start a comment anywhere in a source file (except inside
+ another comment */
+const char comment_chars[] = "#";
+
+/* These chars only start a comment at the beginning of a line. */
+/* Note that for the VAX the are the same as comment_chars above. */
+const char line_comment_chars[] = "#";
+
+const char line_separator_chars[] = "";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* as in 0f123.456 */
+/* or 0H1.234E-12 (see exp chars above) */
+const char FLT_CHARS[] = "dDfFgGhH";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c . Ideally it shouldn't have to know about it at all,
+ but nothing is ideal around here. */
+
+/* Hold details of an operand expression */
+static expressionS exp_of_operand[VIT_MAX_OPERANDS];
+static segT seg_of_operand[VIT_MAX_OPERANDS];
+
+/* A vax instruction after decoding. */
+static struct vit v;
+
+/* Hold details of big operands. */
+LITTLENUM_TYPE big_operand_bits[VIT_MAX_OPERANDS][SIZE_OF_LARGE_NUMBER];
+FLONUM_TYPE float_operand[VIT_MAX_OPERANDS];
+/* Above is made to point into big_operand_bits by md_begin(). */
+
+int flag_hash_long_names; /* -+ */
+int flag_one; /* -1 */
+int flag_show_after_trunc; /* -H */
+int flag_no_hash_mixed_case; /* -h NUM */
+
+/*
+ * For VAX, relative addresses of "just the right length" are easy.
+ * The branch displacement is always the last operand, even in
+ * synthetic instructions.
+ * For VAX, we encode the relax_substateTs (in e.g. fr_substate) as:
+ *
+ * 4 3 2 1 0 bit number
+ * ---/ /--+-------+-------+-------+-------+-------+
+ * | what state ? | how long ? |
+ * ---/ /--+-------+-------+-------+-------+-------+
+ *
+ * The "how long" bits are 00=byte, 01=word, 10=long.
+ * This is a Un*x convention.
+ * Not all lengths are legit for a given value of (what state).
+ * The "how long" refers merely to the displacement length.
+ * The address usually has some constant bytes in it as well.
+ *
+
+ groups for VAX address relaxing.
+
+ 1. "foo" pc-relative.
+ length of byte, word, long
+
+ 2a. J<cond> where <cond> is a simple flag test.
+ length of byte, word, long.
+ VAX opcodes are: (Hex)
+ bneq/bnequ 12
+ beql/beqlu 13
+ bgtr 14
+ bleq 15
+ bgeq 18
+ blss 19
+ bgtru 1a
+ blequ 1b
+ bvc 1c
+ bvs 1d
+ bgequ/bcc 1e
+ blssu/bcs 1f
+ Always, you complement 0th bit to reverse condition.
+ Always, 1-byte opcode, then 1-byte displacement.
+
+ 2b. J<cond> where cond tests a memory bit.
+ length of byte, word, long.
+ Vax opcodes are: (Hex)
+ bbs e0
+ bbc e1
+ bbss e2
+ bbcs e3
+ bbsc e4
+ bbcc e5
+ bbssi e6
+ bbcci e7
+ Always, you complement 0th bit to reverse condition.
+ Always, 1-byte opcde, longword-address, byte-address, 1-byte-displacement
+
+ 2c. J<cond> where cond tests low-order memory bit
+ length of byte,word,long.
+ Vax opcodes are: (Hex)
+ blbs e8
+ blbc e9
+ Always, you complement 0th bit to reverse condition.
+ Always, 1-byte opcode, longword-address, 1-byte displacement.
+
+ 3. Jbs/Jbr.
+ length of byte,word,long.
+ Vax opcodes are: (Hex)
+ bsbb 10
+ brb 11
+ These are like (2) but there is no condition to reverse.
+ Always, 1 byte opcode, then displacement/absolute.
+
+ 4a. JacbX
+ length of word, long.
+ Vax opcodes are: (Hex)
+ acbw 3d
+ acbf 4f
+ acbd 6f
+ abcb 9d
+ acbl f1
+ acbg 4ffd
+ acbh 6ffd
+ Always, we cannot reverse the sense of the branch; we have a word
+ displacement.
+ The double-byte op-codes don't hurt: we never want to modify the
+ opcode, so we don't care how many bytes are between the opcode and
+ the operand.
+
+ 4b. JXobXXX
+ length of long, long, byte.
+ Vax opcodes are: (Hex)
+ aoblss f2
+ aobleq f3
+ sobgeq f4
+ sobgtr f5
+ Always, we cannot reverse the sense of the branch; we have a byte
+ displacement.
+
+ The only time we need to modify the opcode is for class 2 instructions.
+ After relax() we may complement the lowest order bit of such instruction
+ to reverse sense of branch.
+
+ For class 2 instructions, we store context of "where is the opcode literal".
+ We can change an opcode's lowest order bit without breaking anything else.
+
+ We sometimes store context in the operand literal. This way we can figure out
+ after relax() what the original addressing mode was.
+ */
+
+/* These displacements are relative to the start address of the
+ displacement. The first letter is Byte, Word. 2nd letter is
+ Forward, Backward. */
+#define BF (1+ 127)
+#define BB (1+-128)
+#define WF (2+ 32767)
+#define WB (2+-32768)
+/* Dont need LF, LB because they always reach. [They are coded as 0.] */
+
+
+#define C(a,b) ENCODE_RELAX(a,b)
+/* This macro has no side-effects. */
+#define ENCODE_RELAX(what,length) (((what) << 2) + (length))
+
+const relax_typeS md_relax_table[] =
+{
+ {1, 1, 0, 0}, /* error sentinel 0,0 */
+ {1, 1, 0, 0}, /* unused 0,1 */
+ {1, 1, 0, 0}, /* unused 0,2 */
+ {1, 1, 0, 0}, /* unused 0,3 */
+ {BF + 1, BB + 1, 2, C (1, 1)},/* B^"foo" 1,0 */
+ {WF + 1, WB + 1, 3, C (1, 2)},/* W^"foo" 1,1 */
+ {0, 0, 5, 0}, /* L^"foo" 1,2 */
+ {1, 1, 0, 0}, /* unused 1,3 */
+ {BF, BB, 1, C (2, 1)}, /* b<cond> B^"foo" 2,0 */
+ {WF + 2, WB + 2, 4, C (2, 2)},/* br.+? brw X 2,1 */
+ {0, 0, 7, 0}, /* br.+? jmp X 2,2 */
+ {1, 1, 0, 0}, /* unused 2,3 */
+ {BF, BB, 1, C (3, 1)}, /* brb B^foo 3,0 */
+ {WF, WB, 2, C (3, 2)}, /* brw W^foo 3,1 */
+ {0, 0, 5, 0}, /* Jmp L^foo 3,2 */
+ {1, 1, 0, 0}, /* unused 3,3 */
+ {1, 1, 0, 0}, /* unused 4,0 */
+ {WF, WB, 2, C (4, 2)}, /* acb_ ^Wfoo 4,1 */
+ {0, 0, 10, 0}, /* acb_,br,jmp L^foo4,2 */
+ {1, 1, 0, 0}, /* unused 4,3 */
+ {BF, BB, 1, C (5, 1)}, /* Xob___,,foo 5,0 */
+ {WF + 4, WB + 4, 6, C (5, 2)},/* Xob.+2,brb.+3,brw5,1 */
+ {0, 0, 9, 0}, /* Xob.+2,brb.+6,jmp5,2 */
+};
+
+#undef C
+#undef BF
+#undef BB
+#undef WF
+#undef WB
+
+void float_cons ();
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"dfloat", float_cons, 'd'},
+ {"ffloat", float_cons, 'f'},
+ {"gfloat", float_cons, 'g'},
+ {"hfloat", float_cons, 'h'},
+ {0},
+};
+
+#define STATE_PC_RELATIVE (1)
+#define STATE_CONDITIONAL_BRANCH (2)
+#define STATE_ALWAYS_BRANCH (3) /* includes BSB... */
+#define STATE_COMPLEX_BRANCH (4)
+#define STATE_COMPLEX_HOP (5)
+
+#define STATE_BYTE (0)
+#define STATE_WORD (1)
+#define STATE_LONG (2)
+#define STATE_UNDF (3) /* Symbol undefined in pass1 */
+
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+int flonum_gen2vax PARAMS ((char format_letter, FLONUM_TYPE * f,
+ LITTLENUM_TYPE * words));
+static const char *vip_begin PARAMS ((int, const char *, const char *,
+ const char *));
+static void vip_op_defaults PARAMS ((const char *, const char *, const char *));
+static void vip_op PARAMS ((char *, struct vop *));
+static void vip PARAMS ((struct vit *, char *));
+
+void
+md_begin ()
+{
+ const char *errtxt;
+ FLONUM_TYPE *fP;
+ int i;
+
+ if ((errtxt = vip_begin (1, "$", "*", "`")) != 0)
+ {
+ as_fatal (_("VIP_BEGIN error:%s"), errtxt);
+ }
+
+ for (i = 0, fP = float_operand;
+ fP < float_operand + VIT_MAX_OPERANDS;
+ i++, fP++)
+ {
+ fP->low = &big_operand_bits[i][0];
+ fP->high = &big_operand_bits[i][SIZE_OF_LARGE_NUMBER - 1];
+ }
+}
+
+void
+md_number_to_chars (con, value, nbytes)
+ char con[];
+ valueT value;
+ int nbytes;
+{
+ number_to_chars_littleendian (con, value, nbytes);
+}
+
+/* Fix up some data or instructions after we find out the value of a symbol
+ that they reference. */
+
+void /* Knows about order of bytes in address. */
+md_apply_fix (fixP, value)
+ fixS *fixP;
+ long value;
+{
+ number_to_chars_littleendian (fixP->fx_where + fixP->fx_frag->fr_literal,
+ (valueT) value, fixP->fx_size);
+}
+
+long
+md_chars_to_number (con, nbytes)
+ unsigned char con[]; /* Low order byte 1st. */
+ int nbytes; /* Number of bytes in the input. */
+{
+ long retval;
+ for (retval = 0, con += nbytes - 1; nbytes--; con--)
+ {
+ retval <<= BITS_PER_CHAR;
+ retval |= *con;
+ }
+ return retval;
+}
+
+/* vax:md_assemble() emit frags for 1 instruction */
+
+void
+md_assemble (instruction_string)
+ char *instruction_string; /* A string: assemble 1 instruction. */
+{
+ /* Non-zero if operand expression's segment is not known yet. */
+ int is_undefined;
+
+ int length_code;
+ char *p;
+ /* An operand. Scans all operands. */
+ struct vop *operandP;
+ char *save_input_line_pointer;
+ /* What used to live after an expression. */
+ char c_save;
+ /* 1: instruction_string bad for all passes. */
+ int goofed;
+ /* Points to slot just after last operand. */
+ struct vop *end_operandP;
+ /* Points to expression values for this operand. */
+ expressionS *expP;
+ segT *segP;
+
+ /* These refer to an instruction operand expression. */
+ /* Target segment of the address. */
+ segT to_seg;
+ valueT this_add_number;
+ /* Positive (minuend) symbol. */
+ struct symbol *this_add_symbol;
+ /* As a number. */
+ long opcode_as_number;
+ /* Least significant byte 1st. */
+ char *opcode_as_chars;
+ /* As an array of characters. */
+ /* Least significant byte 1st */
+ char *opcode_low_byteP;
+ /* length (bytes) meant by vop_short. */
+ int length;
+ /* 0, or 1 if '@' is in addressing mode. */
+ int at;
+ /* From vop_nbytes: vax_operand_width (in bytes) */
+ int nbytes;
+ FLONUM_TYPE *floatP;
+ LITTLENUM_TYPE literal_float[8];
+ /* Big enough for any floating point literal. */
+
+ vip (&v, instruction_string);
+
+ /*
+ * Now we try to find as many as_warn()s as we can. If we do any as_warn()s
+ * then goofed=1. Notice that we don't make any frags yet.
+ * Should goofed be 1, then this instruction will wedge in any pass,
+ * and we can safely flush it, without causing interpass symbol phase
+ * errors. That is, without changing label values in different passes.
+ */
+ if ((goofed = (*v.vit_error)) != 0)
+ {
+ as_warn (_("Ignoring statement due to \"%s\""), v.vit_error);
+ }
+ /*
+ * We need to use expression() and friends, which require us to diddle
+ * input_line_pointer. So we save it and restore it later.
+ */
+ save_input_line_pointer = input_line_pointer;
+ for (operandP = v.vit_operand,
+ expP = exp_of_operand,
+ segP = seg_of_operand,
+ floatP = float_operand,
+ end_operandP = v.vit_operand + v.vit_operands;
+
+ operandP < end_operandP;
+
+ operandP++, expP++, segP++, floatP++)
+ { /* for each operand */
+ if (operandP->vop_error)
+ {
+ as_warn (_("Ignoring statement because \"%s\""), operandP->vop_error);
+ goofed = 1;
+ }
+ else
+ {
+ /* statement has no syntax goofs: lets sniff the expression */
+ int can_be_short = 0; /* 1 if a bignum can be reduced to a short literal. */
+
+ input_line_pointer = operandP->vop_expr_begin;
+ c_save = operandP->vop_expr_end[1];
+ operandP->vop_expr_end[1] = '\0';
+ /* If to_seg == SEG_PASS1, expression() will have set need_pass_2 = 1. */
+ *segP = expression (expP);
+ switch (expP->X_op)
+ {
+ case O_absent:
+ /* for BSD4.2 compatibility, missing expression is absolute 0 */
+ expP->X_op = O_constant;
+ expP->X_add_number = 0;
+ /* For SEG_ABSOLUTE, we shouldn't need to set X_op_symbol,
+ X_add_symbol to any particular value. But, we will program
+ defensively. Since this situation occurs rarely so it costs
+ us little to do, and stops Dean worrying about the origin of
+ random bits in expressionS's. */
+ expP->X_add_symbol = NULL;
+ expP->X_op_symbol = NULL;
+ break;
+
+ case O_symbol:
+ case O_constant:
+ break;
+
+ default:
+ /*
+ * Major bug. We can't handle the case of a
+ * SEG_OP expression in a VIT_OPCODE_SYNTHETIC
+ * variable-length instruction.
+ * We don't have a frag type that is smart enough to
+ * relax a SEG_OP, and so we just force all
+ * SEG_OPs to behave like SEG_PASS1s.
+ * Clearly, if there is a demand we can invent a new or
+ * modified frag type and then coding up a frag for this
+ * case will be easy. SEG_OP was invented for the
+ * .words after a CASE opcode, and was never intended for
+ * instruction operands.
+ */
+ need_pass_2 = 1;
+ as_warn (_("Can't relocate expression"));
+ break;
+
+ case O_big:
+ /* Preserve the bits. */
+ if (expP->X_add_number > 0)
+ {
+ bignum_copy (generic_bignum, expP->X_add_number,
+ floatP->low, SIZE_OF_LARGE_NUMBER);
+ }
+ else
+ {
+ know (expP->X_add_number < 0);
+ flonum_copy (&generic_floating_point_number,
+ floatP);
+ if (strchr ("s i", operandP->vop_short))
+ {
+ /* Could possibly become S^# */
+ flonum_gen2vax (-expP->X_add_number, floatP, literal_float);
+ switch (-expP->X_add_number)
+ {
+ case 'f':
+ can_be_short =
+ (literal_float[0] & 0xFC0F) == 0x4000
+ && literal_float[1] == 0;
+ break;
+
+ case 'd':
+ can_be_short =
+ (literal_float[0] & 0xFC0F) == 0x4000
+ && literal_float[1] == 0
+ && literal_float[2] == 0
+ && literal_float[3] == 0;
+ break;
+
+ case 'g':
+ can_be_short =
+ (literal_float[0] & 0xFF81) == 0x4000
+ && literal_float[1] == 0
+ && literal_float[2] == 0
+ && literal_float[3] == 0;
+ break;
+
+ case 'h':
+ can_be_short = ((literal_float[0] & 0xFFF8) == 0x4000
+ && (literal_float[1] & 0xE000) == 0
+ && literal_float[2] == 0
+ && literal_float[3] == 0
+ && literal_float[4] == 0
+ && literal_float[5] == 0
+ && literal_float[6] == 0
+ && literal_float[7] == 0);
+ break;
+
+ default:
+ BAD_CASE (-expP->X_add_number);
+ break;
+ } /* switch (float type) */
+ } /* if (could want to become S^#...) */
+ } /* bignum or flonum ? */
+
+ if (operandP->vop_short == 's'
+ || operandP->vop_short == 'i'
+ || (operandP->vop_short == ' '
+ && operandP->vop_reg == 0xF
+ && (operandP->vop_mode & 0xE) == 0x8))
+ {
+ /* Saw a '#'. */
+ if (operandP->vop_short == ' ')
+ {
+ /* We must chose S^ or I^. */
+ if (expP->X_add_number > 0)
+ {
+ /* Bignum: Short literal impossible. */
+ operandP->vop_short = 'i';
+ operandP->vop_mode = 8;
+ operandP->vop_reg = 0xF; /* VAX PC. */
+ }
+ else
+ {
+ /* Flonum: Try to do it. */
+ if (can_be_short)
+ {
+ operandP->vop_short = 's';
+ operandP->vop_mode = 0;
+ operandP->vop_ndx = -1;
+ operandP->vop_reg = -1;
+ expP->X_op = O_constant;
+ }
+ else
+ {
+ operandP->vop_short = 'i';
+ operandP->vop_mode = 8;
+ operandP->vop_reg = 0xF; /* VAX PC */
+ }
+ } /* bignum or flonum ? */
+ } /* if #, but no S^ or I^ seen. */
+ /* No more ' ' case: either 's' or 'i'. */
+ if (operandP->vop_short == 's')
+ {
+ /* Wants to be a short literal. */
+ if (expP->X_add_number > 0)
+ {
+ as_warn (_("Bignum not permitted in short literal. Immediate mode assumed."));
+ operandP->vop_short = 'i';
+ operandP->vop_mode = 8;
+ operandP->vop_reg = 0xF; /* VAX PC. */
+ }
+ else
+ {
+ if (!can_be_short)
+ {
+ as_warn (_("Can't do flonum short literal: immediate mode used."));
+ operandP->vop_short = 'i';
+ operandP->vop_mode = 8;
+ operandP->vop_reg = 0xF; /* VAX PC. */
+ }
+ else
+ { /* Encode short literal now. */
+ int temp = 0;
+
+ switch (-expP->X_add_number)
+ {
+ case 'f':
+ case 'd':
+ temp = literal_float[0] >> 4;
+ break;
+
+ case 'g':
+ temp = literal_float[0] >> 1;
+ break;
+
+ case 'h':
+ temp = ((literal_float[0] << 3) & 070)
+ | ((literal_float[1] >> 13) & 07);
+ break;
+
+ default:
+ BAD_CASE (-expP->X_add_number);
+ break;
+ }
+
+ floatP->low[0] = temp & 077;
+ floatP->low[1] = 0;
+ } /* if can be short literal float */
+ } /* flonum or bignum ? */
+ }
+ else
+ { /* I^# seen: set it up if float. */
+ if (expP->X_add_number < 0)
+ {
+ memcpy (floatP->low, literal_float, sizeof (literal_float));
+ }
+ } /* if S^# seen. */
+ }
+ else
+ {
+ as_warn (_("A bignum/flonum may not be a displacement: 0x%lx used"),
+ (expP->X_add_number = 0x80000000L));
+ /* Chosen so luser gets the most offset bits to patch later. */
+ }
+ expP->X_add_number = floatP->low[0]
+ | ((LITTLENUM_MASK & (floatP->low[1])) << LITTLENUM_NUMBER_OF_BITS);
+ /*
+ * For the O_big case we have:
+ * If vop_short == 's' then a short floating literal is in the
+ * lowest 6 bits of floatP -> low [0], which is
+ * big_operand_bits [---] [0].
+ * If vop_short == 'i' then the appropriate number of elements
+ * of big_operand_bits [---] [...] are set up with the correct
+ * bits.
+ * Also, just in case width is byte word or long, we copy the lowest
+ * 32 bits of the number to X_add_number.
+ */
+ break;
+ }
+ if (input_line_pointer != operandP->vop_expr_end + 1)
+ {
+ as_warn ("Junk at end of expression \"%s\"", input_line_pointer);
+ goofed = 1;
+ }
+ operandP->vop_expr_end[1] = c_save;
+ }
+ } /* for(each operand) */
+
+ input_line_pointer = save_input_line_pointer;
+
+ if (need_pass_2 || goofed)
+ {
+ return;
+ }
+
+
+ /* Emit op-code. */
+ /* Remember where it is, in case we want to modify the op-code later. */
+ opcode_low_byteP = frag_more (v.vit_opcode_nbytes);
+ memcpy (opcode_low_byteP, v.vit_opcode, v.vit_opcode_nbytes);
+ opcode_as_number = md_chars_to_number (opcode_as_chars = v.vit_opcode, 4);
+ for (operandP = v.vit_operand,
+ expP = exp_of_operand,
+ segP = seg_of_operand,
+ floatP = float_operand,
+ end_operandP = v.vit_operand + v.vit_operands;
+
+ operandP < end_operandP;
+
+ operandP++,
+ floatP++,
+ segP++,
+ expP++)
+ {
+ if (operandP->vop_ndx >= 0)
+ {
+ /* indexed addressing byte */
+ /* Legality of indexed mode already checked: it is OK */
+ FRAG_APPEND_1_CHAR (0x40 + operandP->vop_ndx);
+ } /* if(vop_ndx>=0) */
+
+ /* Here to make main operand frag(s). */
+ this_add_number = expP->X_add_number;
+ this_add_symbol = expP->X_add_symbol;
+ to_seg = *segP;
+ is_undefined = (to_seg == SEG_UNKNOWN);
+ at = operandP->vop_mode & 1;
+ length = (operandP->vop_short == 'b'
+ ? 1 : (operandP->vop_short == 'w'
+ ? 2 : (operandP->vop_short == 'l'
+ ? 4 : 0)));
+ nbytes = operandP->vop_nbytes;
+ if (operandP->vop_access == 'b')
+ {
+ if (to_seg == now_seg || is_undefined)
+ {
+ /* If is_undefined, then it might BECOME now_seg. */
+ if (nbytes)
+ {
+ p = frag_more (nbytes);
+ fix_new (frag_now, p - frag_now->fr_literal, nbytes,
+ this_add_symbol, this_add_number, 1, NO_RELOC);
+ }
+ else
+ { /* to_seg==now_seg || to_seg == SEG_UNKNOWN */
+ /* nbytes==0 */
+ length_code = is_undefined ? STATE_UNDF : STATE_BYTE;
+ if (opcode_as_number & VIT_OPCODE_SPECIAL)
+ {
+ if (operandP->vop_width == VAX_WIDTH_UNCONDITIONAL_JUMP)
+ {
+ /* br or jsb */
+ frag_var (rs_machine_dependent, 5, 1,
+ ENCODE_RELAX (STATE_ALWAYS_BRANCH, length_code),
+ this_add_symbol, this_add_number,
+ opcode_low_byteP);
+ }
+ else
+ {
+ if (operandP->vop_width == VAX_WIDTH_WORD_JUMP)
+ {
+ length_code = STATE_WORD;
+ /* JF: There is no state_byte for this one! */
+ frag_var (rs_machine_dependent, 10, 2,
+ ENCODE_RELAX (STATE_COMPLEX_BRANCH, length_code),
+ this_add_symbol, this_add_number,
+ opcode_low_byteP);
+ }
+ else
+ {
+ know (operandP->vop_width == VAX_WIDTH_BYTE_JUMP);
+ frag_var (rs_machine_dependent, 9, 1,
+ ENCODE_RELAX (STATE_COMPLEX_HOP, length_code),
+ this_add_symbol, this_add_number,
+ opcode_low_byteP);
+ }
+ }
+ }
+ else
+ {
+ know (operandP->vop_width == VAX_WIDTH_CONDITIONAL_JUMP);
+ frag_var (rs_machine_dependent, 7, 1,
+ ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, length_code),
+ this_add_symbol, this_add_number,
+ opcode_low_byteP);
+ }
+ }
+ }
+ else
+ {
+ /* to_seg != now_seg && to_seg != SEG_UNKNOWN */
+ /*
+ * --- SEG FLOAT MAY APPEAR HERE ----
+ */
+ if (to_seg == SEG_ABSOLUTE)
+ {
+ if (nbytes)
+ {
+ know (!(opcode_as_number & VIT_OPCODE_SYNTHETIC));
+ p = frag_more (nbytes);
+ /* Conventional relocation. */
+ fix_new (frag_now, p - frag_now->fr_literal,
+ nbytes, &abs_symbol, this_add_number,
+ 1, NO_RELOC);
+ }
+ else
+ {
+ know (opcode_as_number & VIT_OPCODE_SYNTHETIC);
+ if (opcode_as_number & VIT_OPCODE_SPECIAL)
+ {
+ if (operandP->vop_width == VAX_WIDTH_UNCONDITIONAL_JUMP)
+ {
+ /* br or jsb */
+ *opcode_low_byteP = opcode_as_chars[0] + VAX_WIDEN_LONG;
+ know (opcode_as_chars[1] == 0);
+ p = frag_more (5);
+ p[0] = VAX_ABSOLUTE_MODE; /* @#... */
+ md_number_to_chars (p + 1, this_add_number, 4);
+ /* Now (eg) JMP @#foo or JSB @#foo. */
+ }
+ else
+ {
+ if (operandP->vop_width == VAX_WIDTH_WORD_JUMP)
+ {
+ p = frag_more (10);
+ p[0] = 2;
+ p[1] = 0;
+ p[2] = VAX_BRB;
+ p[3] = 6;
+ p[4] = VAX_JMP;
+ p[5] = VAX_ABSOLUTE_MODE; /* @#... */
+ md_number_to_chars (p + 6, this_add_number, 4);
+ /*
+ * Now (eg) ACBx 1f
+ * BRB 2f
+ * 1: JMP @#foo
+ * 2:
+ */
+ }
+ else
+ {
+ know (operandP->vop_width == VAX_WIDTH_BYTE_JUMP);
+ p = frag_more (9);
+ p[0] = 2;
+ p[1] = VAX_BRB;
+ p[2] = 6;
+ p[3] = VAX_JMP;
+ p[4] = VAX_PC_RELATIVE_MODE + 1; /* @#... */
+ md_number_to_chars (p + 5, this_add_number, 4);
+ /*
+ * Now (eg) xOBxxx 1f
+ * BRB 2f
+ * 1: JMP @#foo
+ * 2:
+ */
+ }
+ }
+ }
+ else
+ {
+ /* b<cond> */
+ *opcode_low_byteP ^= 1;
+ /* To reverse the condition in a VAX branch,
+ complement the lowest order bit. */
+ p = frag_more (7);
+ p[0] = 6;
+ p[1] = VAX_JMP;
+ p[2] = VAX_ABSOLUTE_MODE; /* @#... */
+ md_number_to_chars (p + 3, this_add_number, 4);
+ /*
+ * Now (eg) BLEQ 1f
+ * JMP @#foo
+ * 1:
+ */
+ }
+ }
+ }
+ else
+ {
+ /* to_seg != now_seg && to_seg != SEG_UNKNOWN && to_Seg != SEG_ABSOLUTE */
+ if (nbytes > 0)
+ {
+ /* Pc-relative. Conventional relocation. */
+ know (!(opcode_as_number & VIT_OPCODE_SYNTHETIC));
+ p = frag_more (nbytes);
+ fix_new (frag_now, p - frag_now->fr_literal,
+ nbytes, &abs_symbol, this_add_number,
+ 1, NO_RELOC);
+ }
+ else
+ {
+ know (opcode_as_number & VIT_OPCODE_SYNTHETIC);
+ if (opcode_as_number & VIT_OPCODE_SPECIAL)
+ {
+ if (operandP->vop_width == VAX_WIDTH_UNCONDITIONAL_JUMP)
+ {
+ /* br or jsb */
+ know (opcode_as_chars[1] == 0);
+ *opcode_low_byteP = opcode_as_chars[0] + VAX_WIDEN_LONG;
+ p = frag_more (5);
+ p[0] = VAX_PC_RELATIVE_MODE;
+ fix_new (frag_now,
+ p + 1 - frag_now->fr_literal, 4,
+ this_add_symbol,
+ this_add_number, 1, NO_RELOC);
+ /* Now eg JMP foo or JSB foo. */
+ }
+ else
+ {
+ if (operandP->vop_width == VAX_WIDTH_WORD_JUMP)
+ {
+ p = frag_more (10);
+ p[0] = 0;
+ p[1] = 2;
+ p[2] = VAX_BRB;
+ p[3] = 6;
+ p[4] = VAX_JMP;
+ p[5] = VAX_PC_RELATIVE_MODE;
+ fix_new (frag_now,
+ p + 6 - frag_now->fr_literal, 4,
+ this_add_symbol,
+ this_add_number, 1, NO_RELOC);
+ /*
+ * Now (eg) ACBx 1f
+ * BRB 2f
+ * 1: JMP foo
+ * 2:
+ */
+ }
+ else
+ {
+ know (operandP->vop_width == VAX_WIDTH_BYTE_JUMP);
+ p = frag_more (10);
+ p[0] = 2;
+ p[1] = VAX_BRB;
+ p[2] = 6;
+ p[3] = VAX_JMP;
+ p[4] = VAX_PC_RELATIVE_MODE;
+ fix_new (frag_now,
+ p + 5 - frag_now->fr_literal,
+ 4, this_add_symbol,
+ this_add_number, 1, NO_RELOC);
+ /*
+ * Now (eg) xOBxxx 1f
+ * BRB 2f
+ * 1: JMP foo
+ * 2:
+ */
+ }
+ }
+ }
+ else
+ {
+ know (operandP->vop_width == VAX_WIDTH_CONDITIONAL_JUMP);
+ *opcode_low_byteP ^= 1; /* Reverse branch condition. */
+ p = frag_more (7);
+ p[0] = 6;
+ p[1] = VAX_JMP;
+ p[2] = VAX_PC_RELATIVE_MODE;
+ fix_new (frag_now, p + 3 - frag_now->fr_literal,
+ 4, this_add_symbol,
+ this_add_number, 1, NO_RELOC);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ know (operandP->vop_access != 'b'); /* So it is ordinary operand. */
+ know (operandP->vop_access != ' '); /* ' ' target-independent: elsewhere. */
+ know (operandP->vop_access == 'a'
+ || operandP->vop_access == 'm'
+ || operandP->vop_access == 'r'
+ || operandP->vop_access == 'v'
+ || operandP->vop_access == 'w');
+ if (operandP->vop_short == 's')
+ {
+ if (to_seg == SEG_ABSOLUTE)
+ {
+ if (this_add_number >= 64)
+ {
+ as_warn (_("Short literal overflow(%ld.), immediate mode assumed."),
+ (long) this_add_number);
+ operandP->vop_short = 'i';
+ operandP->vop_mode = 8;
+ operandP->vop_reg = 0xF;
+ }
+ }
+ else
+ {
+ as_warn (_("Forced short literal to immediate mode. now_seg=%s to_seg=%s"),
+ segment_name (now_seg), segment_name (to_seg));
+ operandP->vop_short = 'i';
+ operandP->vop_mode = 8;
+ operandP->vop_reg = 0xF;
+ }
+ }
+ if (operandP->vop_reg >= 0 && (operandP->vop_mode < 8
+ || (operandP->vop_reg != 0xF && operandP->vop_mode < 10)))
+ {
+ /* One byte operand. */
+ know (operandP->vop_mode > 3);
+ FRAG_APPEND_1_CHAR (operandP->vop_mode << 4 | operandP->vop_reg);
+ /* All 1-bytes except S^# happen here. */
+ }
+ else
+ {
+ /* {@}{q^}foo{(Rn)} or S^#foo */
+ if (operandP->vop_reg == -1 && operandP->vop_short != 's')
+ {
+ /* "{@}{q^}foo" */
+ if (to_seg == now_seg)
+ {
+ if (length == 0)
+ {
+ know (operandP->vop_short == ' ');
+ p = frag_var (rs_machine_dependent, 10, 2,
+ ENCODE_RELAX (STATE_PC_RELATIVE, STATE_BYTE),
+ this_add_symbol, this_add_number,
+ opcode_low_byteP);
+ know (operandP->vop_mode == 10 + at);
+ *p = at << 4;
+ /* At is the only context we need to carry
+ to other side of relax() process. Must
+ be in the correct bit position of VAX
+ operand spec. byte. */
+ }
+ else
+ {
+ know (length);
+ know (operandP->vop_short != ' ');
+ p = frag_more (length + 1);
+ p[0] = 0xF | ((at + "?\12\14?\16"[length]) << 4);
+ fix_new (frag_now, p + 1 - frag_now->fr_literal,
+ length, this_add_symbol,
+ this_add_number, 1, NO_RELOC);
+ }
+ }
+ else
+ { /* to_seg != now_seg */
+ if (this_add_symbol == NULL)
+ {
+ know (to_seg == SEG_ABSOLUTE);
+ /* Do @#foo: simpler relocation than foo-.(pc) anyway. */
+ p = frag_more (5);
+ p[0] = VAX_ABSOLUTE_MODE; /* @#... */
+ md_number_to_chars (p + 1, this_add_number, 4);
+ if (length && length != 4)
+ {
+ as_warn (_("Length specification ignored. Address mode 9F used"));
+ }
+ }
+ else
+ {
+ /* {@}{q^}other_seg */
+ know ((length == 0 && operandP->vop_short == ' ')
+ || (length > 0 && operandP->vop_short != ' '));
+ if (is_undefined)
+ {
+ /*
+ * We have a SEG_UNKNOWN symbol. It might
+ * turn out to be in the same segment as
+ * the instruction, permitting relaxation.
+ */
+ p = frag_var (rs_machine_dependent, 5, 2,
+ ENCODE_RELAX (STATE_PC_RELATIVE, STATE_UNDF),
+ this_add_symbol, this_add_number,
+ 0);
+ p[0] = at << 4;
+ }
+ else
+ {
+ if (length == 0)
+ {
+ know (operandP->vop_short == ' ');
+ length = 4; /* Longest possible. */
+ }
+ p = frag_more (length + 1);
+ p[0] = 0xF | ((at + "?\12\14?\16"[length]) << 4);
+ md_number_to_chars (p + 1, this_add_number, length);
+ fix_new (frag_now,
+ p + 1 - frag_now->fr_literal,
+ length, this_add_symbol,
+ this_add_number, 1, NO_RELOC);
+ }
+ }
+ }
+ }
+ else
+ {
+ /* {@}{q^}foo(Rn) or S^# or I^# or # */
+ if (operandP->vop_mode < 0xA)
+ {
+ /* # or S^# or I^# */
+ if (operandP->vop_access == 'v'
+ || operandP->vop_access == 'a')
+ {
+ if (operandP->vop_access == 'v')
+ as_warn (_("Invalid operand: immediate value used as base address."));
+ else
+ as_warn (_("Invalid operand: immediate value used as address."));
+ /* gcc 2.6.3 is known to generate these in at least
+ one case. */
+ }
+ if (length == 0
+ && to_seg == SEG_ABSOLUTE && (expP->X_op != O_big)
+ && operandP->vop_mode == 8 /* No '@'. */
+ && this_add_number < 64)
+ {
+ operandP->vop_short = 's';
+ }
+ if (operandP->vop_short == 's')
+ {
+ FRAG_APPEND_1_CHAR (this_add_number);
+ }
+ else
+ {
+ /* I^#... */
+ know (nbytes);
+ p = frag_more (nbytes + 1);
+ know (operandP->vop_reg == 0xF);
+ p[0] = (operandP->vop_mode << 4) | 0xF;
+ if ((to_seg == SEG_ABSOLUTE) && (expP->X_op != O_big))
+ {
+ /*
+ * If nbytes > 4, then we are scrod. We
+ * don't know if the high order bytes
+ * are to be 0xFF or 0x00. BSD4.2 & RMS
+ * say use 0x00. OK --- but this
+ * assembler needs ANOTHER rewrite to
+ * cope properly with this bug. */
+ md_number_to_chars (p + 1, this_add_number, min (4, nbytes));
+ if (nbytes > 4)
+ {
+ memset (p + 5, '\0', nbytes - 4);
+ }
+ }
+ else
+ {
+ if (expP->X_op == O_big)
+ {
+ /*
+ * Problem here is to get the bytes
+ * in the right order. We stored
+ * our constant as LITTLENUMs, not
+ * bytes. */
+ LITTLENUM_TYPE *lP;
+
+ lP = floatP->low;
+ if (nbytes & 1)
+ {
+ know (nbytes == 1);
+ p[1] = *lP;
+ }
+ else
+ {
+ for (p++; nbytes; nbytes -= 2, p += 2, lP++)
+ {
+ md_number_to_chars (p, *lP, 2);
+ }
+ }
+ }
+ else
+ {
+ fix_new (frag_now, p + 1 - frag_now->fr_literal,
+ nbytes, this_add_symbol,
+ this_add_number, 0, NO_RELOC);
+ }
+ }
+ }
+ }
+ else
+ { /* {@}{q^}foo(Rn) */
+ know ((length == 0 && operandP->vop_short == ' ')
+ || (length > 0 && operandP->vop_short != ' '));
+ if (length == 0)
+ {
+ if (to_seg == SEG_ABSOLUTE)
+ {
+ long test;
+
+ test = this_add_number;
+
+ if (test < 0)
+ test = ~test;
+
+ length = test & 0xffff8000 ? 4
+ : test & 0xffffff80 ? 2
+ : 1;
+ }
+ else
+ {
+ length = 4;
+ }
+ }
+ p = frag_more (1 + length);
+ know (operandP->vop_reg >= 0);
+ p[0] = operandP->vop_reg
+ | ((at | "?\12\14?\16"[length]) << 4);
+ if (to_seg == SEG_ABSOLUTE)
+ {
+ md_number_to_chars (p + 1, this_add_number, length);
+ }
+ else
+ {
+ fix_new (frag_now, p + 1 - frag_now->fr_literal,
+ length, this_add_symbol,
+ this_add_number, 0, NO_RELOC);
+ }
+ }
+ }
+ } /* if(single-byte-operand) */
+ }
+ } /* for(operandP) */
+} /* vax_assemble() */
+
+/*
+ * md_estimate_size_before_relax()
+ *
+ * Called just before relax().
+ * Any symbol that is now undefined will not become defined.
+ * Return the correct fr_subtype in the frag.
+ * Return the initial "guess for fr_var" to caller.
+ * The guess for fr_var is ACTUALLY the growth beyond fr_fix.
+ * Whatever we do to grow fr_fix or fr_var contributes to our returned value.
+ * Although it may not be explicit in the frag, pretend fr_var starts with a
+ * 0 value.
+ */
+int
+md_estimate_size_before_relax (fragP, segment)
+ fragS *fragP;
+ segT segment;
+{
+ char *p;
+ int old_fr_fix;
+
+ old_fr_fix = fragP->fr_fix;
+ switch (fragP->fr_subtype)
+ {
+ case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_UNDF):
+ if (S_GET_SEGMENT (fragP->fr_symbol) == segment)
+ { /* A relaxable case. */
+ fragP->fr_subtype = ENCODE_RELAX (STATE_PC_RELATIVE, STATE_BYTE);
+ }
+ else
+ {
+ p = fragP->fr_literal + old_fr_fix;
+ p[0] |= VAX_PC_RELATIVE_MODE; /* Preserve @ bit. */
+ fragP->fr_fix += 1 + 4;
+ fix_new (fragP, old_fr_fix + 1, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ frag_wane (fragP);
+ }
+ break;
+
+ case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_UNDF):
+ if (S_GET_SEGMENT (fragP->fr_symbol) == segment)
+ {
+ fragP->fr_subtype = ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_BYTE);
+ }
+ else
+ {
+ p = fragP->fr_literal + old_fr_fix;
+ *fragP->fr_opcode ^= 1; /* Reverse sense of branch. */
+ p[0] = 6;
+ p[1] = VAX_JMP;
+ p[2] = VAX_PC_RELATIVE_MODE; /* ...(PC) */
+ fragP->fr_fix += 1 + 1 + 1 + 4;
+ fix_new (fragP, old_fr_fix + 3, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ frag_wane (fragP);
+ }
+ break;
+
+ case ENCODE_RELAX (STATE_COMPLEX_BRANCH, STATE_UNDF):
+ if (S_GET_SEGMENT (fragP->fr_symbol) == segment)
+ {
+ fragP->fr_subtype = ENCODE_RELAX (STATE_COMPLEX_BRANCH, STATE_WORD);
+ }
+ else
+ {
+ p = fragP->fr_literal + old_fr_fix;
+ p[0] = 2;
+ p[1] = 0;
+ p[2] = VAX_BRB;
+ p[3] = 6;
+ p[4] = VAX_JMP;
+ p[5] = VAX_PC_RELATIVE_MODE; /* ...(pc) */
+ fragP->fr_fix += 2 + 2 + 1 + 1 + 4;
+ fix_new (fragP, old_fr_fix + 6, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ frag_wane (fragP);
+ }
+ break;
+
+ case ENCODE_RELAX (STATE_COMPLEX_HOP, STATE_UNDF):
+ if (S_GET_SEGMENT (fragP->fr_symbol) == segment)
+ {
+ fragP->fr_subtype = ENCODE_RELAX (STATE_COMPLEX_HOP, STATE_BYTE);
+ }
+ else
+ {
+ p = fragP->fr_literal + old_fr_fix;
+ p[0] = 2;
+ p[1] = VAX_BRB;
+ p[2] = 6;
+ p[3] = VAX_JMP;
+ p[4] = VAX_PC_RELATIVE_MODE; /* ...(pc) */
+ fragP->fr_fix += 1 + 2 + 1 + 1 + 4;
+ fix_new (fragP, old_fr_fix + 5, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ frag_wane (fragP);
+ }
+ break;
+
+ case ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_UNDF):
+ if (S_GET_SEGMENT (fragP->fr_symbol) == segment)
+ {
+ fragP->fr_subtype = ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_BYTE);
+ }
+ else
+ {
+ p = fragP->fr_literal + old_fr_fix;
+ *fragP->fr_opcode += VAX_WIDEN_LONG;
+ p[0] = VAX_PC_RELATIVE_MODE; /* ...(PC) */
+ fragP->fr_fix += 1 + 4;
+ fix_new (fragP, old_fr_fix + 1, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ frag_wane (fragP);
+ }
+ break;
+
+ default:
+ break;
+ }
+ return (fragP->fr_var + fragP->fr_fix - old_fr_fix);
+} /* md_estimate_size_before_relax() */
+
+/*
+ * md_convert_frag();
+ *
+ * Called after relax() is finished.
+ * In: Address of frag.
+ * fr_type == rs_machine_dependent.
+ * fr_subtype is what the address relaxed to.
+ *
+ * Out: Any fixSs and constants are set up.
+ * Caller will turn frag into a ".space 0".
+ */
+void
+md_convert_frag (headers, seg, fragP)
+ object_headers *headers;
+ segT seg;
+ fragS *fragP;
+{
+ char *addressP; /* -> _var to change. */
+ char *opcodeP; /* -> opcode char(s) to change. */
+ short int length_code; /* 2=long 1=word 0=byte */
+ short int extension = 0; /* Size of relaxed address. */
+ /* Added to fr_fix: incl. ALL var chars. */
+ symbolS *symbolP;
+ long where;
+ long address_of_var;
+ /* Where, in file space, is _var of *fragP? */
+ long target_address = 0;
+ /* Where, in file space, does addr point? */
+
+ know (fragP->fr_type == rs_machine_dependent);
+ length_code = fragP->fr_subtype & 3; /* depends on ENCODE_RELAX() */
+ know (length_code >= 0 && length_code < 3);
+ where = fragP->fr_fix;
+ addressP = fragP->fr_literal + where;
+ opcodeP = fragP->fr_opcode;
+ symbolP = fragP->fr_symbol;
+ know (symbolP);
+ target_address = S_GET_VALUE (symbolP) + fragP->fr_offset;
+ address_of_var = fragP->fr_address + where;
+
+ switch (fragP->fr_subtype)
+ {
+
+ case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_BYTE):
+ know (*addressP == 0 || *addressP == 0x10); /* '@' bit. */
+ addressP[0] |= 0xAF; /* Byte displacement. */
+ addressP[1] = target_address - (address_of_var + 2);
+ extension = 2;
+ break;
+
+ case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_WORD):
+ know (*addressP == 0 || *addressP == 0x10); /* '@' bit. */
+ addressP[0] |= 0xCF; /* Word displacement. */
+ md_number_to_chars (addressP + 1, target_address - (address_of_var + 3), 2);
+ extension = 3;
+ break;
+
+ case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_LONG):
+ know (*addressP == 0 || *addressP == 0x10); /* '@' bit. */
+ addressP[0] |= 0xEF; /* Long word displacement. */
+ md_number_to_chars (addressP + 1, target_address - (address_of_var + 5), 4);
+ extension = 5;
+ break;
+
+ case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_BYTE):
+ addressP[0] = target_address - (address_of_var + 1);
+ extension = 1;
+ break;
+
+ case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_WORD):
+ opcodeP[0] ^= 1; /* Reverse sense of test. */
+ addressP[0] = 3;
+ addressP[1] = VAX_BRB + VAX_WIDEN_WORD;
+ md_number_to_chars (addressP + 2, target_address - (address_of_var + 4), 2);
+ extension = 4;
+ break;
+
+ case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_LONG):
+ opcodeP[0] ^= 1; /* Reverse sense of test. */
+ addressP[0] = 6;
+ addressP[1] = VAX_JMP;
+ addressP[2] = VAX_PC_RELATIVE_MODE;
+ md_number_to_chars (addressP + 3, target_address, 4);
+ extension = 7;
+ break;
+
+ case ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_BYTE):
+ addressP[0] = target_address - (address_of_var + 1);
+ extension = 1;
+ break;
+
+ case ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_WORD):
+ opcodeP[0] += VAX_WIDEN_WORD; /* brb -> brw, bsbb -> bsbw */
+ md_number_to_chars (addressP, target_address - (address_of_var + 2), 2);
+ extension = 2;
+ break;
+
+ case ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_LONG):
+ opcodeP[0] += VAX_WIDEN_LONG; /* brb -> jmp, bsbb -> jsb */
+ addressP[0] = VAX_PC_RELATIVE_MODE;
+ md_number_to_chars (addressP + 1, target_address - (address_of_var + 5), 4);
+ extension = 5;
+ break;
+
+ case ENCODE_RELAX (STATE_COMPLEX_BRANCH, STATE_WORD):
+ md_number_to_chars (addressP, target_address - (address_of_var + 2), 2);
+ extension = 2;
+ break;
+
+ case ENCODE_RELAX (STATE_COMPLEX_BRANCH, STATE_LONG):
+ addressP[0] = 2;
+ addressP[1] = 0;
+ addressP[2] = VAX_BRB;
+ addressP[3] = 6;
+ addressP[4] = VAX_JMP;
+ addressP[5] = VAX_PC_RELATIVE_MODE;
+ md_number_to_chars (addressP + 6, target_address, 4);
+ extension = 10;
+ break;
+
+ case ENCODE_RELAX (STATE_COMPLEX_HOP, STATE_BYTE):
+ addressP[0] = target_address - (address_of_var + 1);
+ extension = 1;
+ break;
+
+ case ENCODE_RELAX (STATE_COMPLEX_HOP, STATE_WORD):
+ addressP[0] = 2;
+ addressP[1] = VAX_BRB;
+ addressP[2] = 3;
+ addressP[3] = VAX_BRW;
+ md_number_to_chars (addressP + 4, target_address - (address_of_var + 6), 2);
+ extension = 6;
+ break;
+
+ case ENCODE_RELAX (STATE_COMPLEX_HOP, STATE_LONG):
+ addressP[0] = 2;
+ addressP[1] = VAX_BRB;
+ addressP[2] = 6;
+ addressP[3] = VAX_JMP;
+ addressP[4] = VAX_PC_RELATIVE_MODE;
+ md_number_to_chars (addressP + 5, target_address, 4);
+ extension = 9;
+ break;
+
+ default:
+ BAD_CASE (fragP->fr_subtype);
+ break;
+ }
+ fragP->fr_fix += extension;
+} /* md_convert_frag() */
+
+/* Translate internal format of relocation info into target format.
+
+ On vax: first 4 bytes are normal unsigned long, next three bytes
+ are symbolnum, least sig. byte first. Last byte is broken up with
+ the upper nibble as nuthin, bit 3 as extern, bits 2 & 1 as length, and
+ bit 0 as pcrel. */
+#ifdef comment
+void
+md_ri_to_chars (the_bytes, ri)
+ char *the_bytes;
+ struct reloc_info_generic ri;
+{
+ /* this is easy */
+ md_number_to_chars (the_bytes, ri.r_address, sizeof (ri.r_address));
+ /* now the fun stuff */
+ the_bytes[6] = (ri.r_symbolnum >> 16) & 0x0ff;
+ the_bytes[5] = (ri.r_symbolnum >> 8) & 0x0ff;
+ the_bytes[4] = ri.r_symbolnum & 0x0ff;
+ the_bytes[7] = (((ri.r_extern << 3) & 0x08) | ((ri.r_length << 1) & 0x06) |
+ ((ri.r_pcrel << 0) & 0x01)) & 0x0F;
+}
+
+#endif /* comment */
+
+void
+tc_aout_fix_to_chars (where, fixP, segment_address_in_file)
+ char *where;
+ fixS *fixP;
+ relax_addressT segment_address_in_file;
+{
+ /*
+ * In: length of relocation (or of address) in chars: 1, 2 or 4.
+ * Out: GNU LD relocation length code: 0, 1, or 2.
+ */
+
+ static const unsigned char nbytes_r_length[] = {42, 0, 1, 42, 2};
+ long r_symbolnum;
+
+ know (fixP->fx_addsy != NULL);
+
+ md_number_to_chars (where,
+ fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file,
+ 4);
+
+ r_symbolnum = (S_IS_DEFINED (fixP->fx_addsy)
+ ? S_GET_TYPE (fixP->fx_addsy)
+ : fixP->fx_addsy->sy_number);
+
+ where[6] = (r_symbolnum >> 16) & 0x0ff;
+ where[5] = (r_symbolnum >> 8) & 0x0ff;
+ where[4] = r_symbolnum & 0x0ff;
+ where[7] = ((((!S_IS_DEFINED (fixP->fx_addsy)) << 3) & 0x08)
+ | ((nbytes_r_length[fixP->fx_size] << 1) & 0x06)
+ | (((fixP->fx_pcrel << 0) & 0x01) & 0x0f));
+}
+
+/*
+ * BUGS, GRIPES, APOLOGIA, etc.
+ *
+ * The opcode table 'votstrs' needs to be sorted on opcode frequency.
+ * That is, AFTER we hash it with hash_...(), we want most-used opcodes
+ * to come out of the hash table faster.
+ *
+ * I am sorry to inflict yet another VAX assembler on the world, but
+ * RMS says we must do everything from scratch, to prevent pin-heads
+ * restricting this software.
+ */
+
+/*
+ * This is a vaguely modular set of routines in C to parse VAX
+ * assembly code using DEC mnemonics. It is NOT un*x specific.
+ *
+ * The idea here is that the assembler has taken care of all:
+ * labels
+ * macros
+ * listing
+ * pseudo-ops
+ * line continuation
+ * comments
+ * condensing any whitespace down to exactly one space
+ * and all we have to do is parse 1 line into a vax instruction
+ * partially formed. We will accept a line, and deliver:
+ * an error message (hopefully empty)
+ * a skeleton VAX instruction (tree structure)
+ * textual pointers to all the operand expressions
+ * a warning message that notes a silly operand (hopefully empty)
+ */
+
+/*
+ * E D I T H I S T O R Y
+ *
+ * 17may86 Dean Elsner. Bug if line ends immediately after opcode.
+ * 30apr86 Dean Elsner. New vip_op() uses arg block so change call.
+ * 6jan86 Dean Elsner. Crock vip_begin() to call vip_op_defaults().
+ * 2jan86 Dean Elsner. Invent synthetic opcodes.
+ * Widen vax_opcodeT to 32 bits. Use a bit for VIT_OPCODE_SYNTHETIC,
+ * which means this is not a real opcode, it is like a macro; it will
+ * be relax()ed into 1 or more instructions.
+ * Use another bit for VIT_OPCODE_SPECIAL if the op-code is not optimised
+ * like a regular branch instruction. Option added to vip_begin():
+ * exclude synthetic opcodes. Invent synthetic_votstrs[].
+ * 31dec85 Dean Elsner. Invent vit_opcode_nbytes.
+ * Also make vit_opcode into a char[]. We now have n-byte vax opcodes,
+ * so caller's don't have to know the difference between a 1-byte & a
+ * 2-byte op-code. Still need vax_opcodeT concept, so we know how
+ * big an object must be to hold an op.code.
+ * 30dec85 Dean Elsner. Widen typedef vax_opcodeT in "vax-inst.h"
+ * because vax opcodes may be 16 bits. Our crufty C compiler was
+ * happily initialising 8-bit vot_codes with 16-bit numbers!
+ * (Wouldn't the 'phone company like to compress data so easily!)
+ * 29dec85 Dean Elsner. New static table vax_operand_width_size[].
+ * Invented so we know hw many bytes a "I^#42" needs in its immediate
+ * operand. Revised struct vop in "vax-inst.h": explicitly include
+ * byte length of each operand, and it's letter-code datum type.
+ * 17nov85 Dean Elsner. Name Change.
+ * Due to ar(1) truncating names, we learned the hard way that
+ * "vax-inst-parse.c" -> "vax-inst-parse." dropping the "o" off
+ * the archived object name. SO... we shortened the name of this
+ * source file, and changed the makefile.
+ */
+
+/* handle of the OPCODE hash table */
+static struct hash_control *op_hash;
+
+/*
+ * In: 1 character, from "bdfghloqpw" being the data-type of an operand
+ * of a vax instruction.
+ *
+ * Out: the length of an operand of that type, in bytes.
+ * Special branch operands types "-?!" have length 0.
+ */
+
+static const short int vax_operand_width_size[256] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 8, 0, 4, 8, 16, 0, 0, 0, 4, 0, 0,16, /* ..b.d.fgh...l..o */
+ 0, 8, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, /* .q.....w........ */
+ 0, 0, 1, 0, 8, 0, 4, 8, 16, 0, 0, 0, 4, 0, 0,16, /* ..b.d.fgh...l..o */
+ 0, 8, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, /* .q.....w........ */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+/*
+ * This perversion encodes all the vax opcodes as a bunch of strings.
+ * RMS says we should build our hash-table at run-time. Hmm.
+ * Please would someone arrange these in decreasing frequency of opcode?
+ * Because of the way hash_...() works, the most frequently used opcode
+ * should be textually first and so on.
+ *
+ * Input for this table was 'vax.opcodes', awk(1)ed by 'vax.opcodes.c.awk' .
+ * So change 'vax.opcodes', then re-generate this table.
+ */
+
+#include "opcode/vax.h"
+
+/*
+ * This is a table of optional op-codes. All of them represent
+ * 'synthetic' instructions that seem popular.
+ *
+ * Here we make some pseudo op-codes. Every code has a bit set to say
+ * it is synthetic. This lets you catch them if you want to
+ * ban these opcodes. They are mnemonics for "elastic" instructions
+ * that are supposed to assemble into the fewest bytes needed to do a
+ * branch, or to do a conditional branch, or whatever.
+ *
+ * The opcode is in the usual place [low-order n*8 bits]. This means
+ * that if you mask off the bucky bits, the usual rules apply about
+ * how long the opcode is.
+ *
+ * All VAX branch displacements come at the end of the instruction.
+ * For simple branches (1-byte opcode + 1-byte displacement) the last
+ * operand is coded 'b?' where the "data type" '?' is a clue that we
+ * may reverse the sense of the branch (complement lowest order bit)
+ * and branch around a jump. This is by far the most common case.
+ * That is why the VIT_OPCODE_SYNTHETIC bit is set: it says this is
+ * a 0-byte op-code followed by 2 or more bytes of operand address.
+ *
+ * If the op-code has VIT_OPCODE_SPECIAL set, then we have a more unusual
+ * case.
+ *
+ * For JBSB & JBR the treatment is the similar, except (1) we have a 'bw'
+ * option before (2) we can directly JSB/JMP because there is no condition.
+ * These operands have 'b-' as their access/data type.
+ *
+ * That leaves a bunch of random opcodes: JACBx, JxOBxxx. In these
+ * cases, we do the same idea. JACBxxx are all marked with a 'b!'
+ * JAOBxxx & JSOBxxx are marked with a 'b:'.
+ *
+ */
+#if (VIT_OPCODE_SYNTHETIC != 0x80000000)
+You have just broken the encoding below, which assumes the sign bit
+ means 'I am an imaginary instruction'.
+#endif
+
+#if (VIT_OPCODE_SPECIAL != 0x40000000)
+ You have just broken the encoding below, which assumes the 0x40 M bit means
+ 'I am not to be "optimised" the way normal branches are'.
+#endif
+
+static const struct vot
+ synthetic_votstrs[] =
+{
+ {"jbsb", {"b-", 0xC0000010}}, /* BSD 4.2 */
+/* jsb used already */
+ {"jbr", {"b-", 0xC0000011}}, /* BSD 4.2 */
+ {"jr", {"b-", 0xC0000011}}, /* consistent */
+ {"jneq", {"b?", 0x80000012}},
+ {"jnequ", {"b?", 0x80000012}},
+ {"jeql", {"b?", 0x80000013}},
+ {"jeqlu", {"b?", 0x80000013}},
+ {"jgtr", {"b?", 0x80000014}},
+ {"jleq", {"b?", 0x80000015}},
+/* un-used opcodes here */
+ {"jgeq", {"b?", 0x80000018}},
+ {"jlss", {"b?", 0x80000019}},
+ {"jgtru", {"b?", 0x8000001a}},
+ {"jlequ", {"b?", 0x8000001b}},
+ {"jvc", {"b?", 0x8000001c}},
+ {"jvs", {"b?", 0x8000001d}},
+ {"jgequ", {"b?", 0x8000001e}},
+ {"jcc", {"b?", 0x8000001e}},
+ {"jlssu", {"b?", 0x8000001f}},
+ {"jcs", {"b?", 0x8000001f}},
+
+ {"jacbw", {"rwrwmwb!", 0xC000003d}},
+ {"jacbf", {"rfrfmfb!", 0xC000004f}},
+ {"jacbd", {"rdrdmdb!", 0xC000006f}},
+ {"jacbb", {"rbrbmbb!", 0xC000009d}},
+ {"jacbl", {"rlrlmlb!", 0xC00000f1}},
+ {"jacbg", {"rgrgmgb!", 0xC0004ffd}},
+ {"jacbh", {"rhrhmhb!", 0xC0006ffd}},
+
+ {"jbs", {"rlvbb?", 0x800000e0}},
+ {"jbc", {"rlvbb?", 0x800000e1}},
+ {"jbss", {"rlvbb?", 0x800000e2}},
+ {"jbcs", {"rlvbb?", 0x800000e3}},
+ {"jbsc", {"rlvbb?", 0x800000e4}},
+ {"jbcc", {"rlvbb?", 0x800000e5}},
+ {"jbssi", {"rlvbb?", 0x800000e6}},
+ {"jbcci", {"rlvbb?", 0x800000e7}},
+ {"jlbs", {"rlb?", 0x800000e8}},
+ {"jlbc", {"rlb?", 0x800000e9}},
+
+ {"jaoblss", {"rlmlb:", 0xC00000f2}},
+ {"jaobleq", {"rlmlb:", 0xC00000f3}},
+ {"jsobgeq", {"mlb:", 0xC00000f4}},
+ {"jsobgtr", {"mlb:", 0xC00000f5}},
+
+/* CASEx has no branch addresses in our conception of it. */
+/* You should use ".word ..." statements after the "case ...". */
+
+ {"", {"", 0}} /* empty is end sentinel */
+
+}; /* synthetic_votstrs */
+
+/*
+ * v i p _ b e g i n ( )
+ *
+ * Call me once before you decode any lines.
+ * I decode votstrs into a hash table at op_hash (which I create).
+ * I return an error text or null.
+ * If you want, I will include the 'synthetic' jXXX instructions in the
+ * instruction table.
+ * You must nominate metacharacters for eg DEC's "#", "@", "^".
+ */
+
+static const char *
+vip_begin (synthetic_too, immediate, indirect, displen)
+ int synthetic_too; /* 1 means include jXXX op-codes. */
+ const char *immediate, *indirect, *displen;
+{
+ const struct vot *vP; /* scan votstrs */
+ const char *retval = 0; /* error text */
+
+ op_hash = hash_new ();
+
+ for (vP = votstrs; *vP->vot_name && !retval; vP++)
+ retval = hash_insert (op_hash, vP->vot_name, (PTR) &vP->vot_detail);
+
+ if (synthetic_too)
+ for (vP = synthetic_votstrs; *vP->vot_name && !retval; vP++)
+ retval = hash_insert (op_hash, vP->vot_name, (PTR) &vP->vot_detail);
+
+#ifndef CONST_TABLE
+ vip_op_defaults (immediate, indirect, displen);
+#endif
+
+ return retval;
+}
+
+
+/*
+ * v i p ( )
+ *
+ * This converts a string into a vax instruction.
+ * The string must be a bare single instruction in dec-vax (with BSD4 frobs)
+ * format.
+ * It provides some error messages: at most one fatal error message (which
+ * stops the scan) and at most one warning message for each operand.
+ * The vax instruction is returned in exploded form, since we have no
+ * knowledge of how you parse (or evaluate) your expressions.
+ * We do however strip off and decode addressing modes and operation
+ * mnemonic.
+ *
+ * The exploded instruction is returned to a struct vit of your choice.
+ * #include "vax-inst.h" to know what a struct vit is.
+ *
+ * This function's value is a string. If it is not "" then an internal
+ * logic error was found: read this code to assign meaning to the string.
+ * No argument string should generate such an error string:
+ * it means a bug in our code, not in the user's text.
+ *
+ * You MUST have called vip_begin() once before using this function.
+ */
+
+static void
+vip (vitP, instring)
+ struct vit *vitP; /* We build an exploded instruction here. */
+ char *instring; /* Text of a vax instruction: we modify. */
+{
+ /* How to bit-encode this opcode. */
+ struct vot_wot *vwP;
+ /* 1/skip whitespace.2/scan vot_how */
+ char *p;
+ char *q;
+ /* counts number of operands seen */
+ unsigned char count;
+ /* scan operands in struct vit */
+ struct vop *operandp;
+ /* error over all operands */
+ const char *alloperr;
+ /* Remember char, (we clobber it with '\0' temporarily). */
+ char c;
+ /* Op-code of this instruction. */
+ vax_opcodeT oc;
+
+ if (*instring == ' ')
+ ++instring; /* Skip leading whitespace. */
+ for (p = instring; *p && *p != ' '; p++);; /* MUST end in end-of-string or exactly 1 space. */
+ /* Scanned up to end of operation-code. */
+ /* Operation-code is ended with whitespace. */
+ if (p - instring == 0)
+ {
+ vitP->vit_error = _("No operator");
+ count = 0;
+ memset (vitP->vit_opcode, '\0', sizeof (vitP->vit_opcode));
+ }
+ else
+ {
+ c = *p;
+ *p = '\0';
+ /*
+ * Here with instring pointing to what better be an op-name, and p
+ * pointing to character just past that.
+ * We trust instring points to an op-name, with no whitespace.
+ */
+ vwP = (struct vot_wot *) hash_find (op_hash, instring);
+ *p = c; /* Restore char after op-code. */
+ if (vwP == 0)
+ {
+ vitP->vit_error = _("Unknown operator");
+ count = 0;
+ memset (vitP->vit_opcode, '\0', sizeof (vitP->vit_opcode));
+ }
+ else
+ {
+ /*
+ * We found a match! So lets pick up as many operands as the
+ * instruction wants, and even gripe if there are too many.
+ * We expect comma to seperate each operand.
+ * We let instring track the text, while p tracks a part of the
+ * struct vot.
+ */
+ const char *howp;
+ /*
+ * The lines below know about 2-byte opcodes starting FD,FE or FF.
+ * They also understand synthetic opcodes. Note:
+ * we return 32 bits of opcode, including bucky bits, BUT
+ * an opcode length is either 8 or 16 bits for vit_opcode_nbytes.
+ */
+ oc = vwP->vot_code; /* The op-code. */
+ vitP->vit_opcode_nbytes = (oc & 0xFF) >= 0xFD ? 2 : 1;
+ md_number_to_chars (vitP->vit_opcode, oc, 4);
+ count = 0; /* no operands seen yet */
+ instring = p; /* point just past operation code */
+ alloperr = "";
+ for (howp = vwP->vot_how, operandp = vitP->vit_operand;
+ !(alloperr && *alloperr) && *howp;
+ operandp++, howp += 2)
+ {
+ /*
+ * Here to parse one operand. Leave instring pointing just
+ * past any one ',' that marks the end of this operand.
+ */
+ if (!howp[1])
+ as_fatal (_("odd number of bytes in operand description"));
+ else if (*instring)
+ {
+ for (q = instring; (c = *q) && c != ','; q++)
+ ;
+ /*
+ * Q points to ',' or '\0' that ends argument. C is that
+ * character.
+ */
+ *q = 0;
+ operandp->vop_width = howp[1];
+ operandp->vop_nbytes = vax_operand_width_size[(unsigned) howp[1]];
+ operandp->vop_access = howp[0];
+ vip_op (instring, operandp);
+ *q = c; /* Restore input text. */
+ if (operandp->vop_error)
+ alloperr = _("Bad operand");
+ instring = q + (c ? 1 : 0); /* next operand (if any) */
+ count++; /* won another argument, may have an operr */
+ }
+ else
+ alloperr = _("Not enough operands");
+ }
+ if (!*alloperr)
+ {
+ if (*instring == ' ')
+ instring++; /* Skip whitespace. */
+ if (*instring)
+ alloperr = _("Too many operands");
+ }
+ vitP->vit_error = alloperr;
+ }
+ }
+ vitP->vit_operands = count;
+}
+
+#ifdef test
+
+/*
+ * Test program for above.
+ */
+
+struct vit myvit; /* build an exploded vax instruction here */
+char answer[100]; /* human types a line of vax assembler here */
+char *mybug; /* "" or an internal logic diagnostic */
+int mycount; /* number of operands */
+struct vop *myvop; /* scan operands from myvit */
+int mysynth; /* 1 means want synthetic opcodes. */
+char my_immediate[200];
+char my_indirect[200];
+char my_displen[200];
+
+main ()
+{
+ char *p;
+
+ printf ("0 means no synthetic instructions. ");
+ printf ("Value for vip_begin? ");
+ gets (answer);
+ sscanf (answer, "%d", &mysynth);
+ printf ("Synthetic opcodes %s be included.\n", mysynth ? "will" : "will not");
+ printf ("enter immediate symbols eg enter # ");
+ gets (my_immediate);
+ printf ("enter indirect symbols eg enter @ ");
+ gets (my_indirect);
+ printf ("enter displen symbols eg enter ^ ");
+ gets (my_displen);
+ if (p = vip_begin (mysynth, my_immediate, my_indirect, my_displen))
+ {
+ error ("vip_begin=%s", p);
+ }
+ printf ("An empty input line will quit you from the vax instruction parser\n");
+ for (;;)
+ {
+ printf ("vax instruction: ");
+ fflush (stdout);
+ gets (answer);
+ if (!*answer)
+ {
+ break; /* out of for each input text loop */
+ }
+ vip (&myvit, answer);
+ if (*myvit.vit_error)
+ {
+ printf ("ERR:\"%s\"\n", myvit.vit_error);
+ }
+ printf ("opcode=");
+ for (mycount = myvit.vit_opcode_nbytes, p = myvit.vit_opcode;
+ mycount;
+ mycount--, p++
+ )
+ {
+ printf ("%02x ", *p & 0xFF);
+ }
+ printf (" operand count=%d.\n", mycount = myvit.vit_operands);
+ for (myvop = myvit.vit_operand; mycount; mycount--, myvop++)
+ {
+ printf ("mode=%xx reg=%xx ndx=%xx len='%c'=%c%c%d. expr=\"",
+ myvop->vop_mode, myvop->vop_reg, myvop->vop_ndx,
+ myvop->vop_short, myvop->vop_access, myvop->vop_width,
+ myvop->vop_nbytes);
+ for (p = myvop->vop_expr_begin; p <= myvop->vop_expr_end; p++)
+ {
+ putchar (*p);
+ }
+ printf ("\"\n");
+ if (myvop->vop_error)
+ {
+ printf (" err:\"%s\"\n", myvop->vop_error);
+ }
+ if (myvop->vop_warn)
+ {
+ printf (" wrn:\"%s\"\n", myvop->vop_warn);
+ }
+ }
+ }
+ vip_end ();
+ exit (EXIT_SUCCESS);
+}
+
+#endif /* #ifdef test */
+
+/* end of vax_ins_parse.c */
+
+/* vax_reg_parse.c - convert a VAX register name to a number */
+
+/* Copyright (C) 1987 Free Software Foundation, Inc. A part of GNU. */
+
+/*
+ * v a x _ r e g _ p a r s e ( )
+ *
+ * Take 3 char.s, the last of which may be `\0` (non-existent)
+ * and return the VAX register number that they represent.
+ *
+ * Return -1 if they don't form a register name. Good names return
+ * a number from 0:15 inclusive.
+ *
+ * Case is not important in a name.
+ *
+ * Register names understood are:
+ *
+ * R0
+ * R1
+ * R2
+ * R3
+ * R4
+ * R5
+ * R6
+ * R7
+ * R8
+ * R9
+ * R10
+ * R11
+ * R12 AP
+ * R13 FP
+ * R14 SP
+ * R15 PC
+ *
+ */
+
+#include <ctype.h>
+#define AP (12)
+#define FP (13)
+#define SP (14)
+#define PC (15)
+
+int /* return -1 or 0:15 */
+vax_reg_parse (c1, c2, c3) /* 3 chars of register name */
+ char c1, c2, c3; /* c3 == 0 if 2-character reg name */
+{
+ int retval; /* return -1:15 */
+
+ retval = -1;
+
+ if (isupper (c1))
+ c1 = tolower (c1);
+ if (isupper (c2))
+ c2 = tolower (c2);
+ if (isdigit (c2) && c1 == 'r')
+ {
+ retval = c2 - '0';
+ if (isdigit (c3))
+ {
+ retval = retval * 10 + c3 - '0';
+ retval = (retval > 15) ? -1 : retval;
+ /* clamp the register value to 1 hex digit */
+ }
+ else if (c3)
+ retval = -1; /* c3 must be '\0' or a digit */
+ }
+ else if (c3) /* There are no three letter regs */
+ retval = -1;
+ else if (c2 == 'p')
+ {
+ switch (c1)
+ {
+ case 's':
+ retval = SP;
+ break;
+ case 'f':
+ retval = FP;
+ break;
+ case 'a':
+ retval = AP;
+ break;
+ default:
+ retval = -1;
+ }
+ }
+ else if (c1 == 'p' && c2 == 'c')
+ retval = PC;
+ else
+ retval = -1;
+ return (retval);
+}
+
+/*
+ * v i p _ o p ( )
+ *
+ * Parse a vax operand in DEC assembler notation.
+ * For speed, expect a string of whitespace to be reduced to a single ' '.
+ * This is the case for GNU AS, and is easy for other DEC-compatible
+ * assemblers.
+ *
+ * Knowledge about DEC VAX assembler operand notation lives here.
+ * This doesn't even know what a register name is, except it believes
+ * all register names are 2 or 3 characters, and lets vax_reg_parse() say
+ * what number each name represents.
+ * It does, however, know that PC, SP etc are special registers so it can
+ * detect addressing modes that are silly for those registers.
+ *
+ * Where possible, it delivers 1 fatal or 1 warning message if the operand
+ * is suspect. Exactly what we test for is still evolving.
+ */
+
+/*
+ * B u g s
+ *
+ * Arg block.
+ *
+ * There were a number of 'mismatched argument type' bugs to vip_op.
+ * The most general solution is to typedef each (of many) arguments.
+ * We used instead a typedef'd argument block. This is less modular
+ * than using seperate return pointers for each result, but runs faster
+ * on most engines, and seems to keep programmers happy. It will have
+ * to be done properly if we ever want to use vip_op as a general-purpose
+ * module (it was designed to be).
+ *
+ * G^
+ *
+ * Doesn't support DEC "G^" format operands. These always take 5 bytes
+ * to express, and code as modes 8F or 9F. Reason: "G^" deprives you of
+ * optimising to (say) a "B^" if you are lucky in the way you link.
+ * When someone builds a linker smart enough to convert "G^" to "B^", "W^"
+ * whenever possible, then we should implement it.
+ * If there is some other use for "G^", feel free to code it in!
+ *
+ *
+ * speed
+ *
+ * If I nested if()s more, I could avoid testing (*err) which would save
+ * time, space and page faults. I didn't nest all those if()s for clarity
+ * and because I think the mode testing can be re-arranged 1st to test the
+ * commoner constructs 1st. Does anybody have statistics on this?
+ *
+ *
+ *
+ * error messages
+ *
+ * In future, we should be able to 'compose' error messages in a scratch area
+ * and give the user MUCH more informative error messages. Although this takes
+ * a little more code at run-time, it will make this module much more self-
+ * documenting. As an example of what sucks now: most error messages have
+ * hardwired into them the DEC VAX metacharacters "#^@" which are nothing like
+ * the Un*x characters "$`*", that most users will expect from this AS.
+ */
+
+/*
+ * The input is a string, ending with '\0'.
+ *
+ * We also require a 'hint' of what kind of operand is expected: so
+ * we can remind caller not to write into literals for instance.
+ *
+ * The output is a skeletal instruction.
+ *
+ * The algorithm has two parts.
+ * 1. extract the syntactic features (parse off all the @^#-()+[] mode crud);
+ * 2. express the @^#-()+[] as some parameters suited to further analysis.
+ *
+ * 2nd step is where we detect the googles of possible invalid combinations
+ * a human (or compiler) might write. Note that if we do a half-way
+ * decent assembler, we don't know how long to make (eg) displacement
+ * fields when we first meet them (because they may not have defined values).
+ * So we must wait until we know how many bits are needed for each address,
+ * then we can know both length and opcodes of instructions.
+ * For reason(s) above, we will pass to our caller a 'broken' instruction
+ * of these major components, from which our caller can generate instructions:
+ * - displacement length I^ S^ L^ B^ W^ unspecified
+ * - mode (many)
+ * - register R0-R15 or absent
+ * - index register R0-R15 or absent
+ * - expression text what we don't parse
+ * - error text(s) why we couldn't understand the operand
+ */
+
+/*
+ * To decode output of this, test errtxt. If errtxt[0] == '\0', then
+ * we had no errors that prevented parsing. Also, if we ever report
+ * an internal bug, errtxt[0] is set non-zero. So one test tells you
+ * if the other outputs are to be taken seriously.
+ */
+
+
+/*
+ * Because this module is useful for both VMS and UN*X style assemblers
+ * and because of the variety of UN*X assemblers we must recognise
+ * the different conventions for assembler operand notation. For example
+ * VMS says "#42" for immediate mode, while most UN*X say "$42".
+ * We permit arbitrary sets of (single) characters to represent the
+ * 3 concepts that DEC writes '#', '@', '^'.
+ */
+
+/* character tests */
+#define VIP_IMMEDIATE 01 /* Character is like DEC # */
+#define VIP_INDIRECT 02 /* Char is like DEC @ */
+#define VIP_DISPLEN 04 /* Char is like DEC ^ */
+
+#define IMMEDIATEP(c) (vip_metacharacters [(c)&0xff]&VIP_IMMEDIATE)
+#define INDIRECTP(c) (vip_metacharacters [(c)&0xff]&VIP_INDIRECT)
+#define DISPLENP(c) (vip_metacharacters [(c)&0xff]&VIP_DISPLEN)
+
+/* We assume 8 bits per byte. Use vip_op_defaults() to set these up BEFORE we
+ * are ever called.
+ */
+
+#if defined(CONST_TABLE)
+#define _ 0,
+#define I VIP_IMMEDIATE,
+#define S VIP_INDIRECT,
+#define D VIP_DISPLEN,
+static const char
+vip_metacharacters[256] =
+{
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /* ^@ ^A ^B ^C ^D ^E ^F ^G ^H ^I ^J ^K ^L ^M ^N ^O*/
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /* ^P ^Q ^R ^S ^T ^U ^V ^W ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
+ _ _ _ _ I _ _ _ _ _ S _ _ _ _ _ /* sp ! " # $ % & ' ( ) * + , - . / */
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /*0 1 2 3 4 5 6 7 8 9 : ; < = > ?*/
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /*@ A B C D E F G H I J K L M N O*/
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /*P Q R S T U V W X Y Z [ \ ] ^ _*/
+ D _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /*` a b c d e f g h i j k l m n o*/
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /*p q r s t u v w x y z { | } ~ ^?*/
+
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+};
+#undef _
+#undef I
+#undef S
+#undef D
+#else
+static char vip_metacharacters[256];
+
+static void
+vip_op_1 (bit, syms)
+ int bit;
+ const char *syms;
+{
+ unsigned char t;
+
+ while ((t = *syms++) != 0)
+ vip_metacharacters[t] |= bit;
+}
+
+/* Can be called any time. More arguments may appear in future. */
+static void
+vip_op_defaults (immediate, indirect, displen)
+ const char *immediate;
+ const char *indirect;
+ const char *displen;
+{
+ vip_op_1 (VIP_IMMEDIATE, immediate);
+ vip_op_1 (VIP_INDIRECT, indirect);
+ vip_op_1 (VIP_DISPLEN, displen);
+}
+
+#endif
+
+
+/*
+ * Dec defines the semantics of address modes (and values)
+ * by a two-letter code, explained here.
+ *
+ * letter 1: access type
+ *
+ * a address calculation - no data access, registers forbidden
+ * b branch displacement
+ * m read - let go of bus - write back "modify"
+ * r read
+ * v bit field address: like 'a' but registers are OK
+ * w write
+ * space no operator (eg ".long foo") [our convention]
+ *
+ * letter 2: data type (i.e. width, alignment)
+ *
+ * b byte
+ * d double precision floating point (D format)
+ * f single precision floating point (F format)
+ * g G format floating
+ * h H format floating
+ * l longword
+ * o octaword
+ * q quadword
+ * w word
+ * ? simple synthetic branch operand
+ * - unconditional synthetic JSB/JSR operand
+ * ! complex synthetic branch operand
+ *
+ * The '-?!' letter 2's are not for external consumption. They are used
+ * for various assemblers. Generally, all unknown widths are assumed 0.
+ * We don't limit your choice of width character.
+ *
+ * DEC operands are hard work to parse. For example, '@' as the first
+ * character means indirect (deferred) mode but elswhere it is a shift
+ * operator.
+ * The long-winded explanation of how this is supposed to work is
+ * cancelled. Read a DEC vax manual.
+ * We try hard not to parse anything that MIGHT be part of the expression
+ * buried in that syntax. For example if we see @...(Rn) we don't check
+ * for '-' before the '(' because mode @-(Rn) does not exist.
+ *
+ * After parsing we have:
+ *
+ * at 1 if leading '@' (or Un*x '*')
+ * len takes one value from " bilsw". eg B^ -> 'b'.
+ * hash 1 if leading '#' (or Un*x '$')
+ * expr_begin, expr_end the expression we did not parse
+ * even though we don't interpret it, we make use
+ * of its presence or absence.
+ * sign -1: -(Rn) 0: absent +1: (Rn)+
+ * paren 1 if () are around register
+ * reg major register number 0:15 -1 means absent
+ * ndx index register number 0:15 -1 means absent
+ *
+ * Again, I dare not explain it: just trace ALL the code!
+ */
+
+static void
+vip_op (optext, vopP)
+ /* user's input string e.g.: "@B^foo@bar(AP)[FP]:" */
+ char *optext;
+ /* Input fields: vop_access, vop_width.
+ Output fields: _ndx, _reg, _mode, _short, _warn,
+ _error _expr_begin, _expr_end, _nbytes.
+ vop_nbytes : number of bytes in a datum. */
+ struct vop *vopP;
+{
+ /* track operand text forward */
+ char *p;
+ /* track operand text backward */
+ char *q;
+ /* 1 if leading '@' ('*') seen */
+ int at;
+ /* one of " bilsw" */
+ char len;
+ /* 1 if leading '#' ('$') seen */
+ int hash;
+ /* -1, 0 or +1 */
+ int sign = 0;
+ /* 1 if () surround register */
+ int paren = 0;
+ /* register number, -1:absent */
+ int reg = 0;
+ /* index register number -1:absent */
+ int ndx = 0;
+ /* report illegal operand, ""==OK */
+ /* " " is a FAKE error: means we won */
+ /* ANY err that begins with ' ' is a fake. */
+ /* " " is converted to "" before return */
+ const char *err;
+ /* warn about weird modes pf address */
+ const char *wrn;
+ /* preserve q in case we backup */
+ char *oldq = NULL;
+ /* build up 4-bit operand mode here */
+ /* note: index mode is in ndx, this is */
+ /* the major mode of operand address */
+ int mode = 0;
+ /*
+ * Notice how we move wrong-arg-type bugs INSIDE this module: if we
+ * get the types wrong below, we lose at compile time rather than at
+ * lint or run time.
+ */
+ char access_mode; /* vop_access. */
+ char width; /* vop_width. */
+
+ access_mode = vopP->vop_access;
+ width = vopP->vop_width;
+ /* None of our code bugs (yet), no user text errors, no warnings
+ even. */
+ err = wrn = 0;
+
+ p = optext;
+
+ if (*p == ' ') /* Expect all whitespace reduced to ' '. */
+ p++; /* skip over whitespace */
+
+ if ((at = INDIRECTP (*p)) != 0)
+ { /* 1 if *p=='@'(or '*' for Un*x) */
+ p++; /* at is determined */
+ if (*p == ' ') /* Expect all whitespace reduced to ' '. */
+ p++; /* skip over whitespace */
+ }
+
+ /*
+ * This code is subtle. It tries to detect all legal (letter)'^'
+ * but it doesn't waste time explicitly testing for premature '\0' because
+ * this case is rejected as a mismatch against either (letter) or '^'.
+ */
+ {
+ char c;
+
+ c = *p;
+ if (isupper (c))
+ c = tolower (c);
+ if (DISPLENP (p[1]) && strchr ("bilws", len = c))
+ p += 2; /* skip (letter) '^' */
+ else /* no (letter) '^' seen */
+ len = ' '; /* len is determined */
+ }
+
+ if (*p == ' ') /* Expect all whitespace reduced to ' '. */
+ p++; /* skip over whitespace */
+
+ if ((hash = IMMEDIATEP (*p)) != 0) /* 1 if *p=='#' ('$' for Un*x) */
+ p++; /* hash is determined */
+
+ /*
+ * p points to what may be the beginning of an expression.
+ * We have peeled off the front all that is peelable.
+ * We know at, len, hash.
+ *
+ * Lets point q at the end of the text and parse that (backwards).
+ */
+
+ for (q = p; *q; q++)
+ ;
+ q--; /* now q points at last char of text */
+
+ if (*q == ' ' && q >= p) /* Expect all whitespace reduced to ' '. */
+ q--;
+ /* reverse over whitespace, but don't */
+ /* run back over *p */
+
+ /*
+ * As a matter of policy here, we look for [Rn], although both Rn and S^#
+ * forbid [Rn]. This is because it is easy, and because only a sick
+ * cyborg would have [...] trailing an expression in a VAX-like assembler.
+ * A meticulous parser would first check for Rn followed by '(' or '['
+ * and not parse a trailing ']' if it found another. We just ban expressions
+ * ending in ']'.
+ */
+ if (*q == ']')
+ {
+ while (q >= p && *q != '[')
+ q--;
+ /* either q<p or we got matching '[' */
+ if (q < p)
+ err = _("no '[' to match ']'");
+ else
+ {
+ /*
+ * Confusers like "[]" will eventually lose with a bad register
+ * name error. So again we don't need to check for early '\0'.
+ */
+ if (q[3] == ']')
+ ndx = vax_reg_parse (q[1], q[2], 0);
+ else if (q[4] == ']')
+ ndx = vax_reg_parse (q[1], q[2], q[3]);
+ else
+ ndx = -1;
+ /*
+ * Since we saw a ']' we will demand a register name in the [].
+ * If luser hasn't given us one: be rude.
+ */
+ if (ndx < 0)
+ err = _("bad register in []");
+ else if (ndx == PC)
+ err = _("[PC] index banned");
+ else
+ q--; /* point q just before "[...]" */
+ }
+ }
+ else
+ ndx = -1; /* no ']', so no iNDeX register */
+
+ /*
+ * If err = "..." then we lost: run away.
+ * Otherwise ndx == -1 if there was no "[...]".
+ * Otherwise, ndx is index register number, and q points before "[...]".
+ */
+
+ if (*q == ' ' && q >= p) /* Expect all whitespace reduced to ' '. */
+ q--;
+ /* reverse over whitespace, but don't */
+ /* run back over *p */
+ if (!err || !*err)
+ {
+ sign = 0; /* no ()+ or -() seen yet */
+
+ if (q > p + 3 && *q == '+' && q[-1] == ')')
+ {
+ sign = 1; /* we saw a ")+" */
+ q--; /* q points to ')' */
+ }
+
+ if (*q == ')' && q > p + 2)
+ {
+ paren = 1; /* assume we have "(...)" */
+ while (q >= p && *q != '(')
+ q--;
+ /* either q<p or we got matching '(' */
+ if (q < p)
+ err = _("no '(' to match ')'");
+ else
+ {
+ /*
+ * Confusers like "()" will eventually lose with a bad register
+ * name error. So again we don't need to check for early '\0'.
+ */
+ if (q[3] == ')')
+ reg = vax_reg_parse (q[1], q[2], 0);
+ else if (q[4] == ')')
+ reg = vax_reg_parse (q[1], q[2], q[3]);
+ else
+ reg = -1;
+ /*
+ * Since we saw a ')' we will demand a register name in the ')'.
+ * This is nasty: why can't our hypothetical assembler permit
+ * parenthesised expressions? BECAUSE I AM LAZY! That is why.
+ * Abuse luser if we didn't spy a register name.
+ */
+ if (reg < 0)
+ {
+ /* JF allow parenthasized expressions. I hope this works */
+ paren = 0;
+ while (*q != ')')
+ q++;
+ /* err = "unknown register in ()"; */
+ }
+ else
+ q--; /* point just before '(' of "(...)" */
+ /*
+ * If err == "..." then we lost. Run away.
+ * Otherwise if reg >= 0 then we saw (Rn).
+ */
+ }
+ /*
+ * If err == "..." then we lost.
+ * Otherwise paren==1 and reg = register in "()".
+ */
+ }
+ else
+ paren = 0;
+ /*
+ * If err == "..." then we lost.
+ * Otherwise, q points just before "(Rn)", if any.
+ * If there was a "(...)" then paren==1, and reg is the register.
+ */
+
+ /*
+ * We should only seek '-' of "-(...)" if:
+ * we saw "(...)" paren == 1
+ * we have no errors so far ! *err
+ * we did not see '+' of "(...)+" sign < 1
+ * We don't check len. We want a specific error message later if
+ * user tries "x^...-(Rn)". This is a feature not a bug.
+ */
+ if (!err || !*err)
+ {
+ if (paren && sign < 1)/* !sign is adequate test */
+ {
+ if (*q == '-')
+ {
+ sign = -1;
+ q--;
+ }
+ }
+ /*
+ * We have back-tracked over most
+ * of the crud at the end of an operand.
+ * Unless err, we know: sign, paren. If paren, we know reg.
+ * The last case is of an expression "Rn".
+ * This is worth hunting for if !err, !paren.
+ * We wouldn't be here if err.
+ * We remember to save q, in case we didn't want "Rn" anyway.
+ */
+ if (!paren)
+ {
+ if (*q == ' ' && q >= p) /* Expect all whitespace reduced to ' '. */
+ q--;
+ /* reverse over whitespace, but don't */
+ /* run back over *p */
+ if (q > p && q < p + 3) /* room for Rn or Rnn exactly? */
+ reg = vax_reg_parse (p[0], p[1], q < p + 2 ? 0 : p[2]);
+ else
+ reg = -1; /* always comes here if no register at all */
+ /*
+ * Here with a definitive reg value.
+ */
+ if (reg >= 0)
+ {
+ oldq = q;
+ q = p - 1;
+ }
+ }
+ }
+ }
+ /*
+ * have reg. -1:absent; else 0:15
+ */
+
+ /*
+ * We have: err, at, len, hash, ndx, sign, paren, reg.
+ * Also, any remaining expression is from *p through *q inclusive.
+ * Should there be no expression, q==p-1. So expression length = q-p+1.
+ * This completes the first part: parsing the operand text.
+ */
+
+ /*
+ * We now want to boil the data down, checking consistency on the way.
+ * We want: len, mode, reg, ndx, err, p, q, wrn, bug.
+ * We will deliver a 4-bit reg, and a 4-bit mode.
+ */
+
+ /*
+ * Case of branch operand. Different. No L^B^W^I^S^ allowed for instance.
+ *
+ * in: at ?
+ * len ?
+ * hash ?
+ * p:q ?
+ * sign ?
+ * paren ?
+ * reg ?
+ * ndx ?
+ *
+ * out: mode 0
+ * reg -1
+ * len ' '
+ * p:q whatever was input
+ * ndx -1
+ * err " " or error message, and other outputs trashed
+ */
+ /* branch operands have restricted forms */
+ if ((!err || !*err) && access_mode == 'b')
+ {
+ if (at || hash || sign || paren || ndx >= 0 || reg >= 0 || len != ' ')
+ err = _("invalid branch operand");
+ else
+ err = " ";
+ }
+
+ /* Since nobody seems to use it: comment this 'feature'(?) out for now. */
+#ifdef NEVER
+ /*
+ * Case of stand-alone operand. e.g. ".long foo"
+ *
+ * in: at ?
+ * len ?
+ * hash ?
+ * p:q ?
+ * sign ?
+ * paren ?
+ * reg ?
+ * ndx ?
+ *
+ * out: mode 0
+ * reg -1
+ * len ' '
+ * p:q whatever was input
+ * ndx -1
+ * err " " or error message, and other outputs trashed
+ */
+ if ((!err || !*err) && access_mode == ' ')
+ {
+ if (at)
+ err = _("address prohibits @");
+ else if (hash)
+ err = _("address prohibits #");
+ else if (sign)
+ {
+ if (sign < 0)
+ err = _("address prohibits -()");
+ else
+ err = _("address prohibits ()+");
+ }
+ else if (paren)
+ err = _("address prohibits ()");
+ else if (ndx >= 0)
+ err = _("address prohibits []");
+ else if (reg >= 0)
+ err = _("address prohibits register");
+ else if (len != ' ')
+ err = _("address prohibits displacement length specifier");
+ else
+ {
+ err = " "; /* succeed */
+ mode = 0;
+ }
+ }
+#endif /*#Ifdef NEVER*/
+
+ /*
+ * Case of S^#.
+ *
+ * in: at 0
+ * len 's' definition
+ * hash 1 demand
+ * p:q demand not empty
+ * sign 0 by paren==0
+ * paren 0 by "()" scan logic because "S^" seen
+ * reg -1 or nn by mistake
+ * ndx -1
+ *
+ * out: mode 0
+ * reg -1
+ * len 's'
+ * exp
+ * ndx -1
+ */
+ if ((!err || !*err) && len == 's')
+ {
+ if (!hash || paren || at || ndx >= 0)
+ err = _("invalid operand of S^#");
+ else
+ {
+ if (reg >= 0)
+ {
+ /*
+ * SHIT! we saw S^#Rnn ! put the Rnn back in
+ * expression. KLUDGE! Use oldq so we don't
+ * need to know exact length of reg name.
+ */
+ q = oldq;
+ reg = 0;
+ }
+ /*
+ * We have all the expression we will ever get.
+ */
+ if (p > q)
+ err = _("S^# needs expression");
+ else if (access_mode == 'r')
+ {
+ err = " "; /* WIN! */
+ mode = 0;
+ }
+ else
+ err = _("S^# may only read-access");
+ }
+ }
+
+ /*
+ * Case of -(Rn), which is weird case.
+ *
+ * in: at 0
+ * len '
+ * hash 0
+ * p:q q<p
+ * sign -1 by definition
+ * paren 1 by definition
+ * reg present by definition
+ * ndx optional
+ *
+ * out: mode 7
+ * reg present
+ * len ' '
+ * exp "" enforce empty expression
+ * ndx optional warn if same as reg
+ */
+ if ((!err || !*err) && sign < 0)
+ {
+ if (len != ' ' || hash || at || p <= q)
+ err = _("invalid operand of -()");
+ else
+ {
+ err = " "; /* win */
+ mode = 7;
+ if (reg == PC)
+ wrn = _("-(PC) unpredictable");
+ else if (reg == ndx)
+ wrn = _("[]index same as -()register: unpredictable");
+ }
+ }
+
+ /*
+ * We convert "(Rn)" to "@Rn" for our convenience.
+ * (I hope this is convenient: has someone got a better way to parse this?)
+ * A side-effect of this is that "@Rn" is a valid operand.
+ */
+ if (paren && !sign && !hash && !at && len == ' ' && p > q)
+ {
+ at = 1;
+ paren = 0;
+ }
+
+ /*
+ * Case of (Rn)+, which is slightly different.
+ *
+ * in: at
+ * len ' '
+ * hash 0
+ * p:q q<p
+ * sign +1 by definition
+ * paren 1 by definition
+ * reg present by definition
+ * ndx optional
+ *
+ * out: mode 8+@
+ * reg present
+ * len ' '
+ * exp "" enforce empty expression
+ * ndx optional warn if same as reg
+ */
+ if ((!err || !*err) && sign > 0)
+ {
+ if (len != ' ' || hash || p <= q)
+ err = _("invalid operand of ()+");
+ else
+ {
+ err = " "; /* win */
+ mode = 8 + (at ? 1 : 0);
+ if (reg == PC)
+ wrn = _("(PC)+ unpredictable");
+ else if (reg == ndx)
+ wrn = _("[]index same as ()+register: unpredictable");
+ }
+ }
+
+ /*
+ * Case of #, without S^.
+ *
+ * in: at
+ * len ' ' or 'i'
+ * hash 1 by definition
+ * p:q
+ * sign 0
+ * paren 0
+ * reg absent
+ * ndx optional
+ *
+ * out: mode 8+@
+ * reg PC
+ * len ' ' or 'i'
+ * exp
+ * ndx optional
+ */
+ if ((!err || !*err) && hash)
+ {
+ if (len != 'i' && len != ' ')
+ err = _("# conflicts length");
+ else if (paren)
+ err = _("# bars register");
+ else
+ {
+ if (reg >= 0)
+ {
+ /*
+ * SHIT! we saw #Rnn! Put the Rnn back into the expression.
+ * By using oldq, we don't need to know how long Rnn was.
+ * KLUDGE!
+ */
+ q = oldq;
+ reg = -1; /* no register any more */
+ }
+ err = " "; /* win */
+
+ /* JF a bugfix, I think! */
+ if (at && access_mode == 'a')
+ vopP->vop_nbytes = 4;
+
+ mode = (at ? 9 : 8);
+ reg = PC;
+ if ((access_mode == 'm' || access_mode == 'w') && !at)
+ wrn = _("writing or modifying # is unpredictable");
+ }
+ }
+ /*
+ * If !*err, then sign == 0
+ * hash == 0
+ */
+
+ /*
+ * Case of Rn. We seperate this one because it has a few special
+ * errors the remaining modes lack.
+ *
+ * in: at optional
+ * len ' '
+ * hash 0 by program logic
+ * p:q empty
+ * sign 0 by program logic
+ * paren 0 by definition
+ * reg present by definition
+ * ndx optional
+ *
+ * out: mode 5+@
+ * reg present
+ * len ' ' enforce no length
+ * exp "" enforce empty expression
+ * ndx optional warn if same as reg
+ */
+ if ((!err || !*err) && !paren && reg >= 0)
+ {
+ if (len != ' ')
+ err = _("length not needed");
+ else if (at)
+ {
+ err = " "; /* win */
+ mode = 6; /* @Rn */
+ }
+ else if (ndx >= 0)
+ err = _("can't []index a register, because it has no address");
+ else if (access_mode == 'a')
+ err = _("a register has no address");
+ else
+ {
+ /*
+ * Idea here is to detect from length of datum
+ * and from register number if we will touch PC.
+ * Warn if we do.
+ * vop_nbytes is number of bytes in operand.
+ * Compute highest byte affected, compare to PC0.
+ */
+ if ((vopP->vop_nbytes + reg * 4) > 60)
+ wrn = _("PC part of operand unpredictable");
+ err = " "; /* win */
+ mode = 5; /* Rn */
+ }
+ }
+ /*
+ * If !*err, sign == 0
+ * hash == 0
+ * paren == 1 OR reg==-1
+ */
+
+ /*
+ * Rest of cases fit into one bunch.
+ *
+ * in: at optional
+ * len ' ' or 'b' or 'w' or 'l'
+ * hash 0 by program logic
+ * p:q expected (empty is not an error)
+ * sign 0 by program logic
+ * paren optional
+ * reg optional
+ * ndx optional
+ *
+ * out: mode 10 + @ + len
+ * reg optional
+ * len ' ' or 'b' or 'w' or 'l'
+ * exp maybe empty
+ * ndx optional warn if same as reg
+ */
+ if (!err || !*err)
+ {
+ err = " "; /* win (always) */
+ mode = 10 + (at ? 1 : 0);
+ switch (len)
+ {
+ case 'l':
+ mode += 2;
+ case 'w':
+ mode += 2;
+ case ' ': /* assumed B^ until our caller changes it */
+ case 'b':
+ break;
+ }
+ }
+
+ /*
+ * here with completely specified mode
+ * len
+ * reg
+ * expression p,q
+ * ndx
+ */
+
+ if (*err == ' ')
+ err = 0; /* " " is no longer an error */
+
+ vopP->vop_mode = mode;
+ vopP->vop_reg = reg;
+ vopP->vop_short = len;
+ vopP->vop_expr_begin = p;
+ vopP->vop_expr_end = q;
+ vopP->vop_ndx = ndx;
+ vopP->vop_error = err;
+ vopP->vop_warn = wrn;
+}
+
+/*
+
+ Summary of vip_op outputs.
+
+ mode reg len ndx
+ (Rn) => @Rn
+ {@}Rn 5+@ n ' ' optional
+ branch operand 0 -1 ' ' -1
+ S^#foo 0 -1 's' -1
+ -(Rn) 7 n ' ' optional
+ {@}(Rn)+ 8+@ n ' ' optional
+ {@}#foo, no S^ 8+@ PC " i" optional
+ {@}{q^}{(Rn)} 10+@+q option " bwl" optional
+
+ */
+
+#ifdef TEST /* #Define to use this testbed. */
+
+/*
+ * Follows a test program for this function.
+ * We declare arrays non-local in case some of our tiny-minded machines
+ * default to small stacks. Also, helps with some debuggers.
+ */
+
+#include <stdio.h>
+
+char answer[100]; /* human types into here */
+char *p; /* */
+char *myerr;
+char *mywrn;
+char *mybug;
+char myaccess;
+char mywidth;
+char mymode;
+char myreg;
+char mylen;
+char *myleft;
+char *myright;
+char myndx;
+int my_operand_length;
+char my_immediate[200];
+char my_indirect[200];
+char my_displen[200];
+
+main ()
+{
+ printf ("enter immediate symbols eg enter # ");
+ gets (my_immediate);
+ printf ("enter indirect symbols eg enter @ ");
+ gets (my_indirect);
+ printf ("enter displen symbols eg enter ^ ");
+ gets (my_displen);
+ vip_op_defaults (my_immediate, my_indirect, my_displen);
+ for (;;)
+ {
+ printf ("access,width (eg 'ab' or 'wh') [empty line to quit] : ");
+ fflush (stdout);
+ gets (answer);
+ if (!answer[0])
+ exit (EXIT_SUCCESS);
+ myaccess = answer[0];
+ mywidth = answer[1];
+ switch (mywidth)
+ {
+ case 'b':
+ my_operand_length = 1;
+ break;
+ case 'd':
+ my_operand_length = 8;
+ break;
+ case 'f':
+ my_operand_length = 4;
+ break;
+ case 'g':
+ my_operand_length = 16;
+ break;
+ case 'h':
+ my_operand_length = 32;
+ break;
+ case 'l':
+ my_operand_length = 4;
+ break;
+ case 'o':
+ my_operand_length = 16;
+ break;
+ case 'q':
+ my_operand_length = 8;
+ break;
+ case 'w':
+ my_operand_length = 2;
+ break;
+ case '!':
+ case '?':
+ case '-':
+ my_operand_length = 0;
+ break;
+
+ default:
+ my_operand_length = 2;
+ printf ("I dn't understand access width %c\n", mywidth);
+ break;
+ }
+ printf ("VAX assembler instruction operand: ");
+ fflush (stdout);
+ gets (answer);
+ mybug = vip_op (answer, myaccess, mywidth, my_operand_length,
+ &mymode, &myreg, &mylen, &myleft, &myright, &myndx,
+ &myerr, &mywrn);
+ if (*myerr)
+ {
+ printf ("error: \"%s\"\n", myerr);
+ if (*mybug)
+ printf (" bug: \"%s\"\n", mybug);
+ }
+ else
+ {
+ if (*mywrn)
+ printf ("warning: \"%s\"\n", mywrn);
+ mumble ("mode", mymode);
+ mumble ("register", myreg);
+ mumble ("index", myndx);
+ printf ("width:'%c' ", mylen);
+ printf ("expression: \"");
+ while (myleft <= myright)
+ putchar (*myleft++);
+ printf ("\"\n");
+ }
+ }
+}
+
+mumble (text, value)
+ char *text;
+ int value;
+{
+ printf ("%s:", text);
+ if (value >= 0)
+ printf ("%xx", value);
+ else
+ printf ("ABSENT");
+ printf (" ");
+}
+
+#endif /* ifdef TEST */
+
+/* end: vip_op.c */
+
+const int md_short_jump_size = 3;
+const int md_long_jump_size = 6;
+const int md_reloc_size = 8; /* Size of relocation record */
+
+void
+md_create_short_jump (ptr, from_addr, to_addr, frag, to_symbol)
+ char *ptr;
+ addressT from_addr, to_addr;
+ fragS *frag;
+ symbolS *to_symbol;
+{
+ valueT offset;
+
+ /* This former calculation was off by two:
+ offset = to_addr - (from_addr + 1);
+ We need to account for the one byte instruction and also its
+ two byte operand. */
+ offset = to_addr - (from_addr + 1 + 2);
+ *ptr++ = VAX_BRW; /* branch with word (16 bit) offset */
+ md_number_to_chars (ptr, offset, 2);
+}
+
+void
+md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol)
+ char *ptr;
+ addressT from_addr, to_addr;
+ fragS *frag;
+ symbolS *to_symbol;
+{
+ valueT offset;
+
+ offset = to_addr - S_GET_VALUE (to_symbol);
+ *ptr++ = VAX_JMP; /* arbitrary jump */
+ *ptr++ = VAX_ABSOLUTE_MODE;
+ md_number_to_chars (ptr, offset, 4);
+ fix_new (frag, ptr - frag->fr_literal, 4, to_symbol, (long) 0, 0, NO_RELOC);
+}
+
+#ifdef OBJ_VMS
+CONST char *md_shortopts = "d:STt:V+1h:Hv::";
+#else
+CONST char *md_shortopts = "d:STt:V";
+#endif
+struct option md_longopts[] = {
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ switch (c)
+ {
+ case 'S':
+ as_warn (_("SYMBOL TABLE not implemented"));
+ break;
+
+ case 'T':
+ as_warn (_("TOKEN TRACE not implemented"));
+ break;
+
+ case 'd':
+ as_warn (_("Displacement length %s ignored!"), arg);
+ break;
+
+ case 't':
+ as_warn (_("I don't need or use temp. file \"%s\"."), arg);
+ break;
+
+ case 'V':
+ as_warn (_("I don't use an interpass file! -V ignored"));
+ break;
+
+#ifdef OBJ_VMS
+ case '+': /* For g++. Hash any name > 31 chars long. */
+ flag_hash_long_names = 1;
+ break;
+
+ case '1': /* For backward compatibility */
+ flag_one = 1;
+ break;
+
+ case 'H': /* Show new symbol after hash truncation */
+ flag_show_after_trunc = 1;
+ break;
+
+ case 'h': /* No hashing of mixed-case names */
+ {
+ extern char vms_name_mapping;
+ vms_name_mapping = atoi (arg);
+ flag_no_hash_mixed_case = 1;
+ }
+ break;
+
+ case 'v':
+ {
+ extern char *compiler_version_string;
+ if (!arg || !*arg || access (arg, 0) == 0)
+ return 0; /* have caller show the assembler version */
+ compiler_version_string = arg;
+ }
+ break;
+#endif
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ fprintf(stream, _("\
+VAX options:\n\
+-d LENGTH ignored\n\
+-J ignored\n\
+-S ignored\n\
+-t FILE ignored\n\
+-T ignored\n\
+-V ignored\n"));
+#ifdef OBJ_VMS
+ fprintf (stream, _("\
+VMS options:\n\
+-+ hash encode names longer than 31 characters\n\
+-1 `const' handling compatible with gcc 1.x\n\
+-H show new symbol after hash truncation\n\
+-h NUM don't hash mixed-case names, and adjust case:\n\
+ 0 = upper, 2 = lower, 3 = preserve case\n\
+-v\"VERSION\" code being assembled was produced by compiler \"VERSION\"\n"));
+#endif
+}
+
+/* We have no need to default values of symbols. */
+
+/* ARGSUSED */
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ return 0;
+}
+
+/* Round up a section size to the appropriate boundary. */
+valueT
+md_section_align (segment, size)
+ segT segment;
+ valueT size;
+{
+ return size; /* Byte alignment is fine */
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the vax, they're relative to the address of the offset, plus
+ its size. (??? Is this right? FIXME-SOON) */
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+/* end of tc-vax.c */
diff --git a/gas/config/tc-vax.h b/gas/config/tc-vax.h
new file mode 100644
index 0000000..b6791f7
--- /dev/null
+++ b/gas/config/tc-vax.h
@@ -0,0 +1,45 @@
+/* tc-vax.h -- Header file for tc-vax.c.
+ Copyright (C) 1987, 91, 92, 93, 95, 96, 1997 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#define TC_VAX 1
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define NO_RELOC 0
+#define NOP_OPCODE 0x01
+
+#define tc_aout_pre_write_hook(x) {;} /* not used */
+#define tc_crawl_symbol_chain(a) {;} /* not used */
+#define tc_headers_hook(a) {;} /* not used */
+#define md_operand(x)
+
+long md_chars_to_number PARAMS ((unsigned char *, int));
+
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of tc-vax.h */
diff --git a/gas/config/tc-w65.c b/gas/config/tc-w65.c
new file mode 100644
index 0000000..72201e7
--- /dev/null
+++ b/gas/config/tc-w65.c
@@ -0,0 +1,1219 @@
+/* tc-w65.c -- Assemble code for the W65816
+ Copyright (C) 1995, 1998 Free Software Foundation.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+/*
+ Written By Steve Chamberlain
+ sac@cygnus.com
+ */
+
+#include <stdio.h>
+#include "as.h"
+#include "bfd.h"
+#include "subsegs.h"
+#define DEFINE_TABLE
+#include "../opcodes/w65-opc.h"
+#include <ctype.h>
+
+const char comment_chars[] = "!";
+CONST char line_separator_chars[] = ";";
+const char line_comment_chars[] = "!#";
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ Integer arg to pass to the function
+ */
+
+#define OP_BCC 0x90
+#define OP_BCS 0xB0
+#define OP_BEQ 0xF0
+#define OP_BMI 0x30
+#define OP_BNE 0xD0
+#define OP_BPL 0x10
+#define OP_BRA 0x80
+#define OP_BRL 0x82
+#define OP_BVC 0x50
+#define OP_BVS 0x70
+
+void s_longa ();
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"int", cons, 2},
+ {"word", cons, 2},
+ {"longa", s_longa, 0},
+ {"longi", s_longa, 1},
+ {0, 0, 0}
+};
+
+
+void cons ();
+void s_align_bytes ();
+
+
+/*int md_reloc_size; */
+
+static int relax; /* set if -relax seen */
+
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+
+
+static struct hash_control *opcode_hash_control; /* Opcode mnemonics */
+
+int M; /* M flag */
+int X; /* X flag */
+
+
+
+
+#define C(a,b) ENCODE_RELAX(a,b)
+#define ENCODE_RELAX(what,length) (((what) << 2) + (length))
+
+#define GET_WHAT(x) ((x>>2))
+
+#define BYTE_DISP 1
+#define WORD_DISP 2
+#define UNDEF_BYTE_DISP 0
+#define UNDEF_WORD_DISP 3
+
+#define COND_BRANCH 1
+#define UNCOND_BRANCH 2
+#define END 3
+
+#define BYTE_F 127 /* How far we can branch forwards */
+#define BYTE_B -126 /* How far we can branch backwards */
+#define WORD_F 32767
+#define WORD_B 32768
+
+relax_typeS md_relax_table[C (END, 0)];
+
+/*
+ This function is called once, at assembler startup time. This should
+ set up all the tables, etc that the MD part of the assembler needs
+ */
+
+
+void
+s_longa (xmode)
+{
+ int *p = xmode ? &X : &M;
+ while (*input_line_pointer == ' ')
+ input_line_pointer++;
+ if (strncmp (input_line_pointer, "on", 2) == 0)
+ {
+ input_line_pointer += 2;
+ *p = 0;
+ }
+ else if (strncmp (input_line_pointer, "off", 3) == 0)
+ {
+ *p = 1;
+ input_line_pointer += 3;
+ }
+ else
+ as_bad (_("need on or off."));
+ demand_empty_rest_of_line ();
+}
+void
+md_begin ()
+{
+ relax_typeS *table;
+ struct opinfo *opcode;
+ char *prev_name = "";
+
+ opcode_hash_control = hash_new ();
+
+ /* Insert unique names into hash table */
+ for (opcode = optable; opcode->name; opcode++)
+ {
+ if (strcmp (prev_name, opcode->name))
+ {
+ prev_name = opcode->name;
+ hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
+ }
+ else
+ {
+ /* Make all the opcodes with the same name point to the same
+ string */
+ opcode->name = prev_name;
+ }
+ }
+
+
+ /* Initialize the relax table. We use a local variable to avoid
+ warnings about modifying a supposedly const data structure. */
+ table = (relax_typeS *) md_relax_table;
+ table[C (COND_BRANCH, BYTE_DISP)].rlx_forward = BYTE_F;
+ table[C (COND_BRANCH, BYTE_DISP)].rlx_backward = BYTE_B;
+ table[C (COND_BRANCH, BYTE_DISP)].rlx_length = 2;
+ table[C (COND_BRANCH, BYTE_DISP)].rlx_more = C (COND_BRANCH, WORD_DISP);
+
+ table[C (COND_BRANCH, WORD_DISP)].rlx_forward = WORD_F;
+ table[C (COND_BRANCH, WORD_DISP)].rlx_backward = WORD_B;
+ table[C (COND_BRANCH, WORD_DISP)].rlx_length = 5;
+ table[C (COND_BRANCH, WORD_DISP)].rlx_more = 0;
+
+ table[C (UNCOND_BRANCH, BYTE_DISP)].rlx_forward = BYTE_F;
+ table[C (UNCOND_BRANCH, BYTE_DISP)].rlx_backward = BYTE_B;
+ table[C (UNCOND_BRANCH, BYTE_DISP)].rlx_length = 2;
+ table[C (UNCOND_BRANCH, BYTE_DISP)].rlx_more = C (UNCOND_BRANCH, WORD_DISP);
+
+ table[C (UNCOND_BRANCH, WORD_DISP)].rlx_forward = WORD_F;
+ table[C (UNCOND_BRANCH, WORD_DISP)].rlx_backward = WORD_B;
+ table[C (UNCOND_BRANCH, WORD_DISP)].rlx_length = 3;
+ table[C (UNCOND_BRANCH, WORD_DISP)].rlx_more = 0;
+
+ flag_signed_overflow_ok = 1;
+}
+
+static expressionS immediate; /* absolute expression */
+static expressionS immediate1; /* absolute expression */
+
+
+static symbolS *
+dot ()
+{
+ const char *fake;
+
+ /* JF: '.' is pseudo symbol with value of current location
+ in current segment. */
+ fake = FAKE_LABEL_NAME;
+ return symbol_new (fake,
+ now_seg,
+ (valueT) frag_now_fix (),
+ frag_now);
+
+}
+
+int expr_size;
+int expr_shift;
+int tc_cons_reloc;
+void
+w65_expression (dest, bytes)
+ expressionS *dest;
+ unsigned int bytes;
+{
+ expr_size = 0;
+ expr_shift = 0;
+ tc_cons_reloc = 0;
+ while (*input_line_pointer == ' ')
+ input_line_pointer++;
+
+ if (*input_line_pointer == '<')
+ {
+ expr_size = 1;
+ input_line_pointer++;
+ }
+ else if (*input_line_pointer == '>')
+ {
+ expr_shift = 1;
+ input_line_pointer++;
+ }
+ else if (*input_line_pointer == '^')
+ {
+ expr_shift = 2;
+ input_line_pointer++;
+ }
+
+ expr (0, dest);
+}
+
+int amode;
+static
+char *
+parse_exp (s, bytes)
+ char *s;
+ int bytes;
+{
+ char *save;
+ char *new;
+
+ save = input_line_pointer;
+ input_line_pointer = s;
+ w65_expression (&immediate, bytes);
+ if (immediate.X_op == O_absent)
+ as_bad (_("missing operand"));
+ new = input_line_pointer;
+ input_line_pointer = save;
+ return new;
+}
+
+
+static
+char *
+get_operands (info, ptr)
+ struct opinfo *info;
+ char *ptr;
+{
+ register int override_len = 0;
+ register int bytes = 0;
+ while (*ptr == ' ')
+ ptr++;
+
+ if (ptr[0] == '#')
+ {
+ ptr++;
+ switch (info->amode)
+ {
+ case ADDR_IMMTOI:
+ bytes = X ? 1 : 2;
+ amode = ADDR_IMMTOI;
+ break;
+ case ADDR_IMMTOA:
+ bytes = M ? 1 : 2;
+ amode = ADDR_IMMTOA;
+ break;
+ case ADDR_IMMCOP:
+ bytes = 1;
+ amode = ADDR_IMMCOP;
+ break;
+ case ADDR_DIR:
+ bytes = 2;
+ amode = ADDR_ABS;
+ break;
+ default:
+ abort ();
+ break;
+ }
+ ptr = parse_exp (ptr);
+ }
+ else if (ptr[0] == '!')
+ {
+ ptr = parse_exp (ptr + 1);
+ if (ptr[0] == ',')
+ {
+ if (ptr[1] == 'y')
+ {
+ amode = ADDR_ABS_IDX_Y;
+ bytes = 2;
+ ptr += 2;
+ }
+ else if (ptr[1] == 'x')
+ {
+ amode = ADDR_ABS_IDX_X;
+ bytes = 2;
+ ptr += 2;
+ }
+ else
+ {
+ as_bad (_("syntax error after <exp"));
+ }
+ }
+ else
+ {
+ amode = ADDR_ABS;
+ bytes = 2;
+ }
+ }
+ else if (ptr[0] == '>')
+ {
+ ptr = parse_exp (ptr + 1);
+ if (ptr[0] == ',' && ptr[1] == 'x')
+ {
+ amode = ADDR_ABS_LONG_IDX_X;
+ bytes = 3;
+ ptr += 2;
+ }
+ else
+ {
+ amode = ADDR_ABS_LONG;
+ bytes = 3;
+ }
+ }
+ else if (ptr[0] == '<')
+ {
+ ptr = parse_exp (ptr + 1);
+ if (ptr[0] == ',')
+ {
+ if (ptr[1] == 'y')
+ {
+ amode = ADDR_DIR_IDX_Y;
+ ptr += 2;
+ bytes = 2;
+ }
+ else if (ptr[1] == 'x')
+ {
+ amode = ADDR_DIR_IDX_X;
+ ptr += 2;
+ bytes = 2;
+ }
+ else
+ {
+ as_bad (_("syntax error after <exp"));
+ }
+ }
+ else
+ {
+ amode = ADDR_DIR;
+ bytes = 1;
+ }
+ }
+ else if (ptr[0] == 'a')
+ {
+ amode = ADDR_ACC;
+ }
+ else if (ptr[0] == '(')
+ {
+ /* Look for (exp),y
+ (<exp),y
+ (exp,x)
+ (<exp,x)
+ (exp)
+ (!exp)
+ (exp)
+ (<exp)
+ (exp,x)
+ (!exp,x)
+ (exp,s)
+ (exp,s),y */
+
+ ptr++;
+ if (ptr[0] == '<')
+ {
+ override_len = 1;
+ ptr++;
+ }
+ else if (ptr[0] == '!')
+ {
+ override_len = 2;
+ ptr++;
+ }
+ else if (ptr[0] == '>')
+ {
+ override_len = 3;
+ ptr++;
+ }
+ else
+ {
+ override_len = 0;
+ }
+ ptr = parse_exp (ptr);
+
+ if (ptr[0] == ',')
+ {
+ ptr++;
+ if (ptr[0] == 'x' && ptr[1] == ')')
+ {
+ ptr += 2;
+
+ if (override_len == 1)
+ {
+ amode = ADDR_DIR_IDX_IND_X;
+ bytes = 2;
+ }
+ else
+ {
+ amode = ADDR_ABS_IND_IDX;
+ bytes = 2;
+ }
+ }
+ else if (ptr[0] == 's' && ptr[1] == ')' && ptr[2] == ',' && ptr[3] == 'y')
+ {
+ amode = ADDR_STACK_REL_INDX_IDX;
+ bytes = 1;
+ ptr += 4;
+ }
+ }
+ else if (ptr[0] == ')')
+ {
+ if (ptr[1] == ',' && ptr[2] == 'y')
+ {
+ amode = ADDR_DIR_IND_IDX_Y;
+ ptr += 3;
+ bytes = 2;
+ }
+ else
+ {
+ if (override_len == 1)
+ {
+ amode = ADDR_DIR_IND;
+ bytes = 1;
+ }
+ else
+ {
+ amode = ADDR_ABS_IND;
+ bytes = 2;
+ }
+ ptr++;
+
+ }
+ }
+
+ }
+ else if (ptr[0] == '[')
+ {
+ ptr = parse_exp (ptr + 1);
+ if (ptr[0] == ']')
+ {
+ ptr++;
+ if (ptr[0] == ',' && ptr[1] == 'y')
+ {
+ bytes = 1;
+ amode = ADDR_DIR_IND_IDX_Y_LONG;
+ ptr += 2;
+ }
+ else
+ {
+ if (info->code == O_jmp)
+ {
+ bytes = 2;
+ amode = ADDR_ABS_IND_LONG;
+ }
+ else
+{
+ bytes = 1;
+
+ amode = ADDR_DIR_IND_LONG;
+ }
+ }
+ }
+ }
+ else
+ {
+ ptr = parse_exp (ptr, 2);
+ if (ptr[0] == ',')
+ {
+ if (ptr[1] == 'y')
+ {
+ if (override_len == 1)
+ {
+ bytes = 1;
+ amode = ADDR_DIR_IDX_Y;
+ }
+ else
+ {
+ amode = ADDR_ABS_IDX_Y;
+ bytes = 2;
+ }
+ ptr += 2;
+ }
+ else if (ptr[1] == 'x')
+ {
+ if (override_len == 1)
+ {
+ amode = ADDR_DIR_IDX_X;
+ bytes = 1;
+ }
+ else
+ {
+ amode = ADDR_ABS_IDX_X;
+ bytes = 2;
+ }
+ ptr += 2;
+ }
+ else if (ptr[1] == 's')
+ {
+ bytes = 1;
+ amode = ADDR_STACK_REL;
+ ptr += 2;
+ }
+ else
+ {
+ bytes = 1;
+ immediate1 = immediate;
+ ptr = parse_exp (ptr + 1);
+ amode = ADDR_BLOCK_MOVE;
+ }
+ }
+ else
+ {
+ switch (info->amode)
+ {
+ case ADDR_PC_REL:
+ amode = ADDR_PC_REL;
+ bytes = 1;
+ break;
+ case ADDR_PC_REL_LONG:
+ amode = ADDR_PC_REL_LONG;
+ bytes = 2;
+ break;
+ default:
+ if (override_len == 1)
+ {
+ amode = ADDR_DIR;
+ bytes = 1;
+ }
+ else if (override_len == 3)
+ {
+ bytes = 3;
+ amode = ADDR_ABS_LONG;
+ }
+ else
+ {
+ amode = ADDR_ABS;
+ bytes = 2;
+ }
+ }
+ }
+ }
+
+ switch (bytes)
+ {
+ case 1:
+ switch (expr_shift)
+ {
+ case 0:
+ if (amode == ADDR_DIR)
+ tc_cons_reloc = R_W65_DP;
+ else
+tc_cons_reloc = R_W65_ABS8;
+ break;
+ case 1:
+ tc_cons_reloc = R_W65_ABS8S8;
+ break;
+ case 2:
+ tc_cons_reloc = R_W65_ABS8S16;
+ break;
+ }
+ break;
+ case 2:
+ switch (expr_shift)
+ {
+ case 0:
+ tc_cons_reloc = R_W65_ABS16;
+ break;
+ case 1:
+ tc_cons_reloc = R_W65_ABS16S8;
+ break;
+ case 2:
+ tc_cons_reloc = R_W65_ABS16S16;
+ break;
+ }
+ }
+ return ptr;
+}
+
+/* Passed a pointer to a list of opcodes which use different
+ addressing modes, return the opcode which matches the opcodes
+ provided
+ */
+
+static
+struct opinfo *
+get_specific (opcode)
+ struct opinfo *opcode;
+{
+ int ocode = opcode->code;
+
+ for (; opcode->code == ocode; opcode++)
+ {
+ if (opcode->amode == amode)
+ return opcode;
+ }
+ return 0;
+}
+
+int
+check (operand, low, high)
+ expressionS *operand;
+ int low;
+ int high;
+{
+ if (operand->X_op != O_constant
+ || operand->X_add_number < low
+ || operand->X_add_number > high)
+ {
+ as_bad ("operand must be absolute in range %d..%d", low, high);
+ }
+ return operand->X_add_number;
+}
+
+
+static int log2[] =
+{0, 0, 1, 0, 2};
+
+/* Now we know what sort of opcodes it is, lets build the bytes -
+ */
+static void
+build_Mytes (opcode)
+ struct opinfo *opcode;
+{
+ int size;
+ int type;
+ int pcrel;
+ char *output;
+
+ if (opcode->amode == ADDR_IMPLIED)
+ {
+ output = frag_more (1);
+ }
+ else if (opcode->amode == ADDR_PC_REL)
+ {
+ int type;
+ /* This is a relaxable insn, so we do some special handling */
+ type = opcode->val == OP_BRA ? UNCOND_BRANCH : COND_BRANCH;
+ output = frag_var (rs_machine_dependent,
+ md_relax_table[C (type, WORD_DISP)].rlx_length,
+ md_relax_table[C (type, BYTE_DISP)].rlx_length,
+ C (type, UNDEF_BYTE_DISP),
+ immediate.X_add_symbol,
+ immediate.X_add_number,
+ 0);
+ }
+ else
+ {
+ switch (opcode->amode)
+ {
+ GETINFO (size, type, pcrel);
+ }
+
+ /* If something special was done in the
+ expression modify the reloc type */
+ if (tc_cons_reloc)
+ {
+ type = tc_cons_reloc;
+ }
+
+
+
+ /* 1 byte for the opcode + the bytes for the addrmode */
+ output = frag_more (size + 1);
+
+ if (opcode->amode == ADDR_BLOCK_MOVE)
+ {
+ /* Two relocs for this one */
+ fix_new_exp (frag_now,
+ output + 1 - frag_now->fr_literal,
+ 1,
+ &immediate,
+ 0,
+ R_W65_ABS8S16);
+
+ fix_new_exp (frag_now,
+ output + 2 - frag_now->fr_literal,
+ 1,
+ &immediate1,
+ 0,
+ R_W65_ABS8S16);
+ }
+ else if (type >= 0
+ && opcode->amode != ADDR_IMPLIED
+ && opcode->amode != ADDR_ACC
+ && opcode->amode != ADDR_STACK)
+ {
+ fix_new_exp (frag_now,
+ output + 1 - frag_now->fr_literal,
+ size,
+ &immediate,
+ pcrel,
+ type);
+ }
+ }
+ output[0] = opcode->val;
+}
+
+/* This is the guts of the machine-dependent assembler. STR points to a
+ machine dependent instruction. This function is supposed to emit
+ the frags/bytes it assembles to.
+ */
+
+void
+md_assemble (str)
+ char *str;
+{
+ unsigned char *op_start;
+ unsigned char *op_end;
+ struct opinfo *opcode;
+ char name[20];
+ int nlen = 0;
+ char *p;
+
+ /* Drop leading whitespace */
+ while (*str == ' ')
+ str++;
+
+ /* all opcodes are three letters */
+ name[0] = str[0];
+ name[1] = str[1];
+ name[2] = str[2];
+ name[3] = 0;
+
+ tc_cons_reloc = 0;
+ str += 3;
+ opcode = (struct opinfo *) hash_find (opcode_hash_control, name);
+
+ if (opcode == NULL)
+ {
+ as_bad (_("unknown opcode"));
+ return;
+ }
+
+ if (opcode->amode != ADDR_IMPLIED
+ && opcode->amode != ADDR_STACK)
+ {
+ get_operands (opcode, str);
+ opcode = get_specific (opcode);
+ }
+
+ if (opcode == 0)
+ {
+ /* Couldn't find an opcode which matched the operands */
+
+
+ char *where = frag_more (1);
+
+ where[0] = 0x0;
+ where[1] = 0x0;
+ as_bad (_("invalid operands for opcode"));
+ return;
+ }
+
+ build_Mytes (opcode);
+}
+
+
+void
+DEFUN (tc_crawl_symbol_chain, (headers),
+ object_headers * headers)
+{
+ printf (_("call to tc_crawl_symbol_chain \n"));
+}
+
+symbolS *
+DEFUN (md_undefined_symbol, (name),
+ char *name)
+{
+ return 0;
+}
+
+void
+DEFUN (tc_headers_hook, (headers),
+ object_headers * headers)
+{
+ printf (_("call to tc_headers_hook \n"));
+}
+
+/* Various routines to kill one day */
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+/* Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP . An error message is returned, or NULL on OK.
+ */
+char *
+md_atof (type, litP, sizeP)
+ char type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+ char *atof_ieee ();
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("Bad call to MD_NTOF()");
+ }
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+
+ *sizeP = prec * sizeof (LITTLENUM_TYPE);
+ for (wordP = words + prec - 1; prec--;)
+ {
+ md_number_to_chars (litP, (valueT) (*wordP--), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ return 0;
+}
+
+int
+md_parse_option (c,a)
+ int c;
+ char *a;
+
+{
+ return 1;
+}
+
+void
+tc_Nout_fix_to_chars ()
+{
+ printf (_("call to tc_Nout_fix_to_chars \n"));
+ abort ();
+}
+
+/*
+called after relaxing, change the frags so they know how big they are
+*/
+void
+md_convert_frag (headers, seg, fragP)
+ object_headers *headers;
+ segT seg;
+ fragS *fragP;
+{
+ int disp_size = 0;
+ int inst_size = 0;
+ unsigned char *buffer = (unsigned char *) (fragP->fr_fix + fragP->fr_literal);
+
+ switch (fragP->fr_subtype)
+ {
+ case C (COND_BRANCH, BYTE_DISP):
+ case C (UNCOND_BRANCH, BYTE_DISP):
+ disp_size = 1;
+ inst_size = 1;
+ break;
+
+ /* cond branches to a known 16 bit displacement */
+ case C (COND_BRANCH, WORD_DISP):
+ switch (buffer[0])
+ {
+ case OP_BCC:
+ case OP_BCS:
+ case OP_BEQ:
+ case OP_BMI:
+ case OP_BNE:
+ case OP_BPL:
+ case OP_BVS:
+ case OP_BVC:
+ /* Invert the sense of the test */
+ buffer[0] ^= 0x20;
+ buffer[1] = 3; /* Jump over following brl */
+ buffer[2] = OP_BRL;
+ buffer[3] = 0;
+ buffer[4] = 0;
+ disp_size = 2;
+ inst_size = 3;
+ break;
+ default:
+ abort ();
+ }
+ break;
+ case C (UNCOND_BRANCH, WORD_DISP):
+ /* Unconditional branches to a known 16 bit displacement */
+
+ switch (buffer[0])
+ {
+ case OP_BRA:
+ buffer[0] = OP_BRL;
+ disp_size = 2;
+ inst_size = 1;
+ break;
+ default:
+ abort ();
+ }
+ break;
+ /* got to create a branch over a reloc here */
+ case C (COND_BRANCH, UNDEF_WORD_DISP):
+ buffer[0] ^= 0x20; /* invert test */
+ buffer[1] = 3;
+ buffer[2] = OP_BRL;
+ buffer[3] = 0;
+ buffer[4] = 0;
+ fix_new (fragP,
+ fragP->fr_fix + 3,
+ 4,
+ fragP->fr_symbol,
+ fragP->fr_offset,
+ 0,
+ R_W65_PCR16);
+
+ fragP->fr_fix += disp_size + inst_size;
+ fragP->fr_var = 0;
+ break;
+ case C (UNCOND_BRANCH, UNDEF_WORD_DISP):
+ buffer[0] = OP_BRL;
+ buffer[1] = 0;
+ buffer[2] = 0;
+ fix_new (fragP,
+ fragP->fr_fix + 1,
+ 4,
+ fragP->fr_symbol,
+ fragP->fr_offset,
+ 0,
+ R_W65_PCR16);
+
+ fragP->fr_fix += disp_size + inst_size;
+ fragP->fr_var = 0;
+ break;
+ default:
+ abort ();
+ }
+ if (inst_size)
+ {
+ /* Get the address of the end of the instruction */
+ int next_inst = fragP->fr_fix + fragP->fr_address + disp_size + inst_size;
+ int targ_addr = (S_GET_VALUE (fragP->fr_symbol) +
+ fragP->fr_offset);
+ int disp = targ_addr - next_inst;
+
+ md_number_to_chars (buffer + inst_size, disp, disp_size);
+ fragP->fr_fix += disp_size + inst_size;
+ fragP->fr_var = 0;
+ }
+}
+
+
+valueT
+DEFUN (md_section_align, (seg, size),
+ segT seg AND
+ valueT size)
+{
+ return ((size + (1 << section_alignment[(int) seg]) - 1)
+ & (-1 << section_alignment[(int) seg]));
+
+}
+
+void
+md_apply_fix (fixP, val)
+ fixS *fixP;
+ long val;
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ int addr = fixP->fx_frag->fr_address + fixP->fx_where;
+
+ if (fixP->fx_r_type == 0)
+ {
+ if (fixP->fx_size == 1)
+ fixP->fx_r_type = R_W65_ABS8;
+ else
+ fixP->fx_r_type = R_W65_ABS16;
+ }
+
+ switch (fixP->fx_r_type)
+ {
+ case R_W65_ABS8S16:
+ val >>= 8;
+ case R_W65_ABS8S8:
+ val >>= 8;
+ case R_W65_ABS8:
+ *buf++ = val;
+ break;
+ case R_W65_ABS16S16:
+ val >>= 8;
+ case R_W65_ABS16S8:
+ val >>= 8;
+ case R_W65_ABS16:
+ *buf++ = val >> 0;
+ *buf++ = val >> 8;
+ break;
+ case R_W65_ABS24:
+ *buf++ = val >> 0;
+ *buf++ = val >> 8;
+ *buf++ = val >> 16;
+ break;
+ case R_W65_PCR8:
+ *buf++ = val - addr - 1;
+ break;
+ case R_W65_PCR16:
+ val = val - addr - 1;
+ *buf++ = val;
+ *buf++ = val >> 8;
+ break;
+ case R_W65_DP:
+ *buf++ = val;
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+/* Put number into target byte order */
+
+void
+md_number_to_chars (ptr, use, nbytes)
+ char *ptr;
+ valueT use;
+ int nbytes;
+{
+ number_to_chars_littleendian (ptr, use, nbytes);
+}
+
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ int gap = fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address - 1;
+ return gap;
+}
+
+void
+tc_coff_symbol_emit_hook (x)
+ struct symbol *x;
+{
+}
+
+short
+tc_coff_fix2rtype (fix_ptr)
+ fixS *fix_ptr;
+{
+ return fix_ptr->fx_r_type;
+}
+
+void
+tc_reloc_mangle (fix_ptr, intr, base)
+ fixS *fix_ptr;
+ struct internal_reloc *intr;
+ bfd_vma base;
+
+{
+ symbolS *symbol_ptr;
+
+ symbol_ptr = fix_ptr->fx_addsy;
+
+ /* If this relocation is attached to a symbol then it's ok
+ to output it */
+ if (fix_ptr->fx_r_type == RELOC_32)
+ {
+ /* cons likes to create reloc32's whatever the size of the reloc..
+ */
+ switch (fix_ptr->fx_size)
+ {
+ case 2:
+ intr->r_type = R_IMM16;
+ break;
+ case 1:
+ intr->r_type = R_IMM8;
+ break;
+ default:
+ abort ();
+ }
+ }
+ else
+ {
+ if (fix_ptr->fx_size == 4)
+ intr->r_type = R_W65_ABS24;
+ else
+ intr->r_type = fix_ptr->fx_r_type;
+ }
+
+ intr->r_vaddr = fix_ptr->fx_frag->fr_address + fix_ptr->fx_where + base;
+ intr->r_offset = fix_ptr->fx_offset;
+
+ /* Turn the segment of the symbol into an offset. */
+ if (symbol_ptr)
+ {
+ symbolS *dot;
+
+ dot = segment_info[S_GET_SEGMENT (symbol_ptr)].dot;
+ if (dot)
+ {
+ intr->r_offset += S_GET_VALUE (symbol_ptr);
+ intr->r_symndx = dot->sy_number;
+ }
+ else
+ {
+ intr->r_symndx = symbol_ptr->sy_number;
+ }
+ }
+ else
+ {
+ intr->r_symndx = -1;
+ }
+}
+
+int
+tc_coff_sizemachdep (frag)
+ fragS *frag;
+{
+ return md_relax_table[frag->fr_subtype].rlx_length;
+}
+
+
+
+
+
+/*
+called just before address relaxation, return the length
+by which a fragment must grow to reach it's destination
+*/
+int
+md_estimate_size_before_relax (fragP, segment_type)
+ register fragS *fragP;
+ register segT segment_type;
+{
+ int what = GET_WHAT (fragP->fr_subtype);
+
+ switch (fragP->fr_subtype)
+ {
+ default:
+ abort ();
+ case C (COND_BRANCH, UNDEF_BYTE_DISP):
+ case C (UNCOND_BRANCH, UNDEF_BYTE_DISP):
+ /* used to be a branch to somewhere which was unknown */
+ if (S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
+ {
+ /* Got a symbol and it's defined in this segment, become byte
+ sized - maybe it will fix up */
+ fragP->fr_subtype = C (what, BYTE_DISP);
+ fragP->fr_var = md_relax_table[C (what, BYTE_DISP)].rlx_length;
+ }
+ else
+ {
+ /* Its got a segment, but its not ours, so it will always be long */
+ fragP->fr_subtype = C (what, UNDEF_WORD_DISP);
+ fragP->fr_var = md_relax_table[C (what, WORD_DISP)].rlx_length;
+ return md_relax_table[C (what, WORD_DISP)].rlx_length;
+ }
+ }
+ return fragP->fr_var;
+}
+
+
+
+CONST char *md_shortopts = "";
+struct option md_longopts[] = {
+#define OPTION_RELAX (OPTION_MD_BASE)
+ {NULL, no_argument, NULL, 0}
+};
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+
+}
+
+size_t md_longopts_size = sizeof(md_longopts);
diff --git a/gas/config/tc-w65.h b/gas/config/tc-w65.h
new file mode 100644
index 0000000..9794ad2
--- /dev/null
+++ b/gas/config/tc-w65.h
@@ -0,0 +1,61 @@
+/* This file is tc-w65.h
+ Copyright (C) 1995, 1997, 1998 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+
+#define TC_W65
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#if ANSI_PROTOTYPES
+struct internal_reloc;
+#endif
+
+#define WORKING_DOT_WORD
+
+/* This macro translates between an internal fix and an coff reloc type */
+#define TC_COFF_FIX2RTYPE(fixP) tc_coff_fix2rtype(fixP)
+
+#define BFD_ARCH bfd_arch_w65
+#define COFF_MAGIC 0x6500
+
+#define IGNORE_NONSTANDARD_ESCAPES
+
+#define TC_RELOC_MANGLE(s,a,b,c) tc_reloc_mangle(a,b,c)
+extern void tc_reloc_mangle
+ PARAMS ((struct fix *, struct internal_reloc *, bfd_vma));
+
+#define DO_NOT_STRIP 0
+#define LISTING_HEADER "W65816 GAS "
+#define NEED_FX_R_TYPE 1
+#define RELOC_32 1234
+
+#define TC_COFF_SIZEMACHDEP(frag) tc_coff_sizemachdep(frag)
+#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) w65_expression (EXP, NBYTES)
+#define TC_COUNT_RELOC(x) (1)
+#define TC_CONS_RELOC tc_cons_reloc
+#define DONT_OVERFLOW
+int tc_cons_reloc;
+
+#define md_operand(x)
+
+extern struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+/* end of tc-w65.h */
diff --git a/gas/config/tc-z8k.c b/gas/config/tc-z8k.c
new file mode 100644
index 0000000..1611341
--- /dev/null
+++ b/gas/config/tc-z8k.c
@@ -0,0 +1,1589 @@
+/* tc-z8k.c -- Assemble code for the Zilog Z800n
+ Copyright (C) 1992, 93, 94, 95, 96, 97, 1998 Free Software Foundation.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+/*
+ Written By Steve Chamberlain
+ sac@cygnus.com
+ */
+#define DEFINE_TABLE
+#include <stdio.h>
+
+#include "opcodes/z8k-opc.h"
+
+#include "as.h"
+#include "bfd.h"
+#include <ctype.h>
+
+const char comment_chars[] =
+{'!', 0};
+const char line_separator_chars[] =
+{';', 0};
+const char line_comment_chars[] =
+{'#', 0};
+
+extern int machine;
+extern int coff_flags;
+int segmented_mode;
+const int md_reloc_size;
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ Integer arg to pass to the function
+ */
+
+void cons ();
+
+void
+s_segm ()
+{
+ segmented_mode = 1;
+ machine = bfd_mach_z8001;
+ coff_flags = F_Z8001;
+}
+
+void
+s_unseg ()
+{
+ segmented_mode = 0;
+ machine = bfd_mach_z8002;
+ coff_flags = F_Z8002;
+}
+
+static
+void
+even ()
+{
+ frag_align (1, 0, 0);
+ record_alignment (now_seg, 1);
+}
+
+void obj_coff_section ();
+
+int
+tohex (c)
+ int c;
+{
+ if (isdigit (c))
+ return c - '0';
+ if (islower (c))
+ return c - 'a' + 10;
+ return c - 'A' + 10;
+}
+
+void
+sval ()
+{
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\'')
+ {
+ int c;
+ input_line_pointer++;
+ c = *input_line_pointer++;
+ while (c != '\'')
+ {
+ if (c == '%')
+ {
+ c = (tohex (input_line_pointer[0]) << 4)
+ | tohex (input_line_pointer[1]);
+ input_line_pointer += 2;
+ }
+ FRAG_APPEND_1_CHAR (c);
+ c = *input_line_pointer++;
+ }
+ demand_empty_rest_of_line ();
+ }
+
+}
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"int", cons, 2},
+ {"data.b", cons, 1},
+ {"data.w", cons, 2},
+ {"data.l", cons, 4},
+ {"form", listing_psize, 0},
+ {"heading", listing_title, 0},
+ {"import", s_ignore, 0},
+ {"page", listing_eject, 0},
+ {"program", s_ignore, 0},
+ {"z8001", s_segm, 0},
+ {"z8002", s_unseg, 0},
+
+
+ {"segm", s_segm, 0},
+ {"unsegm", s_unseg, 0},
+ {"unseg", s_unseg, 0},
+ {"name", s_app_file, 0},
+ {"global", s_globl, 0},
+ {"wval", cons, 2},
+ {"lval", cons, 4},
+ {"bval", cons, 1},
+ {"sval", sval, 0},
+ {"rsect", obj_coff_section, 0},
+ {"sect", obj_coff_section, 0},
+ {"block", s_space, 0},
+ {"even", even, 0},
+ {0, 0, 0}
+};
+
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+static struct hash_control *opcode_hash_control; /* Opcode mnemonics */
+
+void
+md_begin ()
+{
+ opcode_entry_type *opcode;
+ char *prev_name = "";
+ int idx = 0;
+
+ opcode_hash_control = hash_new ();
+
+ for (opcode = z8k_table; opcode->name; opcode++)
+ {
+ /* Only enter unique codes into the table */
+ char *src = opcode->name;
+
+ if (strcmp (opcode->name, prev_name))
+ {
+ hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
+ idx++;
+ }
+ opcode->idx = idx;
+ prev_name = opcode->name;
+ }
+
+ /* default to z8002 */
+ s_unseg ();
+
+ /* insert the pseudo ops too */
+ for (idx = 0; md_pseudo_table[idx].poc_name; idx++)
+ {
+ opcode_entry_type *fake_opcode;
+ fake_opcode = (opcode_entry_type *) malloc (sizeof (opcode_entry_type));
+ fake_opcode->name = md_pseudo_table[idx].poc_name,
+ fake_opcode->func = (void *) (md_pseudo_table + idx);
+ fake_opcode->opcode = 250;
+ hash_insert (opcode_hash_control, fake_opcode->name, fake_opcode);
+ }
+
+ linkrelax = 1;
+}
+
+struct z8k_exp
+{
+ char *e_beg;
+ char *e_end;
+ expressionS e_exp;
+};
+typedef struct z8k_op
+{
+ char regsize; /* 'b','w','r','q' */
+ unsigned int reg; /* 0..15 */
+
+ int mode;
+
+ unsigned int x_reg; /* any other register associated with the mode */
+ expressionS exp; /* any expression */
+}
+
+op_type;
+
+static expressionS *da_operand;
+static expressionS *imm_operand;
+
+int reg[16];
+int the_cc;
+int the_ctrl;
+int the_flags;
+int the_interrupt;
+
+char *
+DEFUN (whatreg, (reg, src),
+ int *reg AND
+ char *src)
+{
+ if (isdigit (src[1]))
+ {
+ *reg = (src[0] - '0') * 10 + src[1] - '0';
+ return src + 2;
+ }
+ else
+ {
+ *reg = (src[0] - '0');
+ return src + 1;
+ }
+}
+
+/*
+ parse operands
+
+ rh0-rh7, rl0-rl7
+ r0-r15
+ rr0-rr14
+ rq0--rq12
+ WREG r0,r1,r2,r3,r4,r5,r6,r7,fp,sp
+ r0l,r0h,..r7l,r7h
+ @WREG
+ @WREG+
+ @-WREG
+ #const
+
+ */
+
+/* try and parse a reg name, returns number of chars consumed */
+char *
+DEFUN (parse_reg, (src, mode, reg),
+ char *src AND
+ int *mode AND
+ unsigned int *reg)
+{
+ char *res = 0;
+ char regno;
+
+ if (src[0] == 's' && src[1] == 'p')
+ {
+ if (segmented_mode)
+ {
+ *mode = CLASS_REG_LONG;
+ *reg = 14;
+ }
+ else
+ {
+ *mode = CLASS_REG_WORD;
+ *reg = 15;
+ }
+ return src + 2;
+ }
+ if (src[0] == 'r')
+ {
+ if (src[1] == 'r')
+ {
+ *mode = CLASS_REG_LONG;
+ res = whatreg (reg, src + 2);
+ regno = *reg;
+ if (regno > 14)
+ as_warn (_("register rr%d, out of range."),regno);
+ }
+ else if (src[1] == 'h')
+ {
+ *mode = CLASS_REG_BYTE;
+ res = whatreg (reg, src + 2);
+ regno = *reg;
+ if (regno > 7)
+ as_warn (_("register rh%d, out of range."),regno);
+ }
+ else if (src[1] == 'l')
+ {
+ *mode = CLASS_REG_BYTE;
+ res = whatreg (reg, src + 2);
+ regno = *reg;
+ if (regno > 7)
+ as_warn (_("register rl%d, out of range."),regno);
+ *reg += 8;
+ }
+ else if (src[1] == 'q')
+ {
+ *mode = CLASS_REG_QUAD;
+ res = whatreg (reg, src + 2);
+ regno = *reg;
+ if (regno > 12)
+ as_warn (_("register rq%d, out of range."),regno);
+ }
+ else
+ {
+ *mode = CLASS_REG_WORD;
+ res = whatreg (reg, src + 1);
+ regno = *reg;
+ if (regno > 15)
+ as_warn (_("register r%d, out of range."),regno);
+ }
+ }
+ return res;
+
+}
+
+char *
+DEFUN (parse_exp, (s, op),
+ char *s AND
+ expressionS * op)
+{
+ char *save = input_line_pointer;
+ char *new;
+
+ input_line_pointer = s;
+ expression (op);
+ if (op->X_op == O_absent)
+ as_bad (_("missing operand"));
+ new = input_line_pointer;
+ input_line_pointer = save;
+ return new;
+}
+
+/* The many forms of operand:
+
+ <rb>
+ <r>
+ <rr>
+ <rq>
+ @r
+ #exp
+ exp
+ exp(r)
+ r(#exp)
+ r(r)
+
+
+
+ */
+
+static
+char *
+DEFUN (checkfor, (ptr, what),
+ char *ptr AND
+ char what)
+{
+ if (*ptr == what)
+ ptr++;
+ else
+ {
+ as_bad (_("expected %c"), what);
+ }
+ return ptr;
+}
+
+/* Make sure the mode supplied is the size of a word */
+static void
+DEFUN (regword, (mode, string),
+ int mode AND
+ char *string)
+{
+ int ok;
+
+ ok = CLASS_REG_WORD;
+ if (ok != mode)
+ {
+ as_bad (_("register is wrong size for a word %s"), string);
+ }
+}
+
+/* Make sure the mode supplied is the size of an address */
+static void
+DEFUN (regaddr, (mode, string),
+ int mode AND
+ char *string)
+{
+ int ok;
+
+ ok = segmented_mode ? CLASS_REG_LONG : CLASS_REG_WORD;
+ if (ok != mode)
+ {
+ as_bad (_("register is wrong size for address %s"), string);
+ }
+}
+
+struct ctrl_names
+{
+ int value;
+ char *name;
+};
+
+struct ctrl_names ctrl_table[] =
+{
+ 0x2, "fcw",
+ 0X3, "refresh",
+ 0x4, "psapseg",
+ 0x5, "psapoff",
+ 0x5, "psap",
+ 0x6, "nspseg",
+ 0x7, "nspoff",
+ 0x7, "nsp",
+ 0, 0
+};
+
+static void
+DEFUN (get_ctrl_operand, (ptr, mode, dst),
+ char **ptr AND
+ struct z8k_op *mode AND
+ unsigned int dst)
+{
+ char *src = *ptr;
+ int r;
+ int i;
+
+ while (*src == ' ')
+ src++;
+
+ mode->mode = CLASS_CTRL;
+ for (i = 0; ctrl_table[i].name; i++)
+ {
+ int j;
+
+ for (j = 0; ctrl_table[i].name[j]; j++)
+ {
+ if (ctrl_table[i].name[j] != src[j])
+ goto fail;
+ }
+ the_ctrl = ctrl_table[i].value;
+ *ptr = src + j;
+ return;
+ fail:;
+ }
+ the_ctrl = 0;
+ return;
+}
+
+struct flag_names
+{
+ int value;
+ char *name;
+
+};
+
+struct flag_names flag_table[] =
+{
+ 0x1, "p",
+ 0x1, "v",
+ 0x2, "s",
+ 0x4, "z",
+ 0x8, "c",
+ 0x0, "+",
+ 0, 0
+};
+
+static void
+DEFUN (get_flags_operand, (ptr, mode, dst),
+ char **ptr AND
+ struct z8k_op *mode AND
+ unsigned int dst)
+{
+ char *src = *ptr;
+ int r;
+ int i;
+ int j;
+
+ while (*src == ' ')
+ src++;
+
+ mode->mode = CLASS_FLAGS;
+ the_flags = 0;
+ for (j = 0; j <= 9; j++)
+ {
+ if (!src[j])
+ goto done;
+ for (i = 0; flag_table[i].name; i++)
+ {
+ if (flag_table[i].name[0] == src[j])
+ {
+ the_flags = the_flags | flag_table[i].value;
+ goto match;
+ }
+ }
+ goto done;
+ match:
+ ;
+ }
+ done:
+ *ptr = src + j;
+ return;
+}
+
+
+struct interrupt_names
+{
+ int value;
+ char *name;
+
+};
+
+struct interrupt_names intr_table[] =
+{
+ 0x1, "nvi",
+ 0x2, "vi",
+ 0x3, "both",
+ 0x3, "all",
+ 0, 0
+};
+
+static void
+DEFUN (get_interrupt_operand, (ptr, mode, dst),
+ char **ptr AND
+ struct z8k_op *mode AND
+ unsigned int dst)
+{
+ char *src = *ptr;
+ int r;
+ int i;
+
+ while (*src == ' ')
+ src++;
+
+ mode->mode = CLASS_IMM;
+ for (i = 0; intr_table[i].name; i++)
+ {
+ int j;
+
+ for (j = 0; intr_table[i].name[j]; j++)
+ {
+ if (intr_table[i].name[j] != src[j])
+ goto fail;
+ }
+ the_interrupt = intr_table[i].value;
+ *ptr = src + j;
+ return;
+ fail:;
+ }
+ the_interrupt = 0x0;
+ return;
+}
+
+struct cc_names
+{
+ int value;
+ char *name;
+
+};
+
+struct cc_names table[] =
+{
+ 0x0, "f",
+ 0x1, "lt",
+ 0x2, "le",
+ 0x3, "ule",
+ 0x4, "ov",
+ 0x4, "pe",
+ 0x5, "mi",
+ 0x6, "eq",
+ 0x6, "z",
+ 0x7, "c",
+ 0x7, "ult",
+ 0x8, "t",
+ 0x9, "ge",
+ 0xa, "gt",
+ 0xb, "ugt",
+ 0xc, "nov",
+ 0xc, "po",
+ 0xd, "pl",
+ 0xe, "ne",
+ 0xe, "nz",
+ 0xf, "nc",
+ 0xf, "uge",
+ 0, 0
+};
+
+static void
+DEFUN (get_cc_operand, (ptr, mode, dst),
+ char **ptr AND
+ struct z8k_op *mode AND
+ unsigned int dst)
+{
+ char *src = *ptr;
+ int r;
+ int i;
+
+ while (*src == ' ')
+ src++;
+
+ mode->mode = CLASS_CC;
+ for (i = 0; table[i].name; i++)
+ {
+ int j;
+
+ for (j = 0; table[i].name[j]; j++)
+ {
+ if (table[i].name[j] != src[j])
+ goto fail;
+ }
+ the_cc = table[i].value;
+ *ptr = src + j;
+ return;
+ fail:;
+ }
+ the_cc = 0x8;
+}
+
+static void
+get_operand (ptr, mode, dst)
+ char **ptr;
+ struct z8k_op *mode;
+ unsigned int dst;
+{
+ char *src = *ptr;
+ char *end;
+ unsigned int num;
+ unsigned int len;
+ unsigned int size;
+
+ mode->mode = 0;
+
+ while (*src == ' ')
+ src++;
+ if (*src == '#')
+ {
+ mode->mode = CLASS_IMM;
+ imm_operand = &(mode->exp);
+ src = parse_exp (src + 1, &(mode->exp));
+ }
+ else if (*src == '@')
+ {
+ int d;
+
+ mode->mode = CLASS_IR;
+ src = parse_reg (src + 1, &d, &mode->reg);
+ }
+ else
+ {
+ int regn;
+
+ end = parse_reg (src, &mode->mode, &regn);
+
+ if (end)
+ {
+ int nw, nr;
+
+ src = end;
+ if (*src == '(')
+ {
+ src++;
+ end = parse_reg (src, &nw, &nr);
+ if (end)
+ {
+ /* Got Ra(Rb) */
+ src = end;
+
+ if (*src != ')')
+ {
+ as_bad (_("Missing ) in ra(rb)"));
+ }
+ else
+ {
+ src++;
+ }
+
+ regaddr (mode->mode, "ra(rb) ra");
+/* regword (mode->mode, "ra(rb) rb");*/
+ mode->mode = CLASS_BX;
+ mode->reg = regn;
+ mode->x_reg = nr;
+ reg[ARG_RX] = nr;
+ }
+ else
+ {
+ /* Got Ra(disp) */
+ if (*src == '#')
+ src++;
+ src = parse_exp (src, &(mode->exp));
+ src = checkfor (src, ')');
+ mode->mode = CLASS_BA;
+ mode->reg = regn;
+ mode->x_reg = 0;
+ imm_operand = &(mode->exp);
+ }
+ }
+ else
+ {
+ mode->reg = regn;
+ mode->x_reg = 0;
+ }
+ }
+ else
+ {
+ /* No initial reg */
+ src = parse_exp (src, &(mode->exp));
+ if (*src == '(')
+ {
+ src++;
+ end = parse_reg (src, &(mode->mode), &regn);
+ regword (mode->mode, "addr(Ra) ra");
+ mode->mode = CLASS_X;
+ mode->reg = regn;
+ mode->x_reg = 0;
+ da_operand = &(mode->exp);
+ src = checkfor (end, ')');
+ }
+ else
+ {
+ /* Just an address */
+ mode->mode = CLASS_DA;
+ mode->reg = 0;
+ mode->x_reg = 0;
+ da_operand = &(mode->exp);
+ }
+ }
+ }
+ *ptr = src;
+}
+
+static
+char *
+get_operands (opcode, op_end, operand)
+ opcode_entry_type *opcode;
+ char *op_end;
+ op_type *operand;
+{
+ char *ptr = op_end;
+char *savptr;
+ switch (opcode->noperands)
+ {
+ case 0:
+ operand[0].mode = 0;
+ operand[1].mode = 0;
+ break;
+
+ case 1:
+ ptr++;
+ if (opcode->arg_info[0] == CLASS_CC)
+ {
+ get_cc_operand (&ptr, operand + 0, 0);
+ }
+ else if (opcode->arg_info[0] == CLASS_FLAGS)
+ {
+ get_flags_operand (&ptr, operand + 0, 0);
+ }
+ else if (opcode->arg_info[0] == (CLASS_IMM +(ARG_IMM2)))
+ {
+ get_interrupt_operand (&ptr, operand + 0, 0);
+ }
+ else
+ {
+ get_operand (&ptr, operand + 0, 0);
+ }
+ operand[1].mode = 0;
+ break;
+
+ case 2:
+ ptr++;
+ savptr = ptr;
+ if (opcode->arg_info[0] == CLASS_CC)
+ {
+ get_cc_operand (&ptr, operand + 0, 0);
+ }
+ else if (opcode->arg_info[0] == CLASS_CTRL)
+ {
+ get_ctrl_operand (&ptr, operand + 0, 0);
+ if (the_ctrl == 0)
+ {
+ ptr = savptr;
+ get_operand (&ptr, operand + 0, 0);
+ if (ptr == 0)
+ return;
+ if (*ptr == ',')
+ ptr++;
+ get_ctrl_operand (&ptr, operand + 1, 1);
+ return ptr;
+ }
+ }
+ else
+ {
+ get_operand (&ptr, operand + 0, 0);
+ }
+ if (ptr == 0)
+ return;
+ if (*ptr == ',')
+ ptr++;
+ get_operand (&ptr, operand + 1, 1);
+ break;
+
+ case 3:
+ ptr++;
+ get_operand (&ptr, operand + 0, 0);
+ if (*ptr == ',')
+ ptr++;
+ get_operand (&ptr, operand + 1, 1);
+ if (*ptr == ',')
+ ptr++;
+ get_operand (&ptr, operand + 2, 2);
+ break;
+
+ case 4:
+ ptr++;
+ get_operand (&ptr, operand + 0, 0);
+ if (*ptr == ',')
+ ptr++;
+ get_operand (&ptr, operand + 1, 1);
+ if (*ptr == ',')
+ ptr++;
+ get_operand (&ptr, operand + 2, 2);
+ if (*ptr == ',')
+ ptr++;
+ get_cc_operand (&ptr, operand + 3, 3);
+ break;
+ default:
+ abort ();
+ }
+
+ return ptr;
+}
+
+/* Passed a pointer to a list of opcodes which use different
+ addressing modes, return the opcode which matches the opcodes
+ provided
+ */
+
+static
+opcode_entry_type *
+DEFUN (get_specific, (opcode, operands),
+ opcode_entry_type * opcode AND
+ op_type * operands)
+
+{
+ opcode_entry_type *this_try = opcode;
+ int found = 0;
+ unsigned int noperands = opcode->noperands;
+
+ unsigned int dispreg;
+ unsigned int this_index = opcode->idx;
+
+ while (this_index == opcode->idx && !found)
+ {
+ unsigned int i;
+
+ this_try = opcode++;
+ for (i = 0; i < noperands; i++)
+ {
+ int mode = operands[i].mode;
+
+ if ((mode & CLASS_MASK) != (this_try->arg_info[i] & CLASS_MASK))
+ {
+ /* it could be an pc rel operand, if this is a da mode and
+ we like disps, then insert it */
+
+ if (mode == CLASS_DA && this_try->arg_info[i] == CLASS_DISP)
+ {
+ /* This is the case */
+ operands[i].mode = CLASS_DISP;
+ }
+ else if (mode == CLASS_BA && this_try->arg_info[i])
+ {
+ /* Can't think of a way to turn what we've been given into
+ something that's ok */
+ goto fail;
+ }
+ else if (this_try->arg_info[i] & CLASS_PR)
+ {
+ if (mode == CLASS_REG_LONG && segmented_mode)
+ {
+ /* ok */
+ }
+ else if (mode == CLASS_REG_WORD && !segmented_mode)
+ {
+ /* ok */
+ }
+ else
+ goto fail;
+ }
+ else
+ goto fail;
+ }
+ switch (mode & CLASS_MASK)
+ {
+ default:
+ break;
+ case CLASS_X:
+ case CLASS_IR:
+ case CLASS_BA:
+ case CLASS_BX:
+ case CLASS_DISP:
+ case CLASS_REG:
+ case CLASS_REG_WORD:
+ case CLASS_REG_BYTE:
+ case CLASS_REG_QUAD:
+ case CLASS_REG_LONG:
+ case CLASS_REGN0:
+ reg[this_try->arg_info[i] & ARG_MASK] = operands[i].reg;
+ break;
+ }
+ }
+
+ found = 1;
+ fail:;
+ }
+ if (found)
+ return this_try;
+ else
+ return 0;
+}
+
+static void
+DEFUN (check_operand, (operand, width, string),
+ struct z8k_op *operand AND
+ unsigned int width AND
+ char *string)
+{
+ if (operand->exp.X_add_symbol == 0
+ && operand->exp.X_op_symbol == 0)
+ {
+
+ /* No symbol involved, let's look at offset, it's dangerous if any of
+ the high bits are not 0 or ff's, find out by oring or anding with
+ the width and seeing if the answer is 0 or all fs*/
+ if ((operand->exp.X_add_number & ~width) != 0 &&
+ (operand->exp.X_add_number | width) != (~0))
+ {
+ as_warn (_("operand %s0x%x out of range."), string, operand->exp.X_add_number);
+ }
+ }
+
+}
+
+static char buffer[20];
+
+static void
+DEFUN (newfix, (ptr, type, operand),
+ int ptr AND
+ int type AND
+ expressionS * operand)
+{
+ if (operand->X_add_symbol
+ || operand->X_op_symbol
+ || operand->X_add_number)
+ {
+ fix_new_exp (frag_now,
+ ptr,
+ 1,
+ operand,
+ 0,
+ type);
+ }
+}
+
+static char *
+DEFUN (apply_fix, (ptr, type, operand, size),
+ char *ptr AND
+ int type AND
+ expressionS * operand AND
+ int size)
+{
+ int n = operand->X_add_number;
+
+ operand->X_add_number = n;
+ newfix ((ptr - buffer) / 2, type, operand);
+#if 1
+ switch (size)
+ {
+ case 8: /* 8 nibbles == 32 bits */
+ *ptr++ = n >> 28;
+ *ptr++ = n >> 24;
+ *ptr++ = n >> 20;
+ *ptr++ = n >> 16;
+ case 4: /* 4 niblles == 16 bits */
+ *ptr++ = n >> 12;
+ *ptr++ = n >> 8;
+ case 2:
+ *ptr++ = n >> 4;
+ case 1:
+ *ptr++ = n >> 0;
+ break;
+ }
+#endif
+ return ptr;
+
+}
+
+/* Now we know what sort of opcodes it is, lets build the bytes -
+ */
+#define INSERT(x,y) *x++ = y>>24; *x++ = y>> 16; *x++=y>>8; *x++ =y;
+static void
+build_bytes (this_try, operand)
+ opcode_entry_type * this_try;
+ struct z8k_op *operand;
+{
+ unsigned int i;
+
+ int length;
+ char *output;
+ char *output_ptr = buffer;
+ char part;
+ int c;
+ char high;
+ int nib;
+ int nibble;
+ unsigned int *class_ptr;
+
+ frag_wane (frag_now);
+ frag_new (0);
+
+ memset (buffer, 20, 0);
+ class_ptr = this_try->byte_info;
+top:;
+
+ for (nibble = 0; c = *class_ptr++; nibble++)
+ {
+
+ switch (c & CLASS_MASK)
+ {
+ default:
+
+ abort ();
+ case CLASS_ADDRESS:
+ /* Direct address, we don't cope with the SS mode right now */
+ if (segmented_mode)
+ {
+ da_operand->X_add_number |= 0x80000000;
+ output_ptr = apply_fix (output_ptr, R_IMM32, da_operand, 8);
+ }
+ else
+ {
+ output_ptr = apply_fix (output_ptr, R_IMM16, da_operand, 4);
+ }
+ da_operand = 0;
+ break;
+ case CLASS_DISP8:
+ /* pc rel 8 bit */
+ output_ptr = apply_fix (output_ptr, R_JR, da_operand, 2);
+ da_operand = 0;
+ break;
+
+ case CLASS_0DISP7:
+ /* pc rel 7 bit */
+ *output_ptr = 0;
+ output_ptr = apply_fix (output_ptr, R_DISP7, da_operand, 2);
+ da_operand = 0;
+ break;
+
+ case CLASS_1DISP7:
+ /* pc rel 7 bit */
+ *output_ptr = 0x80;
+ output_ptr = apply_fix (output_ptr, R_DISP7, da_operand, 2);
+ output_ptr[-2] = 0x8;
+ da_operand = 0;
+ break;
+
+ case CLASS_BIT_1OR2:
+ *output_ptr = c & 0xf;
+ if (imm_operand)
+ {
+ if (imm_operand->X_add_number == 2)
+ {
+ *output_ptr |= 2;
+ }
+ else if (imm_operand->X_add_number != 1)
+ {
+ as_bad (_("immediate must be 1 or 2"));
+ }
+ }
+ else
+ {
+ as_bad (_("immediate 1 or 2 expected"));
+ }
+ output_ptr++;
+ break;
+ case CLASS_CC:
+ *output_ptr++ = the_cc;
+ break;
+ case CLASS_0CCC:
+ *output_ptr++ = the_ctrl;
+ break;
+ case CLASS_1CCC:
+ *output_ptr++ = the_ctrl | 0x8;
+ break;
+ case CLASS_00II:
+ *output_ptr++ = (~the_interrupt & 0x3);
+ break;
+ case CLASS_01II:
+ *output_ptr++ = (~the_interrupt & 0x3) | 0x4;
+ break;
+ case CLASS_FLAGS:
+ *output_ptr++ = the_flags;
+ break;
+ case CLASS_BIT:
+ *output_ptr++ = c & 0xf;
+ break;
+ case CLASS_REGN0:
+ if (reg[c & 0xf] == 0)
+ {
+ as_bad (_("can't use R0 here"));
+ }
+ case CLASS_REG:
+ case CLASS_REG_BYTE:
+ case CLASS_REG_WORD:
+ case CLASS_REG_LONG:
+ case CLASS_REG_QUAD:
+ /* Insert bit mattern of
+ right reg */
+ *output_ptr++ = reg[c & 0xf];
+ break;
+ case CLASS_DISP:
+ output_ptr = apply_fix (output_ptr, R_IMM16, da_operand, 4);
+ da_operand = 0;
+ break;
+
+ case CLASS_IMM:
+ {
+ nib = 0;
+ switch (c & ARG_MASK)
+ {
+ case ARG_IMM4:
+ output_ptr = apply_fix (output_ptr, R_IMM4L, imm_operand, 1);
+ break;
+ case ARG_IMM4M1:
+ imm_operand->X_add_number--;
+ output_ptr = apply_fix (output_ptr, R_IMM4L, imm_operand, 1);
+ break;
+ case ARG_IMMNMINUS1:
+ imm_operand->X_add_number--;
+ output_ptr = apply_fix (output_ptr, R_IMM4L, imm_operand, 1);
+ break;
+ case ARG_NIM8:
+ imm_operand->X_add_number = -imm_operand->X_add_number;
+ case ARG_IMM8:
+ output_ptr = apply_fix (output_ptr, R_IMM8, imm_operand, 2);
+ break;
+ case ARG_IMM16:
+ output_ptr = apply_fix (output_ptr, R_IMM16, imm_operand, 4);
+ break;
+
+ case ARG_IMM32:
+ output_ptr = apply_fix (output_ptr, R_IMM32, imm_operand, 8);
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ }
+ }
+
+ /* Copy from the nibble buffer into the frag */
+
+ {
+ int length = (output_ptr - buffer) / 2;
+ char *src = buffer;
+ char *fragp = frag_more (length);
+
+ while (src < output_ptr)
+ {
+ *fragp = (src[0] << 4) | src[1];
+ src += 2;
+ fragp++;
+ }
+
+ }
+
+}
+
+/* This is the guts of the machine-dependent assembler. STR points to a
+ machine dependent instruction. This funciton is supposed to emit
+ the frags/bytes it assembles to.
+ */
+
+void
+DEFUN (md_assemble, (str),
+ char *str)
+{
+ char *op_start;
+ char *op_end;
+ unsigned int i;
+ struct z8k_op operand[3];
+ opcode_entry_type *opcode;
+ opcode_entry_type *prev_opcode;
+
+ char *dot = 0;
+ char c;
+
+ /* Drop leading whitespace */
+ while (*str == ' ')
+ str++;
+
+ /* find the op code end */
+ for (op_start = op_end = str;
+ *op_end != 0 && *op_end != ' ';
+ op_end++)
+ {
+ }
+
+ ;
+
+ if (op_end == op_start)
+ {
+ as_bad (_("can't find opcode "));
+ }
+ c = *op_end;
+
+ *op_end = 0;
+
+ opcode = (opcode_entry_type *) hash_find (opcode_hash_control,
+ op_start);
+
+
+ if (opcode == NULL)
+ {
+ as_bad (_("unknown opcode"));
+ return;
+ }
+
+ if (opcode->opcode == 250)
+ {
+ /* was really a pseudo op */
+
+ pseudo_typeS *p;
+ char oc;
+
+ char *old = input_line_pointer;
+ *op_end = c;
+
+
+ input_line_pointer = op_end;
+
+ oc = *old;
+ *old = '\n';
+ while (*input_line_pointer == ' ')
+ input_line_pointer++;
+ p = (pseudo_typeS *) (opcode->func);
+
+ (p->poc_handler) (p->poc_val);
+ input_line_pointer = old;
+ *old = oc;
+ }
+ else
+ {
+ input_line_pointer = get_operands (opcode, op_end,
+ operand);
+ prev_opcode = opcode;
+
+ opcode = get_specific (opcode, operand);
+
+ if (opcode == 0)
+ {
+ /* Couldn't find an opcode which matched the operands */
+ char *where = frag_more (2);
+
+ where[0] = 0x0;
+ where[1] = 0x0;
+
+ as_bad (_("Can't find opcode to match operands"));
+ return;
+ }
+
+ build_bytes (opcode, operand);
+ }
+}
+
+void
+DEFUN (tc_crawl_symbol_chain, (headers),
+ object_headers * headers)
+{
+ printf (_("call to tc_crawl_symbol_chain \n"));
+}
+
+symbolS *
+DEFUN (md_undefined_symbol, (name),
+ char *name)
+{
+ return 0;
+}
+
+void
+DEFUN (tc_headers_hook, (headers),
+ object_headers * headers)
+{
+ printf (_("call to tc_headers_hook \n"));
+}
+
+/* Various routines to kill one day */
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+/* Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP . An error message is returned, or NULL on OK.
+ */
+char *
+md_atof (type, litP, sizeP)
+ char type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+ char *atof_ieee ();
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("Bad call to MD_ATOF()");
+ }
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+
+ *sizeP = prec * sizeof (LITTLENUM_TYPE);
+ for (wordP = words; prec--;)
+ {
+ md_number_to_chars (litP, (long) (*wordP++), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ return 0;
+}
+
+CONST char *md_shortopts = "z:";
+struct option md_longopts[] = {
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ switch (c)
+ {
+ case 'z':
+ if (!strcmp (arg, "8001"))
+ s_segm ();
+ else if (!strcmp (arg, "8002"))
+ s_unseg ();
+ else
+ {
+ as_bad (_("invalid architecture -z%s"), arg);
+ return 0;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ fprintf(stream, _("\
+Z8K options:\n\
+-z8001 generate segmented code\n\
+-z8002 generate unsegmented code\n"));
+}
+
+void
+tc_aout_fix_to_chars ()
+{
+ printf (_("call to tc_aout_fix_to_chars \n"));
+ abort ();
+}
+
+void
+md_convert_frag (headers, seg, fragP)
+ object_headers *headers;
+ segT seg;
+ fragS *fragP;
+{
+ printf (_("call to md_convert_frag \n"));
+ abort ();
+}
+
+valueT
+DEFUN (md_section_align, (seg, size),
+ segT seg AND
+ valueT size)
+{
+ return ((size + (1 << section_alignment[(int) seg]) - 1) & (-1 << section_alignment[(int) seg]));
+
+}
+
+void
+md_apply_fix (fixP, val)
+ fixS *fixP;
+ long val;
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ switch (fixP->fx_r_type)
+ {
+ case R_IMM4L:
+ buf[0] = (buf[0] & 0xf0) | ((buf[0] + val) & 0xf);
+ break;
+
+ case R_JR:
+
+ *buf++ = val;
+ /* if (val != 0) abort();*/
+ break;
+
+ case R_DISP7:
+
+ *buf++ += val;
+ /* if (val != 0) abort();*/
+ break;
+
+ case R_IMM8:
+ buf[0] += val;
+ break;
+ case R_IMM16:
+ *buf++ = (val >> 8);
+ *buf++ = val;
+ break;
+ case R_IMM32:
+ *buf++ = (val >> 24);
+ *buf++ = (val >> 16);
+ *buf++ = (val >> 8);
+ *buf++ = val;
+ break;
+#if 0
+ case R_DA | R_SEG:
+ *buf++ = (val >> 16);
+ *buf++ = 0x00;
+ *buf++ = (val >> 8);
+ *buf++ = val;
+ break;
+#endif
+
+ case 0:
+ md_number_to_chars (buf, val, fixP->fx_size);
+ break;
+
+ default:
+ abort ();
+
+ }
+}
+
+int
+md_estimate_size_before_relax (fragP, segment_type)
+ register fragS *fragP;
+ register segT segment_type;
+{
+ printf (_("call tomd_estimate_size_before_relax \n"));
+ abort ();
+}
+
+/* Put number into target byte order */
+
+void
+DEFUN (md_number_to_chars, (ptr, use, nbytes),
+ char *ptr AND
+ valueT use AND
+ int nbytes)
+{
+ number_to_chars_bigendian (ptr, use, nbytes);
+}
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ abort ();
+}
+
+void
+tc_coff_symbol_emit_hook (s)
+ struct symbol *s;
+{
+}
+
+void
+tc_reloc_mangle (fix_ptr, intr, base)
+ fixS *fix_ptr;
+ struct internal_reloc *intr;
+ bfd_vma base;
+
+{
+ symbolS *symbol_ptr;
+
+ if (fix_ptr->fx_addsy &&
+ fix_ptr->fx_subsy)
+ {
+ symbolS *add = fix_ptr->fx_addsy;
+ symbolS *sub = fix_ptr->fx_subsy;
+ if (S_GET_SEGMENT(add) != S_GET_SEGMENT(sub))
+ {
+ as_bad(_("Can't subtract symbols in different sections %s %s"),
+ S_GET_NAME(add), S_GET_NAME(sub));
+ }
+ else {
+ int diff = S_GET_VALUE(add) - S_GET_VALUE(sub);
+ fix_ptr->fx_addsy = 0;
+ fix_ptr->fx_subsy = 0;
+ fix_ptr->fx_offset += diff;
+ }
+ }
+ symbol_ptr = fix_ptr->fx_addsy;
+
+ /* If this relocation is attached to a symbol then it's ok
+ to output it */
+ if (fix_ptr->fx_r_type == 0)
+ {
+ /* cons likes to create reloc32's whatever the size of the reloc.. */
+ switch (fix_ptr->fx_size)
+ {
+ case 2:
+ intr->r_type = R_IMM16;
+ break;
+ case 1:
+ intr->r_type = R_IMM8;
+ break;
+ case 4:
+ intr->r_type = R_IMM32;
+ break;
+ default:
+ abort ();
+ }
+
+ }
+ else
+ {
+ intr->r_type = fix_ptr->fx_r_type;
+ }
+
+ intr->r_vaddr = fix_ptr->fx_frag->fr_address + fix_ptr->fx_where + base;
+ intr->r_offset = fix_ptr->fx_offset;
+
+ if (symbol_ptr)
+ intr->r_symndx = symbol_ptr->sy_number;
+ else
+ intr->r_symndx = -1;
+}
+
diff --git a/gas/config/tc-z8k.h b/gas/config/tc-z8k.h
new file mode 100644
index 0000000..d88b656
--- /dev/null
+++ b/gas/config/tc-z8k.h
@@ -0,0 +1,54 @@
+/* This file is tc-z8k.h
+ Copyright (C) 1987-1992, 93, 95, 97, 1998 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+
+#define TC_Z8K
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+#if ANSI_PROTOTYPES
+struct internal_reloc;
+#endif
+
+#define WORKING_DOT_WORD
+
+#ifndef BFD_ASSEMBLER
+#define LOCAL_LABEL(x) 0
+#endif
+
+/* This macro translates between an internal fix and an coff reloc type */
+#define TC_COFF_FIX2RTYPE(fixP) abort();
+
+#define BFD_ARCH bfd_arch_z8k
+#define COFF_MAGIC 0x8000
+#define TC_COUNT_RELOC(x) (1)
+#define IGNORE_NONSTANDARD_ESCAPES
+
+#define TC_RELOC_MANGLE(s,a,b,c) tc_reloc_mangle(a,b,c)
+extern void tc_reloc_mangle
+ PARAMS ((struct fix *, struct internal_reloc *, bfd_vma));
+
+#define DO_NOT_STRIP 0
+#define LISTING_HEADER "Zilog Z8000 GAS "
+#define NEED_FX_R_TYPE 1
+#define RELOC_32 1234
+
+#define md_operand(x)
+
+/* end of tc-z8k.h */
diff --git a/gas/config/te-386bsd.h b/gas/config/te-386bsd.h
new file mode 100644
index 0000000..dbff990
--- /dev/null
+++ b/gas/config/te-386bsd.h
@@ -0,0 +1,31 @@
+/* te-386bsd.h -- 386BSD target environment declarations.
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#define TE_386BSD 1
+
+#include "obj-format.h"
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of te-sun3.h */
diff --git a/gas/config/te-aux.h b/gas/config/te-aux.h
new file mode 100644
index 0000000..da6fa01
--- /dev/null
+++ b/gas/config/te-aux.h
@@ -0,0 +1,17 @@
+#define TE_AUX
+
+/* From obj-coff.h:
+ This internal_lineno crap is to stop namespace pollution from the
+ bfd internal coff headerfile. */
+#define internal_lineno bfd_internal_lineno
+#include "coff/aux-coff.h" /* override bits in coff/internal.h */
+#undef internal_lineno
+
+#define COFF_NOLOAD_PROBLEM
+#define KEEP_RELOC_INFO
+
+#include "obj-format.h"
+
+#ifndef LOCAL_LABELS_FB
+#define LOCAL_LABELS_FB 1
+#endif
diff --git a/gas/config/te-delt88.h b/gas/config/te-delt88.h
new file mode 100644
index 0000000..adcd6b7
--- /dev/null
+++ b/gas/config/te-delt88.h
@@ -0,0 +1,13 @@
+/* This file is te-delta88.h. */
+
+#define TE_DELTA88 1
+
+#define COFF_NOLOAD_PROBLEM 1
+
+/* Added these, because if we don't know what we're targetting we may
+ need an assembler version of libgcc, and that will use local
+ labels. */
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+#include "obj-format.h"
diff --git a/gas/config/te-delta.h b/gas/config/te-delta.h
new file mode 100644
index 0000000..be6c62c
--- /dev/null
+++ b/gas/config/te-delta.h
@@ -0,0 +1,14 @@
+#define TE_DELTA
+
+#include "obj-format.h"
+
+#define COFF_NOLOAD_PROBLEM 1
+#define COFF_COMMON_ADDEND 1
+
+/* Added these, because if we don't know what we're targetting we may
+ need an assembler version of libgcc, and that will use local
+ labels. */
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+/* end of te-delta.h */
diff --git a/gas/config/te-dpx2.h b/gas/config/te-dpx2.h
new file mode 100644
index 0000000..45341ca
--- /dev/null
+++ b/gas/config/te-dpx2.h
@@ -0,0 +1,12 @@
+/* Machine specific defines for the dpx2 machine */
+
+/* The magic number is not the usual MC68MAGIC. */
+#define COFF_MAGIC MC68KBCSMAGIC
+
+#define REGISTER_PREFIX_OPTIONAL 1
+
+#define TARGET_FORMAT "coff-m68k-un"
+
+#include "obj-format.h"
+
+/* end of te-dpx2.h */
diff --git a/gas/config/te-dynix.h b/gas/config/te-dynix.h
new file mode 100644
index 0000000..9e7b30f
--- /dev/null
+++ b/gas/config/te-dynix.h
@@ -0,0 +1,7 @@
+/* This is for i386-sequent-bsd. The assembler probably does not
+ actually work, as the support in BFD is not complete as of this
+ writing. See bfd/i386-dynix.c. */
+
+#define TE_DYNIX 1
+
+#include "obj-format.h"
diff --git a/gas/config/te-epoc-pe.h b/gas/config/te-epoc-pe.h
new file mode 100644
index 0000000..6c5f914
--- /dev/null
+++ b/gas/config/te-epoc-pe.h
@@ -0,0 +1,8 @@
+#define TE_PE
+#define TE_EPOC
+#define LEX_AT 1 /* can have @'s inside labels */
+
+/* The PE format supports long section names. */
+#define COFF_LONG_SECTION_NAMES
+
+#include "obj-format.h"
diff --git a/gas/config/te-generic.h b/gas/config/te-generic.h
new file mode 100644
index 0000000..b8eda45
--- /dev/null
+++ b/gas/config/te-generic.h
@@ -0,0 +1,22 @@
+/*
+ * This file is te-generic.h and is intended to be a template for
+ * target environment specific header files.
+ *
+ * It is my intent that this file will evolve into a file suitable for config,
+ * compile, and copying as an aid for testing and porting. xoxorich.
+ */
+
+/* Added these, because if we don't know what we're targetting we may
+ need an assembler version of libgcc, and that will use local
+ labels. */
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+/* these define interfaces */
+#ifdef OBJ_HEADER
+#include OBJ_HEADER
+#else
+#include "obj-format.h"
+#endif
+
+/* end of te-generic.h */
diff --git a/gas/config/te-go32.h b/gas/config/te-go32.h
new file mode 100644
index 0000000..49fbd2e
--- /dev/null
+++ b/gas/config/te-go32.h
@@ -0,0 +1,16 @@
+/*
+ * This file is te-go32.h
+ */
+
+#define TE_GO32
+
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+#define TARGET_FORMAT "coff-go32"
+
+/* GAS should treat '.align value' as an alignment of 2**value */
+#define USE_ALIGN_PTWO
+
+/* these define interfaces */
+#include "obj-format.h"
diff --git a/gas/config/te-hp300.h b/gas/config/te-hp300.h
new file mode 100644
index 0000000..8e94ab4
--- /dev/null
+++ b/gas/config/te-hp300.h
@@ -0,0 +1,25 @@
+/* te-hp300.h -- hpux 9000/300 target environment declarations.
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+#include "obj-format.h"
+
+/* end of te-hp300.h */
diff --git a/gas/config/te-hppa.h b/gas/config/te-hppa.h
new file mode 100644
index 0000000..8fc4fa7
--- /dev/null
+++ b/gas/config/te-hppa.h
@@ -0,0 +1,26 @@
+/* Machine specific defines for the PA machine
+ Copyright 1987, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/*
+ HP PA-RISC and OSF/1 support was contributed by the Center for
+ Software Science at the University of Utah.
+ */
+
+/* these define interfaces */
+#include "obj-format.h"
diff --git a/gas/config/te-i386aix.h b/gas/config/te-i386aix.h
new file mode 100644
index 0000000..f8f0945
--- /dev/null
+++ b/gas/config/te-i386aix.h
@@ -0,0 +1,29 @@
+/*
+ * This file is te-i386aix.h and is built from pieces of code from Minh Tran-Le
+ * <TRANLE@INTELLICORP.COM> by rich@cygnus.com.
+ */
+
+#define TE_I386AIX 1
+
+#include "obj-format.h"
+
+/*
+ * Undefine REVERSE_SORT_RELOCS to keep the relocation entries sorted
+ * in ascending vaddr.
+ */
+#undef REVERSE_SORT_RELOCS
+
+/*
+ * Define KEEP_RELOC_INFO so that the strip reloc info flag F_RELFLG
+ * is not used in the filehdr for COFF output.
+ */
+#define KEEP_RELOC_INFO
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 79
+ * End:
+ */
+
+/* end of te-i386aix.h */
diff --git a/gas/config/te-ic960.h b/gas/config/te-ic960.h
new file mode 100644
index 0000000..8331fb9
--- /dev/null
+++ b/gas/config/te-ic960.h
@@ -0,0 +1,38 @@
+/* This file is te-ic960.h
+ Copyright (C) 1987-1992, 94, 95, 1997 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+/*
+ * This file is te-ic960.h and is intended to define ic960 environment
+ * specific differences.
+ */
+
+#define OBJ_COFF_OMIT_OPTIONAL_HEADER
+
+#ifndef BFD_ASSEMBLER
+#define LOCAL_LABEL(name) ((name[0] =='L') \
+ || (name[0] =='.' \
+ && (name[1]=='C' \
+ || name[1]=='I' \
+ || name[1]=='.')))
+#endif
+
+#include "obj-format.h"
+
+/* end of te-ic960.h */
diff --git a/gas/config/te-linux.h b/gas/config/te-linux.h
new file mode 100644
index 0000000..c235a7a
--- /dev/null
+++ b/gas/config/te-linux.h
@@ -0,0 +1,4 @@
+#define TE_LINUX
+#define LOCAL_LABELS_FB 1
+
+#include "obj-format.h"
diff --git a/gas/config/te-lnews.h b/gas/config/te-lnews.h
new file mode 100644
index 0000000..acbcc5a
--- /dev/null
+++ b/gas/config/te-lnews.h
@@ -0,0 +1,5 @@
+/* te-lnews.h -- little-endian NEWS emulation. */
+
+#define ECOFF_LITTLE_FORMAT "ecoff-biglittlemips"
+
+#include "obj-format.h"
diff --git a/gas/config/te-lynx.h b/gas/config/te-lynx.h
new file mode 100644
index 0000000..708515d
--- /dev/null
+++ b/gas/config/te-lynx.h
@@ -0,0 +1,7 @@
+#define TE_LYNX
+
+#include "obj-format.h"
+
+#ifndef LOCAL_LABELS_FB
+#define LOCAL_LABELS_FB 1
+#endif
diff --git a/gas/config/te-mach.h b/gas/config/te-mach.h
new file mode 100644
index 0000000..b7547f8
--- /dev/null
+++ b/gas/config/te-mach.h
@@ -0,0 +1,2 @@
+#define TE_Mach
+#include "obj-format.h"
diff --git a/gas/config/te-macos.h b/gas/config/te-macos.h
new file mode 100644
index 0000000..5f48dc4
--- /dev/null
+++ b/gas/config/te-macos.h
@@ -0,0 +1,11 @@
+/* This file is te-macos.h. */
+
+#define TE_POWERMAC 1
+
+/* Added these, because if we don't know what we're targetting we may
+ need an assembler version of libgcc, and that will use local
+ labels. */
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+#include "obj-format.h"
diff --git a/gas/config/te-multi.h b/gas/config/te-multi.h
new file mode 100644
index 0000000..b8eda45
--- /dev/null
+++ b/gas/config/te-multi.h
@@ -0,0 +1,22 @@
+/*
+ * This file is te-generic.h and is intended to be a template for
+ * target environment specific header files.
+ *
+ * It is my intent that this file will evolve into a file suitable for config,
+ * compile, and copying as an aid for testing and porting. xoxorich.
+ */
+
+/* Added these, because if we don't know what we're targetting we may
+ need an assembler version of libgcc, and that will use local
+ labels. */
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+/* these define interfaces */
+#ifdef OBJ_HEADER
+#include OBJ_HEADER
+#else
+#include "obj-format.h"
+#endif
+
+/* end of te-generic.h */
diff --git a/gas/config/te-nbsd.h b/gas/config/te-nbsd.h
new file mode 100644
index 0000000..cee4600
--- /dev/null
+++ b/gas/config/te-nbsd.h
@@ -0,0 +1,23 @@
+/* te-nbsd.h -- NetBSD target environment declarations.
+ Copyright (C) 1987, 90, 91, 92, 94, 95, 1998 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#define TE_NetBSD 1
+#define LOCAL_LABELS_FB 1
+#include "obj-format.h"
diff --git a/gas/config/te-nbsd532.h b/gas/config/te-nbsd532.h
new file mode 100644
index 0000000..8cf9e5f
--- /dev/null
+++ b/gas/config/te-nbsd532.h
@@ -0,0 +1,19 @@
+/*
+ * This file is te-netbsd532.h
+ *
+ * Written by Ian Dall <idall@eleceng.adelaide.edu.au>
+ *
+ * 19-Jun-94
+ *
+ */
+
+#define TARGET_FORMAT "a.out-ns32k-netbsd"
+
+#include "obj-format.h"
+
+/* Maybe these should be more like TC_NS32532 and TC_NS32381 in case
+ * of conflicts. NS32381 is used in opcode/ns32k.h and that is also
+ * used by GDB. Need to check.
+ */
+#define NS32532
+#define NS32381
diff --git a/gas/config/te-pc532mach.h b/gas/config/te-pc532mach.h
new file mode 100644
index 0000000..0ee7ff8
--- /dev/null
+++ b/gas/config/te-pc532mach.h
@@ -0,0 +1,19 @@
+/*
+ * This file is te-pc532.h
+ *
+ * Written by Ian Dall <idall@eleceng.adelaide.edu.au>
+ *
+ * 24-May-94
+ *
+ */
+
+#define TARGET_FORMAT "a.out-pc532-mach"
+
+#include "obj-format.h"
+
+/* Maybe these should be more like TC_NS32532 and TC_NS32381 in case
+ * of conflicts. NS32381 is used in opcode/ns32k.h and that is also
+ * used by GDB. Need to check.
+ */
+#define NS32532
+#define NS32381
diff --git a/gas/config/te-pe.h b/gas/config/te-pe.h
new file mode 100644
index 0000000..1c1f0b2
--- /dev/null
+++ b/gas/config/te-pe.h
@@ -0,0 +1,7 @@
+#define TE_PE
+#define LEX_AT 1 /* can have @'s inside labels */
+
+/* The PE format supports long section names. */
+#define COFF_LONG_SECTION_NAMES
+
+#include "obj-format.h"
diff --git a/gas/config/te-ppcnw.h b/gas/config/te-ppcnw.h
new file mode 100644
index 0000000..2ddf050
--- /dev/null
+++ b/gas/config/te-ppcnw.h
@@ -0,0 +1,31 @@
+/* te-ppcnw.h -- Power PC running Netware environment declarations.
+ Copyright (C) 1994 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Added these, because if we don't know what we're targetting we may
+ need an assembler version of libgcc, and that will use local
+ labels. */
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+/* these define interfaces */
+#include "obj-format.h"
+
+/* gcc uses escape sequences for ppc/netware */
+
+#undef NO_STRING_ESCAPES
diff --git a/gas/config/te-psos.h b/gas/config/te-psos.h
new file mode 100644
index 0000000..2ad4153
--- /dev/null
+++ b/gas/config/te-psos.h
@@ -0,0 +1,22 @@
+/*
+ * This file is te-psos.h for embedded systems running pSOS.
+ * Contributed by Martin Anantharaman (martin@mail.imech.uni-duisburg.de)
+ */
+
+#define TE_PSOS
+
+/* Added these, because if we don't know what we're targetting we may
+ need an assembler version of libgcc, and that will use local
+ labels. */
+
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+/* This makes GAS more versatile and blocks some ELF'isms in
+ tc-m68k.h. */
+
+#define REGISTER_PREFIX_OPTIONAL 1
+
+#include "obj-format.h"
+
+/* end of te-psos.h */
diff --git a/gas/config/te-riscix.h b/gas/config/te-riscix.h
new file mode 100644
index 0000000..7c7253e
--- /dev/null
+++ b/gas/config/te-riscix.h
@@ -0,0 +1,6 @@
+#define TE_RISCIX
+
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+#include "obj-format.h"
diff --git a/gas/config/te-sparcaout.h b/gas/config/te-sparcaout.h
new file mode 100644
index 0000000..6310184
--- /dev/null
+++ b/gas/config/te-sparcaout.h
@@ -0,0 +1,21 @@
+/* te-sparcaout.h -- embedded sparc-aout target environment declarations.
+ Copyright (C) 1996 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#define TE_SPARCAOUT 1
+#include "obj-format.h"
diff --git a/gas/config/te-sun3.h b/gas/config/te-sun3.h
new file mode 100644
index 0000000..475d42b
--- /dev/null
+++ b/gas/config/te-sun3.h
@@ -0,0 +1,48 @@
+/* te-sun3.h -- Sun-3 target environment declarations.
+ Copyright (C) 1987, 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* This header file contains the #defines specific
+ to SUN computer SUN 3 series computers. (The only kind
+ we have around here, unfortunatly.)
+
+ Rumor has it that this file will work on the Sun-2 if the assembler
+ is called with -m68010 This is not tested. */
+
+
+#define TE_SUN3 1
+
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+/* Could also be :
+ #define S_LOCAL_NAME(s) (S_GET_NAME(s)[0] == '.' &&
+ S_GET_NAME(s)[1] == 'L' ||
+ S_GET_NAME(s)[1] == '.')
+ */
+
+#include "obj-format.h"
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of te-sun3.h */
diff --git a/gas/config/te-svr4.h b/gas/config/te-svr4.h
new file mode 100644
index 0000000..7217ee1
--- /dev/null
+++ b/gas/config/te-svr4.h
@@ -0,0 +1,4 @@
+#define TE_SVR4
+#define LOCAL_LABELS_FB 1
+
+#include "obj-format.h"
diff --git a/gas/config/te-sysv32.h b/gas/config/te-sysv32.h
new file mode 100644
index 0000000..923e6e5
--- /dev/null
+++ b/gas/config/te-sysv32.h
@@ -0,0 +1,6 @@
+/* Remove leading underscore from the gcc generated symbol names */
+#define STRIP_UNDERSCORE
+
+#include "obj-format.h"
+
+/* end of te-sysv32.h */
diff --git a/gas/config/vax-inst.h b/gas/config/vax-inst.h
new file mode 100644
index 0000000..2ad8f98
--- /dev/null
+++ b/gas/config/vax-inst.h
@@ -0,0 +1,77 @@
+/* vax-inst.h - GNU - Part of vax.c
+ Copyright (C) 1987, 1992, 1995 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/*
+ * This is part of vax-ins-parse.c & friends.
+ * We want to parse a vax instruction text into a tree defined here.
+ */
+
+#define VIT_MAX_OPERANDS (6) /* maximum number of operands in one */
+/* single vax instruction */
+
+struct vop /* vax instruction operand */
+{
+ short int vop_ndx; /* -1, or index register. eg 7=[R7] */
+ short int vop_reg; /* -1, or register number. eg @I^#=0xF */
+ /* Helps distinguish "abs" from "abs(PC)". */
+ short int vop_mode; /* addressing mode 4 bits. eg I^#=0x9 */
+ char vop_short; /* operand displacement length as written */
+ /* ' '=none, "bilsw"=B^I^L^S^W^. */
+ char vop_access; /* 'b'branch ' 'no-instruction 'amrvw'norm */
+ char vop_width; /* Operand width, one of "bdfghloqw" */
+ const char *vop_warn; /* warning message of this operand, if any */
+ const char *vop_error; /* say if operand is inappropriate */
+ char *vop_expr_begin; /* Unparsed expression, 1st char ... */
+ char *vop_expr_end; /* ... last char. */
+ unsigned char vop_nbytes; /* number of bytes in datum */
+};
+
+
+typedef long vax_opcodeT; /* For initialising array of opcodes */
+/* Some synthetic opcodes > 16 bits! */
+
+#define VIT_OPCODE_SYNTHETIC 0x80000000 /* Not real hardware instruction. */
+#define VIT_OPCODE_SPECIAL 0x40000000 /* Not normal branch optimising. */
+/* Never set without ..._SYNTHETIC */
+
+#define VAX_WIDTH_UNCONDITIONAL_JUMP '-' /* These are encoded into */
+#define VAX_WIDTH_CONDITIONAL_JUMP '?' /* vop_width when vop_access=='b' */
+#define VAX_WIDTH_WORD_JUMP '!' /* and VIT_OPCODE_SYNTHETIC set. */
+#define VAX_WIDTH_BYTE_JUMP ':' /* */
+
+#define VAX_JMP (0x17) /* Useful for branch optimising. Jump instr*/
+#define VAX_PC_RELATIVE_MODE (0xef) /* Use it after VAX_JMP */
+#define VAX_ABSOLUTE_MODE (0x9F)/* Use as @#... */
+#define VAX_BRB (0x11) /* Canonical branch. */
+#define VAX_BRW (0x31) /* Another canonical branch */
+#define VAX_WIDEN_WORD (0x20) /* Add this to byte branch to get word br. */
+#define VAX_WIDEN_LONG (0x6) /* Add this to byte branch to get long jmp.*/
+/* Needs VAX_PC_RELATIVE_MODE byte after it*/
+
+struct vit /* vax instruction tree */
+{
+ /* vit_opcode is char[] for portability. */
+ char vit_opcode[sizeof (vax_opcodeT)];
+ unsigned char vit_opcode_nbytes; /* How long is _opcode? (chars) */
+ unsigned char vit_operands; /* */
+ struct vop vit_operand[VIT_MAX_OPERANDS]; /* operands */
+ const char *vit_error; /* "" or error text */
+};
+
+/* end of vax-inst.h */
diff --git a/gas/config/vms-a-conf.h b/gas/config/vms-a-conf.h
new file mode 100644
index 0000000..688fc68
--- /dev/null
+++ b/gas/config/vms-a-conf.h
@@ -0,0 +1,129 @@
+/* vms-alpha-conf.h. Generated manually from conf.in,
+ and used by config-gas-alpha.com when constructing config.h. */
+
+/* Define if using alloca.c. */
+#ifdef __GNUC__
+#undef C_ALLOCA
+#else
+#define C_ALLOCA
+#endif
+
+/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems.
+ This function is required for alloca.c support on those systems. */
+#undef CRAY_STACKSEG_END
+
+/* Define if you have <alloca.h> and it should be used (not on Ultrix). */
+#undef HAVE_ALLOCA_H
+
+/* Define as __inline if that's what the C compiler calls it. */
+#ifdef __GNUC__
+#undef inline
+#else
+#define inline
+#endif
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at run-time.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown
+ */
+#define STACK_DIRECTION (-1)
+
+/* Should gas use high-level BFD interfaces? */
+#define BFD_ASSEMBLER
+
+/* Some assert/preprocessor combinations are incapable of handling
+ certain kinds of constructs in the argument of assert. For example,
+ quoted strings (if requoting isn't done right) or newlines. */
+#ifdef __GNUC__
+#undef BROKEN_ASSERT
+#else
+#define BROKEN_ASSERT
+#endif
+
+/* If we aren't doing cross-assembling, some operations can be optimized,
+ since byte orders and value sizes don't need to be adjusted. */
+#undef CROSS_COMPILE
+
+/* Some gas code wants to know these parameters. */
+#define TARGET_ALIAS "alpha-vms"
+#define TARGET_CPU "alpha"
+#define TARGET_CANONICAL "alpha-dec-vms"
+#define TARGET_OS "openVMS/Alpha"
+#define TARGET_VENDOR "dec"
+
+/* Sometimes the system header files don't declare malloc and realloc. */
+#undef NEED_DECLARATION_MALLOC
+
+/* Sometimes the system header files don't declare free. */
+#undef NEED_DECLARATION_FREE
+
+/* Sometimes errno.h doesn't declare errno itself. */
+#undef NEED_DECLARATION_ERRNO
+
+#undef MANY_SEGMENTS
+
+/* Needed only for sparc configuration */
+#undef sparcv9
+
+/* Define if you have the remove function. */
+#define HAVE_REMOVE
+
+/* Define if you have the unlink function. */
+#undef HAVE_UNLINK
+
+/* Define if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H
+
+/* Define if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define if you have the <stdarg.h> header file. */
+#define HAVE_STDARG_H
+
+/* Define if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H
+
+/* Define if you have the <string.h> header file. */
+#define HAVE_STRING_H
+
+/* Define if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define if you have the <sys/types.h> header file. */
+#ifdef __GNUC__
+#define HAVE_SYS_TYPES_H
+#else
+#undef HAVE_SYS_TYPES_H
+#endif
+
+/* Define if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H /* config-gas.com will make one if necessary */
+
+/* Define if you have the <varargs.h> header file. */
+#undef HAVE_VARARGS_H
+
+/* VMS-specific: we need to set up EXIT_xxx here because the default
+ values in as.h are inappropriate for VMS, but we also want to prevent
+ as.h's inclusion of <stdlib.h> from triggering redefinition warnings.
+ <stdlib.h> guards itself against multiple inclusion, so including it
+ here turns as.h's later #include into a no-op. (We can't simply use
+ #ifndef HAVE_STDLIB_H here, because the <stdlib.h> in several older
+ gcc-vms distributions neglects to define these two required macros.) */
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if __DECC
+#undef EXIT_SUCCESS
+#undef EXIT_FAILURE
+#define EXIT_SUCCESS 1 /* SS$_NORMAL, STS$K_SUCCESS */
+#define EXIT_FAILURE 0x10000002 /* (STS$K_ERROR | STS$M_INHIB_MSG) */
+#endif
+
+#include <unixlib.h>
+#if __DECC
+extern int strcasecmp ();
+extern int strncasecmp ();
+#endif
diff --git a/gas/config/vms-conf.h b/gas/config/vms-conf.h
new file mode 100644
index 0000000..7225cfc
--- /dev/null
+++ b/gas/config/vms-conf.h
@@ -0,0 +1,179 @@
+/* vms-conf.h. Generated manually from conf.in,
+ and used by config-gas.com when constructing config.h. */
+
+/* Define if using alloca.c. */
+#ifdef __GNUC__
+#undef C_ALLOCA
+#else
+#define C_ALLOCA
+#endif
+
+/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems.
+ This function is required for alloca.c support on those systems. */
+#undef CRAY_STACKSEG_END
+
+/* Define if you have alloca, as a function or macro. */
+#undef HAVE_ALLOCA
+
+/* Define if you have <alloca.h> and it should be used (not on Ultrix). */
+#undef HAVE_ALLOCA_H
+
+/* Define as __inline if that's what the C compiler calls it. */
+#ifdef __GNUC__
+#undef inline
+#else
+#define inline
+#endif
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at run-time.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown
+ */
+#define STACK_DIRECTION (-1)
+
+/* Define if lex declares yytext as a char * by default, not a char[]. */
+#undef YYTEXT_POINTER
+
+/* Name of package. */
+#undef PACKAGE
+
+/* Version of package. */
+/* Define in by config-gas.com */
+/* #undef VERSION */
+
+/* Should gas use high-level BFD interfaces? */
+#undef BFD_ASSEMBLER
+
+/* Some assert/preprocessor combinations are incapable of handling
+ certain kinds of constructs in the argument of assert. For example,
+ quoted strings (if requoting isn't done right) or newlines. */
+#ifdef __GNUC__
+#undef BROKEN_ASSERT
+#else
+#define BROKEN_ASSERT
+#endif
+
+/* If we aren't doing cross-assembling, some operations can be optimized,
+ since byte orders and value sizes don't need to be adjusted. */
+#undef CROSS_COMPILE
+
+/* Some gas code wants to know these parameters. */
+#define TARGET_ALIAS "vms"
+#define TARGET_CPU "vax"
+#define TARGET_CANONICAL "vax-dec-vms"
+#define TARGET_OS "vms"
+#define TARGET_VENDOR "dec"
+
+/* Sometimes the system header files don't declare strstr. */
+#undef NEED_DECLARATION_STRSTR
+
+/* Sometimes the system header files don't declare malloc and realloc. */
+#undef NEED_DECLARATION_MALLOC
+
+/* Sometimes the system header files don't declare free. */
+#undef NEED_DECLARATION_FREE
+
+/* Sometimes the system header files don't declare sbrk. */
+#undef NEED_DECLARATION_SBRK
+
+/* Sometimes errno.h doesn't declare errno itself. */
+#undef NEED_DECLARATION_ERRNO
+
+#undef MANY_SEGMENTS
+
+/* The configure script defines this for some targets based on the
+ target name used. It is not always defined. */
+#undef TARGET_BYTES_BIG_ENDIAN
+
+/* Needed only for some configurations that can produce multiple output
+ formats. */
+#undef DEFAULT_EMULATION
+#undef EMULATIONS
+#undef USE_EMULATIONS
+#undef OBJ_MAYBE_AOUT
+#undef OBJ_MAYBE_BOUT
+#undef OBJ_MAYBE_COFF
+#undef OBJ_MAYBE_ECOFF
+#undef OBJ_MAYBE_ELF
+#undef OBJ_MAYBE_GENERIC
+#undef OBJ_MAYBE_HP300
+#undef OBJ_MAYBE_IEEE
+#undef OBJ_MAYBE_SOM
+#undef OBJ_MAYBE_VMS
+
+/* Used for some of the COFF configurations, when the COFF code needs
+ to select something based on the CPU type before it knows it... */
+#undef I386COFF
+#undef M68KCOFF
+#undef M88KCOFF
+
+/* Using cgen code? */
+#undef USING_CGEN
+
+/* Needed only for sparc configuration. */
+#undef DEFAULT_ARCH
+
+/* Needed only for PowerPC Solaris. */
+#undef TARGET_SOLARIS_COMMENT
+
+/* Needed only for SCO 5. */
+#undef SCO_ELF
+
+/* Define if you have the remove function. */
+#define HAVE_REMOVE
+
+/* Define if you have the sbrk function. */
+/* sbrk() is available, but we don't want gas to use it. */
+#undef HAVE_SBRK
+
+/* Define if you have the unlink function. */
+#undef HAVE_UNLINK
+
+/* Define if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H
+
+/* Define if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define if you have the <stdarg.h> header file. */
+#define HAVE_STDARG_H
+
+/* Define if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H
+
+/* Define if you have the <string.h> header file. */
+#define HAVE_STRING_H
+
+/* Define if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define if you have the <sys/types.h> header file. */
+#ifdef __GNUC__
+#define HAVE_SYS_TYPES_H
+#else
+#undef HAVE_SYS_TYPES_H
+#endif
+
+/* Define if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H /* config-gas.com will make one if necessary */
+
+/* Define if you have the <varargs.h> header file. */
+#undef HAVE_VARARGS_H
+
+/* VMS-specific: we need to set up EXIT_xxx here because the default
+ values in as.h are inappropriate for VMS, but we also want to prevent
+ as.h's inclusion of <stdlib.h> from triggering redefinition warnings.
+ <stdlib.h> guards itself against multiple inclusion, so including it
+ here turns as.h's later #include into a no-op. (We can't simply use
+ #ifndef HAVE_STDLIB_H here, because the <stdlib.h> in several older
+ gcc-vms distributions neglects to define these two required macros.) */
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#undef EXIT_SUCCESS
+#undef EXIT_FAILURE
+#endif
+#define EXIT_SUCCESS 1 /* SS$_NORMAL, STS$K_SUCCESS */
+#define EXIT_FAILURE 0x10000002 /* (STS$K_ERROR | STS$M_INHIB_MSG) */