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