| /* tc-ip2k.c -- Assembler for the Scenix IP2xxx. | 
 |    Copyright (C) 2000, 2002, 2003, 2005, 2006, 2007 | 
 |    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 (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; | 
 | } | 
 |  |