summaryrefslogtreecommitdiffstats
path: root/binutils-2.25/gas/config/tc-ip2k.c
diff options
context:
space:
mode:
Diffstat (limited to 'binutils-2.25/gas/config/tc-ip2k.c')
-rw-r--r--binutils-2.25/gas/config/tc-ip2k.c427
1 files changed, 427 insertions, 0 deletions
diff --git a/binutils-2.25/gas/config/tc-ip2k.c b/binutils-2.25/gas/config/tc-ip2k.c
new file mode 100644
index 00000000..3836cc9e
--- /dev/null
+++ b/binutils-2.25/gas/config/tc-ip2k.c
@@ -0,0 +1,427 @@
+/* tc-ip2k.c -- Assembler for the Scenix IP2xxx.
+ Copyright (C) 2000, 2002, 2003, 2005, 2006, 2007, 2009
+ 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 3, 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, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "subsegs.h"
+#include "symcat.h"
+#include "opcodes/ip2k-desc.h"
+#include "opcodes/ip2k-opc.h"
+#include "cgen.h"
+#include "elf/common.h"
+#include "elf/ip2k.h"
+#include "libbfd.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];
+}
+ip2k_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";
+
+/* Flag to detect when switching to code section where insn alignment is
+ implied. */
+static int force_code_align = 0;
+
+/* Mach selected from command line. */
+static int ip2k_mach = 0;
+static unsigned ip2k_mach_bitmask = 0;
+
+
+static void
+ip2k_elf_section_rtn (int i)
+{
+ obj_elf_section(i);
+
+ if (force_code_align)
+ {
+ /* The s_align_ptwo function expects that we are just after a .align
+ directive and it will either try and read the align value or stop
+ if end of line so we must fake it out so it thinks we are at the
+ end of the line. */
+ char *old_input_line_pointer = input_line_pointer;
+ input_line_pointer = "\n";
+ s_align_ptwo (1);
+ force_code_align = 0;
+ /* Restore. */
+ input_line_pointer = old_input_line_pointer;
+ }
+}
+
+static void
+ip2k_elf_section_text (int i)
+{
+ char *old_input_line_pointer;
+ obj_elf_text(i);
+
+ /* the s_align_ptwo function expects that we are just after a .align
+ directive and it will either try and read the align value or stop if
+ end of line so we must fake it out so it thinks we are at the end of
+ the line. */
+ old_input_line_pointer = input_line_pointer;
+ input_line_pointer = "\n";
+ s_align_ptwo (1);
+ force_code_align = 0;
+ /* Restore. */
+ input_line_pointer = old_input_line_pointer;
+}
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "text", ip2k_elf_section_text, 0 },
+ { "sect", ip2k_elf_section_rtn, 0 },
+ { NULL, NULL, 0 }
+};
+
+
+
+enum options
+{
+ OPTION_CPU_IP2022 = OPTION_MD_BASE,
+ OPTION_CPU_IP2022EXT
+};
+
+struct option md_longopts[] =
+{
+ { "mip2022", no_argument, NULL, OPTION_CPU_IP2022 },
+ { "mip2022ext", no_argument, NULL, OPTION_CPU_IP2022EXT },
+ { NULL, no_argument, NULL, 0 },
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+const char * md_shortopts = "";
+
+int
+md_parse_option (int c ATTRIBUTE_UNUSED, char * arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ case OPTION_CPU_IP2022:
+ ip2k_mach = bfd_mach_ip2022;
+ ip2k_mach_bitmask = 1 << MACH_IP2022;
+ break;
+
+ case OPTION_CPU_IP2022EXT:
+ ip2k_mach = bfd_mach_ip2022ext;
+ ip2k_mach_bitmask = 1 << MACH_IP2022EXT;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE * stream)
+{
+ fprintf (stream, _("IP2K specific command line options:\n"));
+ fprintf (stream, _(" -mip2022 restrict to IP2022 insns \n"));
+ fprintf (stream, _(" -mip2022ext permit extended IP2022 insn\n"));
+}
+
+
+void
+md_begin (void)
+{
+ /* Initialize the `cgen' interface. */
+
+ /* Set the machine number and endian. */
+ gas_cgen_cpu_desc = ip2k_cgen_cpu_open (CGEN_CPU_OPEN_MACHS,
+ ip2k_mach_bitmask,
+ CGEN_CPU_OPEN_ENDIAN,
+ CGEN_ENDIAN_BIG,
+ CGEN_CPU_OPEN_END);
+ ip2k_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);
+
+ /* Set the machine type. */
+ bfd_default_set_arch_mach (stdoutput, bfd_arch_ip2k, ip2k_mach);
+}
+
+
+void
+md_assemble (char * str)
+{
+ ip2k_insn insn;
+ char * errmsg;
+
+ /* Initialize GAS's cgen interface for a new instruction. */
+ gas_cgen_init_parse ();
+
+ insn.insn = ip2k_cgen_assemble_insn
+ (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg);
+
+ if (!insn.insn)
+ {
+ as_bad ("%s", errmsg);
+ return;
+ }
+
+ /* Check for special relocation required by SKIP instructions. */
+ if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_SKIPA))
+ /* Unconditional skip has a 1-bit relocation of the current pc, so
+ that we emit either sb pcl.0 or snb pcl.0 depending on whether
+ the PCL (pc + 2) >> 1 is odd or even. */
+ {
+ enum cgen_parse_operand_result result_type;
+ bfd_vma value;
+ const char *curpc_plus_2 = ".+2";
+ const char *err;
+
+ err = cgen_parse_address (gas_cgen_cpu_desc, & curpc_plus_2,
+ IP2K_OPERAND_ADDR16CJP,
+ BFD_RELOC_IP2K_PC_SKIP,
+ & result_type, & value);
+ if (err)
+ {
+ as_bad ("%s", err);
+ 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);
+}
+
+valueT
+md_section_align (segT segment, valueT size)
+{
+ int align = bfd_get_section_alignment (stdoutput, segment);
+
+ return ((size + (1 << align) - 1) & (-1 << align));
+}
+
+
+symbolS *
+md_undefined_symbol (char * name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+int
+md_estimate_size_before_relax (fragS * fragP ATTRIBUTE_UNUSED,
+ segT segment ATTRIBUTE_UNUSED)
+{
+ as_fatal (_("relaxation not supported\n"));
+ return 1;
+}
+
+
+/* *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 (bfd * abfd ATTRIBUTE_UNUSED,
+ segT sec ATTRIBUTE_UNUSED,
+ fragS * fragP ATTRIBUTE_UNUSED)
+{
+}
+
+
+/* Functions concerning relocs. */
+
+long
+md_pcrel_from (fixS *fixP ATTRIBUTE_UNUSED)
+{
+ abort ();
+}
+
+
+/* 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 (const CGEN_INSN * insn ATTRIBUTE_UNUSED,
+ const CGEN_OPERAND * operand,
+ fixS * fixP ATTRIBUTE_UNUSED)
+{
+ bfd_reloc_code_real_type result;
+
+ result = BFD_RELOC_NONE;
+
+ switch (operand->type)
+ {
+ case IP2K_OPERAND_FR:
+ case IP2K_OPERAND_ADDR16L:
+ case IP2K_OPERAND_ADDR16H:
+ case IP2K_OPERAND_LIT8:
+ /* These may have been processed at parse time. */
+ if (fixP->fx_cgen.opinfo != 0)
+ result = fixP->fx_cgen.opinfo;
+ fixP->fx_no_overflow = 1;
+ break;
+
+ case IP2K_OPERAND_ADDR16CJP:
+ result = fixP->fx_cgen.opinfo;
+ if (result == 0 || result == BFD_RELOC_NONE)
+ result = BFD_RELOC_IP2K_ADDR16CJP;
+ fixP->fx_no_overflow = 1;
+ break;
+
+ case IP2K_OPERAND_ADDR16P:
+ result = BFD_RELOC_IP2K_PAGE3;
+ fixP->fx_no_overflow = 1;
+ break;
+
+ default:
+ result = BFD_RELOC_NONE;
+ break;
+ }
+
+ return result;
+}
+
+
+/* Write a value out to the object file, using the appropriate endianness. */
+
+void
+md_number_to_chars (char * buf, valueT val, int n)
+{
+ number_to_chars_bigendian (buf, val, n);
+}
+
+char *
+md_atof (int type, char * litP, int * sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+
+/* See whether we need to force a relocation into the output file.
+ Force most of them, since the linker's bfd relocation engine
+ understands range limits better than gas' cgen fixup engine.
+ Consider the case of a fixup intermediate value being larger than
+ the instruction it will be eventually encoded within. */
+
+int
+ip2k_force_relocation (fixS * fix)
+{
+ switch (fix->fx_r_type)
+ {
+ case BFD_RELOC_IP2K_FR9:
+ case BFD_RELOC_IP2K_FR_OFFSET:
+ case BFD_RELOC_IP2K_BANK:
+ case BFD_RELOC_IP2K_ADDR16CJP:
+ case BFD_RELOC_IP2K_PAGE3:
+ case BFD_RELOC_IP2K_LO8DATA:
+ case BFD_RELOC_IP2K_HI8DATA:
+ case BFD_RELOC_IP2K_EX8DATA:
+ case BFD_RELOC_IP2K_LO8INSN:
+ case BFD_RELOC_IP2K_HI8INSN:
+ case BFD_RELOC_IP2K_PC_SKIP:
+ case BFD_RELOC_IP2K_TEXT:
+ return 1;
+
+ case BFD_RELOC_16:
+ if (fix->fx_subsy && S_IS_DEFINED (fix->fx_subsy)
+ && fix->fx_addsy && S_IS_DEFINED (fix->fx_addsy)
+ && (S_GET_SEGMENT (fix->fx_addsy)->flags & SEC_CODE))
+ {
+ fix->fx_r_type = BFD_RELOC_IP2K_TEXT;
+ return 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return generic_force_reloc (fix);
+}
+
+void
+ip2k_apply_fix (fixS *fixP, valueT *valueP, segT seg)
+{
+ if (fixP->fx_r_type == BFD_RELOC_IP2K_TEXT
+ && ! fixP->fx_addsy
+ && ! fixP->fx_subsy)
+ {
+ *valueP = ((int)(* valueP)) / 2;
+ fixP->fx_r_type = BFD_RELOC_16;
+ }
+ else if (fixP->fx_r_type == BFD_RELOC_UNUSED + IP2K_OPERAND_FR)
+ {
+ /* Must be careful when we are fixing up an FR. We could be
+ fixing up an offset to (SP) or (DP) in which case we don't
+ want to step on the top 2 bits of the FR operand. The
+ gas_cgen_md_apply_fix doesn't know any better and overwrites
+ the entire operand. We counter this by adding the bits
+ to the new value. */
+ char *where = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ /* Canonical name, since used a lot. */
+ CGEN_CPU_DESC cd = gas_cgen_cpu_desc;
+ CGEN_INSN_INT insn_value
+ = cgen_get_insn_value (cd, (unsigned char *) where,
+ CGEN_INSN_BITSIZE (fixP->fx_cgen.insn));
+ /* Preserve (DP) or (SP) specification. */
+ *valueP += (insn_value & 0x180);
+ }
+
+ gas_cgen_md_apply_fix (fixP, valueP, seg);
+}
+
+int
+ip2k_elf_section_flags (flagword flags,
+ bfd_vma attr ATTRIBUTE_UNUSED,
+ int type ATTRIBUTE_UNUSED)
+{
+ /* This is used to detect when the section changes to an executable section.
+ This function is called by the elf section processing. When we note an
+ executable section specifier we set an internal flag to denote when
+ word alignment should be forced. */
+ if (flags & SEC_CODE)
+ force_code_align = 1;
+
+ return flags;
+}
+