1  /* tc-xstormy16.c -- Assembler for the Sanyo XSTORMY16.
       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/xstormy16-desc.h"
      25  #include "opcodes/xstormy16-opc.h"
      26  #include "cgen.h"
      27  
      28  /* Structure to hold all of the different components describing
      29     an individual instruction.  */
      30  typedef struct
      31  {
      32    const CGEN_INSN *	insn;
      33    const CGEN_INSN *	orig_insn;
      34    CGEN_FIELDS		fields;
      35  #if CGEN_INT_INSN_P
      36    CGEN_INSN_INT         buffer [1];
      37  #define INSN_VALUE(buf) (*(buf))
      38  #else
      39    unsigned char         buffer [CGEN_MAX_INSN_SIZE];
      40  #define INSN_VALUE(buf) (buf)
      41  #endif
      42    char *		addr;
      43    fragS *		frag;
      44    int                   num_fixups;
      45    fixS *                fixups [GAS_CGEN_MAX_FIXUPS];
      46    int                   indices [MAX_OPERAND_INSTANCES];
      47  }
      48  xstormy16_insn;
      49  
      50  const char comment_chars[]        = ";";
      51  const char line_comment_chars[]   = "#";
      52  const char line_separator_chars[] = "|";
      53  const char EXP_CHARS[]            = "eE";
      54  const char FLT_CHARS[]            = "dD";
      55  
      56  #define O_fptr_symbol	(O_max + 1)
      57  
      58  #define XSTORMY16_SHORTOPTS ""
      59  const char * md_shortopts = XSTORMY16_SHORTOPTS;
      60  
      61  struct option md_longopts[] =
      62  {
      63    {NULL, no_argument, NULL, 0}
      64  };
      65  size_t md_longopts_size = sizeof (md_longopts);
      66  
      67  int
      68  md_parse_option (int    c ATTRIBUTE_UNUSED,
      69  		 const char * arg ATTRIBUTE_UNUSED)
      70  {
      71    return 0;
      72  }
      73  
      74  void
      75  md_show_usage (FILE * stream)
      76  {
      77    fprintf (stream, _(" XSTORMY16 specific command line options:\n"));
      78  }
      79  
      80  /* The target specific pseudo-ops which we support.  */
      81  const pseudo_typeS md_pseudo_table[] =
      82  {
      83    { "word",	cons,		4 },
      84    { NULL, 	NULL, 		0 }
      85  };
      86  
      87  
      88  void
      89  md_begin (void)
      90  {
      91    /* Initialize the `cgen' interface.  */
      92  
      93    /* Set the machine number and endian.  */
      94    gas_cgen_cpu_desc = xstormy16_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, 0,
      95  					  CGEN_CPU_OPEN_ENDIAN,
      96  					  CGEN_ENDIAN_LITTLE,
      97  					  CGEN_CPU_OPEN_END);
      98    xstormy16_cgen_init_asm (gas_cgen_cpu_desc);
      99  
     100    /* This is a callback from cgen to gas to parse operands.  */
     101    cgen_set_parse_operand_fn (gas_cgen_cpu_desc, gas_cgen_parse_operand);
     102  }
     103  
     104  static bool skipping_fptr = false;
     105  
     106  void
     107  md_assemble (char * str)
     108  {
     109    xstormy16_insn insn;
     110    char *    errmsg;
     111  
     112    /* Make sure that if we had an erroneous input line which triggered
     113       the skipping_fptr boolean that it does not affect following lines.  */
     114    skipping_fptr = false;
     115  
     116    /* Initialize GAS's cgen interface for a new instruction.  */
     117    gas_cgen_init_parse ();
     118  
     119    insn.insn = xstormy16_cgen_assemble_insn
     120      (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg);
     121  
     122    if (!insn.insn)
     123      {
     124        as_bad ("%s", errmsg);
     125        return;
     126      }
     127  
     128    /* Doesn't really matter what we pass for RELAX_P here.  */
     129    gas_cgen_finish_insn (insn.insn, insn.buffer,
     130  			CGEN_FIELDS_BITSIZE (& insn.fields), 0, NULL);
     131  }
     132  
     133  void
     134  md_operand (expressionS * e)
     135  {
     136    if (*input_line_pointer != '@')
     137      return;
     138  
     139    if (startswith (input_line_pointer + 1, "fptr"))
     140      {
     141        input_line_pointer += 5;
     142        SKIP_WHITESPACE ();
     143        if (*input_line_pointer != '(')
     144  	{
     145  	  as_bad (_("Expected '('"));
     146  	  goto err;
     147  	}
     148        input_line_pointer++;
     149  
     150        expression (e);
     151  
     152        if (*input_line_pointer != ')')
     153  	{
     154  	  as_bad (_("Missing ')'"));
     155  	  goto err;
     156  	}
     157        input_line_pointer++;
     158        SKIP_WHITESPACE ();
     159  
     160        if (e->X_op != O_symbol)
     161  	as_bad (_("Not a symbolic expression"));
     162        else if (* input_line_pointer == '-')
     163  	/* We are computing the difference of two function pointers
     164  	   like this:
     165  
     166  	    .hword  @fptr (foo) - @fptr (bar)
     167  
     168  	  In this situation we do not want to generate O_fptr_symbol
     169  	  operands because the result is an absolute value, not a
     170  	  function pointer.
     171  
     172  	  We need to make the check here, rather than when the fixup
     173  	  is generated as the function names (foo & bar in the above
     174  	  example) might be local symbols and we want the expression
     175  	  to be evaluated now.  This kind of thing can happen when
     176  	  gcc is generating computed gotos.  */
     177  	skipping_fptr = true;
     178        else if (skipping_fptr)
     179  	skipping_fptr = false;
     180        else
     181          e->X_op = O_fptr_symbol;
     182      }
     183  
     184    return;
     185   err:
     186    ignore_rest_of_line ();
     187  }
     188  
     189  /* Called while parsing data to create a fixup.
     190     Create BFD_RELOC_XSTORMY16_FPTR16 relocations.  */
     191  
     192  void
     193  xstormy16_cons_fix_new (fragS *f,
     194  			int where,
     195  			int nbytes,
     196  			expressionS *exp,
     197  			bfd_reloc_code_real_type code)
     198  {
     199    if (exp->X_op == O_fptr_symbol)
     200      {
     201        switch (nbytes)
     202  	{
     203   	case 4:
     204   	  /* This can happen when gcc is generating debug output.
     205   	     For example it can create a stab with the address of
     206   	     a function:
     207  
     208   	     	.stabs	"foo:F(0,21)",36,0,0,@fptr(foo)
     209  
     210   	     Since this does not involve switching code pages, we
     211   	     just allow the reloc to be generated without any
     212   	     @fptr behaviour.  */
     213   	  exp->X_op = O_symbol;
     214   	  code = BFD_RELOC_32;
     215   	  break;
     216  
     217   	case 2:
     218   	  exp->X_op = O_symbol;
     219   	  code = BFD_RELOC_XSTORMY16_FPTR16;
     220   	  break;
     221  
     222   	default:
     223  	  as_bad (_("unsupported fptr fixup size %d"), nbytes);
     224  	  return;
     225  	}
     226      }
     227    else if (nbytes == 1)
     228      code = BFD_RELOC_8;
     229    else if (nbytes == 2)
     230      code = BFD_RELOC_16;
     231    else if (nbytes == 4)
     232      code = BFD_RELOC_32;
     233    else
     234      {
     235        as_bad (_("unsupported fixup size %d"), nbytes);
     236        return;
     237      }
     238  
     239    fix_new_exp (f, where, nbytes, exp, 0, code);
     240  }
     241  
     242  /* Called while parsing an instruction to create a fixup.
     243     Create BFD_RELOC_XSTORMY16_FPTR16 relocations.  */
     244  
     245  fixS *
     246  xstormy16_cgen_record_fixup_exp (fragS *              frag,
     247  				 int                  where,
     248  				 const CGEN_INSN *    insn,
     249  				 int                  length,
     250  				 const CGEN_OPERAND * operand,
     251  				 int                  opinfo,
     252  				 expressionS *        exp)
     253  {
     254    fixS *fixP;
     255    operatorT op = exp->X_op;
     256  
     257    if (op == O_fptr_symbol)
     258      exp->X_op = O_symbol;
     259  
     260    fixP = gas_cgen_record_fixup_exp (frag, where, insn, length,
     261  				    operand, opinfo, exp);
     262  
     263    if (op == O_fptr_symbol)
     264      {
     265        if (operand->type != XSTORMY16_OPERAND_IMM16)
     266  	as_bad (_("unsupported fptr fixup"));
     267        else
     268  	{
     269  	  fixP->fx_r_type = BFD_RELOC_XSTORMY16_FPTR16;
     270  	  fixP->fx_where += 2;
     271  	}
     272      }
     273  
     274    return fixP;
     275  }
     276  
     277  valueT
     278  md_section_align (segT segment, valueT size)
     279  {
     280    int align = bfd_section_alignment (segment);
     281  
     282    return ((size + (1 << align) - 1) & -(1 << align));
     283  }
     284  
     285  symbolS *
     286  md_undefined_symbol (char * name ATTRIBUTE_UNUSED)
     287  {
     288    return 0;
     289  }
     290  
     291  /* Return an initial guess of the length by which a fragment must grow to
     292     hold a branch to reach its destination.
     293     Also updates fr_type/fr_subtype as necessary.
     294  
     295     Called just before doing relaxation.
     296     Any symbol that is now undefined will not become defined.
     297     The guess for fr_var is ACTUALLY the growth beyond fr_fix.
     298     Whatever we do to grow fr_fix or fr_var contributes to our returned value.
     299     Although it may not be explicit in the frag, pretend fr_var starts with a
     300     0 value.  */
     301  
     302  int
     303  md_estimate_size_before_relax (fragS * fragP ATTRIBUTE_UNUSED,
     304  			       segT    segment ATTRIBUTE_UNUSED)
     305  {
     306    /* No assembler relaxation is defined (or necessary) for this port.  */
     307    abort ();
     308  }
     309  
     310  /* *fragP has been relaxed to its final size, and now needs to have
     311     the bytes inside it modified to conform to the new size.
     312  
     313     Called after relaxation is finished.
     314     fragP->fr_type == rs_machine_dependent.
     315     fragP->fr_subtype is the subtype of what the address relaxed to.  */
     316  
     317  void
     318  md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
     319  		 segT    sec ATTRIBUTE_UNUSED,
     320  		 fragS * fragP ATTRIBUTE_UNUSED)
     321  {
     322    /* No assembler relaxation is defined (or necessary) for this port.  */
     323    abort ();
     324  }
     325  
     326  /* Functions concerning relocs.  */
     327  
     328  /* The location from which a PC relative jump should be calculated,
     329     given a PC relative reloc.  */
     330  
     331  long
     332  md_pcrel_from_section (fixS * fixP, segT sec)
     333  {
     334    if ((fixP->fx_addsy != (symbolS *) NULL
     335         && (! S_IS_DEFINED (fixP->fx_addsy)
     336  	   || S_GET_SEGMENT (fixP->fx_addsy) != sec))
     337        || xstormy16_force_relocation (fixP))
     338      /* The symbol is undefined,
     339         or it is defined but not in this section,
     340         or the relocation will be relative to this symbol not the section symbol.
     341         Let the linker figure it out.  */
     342      return 0;
     343  
     344    return fixP->fx_frag->fr_address + fixP->fx_where;
     345  }
     346  
     347  /* Return the bfd reloc type for OPERAND of INSN at fixup FIXP.
     348     Returns BFD_RELOC_NONE if no reloc type can be found.
     349     *FIXP may be modified if desired.  */
     350  
     351  bfd_reloc_code_real_type
     352  md_cgen_lookup_reloc (const CGEN_INSN *    insn ATTRIBUTE_UNUSED,
     353  		      const CGEN_OPERAND * operand,
     354  		      fixS *               fixP)
     355  {
     356    switch (operand->type)
     357      {
     358      case XSTORMY16_OPERAND_IMM2:
     359      case XSTORMY16_OPERAND_IMM3:
     360      case XSTORMY16_OPERAND_IMM3B:
     361      case XSTORMY16_OPERAND_IMM4:
     362      case XSTORMY16_OPERAND_HMEM8:
     363        return BFD_RELOC_NONE;
     364  
     365      case XSTORMY16_OPERAND_IMM12:
     366        fixP->fx_where += 2;
     367        return BFD_RELOC_XSTORMY16_12;
     368  
     369      case XSTORMY16_OPERAND_IMM8:
     370      case XSTORMY16_OPERAND_LMEM8:
     371        return fixP->fx_pcrel ? BFD_RELOC_8_PCREL : BFD_RELOC_8;
     372  
     373      case XSTORMY16_OPERAND_IMM16:
     374        /* This might have been processed at parse time.  */
     375        fixP->fx_where += 2;
     376        if (fixP->fx_cgen.opinfo && fixP->fx_cgen.opinfo != BFD_RELOC_NONE)
     377  	return fixP->fx_cgen.opinfo;
     378        return fixP->fx_pcrel ? BFD_RELOC_16_PCREL : BFD_RELOC_16;
     379  
     380      case XSTORMY16_OPERAND_ABS24:
     381        return BFD_RELOC_XSTORMY16_24;
     382  
     383      case XSTORMY16_OPERAND_REL8_4:
     384        fixP->fx_addnumber -= 2;
     385        /* Fall through.  */
     386      case XSTORMY16_OPERAND_REL8_2:
     387        fixP->fx_addnumber -= 2;
     388        fixP->fx_pcrel = 1;
     389        return BFD_RELOC_8_PCREL;
     390  
     391      case XSTORMY16_OPERAND_REL12:
     392        fixP->fx_where += 2;
     393        /* Fall through.  */
     394      case XSTORMY16_OPERAND_REL12A:
     395        fixP->fx_addnumber -= 2;
     396        fixP->fx_pcrel = 1;
     397        return BFD_RELOC_XSTORMY16_REL_12;
     398  
     399      default : /* avoid -Wall warning */
     400        abort ();
     401      }
     402  }
     403  
     404  /* See whether we need to force a relocation into the output file.
     405     This is used to force out switch and PC relative relocations when
     406     relaxing.  */
     407  
     408  int
     409  xstormy16_force_relocation (fixS * fix)
     410  {
     411    if (fix->fx_r_type == BFD_RELOC_XSTORMY16_FPTR16)
     412      return 1;
     413  
     414    return generic_force_reloc (fix);
     415  }
     416  
     417  /* Return true if a relocation against a symbol may be replaced with
     418     a relocation against section+offset.  */
     419  
     420  bool
     421  xstormy16_fix_adjustable (fixS * fixP)
     422  {
     423    /* We need the symbol name for the VTABLE entries.  */
     424    if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
     425        || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
     426      return false;
     427  
     428    if (fixP->fx_r_type == BFD_RELOC_XSTORMY16_FPTR16)
     429      return false;
     430  
     431    return true;
     432  }
     433  
     434  /* This is a copy of gas_cgen_md_apply_fix, with some enhancements to
     435     do various things that would not be valid for all ports.  */
     436  
     437  void
     438  xstormy16_md_apply_fix (fixS *   fixP,
     439  			 valueT * valueP,
     440  			 segT     seg ATTRIBUTE_UNUSED)
     441  {
     442    char *where = fixP->fx_frag->fr_literal + fixP->fx_where;
     443    valueT value = *valueP;
     444    /* Canonical name, since used a lot.  */
     445    CGEN_CPU_DESC cd = gas_cgen_cpu_desc;
     446  
     447    /* md_cgen_lookup_reloc() will adjust this to compensate for where
     448       in the opcode the relocation happens, for pcrel relocations.  We
     449       have no other way of keeping track of what this offset needs to
     450       be.  */
     451    fixP->fx_addnumber = 0;
     452  
     453    /* This port has pc-relative relocs and DIFF_EXPR_OK defined, so
     454       it must deal with turning a BFD_RELOC_{8,16,32,64} into a
     455       BFD_RELOC_*_PCREL for the case of
     456  
     457  	.word something-.  */
     458    if (fixP->fx_pcrel)
     459      switch (fixP->fx_r_type)
     460        {
     461        case BFD_RELOC_8:
     462  	fixP->fx_r_type = BFD_RELOC_8_PCREL;
     463  	break;
     464        case BFD_RELOC_16:
     465  	fixP->fx_r_type = BFD_RELOC_16_PCREL;
     466  	break;
     467        case BFD_RELOC_32:
     468  	fixP->fx_r_type = BFD_RELOC_32_PCREL;
     469  	break;
     470        case BFD_RELOC_64:
     471  	fixP->fx_r_type = BFD_RELOC_64_PCREL;
     472  	break;
     473        default:
     474  	break;
     475        }
     476  
     477    if (fixP->fx_addsy == (symbolS *) NULL)
     478      fixP->fx_done = 1;
     479  
     480    /* We don't actually support subtracting a symbol.  */
     481    if (fixP->fx_subsy != (symbolS *) NULL)
     482      as_bad_subtract (fixP);
     483  
     484    if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
     485      {
     486        int opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
     487        const CGEN_OPERAND *operand = cgen_operand_lookup_by_num (cd, opindex);
     488        const char *errmsg;
     489        bfd_reloc_code_real_type reloc_type;
     490        const CGEN_INSN *insn = fixP->fx_cgen.insn;
     491  
     492        /* If the reloc has been fully resolved finish the operand here.  */
     493        /* FIXME: This duplicates the capabilities of code in BFD.  */
     494        if (fixP->fx_done)
     495  	{
     496  	  CGEN_FIELDS *fields = xmalloc (CGEN_CPU_SIZEOF_FIELDS (cd));
     497  
     498  	  CGEN_CPU_SET_FIELDS_BITSIZE (cd) (fields, CGEN_INSN_BITSIZE (insn));
     499  	  CGEN_CPU_SET_VMA_OPERAND (cd) (cd, opindex, fields, (bfd_vma) value);
     500  
     501  #if CGEN_INT_INSN_P
     502  	  {
     503  	    CGEN_INSN_INT insn_value =
     504  	      cgen_get_insn_value (cd, (unsigned char *) where,
     505  				   CGEN_INSN_BITSIZE (insn),
     506  				   gas_cgen_cpu_desc->insn_endian);
     507  
     508  	    /* ??? 0 is passed for `pc'.  */
     509  	    errmsg = CGEN_CPU_INSERT_OPERAND (cd) (cd, opindex, fields,
     510  						   &insn_value, (bfd_vma) 0);
     511  	    cgen_put_insn_value (cd, (unsigned char *) where,
     512  				 CGEN_INSN_BITSIZE (insn), insn_value,
     513  				 gas_cgen_cpu_desc->insn_endian);
     514  	  }
     515  #else
     516  	  /* ??? 0 is passed for `pc'.  */
     517  	  errmsg = CGEN_CPU_INSERT_OPERAND (cd) (cd, opindex, fields,
     518  						 (unsigned char *) where,
     519  						 (bfd_vma) 0);
     520  #endif
     521  	  if (errmsg)
     522  	    as_bad_where (fixP->fx_file, fixP->fx_line, "%s", errmsg);
     523  
     524  	  free (fields);
     525  	}
     526  
     527        if (fixP->fx_done)
     528  	return;
     529  
     530        /* The operand isn't fully resolved.  Determine a BFD reloc value
     531  	 based on the operand information and leave it to
     532  	 bfd_install_relocation.  Note that this doesn't work when
     533  	 !partial_inplace.  */
     534  
     535        reloc_type = md_cgen_lookup_reloc (insn, operand, fixP);
     536        if (reloc_type != BFD_RELOC_NONE)
     537  	fixP->fx_r_type = reloc_type;
     538        else
     539  	{
     540  	  as_bad_where (fixP->fx_file, fixP->fx_line,
     541  			_("unresolved expression that must be resolved"));
     542  	  fixP->fx_done = 1;
     543  	  return;
     544  	}
     545      }
     546    else if (fixP->fx_done)
     547      {
     548        /* We're finished with this fixup.  Install it because
     549  	 bfd_install_relocation won't be called to do it.  */
     550        switch (fixP->fx_r_type)
     551  	{
     552  	case BFD_RELOC_8:
     553  	  md_number_to_chars (where, value, 1);
     554  	  break;
     555  	case BFD_RELOC_16:
     556  	  md_number_to_chars (where, value, 2);
     557  	  break;
     558  	case BFD_RELOC_32:
     559  	  md_number_to_chars (where, value, 4);
     560  	  break;
     561  	case BFD_RELOC_64:
     562  	  md_number_to_chars (where, value, 8);
     563  	  break;
     564  	default:
     565  	  as_bad_where (fixP->fx_file, fixP->fx_line,
     566  			_("internal error: can't install fix for reloc type %d (`%s')"),
     567  			fixP->fx_r_type, bfd_get_reloc_code_name (fixP->fx_r_type));
     568  	  break;
     569  	}
     570      }
     571    else
     572      {
     573        /* bfd_install_relocation will be called to finish things up.  */
     574      }
     575  
     576    /* This is a RELA port.  Thus, it does not need to store a
     577       value if it is going to make a reloc.  What's more, when
     578       assembling a line like
     579  
     580       .byte global-0x7f00
     581  
     582       we'll get a spurious error message if we try to stuff 0x7f00 into
     583       the byte.  */
     584    if (! fixP->fx_done)
     585      *valueP = 0;
     586  
     587    /* Tuck `value' away for use by tc_gen_reloc.
     588       See the comment describing fx_addnumber in write.h.
     589       This field is misnamed (or misused :-).  */
     590    fixP->fx_addnumber += value;
     591  }
     592  
     593  
     594  /* Write a value out to the object file, using the appropriate endianness.  */
     595  
     596  void
     597  md_number_to_chars (char * buf, valueT val, int n)
     598  {
     599    number_to_chars_littleendian (buf, val, n);
     600  }
     601  
     602  const char *
     603  md_atof (int type, char * litP, int * sizeP)
     604  {
     605    return ieee_md_atof (type, litP, sizeP, false);
     606  }