1  /* tc-ip2k.c -- Assembler for the Scenix IP2xxx.
       2     Copyright (C) 2000-2023 Free Software Foundation, Inc.
       3  
       4     This file is part of GAS, the GNU Assembler.
       5  
       6     GAS is free software; you can redistribute it and/or modify
       7     it under the terms of the GNU General Public License as published by
       8     the Free Software Foundation; either version 3, or (at your option)
       9     any later version.
      10  
      11     GAS is distributed in the hope that it will be useful,
      12     but WITHOUT ANY WARRANTY; without even the implied warranty of
      13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14     GNU General Public License for more details.
      15  
      16     You should have received a copy of the GNU General Public License
      17     along with GAS; see the file COPYING.  If not, write to
      18     the Free Software Foundation, 51 Franklin Street - Fifth Floor,
      19     Boston, MA 02110-1301, USA.  */
      20  
      21  #include "as.h"
      22  #include "subsegs.h"
      23  #include "symcat.h"
      24  #include "opcodes/ip2k-desc.h"
      25  #include "opcodes/ip2k-opc.h"
      26  #include "cgen.h"
      27  #include "elf/common.h"
      28  #include "elf/ip2k.h"
      29  
      30  /* Structure to hold all of the different components describing
      31     an individual instruction.  */
      32  typedef struct
      33  {
      34    const CGEN_INSN *	insn;
      35    const CGEN_INSN *	orig_insn;
      36    CGEN_FIELDS		fields;
      37  #if CGEN_INT_INSN_P
      38    CGEN_INSN_INT         buffer [1];
      39  #define INSN_VALUE(buf) (*(buf))
      40  #else
      41    unsigned char         buffer [CGEN_MAX_INSN_SIZE];
      42  #define INSN_VALUE(buf) (buf)
      43  #endif
      44    char *		addr;
      45    fragS *		frag;
      46    int                   num_fixups;
      47    fixS *                fixups [GAS_CGEN_MAX_FIXUPS];
      48    int                   indices [MAX_OPERAND_INSTANCES];
      49  }
      50  ip2k_insn;
      51  
      52  const char comment_chars[]        = ";";
      53  const char line_comment_chars[]   = "#";
      54  const char line_separator_chars[] = "";
      55  const char EXP_CHARS[]            = "eE";
      56  const char FLT_CHARS[]            = "dD";
      57  
      58  /* Flag to detect when switching to code section where insn alignment is
      59     implied.  */
      60  static int force_code_align = 0;
      61  
      62  /* Mach selected from command line.  */
      63  static int ip2k_mach = 0;
      64  static unsigned ip2k_mach_bitmask = 0;
      65  
      66  
      67  static void
      68  ip2k_elf_section_rtn (int i)
      69  {
      70    obj_elf_section(i);
      71  
      72    if (force_code_align)
      73      {
      74        do_align (1, NULL, 0, 0);
      75        force_code_align = 0;
      76      }
      77  }
      78  
      79  static void
      80  ip2k_elf_section_text (int i)
      81  {
      82    obj_elf_text(i);
      83  
      84    do_align (1, NULL, 0, 0);
      85    force_code_align = 0;
      86  }
      87  
      88  /* The target specific pseudo-ops which we support.  */
      89  const pseudo_typeS md_pseudo_table[] =
      90  {
      91      { "text",   ip2k_elf_section_text,  0 },
      92      { "sect",   ip2k_elf_section_rtn,   0 },
      93      { NULL, 	NULL,			0 }
      94  };
      95  
      96  
      97  
      98  enum options
      99  {
     100    OPTION_CPU_IP2022 = OPTION_MD_BASE,
     101    OPTION_CPU_IP2022EXT
     102  };
     103  
     104  struct option md_longopts[] =
     105  {
     106    { "mip2022",     no_argument, NULL, OPTION_CPU_IP2022 },
     107    { "mip2022ext",  no_argument, NULL, OPTION_CPU_IP2022EXT },
     108    { NULL,           no_argument, NULL, 0 },
     109  };
     110  size_t md_longopts_size = sizeof (md_longopts);
     111  
     112  const char * md_shortopts = "";
     113  
     114  int
     115  md_parse_option (int c ATTRIBUTE_UNUSED, const char * arg ATTRIBUTE_UNUSED)
     116  {
     117    switch (c)
     118      {
     119      case OPTION_CPU_IP2022:
     120        ip2k_mach = bfd_mach_ip2022;
     121        ip2k_mach_bitmask = 1 << MACH_IP2022;
     122        break;
     123  
     124      case OPTION_CPU_IP2022EXT:
     125        ip2k_mach = bfd_mach_ip2022ext;
     126        ip2k_mach_bitmask = 1 << MACH_IP2022EXT;
     127        break;
     128  
     129      default:
     130        return 0;
     131      }
     132  
     133    return 1;
     134  }
     135  
     136  void
     137  md_show_usage (FILE * stream)
     138  {
     139    fprintf (stream, _("IP2K specific command line options:\n"));
     140    fprintf (stream, _("  -mip2022               restrict to IP2022 insns \n"));
     141    fprintf (stream, _("  -mip2022ext            permit extended IP2022 insn\n"));
     142  }
     143  
     144  
     145  void
     146  md_begin (void)
     147  {
     148    /* Initialize the `cgen' interface.  */
     149  
     150    /* Set the machine number and endian.  */
     151    gas_cgen_cpu_desc = ip2k_cgen_cpu_open (CGEN_CPU_OPEN_MACHS,
     152  					  ip2k_mach_bitmask,
     153  					  CGEN_CPU_OPEN_ENDIAN,
     154  					  CGEN_ENDIAN_BIG,
     155  					  CGEN_CPU_OPEN_END);
     156    ip2k_cgen_init_asm (gas_cgen_cpu_desc);
     157  
     158    /* This is a callback from cgen to gas to parse operands.  */
     159    cgen_set_parse_operand_fn (gas_cgen_cpu_desc, gas_cgen_parse_operand);
     160  
     161    /* Set the machine type.  */
     162    bfd_default_set_arch_mach (stdoutput, bfd_arch_ip2k, ip2k_mach);
     163  
     164    literal_prefix_dollar_hex = true;
     165  }
     166  
     167  
     168  void
     169  md_assemble (char * str)
     170  {
     171    ip2k_insn insn;
     172    char * errmsg;
     173  
     174    /* Initialize GAS's cgen interface for a new instruction.  */
     175    gas_cgen_init_parse ();
     176  
     177    insn.insn = ip2k_cgen_assemble_insn
     178        (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg);
     179  
     180    if (!insn.insn)
     181      {
     182        as_bad ("%s", errmsg);
     183        return;
     184      }
     185  
     186    /* Check for special relocation required by SKIP instructions.  */
     187    if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_SKIPA))
     188      /* Unconditional skip has a 1-bit relocation of the current pc, so
     189         that we emit either sb pcl.0 or snb pcl.0 depending on whether
     190         the PCL (pc + 2) >> 1 is odd or even.  */
     191      {
     192        enum cgen_parse_operand_result result_type;
     193        bfd_vma value;
     194        const char *curpc_plus_2 = ".+2";
     195        const char *err;
     196  
     197        err = cgen_parse_address (gas_cgen_cpu_desc, & curpc_plus_2,
     198  				IP2K_OPERAND_ADDR16CJP,
     199  				BFD_RELOC_IP2K_PC_SKIP,
     200  				& result_type, & value);
     201        if (err)
     202  	{
     203  	  as_bad ("%s", err);
     204  	  return;
     205  	}
     206      }
     207  
     208    /* Doesn't really matter what we pass for RELAX_P here.  */
     209    gas_cgen_finish_insn (insn.insn, insn.buffer,
     210  			CGEN_FIELDS_BITSIZE (& insn.fields), 1, NULL);
     211  }
     212  
     213  valueT
     214  md_section_align (segT segment, valueT size)
     215  {
     216    int align = bfd_section_alignment (segment);
     217  
     218    return ((size + (1 << align) - 1) & -(1 << align));
     219  }
     220  
     221  
     222  symbolS *
     223  md_undefined_symbol (char * name ATTRIBUTE_UNUSED)
     224  {
     225      return 0;
     226  }
     227  
     228  int
     229  md_estimate_size_before_relax (fragS * fragP ATTRIBUTE_UNUSED,
     230  			       segT    segment ATTRIBUTE_UNUSED)
     231  {
     232    as_fatal (_("relaxation not supported\n"));
     233    return 1;
     234  }
     235  
     236  
     237  /* *fragP has been relaxed to its final size, and now needs to have
     238     the bytes inside it modified to conform to the new size.
     239  
     240     Called after relaxation is finished.
     241     fragP->fr_type == rs_machine_dependent.
     242     fragP->fr_subtype is the subtype of what the address relaxed to.  */
     243  
     244  void
     245  md_convert_frag (bfd   * abfd  ATTRIBUTE_UNUSED,
     246  		 segT    sec   ATTRIBUTE_UNUSED,
     247  		 fragS * fragP ATTRIBUTE_UNUSED)
     248  {
     249  }
     250  
     251  
     252  /* Functions concerning relocs.  */
     253  
     254  long
     255  md_pcrel_from (fixS *fixP ATTRIBUTE_UNUSED)
     256  {
     257    abort ();
     258  }
     259  
     260  
     261  /* Return the bfd reloc type for OPERAND of INSN at fixup FIXP.
     262     Returns BFD_RELOC_NONE if no reloc type can be found.
     263     *FIXP may be modified if desired.  */
     264  
     265  bfd_reloc_code_real_type
     266  md_cgen_lookup_reloc (const CGEN_INSN *    insn     ATTRIBUTE_UNUSED,
     267  		      const CGEN_OPERAND * operand,
     268  		      fixS *               fixP     ATTRIBUTE_UNUSED)
     269  {
     270    bfd_reloc_code_real_type result;
     271  
     272    result = BFD_RELOC_NONE;
     273  
     274    switch (operand->type)
     275      {
     276      case IP2K_OPERAND_FR:
     277      case IP2K_OPERAND_ADDR16L:
     278      case IP2K_OPERAND_ADDR16H:
     279      case IP2K_OPERAND_LIT8:
     280        /* These may have been processed at parse time.  */
     281        if (fixP->fx_cgen.opinfo != 0)
     282  	result = fixP->fx_cgen.opinfo;
     283        fixP->fx_no_overflow = 1;
     284        break;
     285  
     286      case IP2K_OPERAND_ADDR16CJP:
     287        result = fixP->fx_cgen.opinfo;
     288        if (result == 0 || result == BFD_RELOC_NONE)
     289  	result = BFD_RELOC_IP2K_ADDR16CJP;
     290        fixP->fx_no_overflow = 1;
     291        break;
     292  
     293      case IP2K_OPERAND_ADDR16P:
     294        result = BFD_RELOC_IP2K_PAGE3;
     295        fixP->fx_no_overflow = 1;
     296        break;
     297  
     298      default:
     299        result = BFD_RELOC_NONE;
     300        break;
     301      }
     302  
     303    return result;
     304  }
     305  
     306  
     307  /* Write a value out to the object file, using the appropriate endianness.  */
     308  
     309  void
     310  md_number_to_chars (char * buf, valueT val, int n)
     311  {
     312    number_to_chars_bigendian (buf, val, n);
     313  }
     314  
     315  const char *
     316  md_atof (int type, char * litP, int *  sizeP)
     317  {
     318    return ieee_md_atof (type, litP, sizeP, true);
     319  }
     320  
     321  
     322  /* See whether we need to force a relocation into the output file.
     323     Force most of them, since the linker's bfd relocation engine
     324     understands range limits better than gas' cgen fixup engine.
     325     Consider the case of a fixup intermediate value being larger than
     326     the instruction it will be eventually encoded within.  */
     327  
     328  int
     329  ip2k_force_relocation (fixS * fix)
     330  {
     331    switch (fix->fx_r_type)
     332      {
     333      case BFD_RELOC_IP2K_FR9:
     334      case BFD_RELOC_IP2K_FR_OFFSET:
     335      case BFD_RELOC_IP2K_BANK:
     336      case BFD_RELOC_IP2K_ADDR16CJP:
     337      case BFD_RELOC_IP2K_PAGE3:
     338      case BFD_RELOC_IP2K_LO8DATA:
     339      case BFD_RELOC_IP2K_HI8DATA:
     340      case BFD_RELOC_IP2K_EX8DATA:
     341      case BFD_RELOC_IP2K_LO8INSN:
     342      case BFD_RELOC_IP2K_HI8INSN:
     343      case BFD_RELOC_IP2K_PC_SKIP:
     344      case BFD_RELOC_IP2K_TEXT:
     345        return 1;
     346  
     347      case BFD_RELOC_16:
     348        if (fix->fx_subsy && S_IS_DEFINED (fix->fx_subsy)
     349  	  && fix->fx_addsy && S_IS_DEFINED (fix->fx_addsy)
     350  	  && (S_GET_SEGMENT (fix->fx_addsy)->flags & SEC_CODE))
     351  	{
     352  	  fix->fx_r_type = BFD_RELOC_IP2K_TEXT;
     353  	  return 0;
     354  	}
     355        break;
     356  
     357      default:
     358        break;
     359      }
     360  
     361    return generic_force_reloc (fix);
     362  }
     363  
     364  void
     365  ip2k_apply_fix (fixS *fixP, valueT *valueP, segT seg)
     366  {
     367    if (fixP->fx_r_type == BFD_RELOC_IP2K_TEXT
     368        && ! fixP->fx_addsy
     369        && ! fixP->fx_subsy)
     370      {
     371        *valueP = ((int)(* valueP)) / 2;
     372        fixP->fx_r_type = BFD_RELOC_16;
     373      }
     374    else if (fixP->fx_r_type == BFD_RELOC_UNUSED + IP2K_OPERAND_FR)
     375      {
     376        /* Must be careful when we are fixing up an FR.  We could be
     377  	 fixing up an offset to (SP) or (DP) in which case we don't
     378  	 want to step on the top 2 bits of the FR operand.  The
     379  	 gas_cgen_md_apply_fix doesn't know any better and overwrites
     380  	 the entire operand.  We counter this by adding the bits
     381  	 to the new value.  */
     382        char *where = fixP->fx_frag->fr_literal + fixP->fx_where;
     383  
     384        /* Canonical name, since used a lot.  */
     385        CGEN_CPU_DESC cd = gas_cgen_cpu_desc;
     386        CGEN_INSN_INT insn_value
     387  	= cgen_get_insn_value (cd, (unsigned char *) where,
     388  			       CGEN_INSN_BITSIZE (fixP->fx_cgen.insn),
     389  			       gas_cgen_cpu_desc->insn_endian);
     390        /* Preserve (DP) or (SP) specification.  */
     391        *valueP += (insn_value & 0x180);
     392      }
     393  
     394    gas_cgen_md_apply_fix (fixP, valueP, seg);
     395  }
     396  
     397  int
     398  ip2k_elf_section_flags (flagword flags,
     399  			bfd_vma attr ATTRIBUTE_UNUSED,
     400  			int type ATTRIBUTE_UNUSED)
     401  {
     402    /* This is used to detect when the section changes to an executable section.
     403       This function is called by the elf section processing.  When we note an
     404       executable section specifier we set an internal flag to denote when
     405       word alignment should be forced.  */
     406    if (flags & SEC_CODE)
     407      force_code_align = 1;
     408  
     409    return flags;
     410  }
     411