1214571Sdim/* spu.c -- Assembler for the IBM Synergistic Processing Unit (SPU)
2214571Sdim
3214571Sdim   Copyright 2006, 2007 Free Software Foundation, Inc.
4214571Sdim
5214571Sdim   This file is part of GAS, the GNU Assembler.
6214571Sdim
7214571Sdim   GAS is free software; you can redistribute it and/or modify
8214571Sdim   it under the terms of the GNU General Public License as published by
9214571Sdim   the Free Software Foundation; either version 2, or (at your option)
10214571Sdim   any later version.
11214571Sdim
12214571Sdim   GAS is distributed in the hope that it will be useful,
13214571Sdim   but WITHOUT ANY WARRANTY; without even the implied warranty of
14214571Sdim   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15214571Sdim   GNU General Public License for more details.
16214571Sdim
17214571Sdim   You should have received a copy of the GNU General Public License
18214571Sdim   along with GAS; see the file COPYING.  If not, write to the Free
19214571Sdim   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
20214571Sdim   02110-1301, USA.  */
21214571Sdim
22214571Sdim#include "as.h"
23214571Sdim#include "safe-ctype.h"
24214571Sdim#include "subsegs.h"
25214571Sdim#include "dwarf2dbg.h"
26214571Sdim
27214571Sdimconst struct spu_opcode spu_opcodes[] = {
28214571Sdim#define APUOP(TAG,MACFORMAT,OPCODE,MNEMONIC,ASMFORMAT,DEP,PIPE) \
29214571Sdim	{ MACFORMAT, (OPCODE) << (32-11), MNEMONIC, ASMFORMAT },
30214571Sdim#define APUOPFB(TAG,MACFORMAT,OPCODE,FB,MNEMONIC,ASMFORMAT,DEP,PIPE) \
31214571Sdim	{ MACFORMAT, ((OPCODE) << (32-11)) | ((FB) << (32-18)), MNEMONIC, ASMFORMAT },
32214571Sdim#include "opcode/spu-insns.h"
33214571Sdim#undef APUOP
34214571Sdim#undef APUOPFB
35214571Sdim};
36214571Sdim
37214571Sdimstatic const int spu_num_opcodes =
38214571Sdim  sizeof (spu_opcodes) / sizeof (spu_opcodes[0]);
39214571Sdim
40214571Sdim#define MAX_RELOCS 2
41214571Sdim
42214571Sdimstruct spu_insn
43214571Sdim{
44214571Sdim  unsigned int opcode;
45214571Sdim  expressionS exp[MAX_RELOCS];
46214571Sdim  int reloc_arg[MAX_RELOCS];
47214571Sdim  int flag[MAX_RELOCS];
48214571Sdim  enum spu_insns tag;
49214571Sdim};
50214571Sdim
51214571Sdimstatic const char *get_imm (const char *param, struct spu_insn *insn, int arg);
52214571Sdimstatic const char *get_reg (const char *param, struct spu_insn *insn, int arg,
53214571Sdim			    int accept_expr);
54214571Sdimstatic int calcop (struct spu_opcode *format, const char *param,
55214571Sdim		   struct spu_insn *insn);
56214571Sdimstatic void spu_cons (int);
57214571Sdim
58214571Sdimextern char *myname;
59214571Sdimstatic struct hash_control *op_hash = NULL;
60214571Sdim
61214571Sdim/* These bits should be turned off in the first address of every segment */
62214571Sdimint md_seg_align = 7;
63214571Sdim
64214571Sdim/* These chars start a comment anywhere in a source file (except inside
65214571Sdim   another comment */
66214571Sdimconst char comment_chars[] = "#";
67214571Sdim
68214571Sdim/* These chars only start a comment at the beginning of a line.  */
69214571Sdimconst char line_comment_chars[] = "#";
70214571Sdim
71214571Sdim/* gods own line continuation char */
72214571Sdimconst char line_separator_chars[] = ";";
73214571Sdim
74214571Sdim/* Chars that can be used to separate mant from exp in floating point nums */
75214571Sdimconst char EXP_CHARS[] = "eE";
76214571Sdim
77214571Sdim/* Chars that mean this number is a floating point constant */
78214571Sdim/* as in 0f123.456 */
79214571Sdim/* or    0H1.234E-12 (see exp chars above) */
80214571Sdimconst char FLT_CHARS[] = "dDfF";
81214571Sdim
82214571Sdimconst pseudo_typeS md_pseudo_table[] =
83214571Sdim{
84214571Sdim  {"align", s_align_ptwo, 4},
85214571Sdim  {"bss", s_lcomm_bytes, 1},
86214571Sdim  {"def", s_set, 0},
87214571Sdim  {"dfloat", float_cons, 'd'},
88214571Sdim  {"ffloat", float_cons, 'f'},
89214571Sdim  {"global", s_globl, 0},
90214571Sdim  {"half", cons, 2},
91214571Sdim  {"int", spu_cons, 4},
92214571Sdim  {"long", spu_cons, 4},
93214571Sdim  {"quad", spu_cons, 8},
94214571Sdim  {"string", stringer, 1},
95214571Sdim  {"word", spu_cons, 4},
96214571Sdim  /* Force set to be treated as an instruction.  */
97214571Sdim  {"set", NULL, 0},
98214571Sdim  {".set", s_set, 0},
99214571Sdim  /* Likewise for eqv.  */
100214571Sdim  {"eqv", NULL, 0},
101214571Sdim  {".eqv", s_set, -1},
102214571Sdim  {"file", (void (*) PARAMS ((int))) dwarf2_directive_file, 0 },
103214571Sdim  {"loc", dwarf2_directive_loc, 0},
104214571Sdim  {0,0,0}
105214571Sdim};
106214571Sdim
107214571Sdimvoid
108214571Sdimmd_begin (void)
109214571Sdim{
110214571Sdim  const char *retval = NULL;
111214571Sdim  int i;
112214571Sdim
113214571Sdim  /* initialize hash table */
114214571Sdim
115214571Sdim  op_hash = hash_new ();
116214571Sdim
117214571Sdim  /* loop until you see the end of the list */
118214571Sdim
119214571Sdim  for (i = 0; i < spu_num_opcodes; i++)
120214571Sdim    {
121214571Sdim      /* hash each mnemonic and record its position */
122214571Sdim
123214571Sdim      retval = hash_insert (op_hash, spu_opcodes[i].mnemonic, (PTR)&spu_opcodes[i]);
124214571Sdim
125214571Sdim      if (retval != NULL && strcmp (retval, "exists") != 0)
126214571Sdim	as_fatal (_("Can't hash instruction '%s':%s"),
127214571Sdim		  spu_opcodes[i].mnemonic, retval);
128214571Sdim    }
129214571Sdim}
130214571Sdim
131214571Sdimconst char *md_shortopts = "";
132214571Sdimstruct option md_longopts[] = {
133214571Sdim#define OPTION_APUASM (OPTION_MD_BASE)
134214571Sdim  {"apuasm", no_argument, NULL, OPTION_APUASM},
135214571Sdim#define OPTION_DD2 (OPTION_MD_BASE+1)
136214571Sdim  {"mdd2.0", no_argument, NULL, OPTION_DD2},
137214571Sdim#define OPTION_DD1 (OPTION_MD_BASE+2)
138214571Sdim  {"mdd1.0", no_argument, NULL, OPTION_DD1},
139214571Sdim#define OPTION_DD3 (OPTION_MD_BASE+3)
140214571Sdim  {"mdd3.0", no_argument, NULL, OPTION_DD3},
141214571Sdim  { NULL, no_argument, NULL, 0 }
142214571Sdim};
143214571Sdimsize_t md_longopts_size = sizeof (md_longopts);
144214571Sdim
145214571Sdim/* When set (by -apuasm) our assembler emulates the behaviour of apuasm.
146214571Sdim * e.g. don't add bias to float conversion and don't right shift
147214571Sdim * immediate values. */
148214571Sdimstatic int emulate_apuasm;
149214571Sdim
150214571Sdim/* Use the dd2.0 instructions set.  The only differences are some new
151214571Sdim * register names and the orx insn */
152214571Sdimstatic int use_dd2 = 1;
153214571Sdim
154214571Sdimint
155214571Sdimmd_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
156214571Sdim{
157214571Sdim  switch (c)
158214571Sdim    {
159214571Sdim    case OPTION_APUASM:
160214571Sdim      emulate_apuasm = 1;
161214571Sdim      break;
162214571Sdim    case OPTION_DD3:
163214571Sdim      use_dd2 = 1;
164214571Sdim      break;
165214571Sdim    case OPTION_DD2:
166214571Sdim      use_dd2 = 1;
167214571Sdim      break;
168214571Sdim    case OPTION_DD1:
169214571Sdim      use_dd2 = 0;
170214571Sdim      break;
171214571Sdim    default:
172214571Sdim      return 0;
173214571Sdim    }
174214571Sdim  return 1;
175214571Sdim}
176214571Sdim
177214571Sdimvoid
178214571Sdimmd_show_usage (FILE *stream)
179214571Sdim{
180214571Sdim  fputs (_("\
181214571SdimSPU options:\n\
182214571Sdim  --apuasm		  emulate behaviour of apuasm\n"),
183214571Sdim	stream);
184214571Sdim}
185214571Sdim
186214571Sdim
187214571Sdimstruct arg_encode {
188214571Sdim  int size;
189214571Sdim  int pos;
190214571Sdim  int rshift;
191214571Sdim  int lo, hi;
192214571Sdim  int wlo, whi;
193214571Sdim  bfd_reloc_code_real_type reloc;
194214571Sdim};
195214571Sdim
196214571Sdimstatic struct arg_encode arg_encode[A_MAX] = {
197214571Sdim  {  7,  0, 0,       0,    127,    0,   -1,  0 }, /* A_T */
198214571Sdim  {  7,  7, 0,       0,    127,    0,   -1,  0 }, /* A_A */
199214571Sdim  {  7, 14, 0,       0,    127,    0,   -1,  0 }, /* A_B */
200214571Sdim  {  7, 21, 0,       0,    127,    0,   -1,  0 }, /* A_C */
201214571Sdim  {  7,  7, 0,       0,    127,    0,   -1,  0 }, /* A_S */
202214571Sdim  {  7,  7, 0,       0,    127,    0,   -1,  0 }, /* A_H */
203214571Sdim  {  0,  0, 0,       0,     -1,    0,   -1,  0 }, /* A_P */
204214571Sdim  {  7, 14, 0,       0,     -1,    0,   -1,  BFD_RELOC_SPU_IMM7 }, /* A_S3 */
205214571Sdim  {  7, 14, 0,     -32,     31,  -31,    0,  BFD_RELOC_SPU_IMM7 }, /* A_S6 */
206214571Sdim  {  7, 14, 0,       0,     -1,    0,   -1,  BFD_RELOC_SPU_IMM7 }, /* A_S7N */
207214571Sdim  {  7, 14, 0,     -64,     63,  -63,    0,  BFD_RELOC_SPU_IMM7 }, /* A_S7 */
208214571Sdim  {  8, 14, 0,       0,    127,    0,   -1,  BFD_RELOC_SPU_IMM8 }, /* A_U7A */
209214571Sdim  {  8, 14, 0,       0,    127,    0,   -1,  BFD_RELOC_SPU_IMM8 }, /* A_U7B */
210214571Sdim  { 10, 14, 0,    -512,    511, -128,  255,  BFD_RELOC_SPU_IMM10 }, /* A_S10B */
211214571Sdim  { 10, 14, 0,    -512,    511,    0,   -1,  BFD_RELOC_SPU_IMM10 }, /* A_S10 */
212214571Sdim  {  2, 23, 9,   -1024,   1023,    0,   -1,  BFD_RELOC_SPU_PCREL9a }, /* A_S11 */
213214571Sdim  {  2, 14, 9,   -1024,   1023,    0,   -1,  BFD_RELOC_SPU_PCREL9b }, /* A_S11I */
214214571Sdim  { 10, 14, 4,   -8192,   8191,    0,   -1,  BFD_RELOC_SPU_IMM10W }, /* A_S14 */
215214571Sdim  { 16,  7, 0,  -32768,  32767,    0,   -1,  BFD_RELOC_SPU_IMM16 }, /* A_S16 */
216214571Sdim  { 16,  7, 2, -131072, 262143,    0,   -1,  BFD_RELOC_SPU_IMM16W }, /* A_S18 */
217214571Sdim  { 16,  7, 2, -262144, 262143,    0,   -1,  BFD_RELOC_SPU_PCREL16 }, /* A_R18 */
218214571Sdim  {  7, 14, 0,       0,     -1,    0,   -1,  BFD_RELOC_SPU_IMM7 }, /* A_U3 */
219214571Sdim  {  7, 14, 0,       0,    127,    0,   31,  BFD_RELOC_SPU_IMM7 }, /* A_U5 */
220214571Sdim  {  7, 14, 0,       0,    127,    0,   63,  BFD_RELOC_SPU_IMM7 }, /* A_U6 */
221214571Sdim  {  7, 14, 0,       0,     -1,    0,   -1,  BFD_RELOC_SPU_IMM7 }, /* A_U7 */
222214571Sdim  { 14,  0, 0,       0,  16383,    0,   -1,  0 }, /* A_U14 */
223214571Sdim  { 16,  7, 0,  -32768,  65535,    0,   -1,  BFD_RELOC_SPU_IMM16 }, /* A_X16 */
224214571Sdim  { 18,  7, 0,       0, 262143,    0,   -1,  BFD_RELOC_SPU_IMM18 }, /* A_U18 */
225214571Sdim};
226214571Sdim
227214571Sdim/* Some flags for handling errors.  This is very hackish and added after
228214571Sdim * the fact. */
229214571Sdimstatic int syntax_error_arg;
230214571Sdimstatic const char *syntax_error_param;
231214571Sdimstatic int syntax_reg;
232214571Sdim
233214571Sdimstatic char *
234214571Sdiminsn_fmt_string (struct spu_opcode *format)
235214571Sdim{
236214571Sdim  static char buf[64];
237214571Sdim  int len = 0;
238214571Sdim  int i;
239214571Sdim
240214571Sdim  len += sprintf (&buf[len], "%s\t", format->mnemonic);
241214571Sdim  for (i = 1; i <= format->arg[0]; i++)
242214571Sdim    {
243214571Sdim      int arg = format->arg[i];
244214571Sdim      char *exp;
245214571Sdim      if (i > 1 && arg != A_P && format->arg[i-1] != A_P)
246214571Sdim	buf[len++] =  ',';
247214571Sdim      if (arg == A_P)
248214571Sdim	exp = "(";
249214571Sdim      else if (arg < A_P)
250214571Sdim	exp = i == syntax_error_arg ? "REG" : "reg";
251214571Sdim      else
252214571Sdim	exp = i == syntax_error_arg ? "IMM" : "imm";
253214571Sdim      len += sprintf (&buf[len], "%s", exp);
254214571Sdim      if (i > 1 && format->arg[i-1] == A_P)
255214571Sdim	buf[len++] =  ')';
256214571Sdim    }
257214571Sdim  buf[len] = 0;
258214571Sdim  return buf;
259214571Sdim}
260214571Sdim
261214571Sdimvoid
262214571Sdimmd_assemble (char *op)
263214571Sdim{
264214571Sdim  char *param, *thisfrag;
265214571Sdim  char c;
266214571Sdim  struct spu_opcode *format;
267214571Sdim  struct spu_insn insn;
268214571Sdim  int i;
269214571Sdim
270214571Sdim  assert (op);
271214571Sdim
272214571Sdim  /* skip over instruction to find parameters */
273214571Sdim
274214571Sdim  for (param = op; *param != 0 && !ISSPACE (*param); param++)
275214571Sdim    ;
276214571Sdim  c = *param;
277214571Sdim  *param = 0;
278214571Sdim
279214571Sdim  if (c != 0 && c != '\n')
280214571Sdim    param++;
281214571Sdim
282214571Sdim  /* try to find the instruction in the hash table */
283214571Sdim
284214571Sdim  if ((format = (struct spu_opcode *) hash_find (op_hash, op)) == NULL)
285214571Sdim    {
286214571Sdim      as_bad (_("Invalid mnemonic '%s'"), op);
287214571Sdim      return;
288214571Sdim    }
289214571Sdim
290214571Sdim  if (!use_dd2 && strcmp (format->mnemonic, "orx") == 0)
291214571Sdim    {
292214571Sdim      as_bad (_("'%s' is only available in DD2.0 or higher."), op);
293214571Sdim      return;
294214571Sdim    }
295214571Sdim
296214571Sdim  while (1)
297214571Sdim    {
298214571Sdim      /* try parsing this instruction into insn */
299214571Sdim      for (i = 0; i < MAX_RELOCS; i++)
300214571Sdim	{
301214571Sdim	  insn.exp[i].X_add_symbol = 0;
302214571Sdim	  insn.exp[i].X_op_symbol = 0;
303214571Sdim	  insn.exp[i].X_add_number = 0;
304214571Sdim	  insn.exp[i].X_op = O_illegal;
305214571Sdim	  insn.reloc_arg[i] = -1;
306214571Sdim	  insn.flag[i] = 0;
307214571Sdim	}
308214571Sdim      insn.opcode = format->opcode;
309214571Sdim      insn.tag = (enum spu_insns) (format - spu_opcodes);
310214571Sdim
311214571Sdim      syntax_error_arg = 0;
312214571Sdim      syntax_error_param = 0;
313214571Sdim      syntax_reg = 0;
314214571Sdim      if (calcop (format, param, &insn))
315214571Sdim	break;
316214571Sdim
317214571Sdim      /* if it doesn't parse try the next instruction */
318214571Sdim      if (!strcmp (format[0].mnemonic, format[1].mnemonic))
319214571Sdim	format++;
320214571Sdim      else
321214571Sdim	{
322214571Sdim	  int parg = format[0].arg[syntax_error_arg-1];
323214571Sdim
324214571Sdim	  as_fatal (_("Error in argument %d.  Expecting:  \"%s\""),
325214571Sdim		    syntax_error_arg - (parg == A_P),
326214571Sdim		    insn_fmt_string (format));
327214571Sdim	  return;
328214571Sdim	}
329214571Sdim    }
330214571Sdim
331214571Sdim  if ((syntax_reg & 4)
332214571Sdim      && ! (insn.tag == M_RDCH
333214571Sdim	    || insn.tag == M_RCHCNT
334214571Sdim	    || insn.tag == M_WRCH))
335214571Sdim    as_warn (_("Mixing register syntax, with and without '$'."));
336214571Sdim  if (syntax_error_param)
337214571Sdim    {
338214571Sdim      const char *d = syntax_error_param;
339214571Sdim      while (*d != '$')
340214571Sdim	d--;
341214571Sdim      as_warn (_("Treating '%-*s' as a symbol."), (int)(syntax_error_param - d), d);
342214571Sdim    }
343214571Sdim
344214571Sdim  /* grow the current frag and plop in the opcode */
345214571Sdim
346214571Sdim  thisfrag = frag_more (4);
347214571Sdim  md_number_to_chars (thisfrag, insn.opcode, 4);
348214571Sdim
349214571Sdim  /* if this instruction requires labels mark it for later */
350214571Sdim
351214571Sdim  for (i = 0; i < MAX_RELOCS; i++)
352214571Sdim    if (insn.reloc_arg[i] >= 0)
353214571Sdim      {
354214571Sdim        fixS *fixP;
355214571Sdim        bfd_reloc_code_real_type reloc = arg_encode[insn.reloc_arg[i]].reloc;
356214571Sdim	int pcrel = 0;
357214571Sdim
358214571Sdim        if (reloc == BFD_RELOC_SPU_PCREL9a
359214571Sdim	    || reloc == BFD_RELOC_SPU_PCREL9b
360214571Sdim            || reloc == BFD_RELOC_SPU_PCREL16)
361214571Sdim	  pcrel = 1;
362214571Sdim	if (insn.flag[i] == 1)
363214571Sdim	  reloc = BFD_RELOC_SPU_HI16;
364214571Sdim	else if (insn.flag[i] == 2)
365214571Sdim	  reloc = BFD_RELOC_SPU_LO16;
366214571Sdim	fixP = fix_new_exp (frag_now,
367214571Sdim			    thisfrag - frag_now->fr_literal,
368214571Sdim			    4,
369214571Sdim			    &insn.exp[i],
370214571Sdim			    pcrel,
371214571Sdim			    reloc);
372214571Sdim	fixP->tc_fix_data.arg_format = insn.reloc_arg[i];
373214571Sdim	fixP->tc_fix_data.insn_tag = insn.tag;
374214571Sdim      }
375214571Sdim  dwarf2_emit_insn (4);
376214571Sdim}
377214571Sdim
378214571Sdimstatic int
379214571Sdimcalcop (struct spu_opcode *format, const char *param, struct spu_insn *insn)
380214571Sdim{
381214571Sdim  int i;
382214571Sdim  int paren = 0;
383214571Sdim  int arg;
384214571Sdim
385214571Sdim  for (i = 1; i <= format->arg[0]; i++)
386214571Sdim    {
387214571Sdim      arg = format->arg[i];
388214571Sdim      syntax_error_arg = i;
389214571Sdim
390214571Sdim      while (ISSPACE (*param))
391214571Sdim        param++;
392214571Sdim      if (*param == 0 || *param == ',')
393214571Sdim	return 0;
394214571Sdim      if (arg < A_P)
395214571Sdim        param = get_reg (param, insn, arg, 1);
396214571Sdim      else if (arg > A_P)
397214571Sdim        param = get_imm (param, insn,  arg);
398214571Sdim      else if (arg == A_P)
399214571Sdim	{
400214571Sdim	  paren++;
401214571Sdim	  if ('(' != *param++)
402214571Sdim	    return 0;
403214571Sdim	}
404214571Sdim
405214571Sdim      if (!param)
406214571Sdim	return 0;
407214571Sdim
408214571Sdim      while (ISSPACE (*param))
409214571Sdim        param++;
410214571Sdim
411214571Sdim      if (arg != A_P && paren)
412214571Sdim	{
413214571Sdim	  paren--;
414214571Sdim	  if (')' != *param++)
415214571Sdim	    return 0;
416214571Sdim	}
417214571Sdim      else if (i < format->arg[0]
418214571Sdim	       && format->arg[i] != A_P
419214571Sdim	       && format->arg[i+1] != A_P)
420214571Sdim	{
421214571Sdim	  if (',' != *param++)
422214571Sdim	    {
423214571Sdim	      syntax_error_arg++;
424214571Sdim	      return 0;
425214571Sdim	    }
426214571Sdim	}
427214571Sdim    }
428214571Sdim  while (ISSPACE (*param))
429214571Sdim    param++;
430214571Sdim  return !paren && (*param == 0 || *param == '\n');
431214571Sdim}
432214571Sdim
433214571Sdimstruct reg_name {
434214571Sdim    unsigned int regno;
435214571Sdim    unsigned int length;
436214571Sdim    char name[32];
437214571Sdim};
438214571Sdim
439214571Sdim#define REG_NAME(NO,NM) { NO, sizeof (NM) - 1, NM }
440214571Sdim
441214571Sdimstatic struct reg_name reg_name[] = {
442214571Sdim  REG_NAME (0, "lr"),  /* link register */
443214571Sdim  REG_NAME (1, "sp"),  /* stack pointer */
444214571Sdim  REG_NAME (0, "rp"),  /* link register */
445214571Sdim  REG_NAME (127, "fp"),  /* frame pointer */
446214571Sdim};
447214571Sdim
448214571Sdimstatic struct reg_name sp_reg_name[] = {
449214571Sdim};
450214571Sdim
451214571Sdimstatic struct reg_name ch_reg_name[] = {
452214571Sdim  REG_NAME (  0, "SPU_RdEventStat"),
453214571Sdim  REG_NAME (  1, "SPU_WrEventMask"),
454214571Sdim  REG_NAME (  2, "SPU_WrEventAck"),
455214571Sdim  REG_NAME (  3, "SPU_RdSigNotify1"),
456214571Sdim  REG_NAME (  4, "SPU_RdSigNotify2"),
457214571Sdim  REG_NAME (  7, "SPU_WrDec"),
458214571Sdim  REG_NAME (  8, "SPU_RdDec"),
459214571Sdim  REG_NAME ( 11, "SPU_RdEventMask"), /* DD2.0 only */
460214571Sdim  REG_NAME ( 13, "SPU_RdMachStat"),
461214571Sdim  REG_NAME ( 14, "SPU_WrSRR0"),
462214571Sdim  REG_NAME ( 15, "SPU_RdSRR0"),
463214571Sdim  REG_NAME ( 28, "SPU_WrOutMbox"),
464214571Sdim  REG_NAME ( 29, "SPU_RdInMbox"),
465214571Sdim  REG_NAME ( 30, "SPU_WrOutIntrMbox"),
466214571Sdim  REG_NAME (  9, "MFC_WrMSSyncReq"),
467214571Sdim  REG_NAME ( 12, "MFC_RdTagMask"),   /* DD2.0 only */
468214571Sdim  REG_NAME ( 16, "MFC_LSA"),
469214571Sdim  REG_NAME ( 17, "MFC_EAH"),
470214571Sdim  REG_NAME ( 18, "MFC_EAL"),
471214571Sdim  REG_NAME ( 19, "MFC_Size"),
472214571Sdim  REG_NAME ( 20, "MFC_TagID"),
473214571Sdim  REG_NAME ( 21, "MFC_Cmd"),
474214571Sdim  REG_NAME ( 22, "MFC_WrTagMask"),
475214571Sdim  REG_NAME ( 23, "MFC_WrTagUpdate"),
476214571Sdim  REG_NAME ( 24, "MFC_RdTagStat"),
477214571Sdim  REG_NAME ( 25, "MFC_RdListStallStat"),
478214571Sdim  REG_NAME ( 26, "MFC_WrListStallAck"),
479214571Sdim  REG_NAME ( 27, "MFC_RdAtomicStat"),
480214571Sdim};
481214571Sdim#undef REG_NAME
482214571Sdim
483214571Sdimstatic const char *
484214571Sdimget_reg (const char *param, struct spu_insn *insn, int arg, int accept_expr)
485214571Sdim{
486214571Sdim  unsigned regno;
487214571Sdim  int saw_prefix = 0;
488214571Sdim
489214571Sdim  if (*param == '$')
490214571Sdim    {
491214571Sdim      saw_prefix = 1;
492214571Sdim      param++;
493214571Sdim    }
494214571Sdim
495214571Sdim  if (arg == A_H) /* Channel */
496214571Sdim    {
497214571Sdim      if ((param[0] == 'c' || param[0] == 'C')
498214571Sdim	  && (param[1] == 'h' || param[1] == 'H')
499214571Sdim	  && ISDIGIT (param[2]))
500214571Sdim        param += 2;
501214571Sdim    }
502214571Sdim  else if (arg == A_S) /* Special purpose register */
503214571Sdim    {
504214571Sdim      if ((param[0] == 's' || param[0] == 'S')
505214571Sdim	  && (param[1] == 'p' || param[1] == 'P')
506214571Sdim	  && ISDIGIT (param[2]))
507214571Sdim        param += 2;
508214571Sdim    }
509214571Sdim
510214571Sdim  if (ISDIGIT (*param))
511214571Sdim    {
512214571Sdim      regno = 0;
513214571Sdim      while (ISDIGIT (*param))
514214571Sdim	regno = regno * 10 + *param++ - '0';
515214571Sdim    }
516214571Sdim  else
517214571Sdim    {
518214571Sdim      struct reg_name *rn;
519214571Sdim      unsigned int i, n, l = 0;
520214571Sdim
521214571Sdim      if (arg == A_H) /* Channel */
522214571Sdim	{
523214571Sdim	  rn = ch_reg_name;
524214571Sdim	  n = sizeof (ch_reg_name) / sizeof (*ch_reg_name);
525214571Sdim	}
526214571Sdim      else if (arg == A_S) /* Special purpose register */
527214571Sdim	{
528214571Sdim	  rn = sp_reg_name;
529214571Sdim	  n = sizeof (sp_reg_name) / sizeof (*sp_reg_name);
530214571Sdim	}
531214571Sdim      else
532214571Sdim	{
533214571Sdim	  rn = reg_name;
534214571Sdim	  n = sizeof (reg_name) / sizeof (*reg_name);
535214571Sdim	}
536214571Sdim      regno = 128;
537214571Sdim      for (i = 0; i < n; i++)
538214571Sdim	if (rn[i].length > l
539214571Sdim	    && 0 == strncasecmp (param, rn[i].name, rn[i].length))
540214571Sdim          {
541214571Sdim	    l = rn[i].length;
542214571Sdim            regno = rn[i].regno;
543214571Sdim          }
544214571Sdim      param += l;
545214571Sdim    }
546214571Sdim
547214571Sdim  if (!use_dd2
548214571Sdim      && arg == A_H)
549214571Sdim    {
550214571Sdim      if (regno == 11)
551214571Sdim	as_bad (_("'SPU_RdEventMask' (channel 11) is only available in DD2.0 or higher."));
552214571Sdim      else if (regno == 12)
553214571Sdim	as_bad (_("'MFC_RdTagMask' (channel 12) is only available in DD2.0 or higher."));
554214571Sdim    }
555214571Sdim
556214571Sdim  if (regno < 128)
557214571Sdim    {
558214571Sdim      insn->opcode |= regno << arg_encode[arg].pos;
559214571Sdim      if ((!saw_prefix && syntax_reg == 1)
560214571Sdim	  || (saw_prefix && syntax_reg == 2))
561214571Sdim	syntax_reg |= 4;
562214571Sdim      syntax_reg |= saw_prefix ? 1 : 2;
563214571Sdim      return param;
564214571Sdim    }
565214571Sdim
566214571Sdim  if (accept_expr)
567214571Sdim    {
568214571Sdim      char *save_ptr;
569214571Sdim      expressionS ex;
570214571Sdim      save_ptr = input_line_pointer;
571214571Sdim      input_line_pointer = (char *)param;
572214571Sdim      expression (&ex);
573214571Sdim      param = input_line_pointer;
574214571Sdim      input_line_pointer = save_ptr;
575214571Sdim      if (ex.X_op == O_register || ex.X_op == O_constant)
576214571Sdim	{
577214571Sdim	  insn->opcode |= ex.X_add_number << arg_encode[arg].pos;
578214571Sdim	  return param;
579214571Sdim	}
580214571Sdim    }
581214571Sdim  return 0;
582214571Sdim}
583214571Sdim
584214571Sdimstatic const char *
585214571Sdimget_imm (const char *param, struct spu_insn *insn, int arg)
586214571Sdim{
587214571Sdim  int val;
588214571Sdim  char *save_ptr;
589214571Sdim  int low = 0, high = 0;
590214571Sdim  int reloc_i = insn->reloc_arg[0] >= 0 ? 1 : 0;
591214571Sdim
592214571Sdim  if (strncasecmp (param, "%lo(", 4) == 0)
593214571Sdim    {
594214571Sdim      param += 3;
595214571Sdim      low = 1;
596214571Sdim      as_warn (_("Using old style, %%lo(expr), please change to PPC style, expr@l."));
597214571Sdim    }
598214571Sdim  else if (strncasecmp (param, "%hi(", 4) == 0)
599214571Sdim    {
600214571Sdim      param += 3;
601214571Sdim      high = 1;
602214571Sdim      as_warn (_("Using old style, %%hi(expr), please change to PPC style, expr@h."));
603214571Sdim    }
604214571Sdim  else if (strncasecmp (param, "%pic(", 5) == 0)
605214571Sdim    {
606214571Sdim      /* Currently we expect %pic(expr) == expr, so do nothing here.
607214571Sdim	 i.e. for code loaded at address 0 $toc will be 0.  */
608214571Sdim      param += 4;
609214571Sdim    }
610214571Sdim
611214571Sdim  if (*param == '$')
612214571Sdim    {
613214571Sdim      /* Symbols can start with $, but if this symbol matches a register
614214571Sdim	 name, it's probably a mistake.  The only way to avoid this
615214571Sdim	 warning is to rename the symbol.  */
616214571Sdim      struct spu_insn tmp_insn;
617214571Sdim      const char *np = get_reg (param, &tmp_insn, arg, 0);
618214571Sdim
619214571Sdim      if (np)
620214571Sdim	syntax_error_param = np;
621214571Sdim    }
622214571Sdim
623214571Sdim  save_ptr = input_line_pointer;
624214571Sdim  input_line_pointer = (char *) param;
625214571Sdim  expression (&insn->exp[reloc_i]);
626214571Sdim  param = input_line_pointer;
627214571Sdim  input_line_pointer = save_ptr;
628214571Sdim
629214571Sdim  /* Similar to ppc_elf_suffix in tc-ppc.c.  We have so few cases to
630214571Sdim     handle we do it inlined here. */
631214571Sdim  if (param[0] == '@' && !ISALNUM (param[2]) && param[2] != '@')
632214571Sdim    {
633214571Sdim      if (param[1] == 'h' || param[1] == 'H')
634214571Sdim	{
635214571Sdim	  high = 1;
636214571Sdim	  param += 2;
637214571Sdim	}
638214571Sdim      else if (param[1] == 'l' || param[1] == 'L')
639214571Sdim	{
640214571Sdim	  low = 1;
641214571Sdim	  param += 2;
642214571Sdim	}
643214571Sdim    }
644214571Sdim
645214571Sdim  if (insn->exp[reloc_i].X_op == O_constant)
646214571Sdim    {
647214571Sdim      val = insn->exp[reloc_i].X_add_number;
648214571Sdim
649214571Sdim      if (emulate_apuasm)
650214571Sdim	{
651214571Sdim	  /* Convert the value to a format we expect. */
652214571Sdim          val <<= arg_encode[arg].rshift;
653214571Sdim	  if (arg == A_U7A)
654214571Sdim	    val = 173 - val;
655214571Sdim	  else if (arg == A_U7B)
656214571Sdim	    val = 155 - val;
657214571Sdim	}
658214571Sdim
659214571Sdim      if (high)
660214571Sdim	val = val >> 16;
661214571Sdim      else if (low)
662214571Sdim	val = val & 0xffff;
663214571Sdim
664214571Sdim      /* Warn about out of range expressions. */
665214571Sdim      {
666214571Sdim	int hi = arg_encode[arg].hi;
667214571Sdim	int lo = arg_encode[arg].lo;
668214571Sdim	int whi = arg_encode[arg].whi;
669214571Sdim	int wlo = arg_encode[arg].wlo;
670214571Sdim
671214571Sdim	if (hi > lo && (val < lo || val > hi))
672214571Sdim	  as_fatal (_("Constant expression %d out of range, [%d, %d]."),
673214571Sdim		    val, lo, hi);
674214571Sdim	else if (whi > wlo && (val < wlo || val > whi))
675214571Sdim	  as_warn (_("Constant expression %d out of range, [%d, %d]."),
676214571Sdim		   val, wlo, whi);
677214571Sdim      }
678214571Sdim
679214571Sdim      if (arg == A_U7A)
680214571Sdim        val = 173 - val;
681214571Sdim      else if (arg == A_U7B)
682214571Sdim        val = 155 - val;
683214571Sdim
684214571Sdim      /* Branch hints have a split encoding.  Do the bottom part. */
685214571Sdim      if (arg == A_S11 || arg == A_S11I)
686214571Sdim	insn->opcode |= ((val >> 2) & 0x7f);
687214571Sdim
688214571Sdim      insn->opcode |= (((val >> arg_encode[arg].rshift)
689214571Sdim			& ((1 << arg_encode[arg].size) - 1))
690214571Sdim		       << arg_encode[arg].pos);
691214571Sdim      insn->reloc_arg[reloc_i] = -1;
692214571Sdim      insn->flag[reloc_i] = 0;
693214571Sdim    }
694214571Sdim  else
695214571Sdim    {
696214571Sdim      insn->reloc_arg[reloc_i] = arg;
697214571Sdim      if (high)
698214571Sdim	insn->flag[reloc_i] = 1;
699214571Sdim      else if (low)
700214571Sdim	insn->flag[reloc_i] = 2;
701214571Sdim    }
702214571Sdim
703214571Sdim  return param;
704214571Sdim}
705214571Sdim
706214571Sdim#define MAX_LITTLENUMS 6
707214571Sdim
708214571Sdim/* Turn a string in input_line_pointer into a floating point constant of type
709214571Sdim   type, and store the appropriate bytes in *litP.  The number of LITTLENUMS
710214571Sdim   emitted is stored in *sizeP .  An error message is returned, or NULL on OK.
711214571Sdim */
712214571Sdimchar *
713214571Sdimmd_atof (int type, char *litP, int *sizeP)
714214571Sdim{
715214571Sdim  int prec;
716214571Sdim  LITTLENUM_TYPE words[MAX_LITTLENUMS];
717214571Sdim  LITTLENUM_TYPE *wordP;
718214571Sdim  char *t;
719214571Sdim
720214571Sdim  switch (type)
721214571Sdim    {
722214571Sdim    case 'f':
723214571Sdim    case 'F':
724214571Sdim    case 's':
725214571Sdim    case 'S':
726214571Sdim      prec = 2;
727214571Sdim      break;
728214571Sdim
729214571Sdim    case 'd':
730214571Sdim    case 'D':
731214571Sdim    case 'r':
732214571Sdim    case 'R':
733214571Sdim      prec = 4;
734214571Sdim      break;
735214571Sdim
736214571Sdim    case 'x':
737214571Sdim    case 'X':
738214571Sdim      prec = 6;
739214571Sdim      break;
740214571Sdim
741214571Sdim    case 'p':
742214571Sdim    case 'P':
743214571Sdim      prec = 6;
744214571Sdim      break;
745214571Sdim
746214571Sdim    default:
747214571Sdim      *sizeP = 0;
748214571Sdim      return _("Bad call to MD_ATOF()");
749214571Sdim    }
750214571Sdim  t = atof_ieee (input_line_pointer, type, words);
751214571Sdim  if (t)
752214571Sdim    input_line_pointer = t;
753214571Sdim
754214571Sdim  *sizeP = prec * sizeof (LITTLENUM_TYPE);
755214571Sdim  for (wordP = words; prec--;)
756214571Sdim    {
757214571Sdim      md_number_to_chars (litP, (long) (*wordP++), sizeof (LITTLENUM_TYPE));
758214571Sdim      litP += sizeof (LITTLENUM_TYPE);
759214571Sdim    }
760214571Sdim  return 0;
761214571Sdim}
762214571Sdim
763214571Sdim#ifndef WORKING_DOT_WORD
764214571Sdimint md_short_jump_size = 4;
765214571Sdim
766214571Sdimvoid
767214571Sdimmd_create_short_jump (char *ptr,
768214571Sdim		      addressT from_addr ATTRIBUTE_UNUSED,
769214571Sdim		      addressT to_addr ATTRIBUTE_UNUSED,
770214571Sdim		      fragS *frag,
771214571Sdim		      symbolS *to_symbol)
772214571Sdim{
773214571Sdim  ptr[0] = (char) 0xc0;
774214571Sdim  ptr[1] = 0x00;
775214571Sdim  ptr[2] = 0x00;
776214571Sdim  ptr[3] = 0x00;
777214571Sdim  fix_new (frag,
778214571Sdim	   ptr - frag->fr_literal,
779214571Sdim	   4,
780214571Sdim	   to_symbol,
781214571Sdim	   (offsetT) 0,
782214571Sdim	   0,
783214571Sdim	   BFD_RELOC_SPU_PCREL16);
784214571Sdim}
785214571Sdim
786214571Sdimint md_long_jump_size = 4;
787214571Sdim
788214571Sdimvoid
789214571Sdimmd_create_long_jump (char *ptr,
790214571Sdim		     addressT from_addr ATTRIBUTE_UNUSED,
791214571Sdim		     addressT to_addr ATTRIBUTE_UNUSED,
792214571Sdim		     fragS *frag,
793214571Sdim		     symbolS *to_symbol)
794214571Sdim{
795214571Sdim  ptr[0] = (char) 0xc0;
796214571Sdim  ptr[1] = 0x00;
797214571Sdim  ptr[2] = 0x00;
798214571Sdim  ptr[3] = 0x00;
799214571Sdim  fix_new (frag,
800214571Sdim	   ptr - frag->fr_literal,
801214571Sdim	   4,
802214571Sdim	   to_symbol,
803214571Sdim	   (offsetT) 0,
804214571Sdim	   0,
805214571Sdim	   BFD_RELOC_SPU_PCREL16);
806214571Sdim}
807214571Sdim#endif
808214571Sdim
809214571Sdim/* Support @ppu on symbols referenced in .int/.long/.word/.quad.  */
810214571Sdimstatic void
811214571Sdimspu_cons (int nbytes)
812214571Sdim{
813214571Sdim  expressionS exp;
814214571Sdim
815214571Sdim  if (is_it_end_of_statement ())
816214571Sdim    {
817214571Sdim      demand_empty_rest_of_line ();
818214571Sdim      return;
819214571Sdim    }
820214571Sdim
821214571Sdim  do
822214571Sdim    {
823214571Sdim      deferred_expression (&exp);
824214571Sdim      if ((exp.X_op == O_symbol
825214571Sdim	   || exp.X_op == O_constant)
826214571Sdim	  && strncasecmp (input_line_pointer, "@ppu", 4) == 0)
827214571Sdim	{
828214571Sdim	  char *p = frag_more (nbytes);
829214571Sdim	  enum bfd_reloc_code_real reloc;
830214571Sdim
831214571Sdim	  /* Check for identifier@suffix+constant.  */
832214571Sdim	  input_line_pointer += 4;
833214571Sdim	  if (*input_line_pointer == '-' || *input_line_pointer == '+')
834214571Sdim	    {
835214571Sdim	      expressionS new_exp;
836214571Sdim
837214571Sdim	      expression (&new_exp);
838214571Sdim	      if (new_exp.X_op == O_constant)
839214571Sdim		exp.X_add_number += new_exp.X_add_number;
840214571Sdim	    }
841214571Sdim
842214571Sdim	  reloc = nbytes == 4 ? BFD_RELOC_SPU_PPU32 : BFD_RELOC_SPU_PPU64;
843214571Sdim	  fix_new_exp (frag_now, p - frag_now->fr_literal, nbytes,
844214571Sdim		       &exp, 0, reloc);
845214571Sdim	}
846214571Sdim      else
847214571Sdim	emit_expr (&exp, nbytes);
848214571Sdim    }
849214571Sdim  while (*input_line_pointer++ == ',');
850214571Sdim
851214571Sdim  /* Put terminator back into stream.  */
852214571Sdim  input_line_pointer--;
853214571Sdim  demand_empty_rest_of_line ();
854214571Sdim}
855214571Sdim
856214571Sdimint
857214571Sdimmd_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED,
858214571Sdim			       segT segment_type ATTRIBUTE_UNUSED)
859214571Sdim{
860214571Sdim  as_fatal (_("Relaxation should never occur"));
861214571Sdim  return -1;
862214571Sdim}
863214571Sdim
864214571Sdim/* If while processing a fixup, a reloc really needs to be created,
865214571Sdim   then it is done here.  */
866214571Sdim
867214571Sdimarelent *
868214571Sdimtc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
869214571Sdim{
870214571Sdim  arelent *reloc;
871214571Sdim  reloc = (arelent *) xmalloc (sizeof (arelent));
872214571Sdim  reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
873214571Sdim  if (fixp->fx_addsy)
874214571Sdim    *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
875214571Sdim  else if (fixp->fx_subsy)
876214571Sdim    *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
877214571Sdim  else
878214571Sdim    abort ();
879214571Sdim  reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
880214571Sdim  reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
881214571Sdim  if (reloc->howto == (reloc_howto_type *) NULL)
882214571Sdim    {
883214571Sdim      as_bad_where (fixp->fx_file, fixp->fx_line,
884214571Sdim		    _("reloc %d not supported by object file format"),
885214571Sdim		    (int) fixp->fx_r_type);
886214571Sdim      return NULL;
887214571Sdim    }
888214571Sdim  reloc->addend = fixp->fx_addnumber;
889214571Sdim  return reloc;
890214571Sdim}
891214571Sdim
892214571Sdim/* Round up a section's size to the appropriate boundary.  */
893214571Sdim
894214571SdimvalueT
895214571Sdimmd_section_align (segT seg, valueT size)
896214571Sdim{
897214571Sdim  int align = bfd_get_section_alignment (stdoutput, seg);
898214571Sdim  valueT mask = ((valueT) 1 << align) - 1;
899214571Sdim
900214571Sdim  return (size + mask) & ~mask;
901214571Sdim}
902214571Sdim
903214571Sdim/* Where a PC relative offset is calculated from.  On the spu they
904214571Sdim   are calculated from the beginning of the branch instruction.  */
905214571Sdim
906214571Sdimlong
907214571Sdimmd_pcrel_from (fixS *fixp)
908214571Sdim{
909214571Sdim  return fixp->fx_frag->fr_address + fixp->fx_where;
910214571Sdim}
911214571Sdim
912214571Sdim/* Fill in rs_align_code fragments.  */
913214571Sdim
914214571Sdimvoid
915214571Sdimspu_handle_align (fragS *fragp)
916214571Sdim{
917214571Sdim  static const unsigned char nop_pattern[8] = {
918214571Sdim    0x40, 0x20, 0x00, 0x00, /* even nop */
919214571Sdim    0x00, 0x20, 0x00, 0x00, /* odd  nop */
920214571Sdim  };
921214571Sdim
922214571Sdim  int bytes;
923214571Sdim  char *p;
924214571Sdim
925214571Sdim  if (fragp->fr_type != rs_align_code)
926214571Sdim    return;
927214571Sdim
928214571Sdim  bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
929214571Sdim  p = fragp->fr_literal + fragp->fr_fix;
930214571Sdim
931214571Sdim  if (bytes & 3)
932214571Sdim    {
933214571Sdim      int fix = bytes & 3;
934214571Sdim      memset (p, 0, fix);
935214571Sdim      p += fix;
936214571Sdim      bytes -= fix;
937214571Sdim      fragp->fr_fix += fix;
938214571Sdim    }
939214571Sdim  if (bytes & 4)
940214571Sdim    {
941214571Sdim      memcpy (p, &nop_pattern[4], 4);
942214571Sdim      p += 4;
943214571Sdim      bytes -= 4;
944214571Sdim      fragp->fr_fix += 4;
945214571Sdim    }
946214571Sdim
947214571Sdim  memcpy (p, nop_pattern, 8);
948214571Sdim  fragp->fr_var = 8;
949214571Sdim}
950214571Sdim
951214571Sdimvoid
952214571Sdimmd_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
953214571Sdim{
954214571Sdim  unsigned int res;
955214571Sdim  valueT val = *valP;
956214571Sdim  char *place = fixP->fx_where + fixP->fx_frag->fr_literal;
957214571Sdim
958214571Sdim  if (fixP->fx_subsy != (symbolS *) NULL)
959214571Sdim    {
960214571Sdim      /* We can't actually support subtracting a symbol.  */
961214571Sdim      as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
962214571Sdim    }
963214571Sdim
964214571Sdim  if (fixP->fx_addsy != NULL)
965214571Sdim    {
966214571Sdim      if (fixP->fx_pcrel)
967214571Sdim	{
968214571Sdim	  /* Hack around bfd_install_relocation brain damage.  */
969214571Sdim	  val += fixP->fx_frag->fr_address + fixP->fx_where;
970214571Sdim
971214571Sdim	  switch (fixP->fx_r_type)
972214571Sdim	    {
973214571Sdim	    case BFD_RELOC_32:
974214571Sdim	      fixP->fx_r_type = BFD_RELOC_32_PCREL;
975214571Sdim	      break;
976214571Sdim
977214571Sdim	    case BFD_RELOC_SPU_PCREL16:
978214571Sdim	    case BFD_RELOC_SPU_PCREL9a:
979214571Sdim	    case BFD_RELOC_SPU_PCREL9b:
980214571Sdim	    case BFD_RELOC_32_PCREL:
981214571Sdim	      break;
982214571Sdim
983214571Sdim	    default:
984214571Sdim	      as_bad_where (fixP->fx_file, fixP->fx_line,
985214571Sdim			    _("expression too complex"));
986214571Sdim	      break;
987214571Sdim	    }
988214571Sdim	}
989214571Sdim    }
990214571Sdim
991214571Sdim  fixP->fx_addnumber = val;
992214571Sdim
993214571Sdim  if (fixP->fx_r_type == BFD_RELOC_SPU_PPU32
994214571Sdim      || fixP->fx_r_type == BFD_RELOC_SPU_PPU64)
995214571Sdim    return;
996214571Sdim
997214571Sdim  if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
998214571Sdim    {
999214571Sdim      fixP->fx_done = 1;
1000214571Sdim      res = 0;
1001214571Sdim      if (fixP->tc_fix_data.arg_format > A_P)
1002214571Sdim	{
1003214571Sdim	  int hi = arg_encode[fixP->tc_fix_data.arg_format].hi;
1004214571Sdim	  int lo = arg_encode[fixP->tc_fix_data.arg_format].lo;
1005214571Sdim	  if (hi > lo && ((offsetT) val < lo || (offsetT) val > hi))
1006214571Sdim	    as_bad_where (fixP->fx_file, fixP->fx_line,
1007214571Sdim			  "Relocation doesn't fit. (relocation value = 0x%lx)",
1008214571Sdim			  (long) val);
1009214571Sdim	}
1010214571Sdim
1011214571Sdim      switch (fixP->fx_r_type)
1012214571Sdim        {
1013214571Sdim        case BFD_RELOC_8:
1014214571Sdim	  md_number_to_chars (place, val, 1);
1015214571Sdim	  return;
1016214571Sdim
1017214571Sdim        case BFD_RELOC_16:
1018214571Sdim	  md_number_to_chars (place, val, 2);
1019214571Sdim	  return;
1020214571Sdim
1021214571Sdim        case BFD_RELOC_32:
1022214571Sdim	  md_number_to_chars (place, val, 4);
1023214571Sdim	  return;
1024214571Sdim
1025214571Sdim        case BFD_RELOC_64:
1026214571Sdim	  md_number_to_chars (place, val, 8);
1027214571Sdim	  return;
1028214571Sdim
1029214571Sdim        case BFD_RELOC_SPU_IMM7:
1030214571Sdim          res = (val & 0x7f) << 14;
1031214571Sdim          break;
1032214571Sdim
1033214571Sdim        case BFD_RELOC_SPU_IMM8:
1034214571Sdim          res = (val & 0xff) << 14;
1035214571Sdim          break;
1036214571Sdim
1037214571Sdim        case BFD_RELOC_SPU_IMM10:
1038214571Sdim          res = (val & 0x3ff) << 14;
1039214571Sdim          break;
1040214571Sdim
1041214571Sdim        case BFD_RELOC_SPU_IMM10W:
1042214571Sdim          res = (val & 0x3ff0) << 10;
1043214571Sdim          break;
1044214571Sdim
1045214571Sdim        case BFD_RELOC_SPU_IMM16:
1046214571Sdim          res = (val & 0xffff) << 7;
1047214571Sdim          break;
1048214571Sdim
1049214571Sdim        case BFD_RELOC_SPU_IMM16W:
1050214571Sdim          res = (val & 0x3fffc) << 5;
1051214571Sdim          break;
1052214571Sdim
1053214571Sdim        case BFD_RELOC_SPU_IMM18:
1054214571Sdim          res = (val & 0x3ffff) << 7;
1055214571Sdim          break;
1056214571Sdim
1057214571Sdim        case BFD_RELOC_SPU_PCREL9a:
1058214571Sdim          res = ((val & 0x1fc) >> 2) | ((val & 0x600) << 14);
1059214571Sdim          break;
1060214571Sdim
1061214571Sdim        case BFD_RELOC_SPU_PCREL9b:
1062214571Sdim          res = ((val & 0x1fc) >> 2) | ((val & 0x600) << 5);
1063214571Sdim          break;
1064214571Sdim
1065214571Sdim        case BFD_RELOC_SPU_PCREL16:
1066214571Sdim          res = (val & 0x3fffc) << 5;
1067214571Sdim          break;
1068214571Sdim
1069214571Sdim        default:
1070214571Sdim          as_bad_where (fixP->fx_file, fixP->fx_line,
1071214571Sdim                        _("reloc %d not supported by object file format"),
1072214571Sdim                        (int) fixP->fx_r_type);
1073214571Sdim        }
1074214571Sdim
1075214571Sdim      if (res != 0)
1076214571Sdim        {
1077214571Sdim          place[0] |= (res >> 24) & 0xff;
1078214571Sdim          place[1] |= (res >> 16) & 0xff;
1079214571Sdim          place[2] |= (res >> 8) & 0xff;
1080214571Sdim          place[3] |= (res) & 0xff;
1081214571Sdim        }
1082214571Sdim    }
1083214571Sdim}
1084