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