diff options
Diffstat (limited to 'gas/config/tc-ip2k.c')
-rw-r--r-- | gas/config/tc-ip2k.c | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/gas/config/tc-ip2k.c b/gas/config/tc-ip2k.c new file mode 100644 index 0000000..4c217f2 --- /dev/null +++ b/gas/config/tc-ip2k.c @@ -0,0 +1,507 @@ +/* tc-ip2k.c -- Assembler for the Scenix IP2xxx. + Copyright (C) 2000, 2002 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 "dwarf2dbg.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"; + +static void ip2k_elf_section_text (int); +static void ip2k_elf_section_rtn (int); + +/* The target specific pseudo-ops which we support. */ +const pseudo_typeS md_pseudo_table[] = +{ + { "file", dwarf2_directive_file, 0 }, + { "loc", dwarf2_directive_loc, 0 }, + { "text", ip2k_elf_section_text, 0 }, + { "sect", ip2k_elf_section_rtn, 0 }, + { NULL, NULL, 0 } +}; + + + +#define OPTION_CPU_IP2022 (OPTION_MD_BASE) +#define OPTION_CPU_IP2022EXT (OPTION_MD_BASE+1) + +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 = ""; + +/* Flag to detect when switching to code section where insn alignment is + implied. */ +static int force_code_align = 0; + +/* Mach selected from command line. */ +int ip2k_mach = 0; +unsigned ip2k_mach_bitmask = 0; + +int +md_parse_option (c, arg) + 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 (stream) + 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 () +{ + /* 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 (str) + 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; + long value; + const char *curpc_plus_2 = ".+2"; + + errmsg = cgen_parse_address (gas_cgen_cpu_desc, & curpc_plus_2, + IP2K_OPERAND_ADDR16CJP, + BFD_RELOC_IP2K_PC_SKIP, + & result_type, & value); + if (errmsg) + { + as_bad ("%s", 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); +} + +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 ATTRIBUTE_UNUSED; +{ + return 0; +} + +int +md_estimate_size_before_relax (fragP, segment) + fragS * fragP ATTRIBUTE_UNUSED; + segT segment ATTRIBUTE_UNUSED; +{ + as_fatal (_("md_estimate_size_before_relax\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 (abfd, sec, fragP) + bfd * abfd ATTRIBUTE_UNUSED; + segT sec ATTRIBUTE_UNUSED; + fragS * fragP ATTRIBUTE_UNUSED; +{ +} + + +/* Functions concerning relocs. */ + +long +md_pcrel_from (fixP) + fixS *fixP; +{ + as_fatal (_("md_pcrel_from\n")); + + /* Return the address of the delay slot. */ + return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address; +} + + +/* 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 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 (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 prec; + LITTLENUM_TYPE words [MAX_LITTLENUMS]; + LITTLENUM_TYPE *wordP; + char * t; + char * atof_ieee PARAMS ((char *, int, LITTLENUM_TYPE *)); + + 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); + + /* This loops outputs the LITTLENUMs in REVERSE order; in accord with + the ip2k endianness. */ + for (wordP = words; prec--;) + { + md_number_to_chars (litP, (valueT) (*wordP++), sizeof (LITTLENUM_TYPE)); + litP += sizeof (LITTLENUM_TYPE); + } + + return 0; +} + + +/* 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 (fix) + fixS * fix; +{ + switch (fix->fx_r_type) + { + /* (No C++ support in ip2k. */ + /* case BFD_RELOC_VTABLE_INHERIT: */ + /* case BFD_RELOC_VTABLE_ENTRY: */ + + 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; + } + return 0; + + default: + return 0; + } +} + +void +ip2k_apply_fix3 (fixP, valueP, seg) + 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_fix3 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, where, + CGEN_INSN_BITSIZE (fixP->fx_cgen.insn)); + /* Preserve (DP) or (SP) specification. */ + *valueP += (insn_value & 0x180); + } + + gas_cgen_md_apply_fix3 (fixP, valueP, seg); +} + +int +ip2k_elf_section_flags (flags, attr, type) + int flags; + int 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; +} + +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; +} |