1/* tc-mt.c -- Assembler for the Morpho Technologies mt .
2   Copyright (C) 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, 59 Temple Place - Suite 330,
19   Boston, MA 02111-1307, USA.  */
20
21#include <stdio.h>
22#include "as.h"
23#include "dwarf2dbg.h"
24#include "subsegs.h"
25#include "symcat.h"
26#include "opcodes/mt-desc.h"
27#include "opcodes/mt-opc.h"
28#include "cgen.h"
29#include "elf/common.h"
30#include "elf/mt.h"
31#include "libbfd.h"
32
33/* Structure to hold all of the different components
34   describing an individual instruction.  */
35typedef struct
36{
37  const CGEN_INSN *	insn;
38  const CGEN_INSN *	orig_insn;
39  CGEN_FIELDS		fields;
40#if CGEN_INT_INSN_P
41  CGEN_INSN_INT         buffer [1];
42#define INSN_VALUE(buf) (*(buf))
43#else
44  unsigned char         buffer [CGEN_MAX_INSN_SIZE];
45#define INSN_VALUE(buf) (buf)
46#endif
47  char *		addr;
48  fragS *		frag;
49  int                   num_fixups;
50  fixS *                fixups [GAS_CGEN_MAX_FIXUPS];
51  int                   indices [MAX_OPERAND_INSTANCES];
52}
53mt_insn;
54
55
56const char comment_chars[]        = ";";
57const char line_comment_chars[]   = "#";
58const char line_separator_chars[] = "";
59const char EXP_CHARS[]            = "eE";
60const char FLT_CHARS[]            = "dD";
61
62/* The target specific pseudo-ops which we support.  */
63const pseudo_typeS md_pseudo_table[] =
64{
65    { "word",   cons,                   4 },
66    { NULL, 	NULL,			0 }
67};
68
69
70
71static int no_scheduling_restrictions = 0;
72
73struct option md_longopts[] =
74{
75#define OPTION_NO_SCHED_REST	(OPTION_MD_BASE)
76  { "nosched",	   no_argument, NULL, OPTION_NO_SCHED_REST },
77#define OPTION_MARCH		(OPTION_MD_BASE + 1)
78  { "march", required_argument, NULL, OPTION_MARCH},
79  { NULL,	   no_argument, NULL, 0 },
80};
81size_t md_longopts_size = sizeof (md_longopts);
82
83const char * md_shortopts = "";
84
85/* Mach selected from command line.  */
86static int mt_mach = bfd_mach_ms1;
87static unsigned mt_mach_bitmask = 1 << MACH_MS1;
88
89/* Flags to set in the elf header */
90static flagword mt_flags = EF_MT_CPU_MRISC;
91
92/* The architecture to use.  */
93enum mt_architectures
94  {
95    ms1_64_001,
96    ms1_16_002,
97    ms1_16_003,
98    ms2
99  };
100
101/* MT architecture we are using for this output file.  */
102static enum mt_architectures mt_arch = ms1_16_002;
103
104int
105md_parse_option (int c ATTRIBUTE_UNUSED, char * arg)
106{
107  switch (c)
108    {
109    case OPTION_MARCH:
110      if (strcmp (arg, "ms1-64-001") == 0)
111 	{
112 	  mt_flags = (mt_flags & ~EF_MT_CPU_MASK) | EF_MT_CPU_MRISC;
113 	  mt_mach = bfd_mach_ms1;
114 	  mt_mach_bitmask = 1 << MACH_MS1;
115 	  mt_arch = ms1_64_001;
116 	}
117      else if (strcmp (arg, "ms1-16-002") == 0)
118 	{
119 	  mt_flags = (mt_flags & ~EF_MT_CPU_MASK) | EF_MT_CPU_MRISC;
120 	  mt_mach = bfd_mach_ms1;
121 	  mt_mach_bitmask = 1 << MACH_MS1;
122 	  mt_arch = ms1_16_002;
123 	}
124      else if (strcmp (arg, "ms1-16-003") == 0)
125 	{
126 	  mt_flags = (mt_flags & ~EF_MT_CPU_MASK) | EF_MT_CPU_MRISC2;
127 	  mt_mach = bfd_mach_mrisc2;
128 	  mt_mach_bitmask = 1 << MACH_MS1_003;
129 	  mt_arch = ms1_16_003;
130 	}
131      else if (strcmp (arg, "ms2") == 0)
132 	{
133 	  mt_flags = (mt_flags & ~EF_MT_CPU_MASK) | EF_MT_CPU_MS2;
134 	  mt_mach = bfd_mach_mrisc2;
135 	  mt_mach_bitmask = 1 << MACH_MS2;
136 	  mt_arch = ms2;
137 	}
138    case OPTION_NO_SCHED_REST:
139      no_scheduling_restrictions = 1;
140      break;
141    default:
142      return 0;
143    }
144
145  return 1;
146}
147
148
149void
150md_show_usage (FILE * stream)
151{
152  fprintf (stream, _("MT specific command line options:\n"));
153  fprintf (stream, _("  -march=ms1-64-001         allow ms1-64-001 instructions\n"));
154  fprintf (stream, _("  -march=ms1-16-002         allow ms1-16-002 instructions (default)\n"));
155  fprintf (stream, _("  -march=ms1-16-003         allow ms1-16-003 instructions\n"));
156  fprintf (stream, _("  -march=ms2                allow ms2 instructions \n"));
157  fprintf (stream, _("  -nosched                  disable scheduling restrictions\n"));
158}
159
160
161void
162md_begin (void)
163{
164  /* Initialize the `cgen' interface.  */
165
166  /* Set the machine number and endian.  */
167  gas_cgen_cpu_desc = mt_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, mt_mach_bitmask,
168					CGEN_CPU_OPEN_ENDIAN,
169					CGEN_ENDIAN_BIG,
170					CGEN_CPU_OPEN_END);
171  mt_cgen_init_asm (gas_cgen_cpu_desc);
172
173  /* This is a callback from cgen to gas to parse operands.  */
174  cgen_set_parse_operand_fn (gas_cgen_cpu_desc, gas_cgen_parse_operand);
175
176  /* Set the ELF flags if desired. */
177  if (mt_flags)
178    bfd_set_private_flags (stdoutput, mt_flags);
179
180  /* Set the machine type.  */
181  bfd_default_set_arch_mach (stdoutput, bfd_arch_mt, mt_mach);
182}
183
184void
185md_assemble (char * str)
186{
187  static long delayed_load_register = 0;
188  static long prev_delayed_load_register = 0;
189  static int last_insn_had_delay_slot = 0;
190  static int last_insn_in_noncond_delay_slot = 0;
191  static int last_insn_has_load_delay = 0;
192  static int last_insn_was_memory_access = 0;
193  static int last_insn_was_io_insn = 0;
194  static int last_insn_was_arithmetic_or_logic = 0;
195  static int last_insn_was_branch_insn = 0;
196  static int last_insn_was_conditional_branch_insn = 0;
197
198  mt_insn insn;
199  char * errmsg;
200
201  /* Initialize GAS's cgen interface for a new instruction.  */
202  gas_cgen_init_parse ();
203
204  insn.insn = mt_cgen_assemble_insn
205      (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg);
206
207  if (!insn.insn)
208    {
209      as_bad ("%s", errmsg);
210      return;
211    }
212
213  /* Doesn't really matter what we pass for RELAX_P here.  */
214  gas_cgen_finish_insn (insn.insn, insn.buffer,
215			CGEN_FIELDS_BITSIZE (& insn.fields), 1, NULL);
216
217
218  /* Handle Scheduling Restrictions.  */
219  if (!no_scheduling_restrictions)
220    {
221      /* Detect consecutive Memory Accesses.  */
222      if (last_insn_was_memory_access
223	  && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_MEMORY_ACCESS)
224	  && mt_mach == ms1_64_001)
225	as_warn (_("instruction %s may not follow another memory access instruction."),
226		 CGEN_INSN_NAME (insn.insn));
227
228      /* Detect consecutive I/O Instructions.  */
229      else if (last_insn_was_io_insn
230	       && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_IO_INSN))
231	as_warn (_("instruction %s may not follow another I/O instruction."),
232		 CGEN_INSN_NAME (insn.insn));
233
234      /* Detect consecutive branch instructions.  */
235      else if (last_insn_was_branch_insn
236	       && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_BR_INSN))
237	as_warn (_("%s may not occupy the delay slot of another branch insn."),
238		 CGEN_INSN_NAME (insn.insn));
239
240      /* Detect data dependencies on delayed loads: memory and input insns.  */
241      if (last_insn_has_load_delay && delayed_load_register)
242	{
243	  if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR1)
244	      && insn.fields.f_sr1 == delayed_load_register)
245	    as_warn (_("operand references R%ld of previous load."),
246		     insn.fields.f_sr1);
247
248	  if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR2)
249	      && insn.fields.f_sr2 == delayed_load_register)
250	    as_warn (_("operand references R%ld of previous load."),
251		     insn.fields.f_sr2);
252	}
253
254      /* Detect JAL/RETI hazard */
255      if (mt_mach == ms2
256	  && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_JAL_HAZARD))
257	{
258	  if ((CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR1)
259	       && insn.fields.f_sr1 == delayed_load_register)
260	      || (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR2)
261		  && insn.fields.f_sr2 == delayed_load_register))
262	    as_warn (_("operand references R%ld of previous instrutcion."),
263		     delayed_load_register);
264	  else if ((CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR1)
265		    && insn.fields.f_sr1 == prev_delayed_load_register)
266		   || (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR2)
267		       && insn.fields.f_sr2 == prev_delayed_load_register))
268	    as_warn (_("operand references R%ld of instructcion before previous."),
269		     prev_delayed_load_register);
270	}
271
272      /* Detect data dependency between conditional branch instruction
273         and an immediately preceding arithmetic or logical instruction.  */
274      if (last_insn_was_arithmetic_or_logic
275	  && !last_insn_in_noncond_delay_slot
276	  && (delayed_load_register != 0)
277	  && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_BR_INSN)
278	  && mt_arch == ms1_64_001)
279	{
280	  if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR1)
281	      && insn.fields.f_sr1 == delayed_load_register)
282	    as_warn (_("conditional branch or jal insn's operand references R%ld of previous arithmetic or logic insn."),
283		     insn.fields.f_sr1);
284
285	  if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR2)
286	      && insn.fields.f_sr2 == delayed_load_register)
287	    as_warn (_("conditional branch or jal insn's operand references R%ld of previous arithmetic or logic insn."),
288		     insn.fields.f_sr2);
289	}
290    }
291
292  /* Keep track of details of this insn for processing next insn.  */
293  last_insn_in_noncond_delay_slot = last_insn_was_branch_insn
294    && !last_insn_was_conditional_branch_insn;
295
296  last_insn_had_delay_slot =
297    CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_DELAY_SLOT);
298
299  last_insn_has_load_delay =
300    CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_LOAD_DELAY);
301
302  last_insn_was_memory_access =
303    CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_MEMORY_ACCESS);
304
305  last_insn_was_io_insn =
306    CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_IO_INSN);
307
308  last_insn_was_arithmetic_or_logic =
309    CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_AL_INSN);
310
311  last_insn_was_branch_insn =
312    CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_BR_INSN);
313
314  last_insn_was_conditional_branch_insn =
315  CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_BR_INSN)
316    && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR2);
317
318  prev_delayed_load_register = delayed_load_register;
319
320  if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRDR))
321     delayed_load_register = insn.fields.f_dr;
322  else if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRDRRR))
323     delayed_load_register = insn.fields.f_drrr;
324  else  /* Insns has no destination register.  */
325     delayed_load_register = 0;
326
327  /* Generate dwarf2 line numbers.  */
328  dwarf2_emit_insn (4);
329}
330
331valueT
332md_section_align (segT segment, valueT size)
333{
334  int align = bfd_get_section_alignment (stdoutput, segment);
335
336  return ((size + (1 << align) - 1) & (-1 << align));
337}
338
339symbolS *
340md_undefined_symbol (char * name ATTRIBUTE_UNUSED)
341{
342    return NULL;
343}
344
345int
346md_estimate_size_before_relax (fragS * fragP ATTRIBUTE_UNUSED,
347			       segT    segment ATTRIBUTE_UNUSED)
348{
349  as_fatal (_("md_estimate_size_before_relax\n"));
350  return 1;
351}
352
353/* *fragP has been relaxed to its final size, and now needs to have
354   the bytes inside it modified to conform to the new size.
355
356   Called after relaxation is finished.
357   fragP->fr_type == rs_machine_dependent.
358   fragP->fr_subtype is the subtype of what the address relaxed to.  */
359
360void
361md_convert_frag (bfd   * abfd  ATTRIBUTE_UNUSED,
362		 segT    sec   ATTRIBUTE_UNUSED,
363		 fragS * fragP ATTRIBUTE_UNUSED)
364{
365}
366
367
368/* Functions concerning relocs.  */
369
370long
371md_pcrel_from_section (fixS *fixP, segT sec)
372{
373  if (fixP->fx_addsy != (symbolS *) NULL
374      && (!S_IS_DEFINED (fixP->fx_addsy)
375	  || S_GET_SEGMENT (fixP->fx_addsy) != sec))
376    /* The symbol is undefined (or is defined but not in this section).
377       Let the linker figure it out.  */
378    return 0;
379
380  /* Return the address of the opcode - cgen adjusts for opcode size
381     itself, to be consistent with the disassembler, which must do
382     so.  */
383  return fixP->fx_where + fixP->fx_frag->fr_address;
384}
385
386
387/* Return the bfd reloc type for OPERAND of INSN at fixup FIXP.
388   Returns BFD_RELOC_NONE if no reloc type can be found.
389   *FIXP may be modified if desired.  */
390
391bfd_reloc_code_real_type
392md_cgen_lookup_reloc (const CGEN_INSN *    insn     ATTRIBUTE_UNUSED,
393		      const CGEN_OPERAND * operand,
394		      fixS *               fixP     ATTRIBUTE_UNUSED)
395{
396  bfd_reloc_code_real_type result;
397
398  result = BFD_RELOC_NONE;
399
400  switch (operand->type)
401    {
402    case MT_OPERAND_IMM16O:
403      result = BFD_RELOC_16_PCREL;
404      fixP->fx_pcrel = 1;
405      /* fixP->fx_no_overflow = 1; */
406      break;
407    case MT_OPERAND_IMM16:
408    case MT_OPERAND_IMM16Z:
409      /* These may have been processed at parse time.  */
410      if (fixP->fx_cgen.opinfo != 0)
411        result = fixP->fx_cgen.opinfo;
412      fixP->fx_no_overflow = 1;
413      break;
414    case MT_OPERAND_LOOPSIZE:
415      result = BFD_RELOC_MT_PCINSN8;
416      fixP->fx_pcrel = 1;
417      /* Adjust for the delay slot, which is not part of the loop  */
418      fixP->fx_offset -= 8;
419      break;
420    default:
421      result = BFD_RELOC_NONE;
422      break;
423    }
424
425  return result;
426}
427
428/* Write a value out to the object file, using the appropriate endianness.  */
429
430void
431md_number_to_chars (char * buf, valueT val, int n)
432{
433  number_to_chars_bigendian (buf, val, n);
434}
435
436/* Turn a string in input_line_pointer into a floating point constant of type
437   type, and store the appropriate bytes in *litP.  The number of LITTLENUMS
438   emitted is stored in *sizeP .  An error message is returned, or NULL on OK.  */
439
440/* Equal to MAX_PRECISION in atof-ieee.c.  */
441#define MAX_LITTLENUMS 6
442
443char *
444md_atof (type, litP, sizeP)
445     char   type;
446     char * litP;
447     int *  sizeP;
448{
449  int              prec;
450  LITTLENUM_TYPE   words [MAX_LITTLENUMS];
451  LITTLENUM_TYPE * wordP;
452  char *           t;
453
454  switch (type)
455    {
456    case 'f':
457    case 'F':
458    case 's':
459    case 'S':
460      prec = 2;
461      break;
462
463    case 'd':
464    case 'D':
465    case 'r':
466    case 'R':
467      prec = 4;
468      break;
469
470   /* FIXME: Some targets allow other format chars for bigger sizes here.  */
471
472    default:
473      * sizeP = 0;
474      return _("Bad call to md_atof()");
475    }
476
477  t = atof_ieee (input_line_pointer, type, words);
478  if (t)
479    input_line_pointer = t;
480  * sizeP = prec * sizeof (LITTLENUM_TYPE);
481
482  /* This loops outputs the LITTLENUMs in REVERSE order;
483     in accord with the mt endianness.  */
484  for (wordP = words; prec--;)
485    {
486      md_number_to_chars (litP, (valueT) (*wordP++), sizeof (LITTLENUM_TYPE));
487      litP += sizeof (LITTLENUM_TYPE);
488    }
489
490  return 0;
491}
492
493/* See whether we need to force a relocation into the output file.  */
494
495int
496mt_force_relocation (fixS * fixp ATTRIBUTE_UNUSED)
497{
498  return 0;
499}
500
501void
502mt_apply_fix (fixS *fixP, valueT *valueP, segT seg)
503{
504  if ((fixP->fx_pcrel != 0) && (fixP->fx_r_type == BFD_RELOC_32))
505    fixP->fx_r_type = BFD_RELOC_32_PCREL;
506
507  gas_cgen_md_apply_fix (fixP, valueP, seg);
508}
509
510bfd_boolean
511mt_fix_adjustable (fixS * fixP)
512{
513  bfd_reloc_code_real_type reloc_type;
514
515  if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
516    {
517      const CGEN_INSN *insn = NULL;
518      int opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
519      const CGEN_OPERAND *operand;
520
521      operand = cgen_operand_lookup_by_num(gas_cgen_cpu_desc, opindex);
522      reloc_type = md_cgen_lookup_reloc (insn, operand, fixP);
523    }
524  else
525    reloc_type = fixP->fx_r_type;
526
527  if (fixP->fx_addsy == NULL)
528    return TRUE;
529
530  /* Prevent all adjustments to global symbols.  */
531  if (S_IS_EXTERNAL (fixP->fx_addsy))
532    return FALSE;
533
534  if (S_IS_WEAK (fixP->fx_addsy))
535    return FALSE;
536
537  return 1;
538}
539