1 /*-
2    tc-pj.c -- Assemble code for Pico Java
3    Copyright 1999, 2000, 2001, 2002, 2003, 2005
4    Free Software Foundation, Inc.
5 
6    This file is part of GAS, the GNU Assembler.
7 
8    GAS is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2, or (at your option)
11    any later version.
12 
13    GAS is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with GAS; see the file COPYING.  If not, write to
20    the Free Software Foundation, 51 Franklin Street - Fifth Floor,
21    Boston, MA 02110-1301, USA.  */
22 
23 /* Contributed by Steve Chamberlain of Transmeta <sac@pobox.com>.  */
24 
25 #include "as.h"
26 #include "safe-ctype.h"
27 #include "opcode/pj.h"
28 
29 extern const pj_opc_info_t pj_opc_info[512];
30 
31 const char comment_chars[]        = "!/";
32 const char line_separator_chars[] = ";";
33 const char line_comment_chars[]   = "/!#";
34 
35 static int pending_reloc;
36 static struct hash_control *opcode_hash_control;
37 
38 static void
39 little (int ignore ATTRIBUTE_UNUSED)
40 {
41   target_big_endian = 0;
42 }
43 
44 static void
45 big (int ignore ATTRIBUTE_UNUSED)
46 {
47   target_big_endian = 1;
48 }
49 
50 const pseudo_typeS md_pseudo_table[] =
51 {
52   {"ml",    little, 0},
53   {"mb",    big,    0},
54   {0, 0, 0}
55 };
56 
57 const char FLT_CHARS[] = "rRsSfFdDxXpP";
58 const char EXP_CHARS[] = "eE";
59 
60 void
61 md_operand (expressionS *op)
62 {
63   if (strncmp (input_line_pointer, "%hi16", 5) == 0)
64     {
65       if (pending_reloc)
66 	as_bad (_("confusing relocation expressions"));
67       pending_reloc = BFD_RELOC_PJ_CODE_HI16;
68       input_line_pointer += 5;
69       expression (op);
70     }
71 
72   if (strncmp (input_line_pointer, "%lo16", 5) == 0)
73     {
74       if (pending_reloc)
75 	as_bad (_("confusing relocation expressions"));
76       pending_reloc = BFD_RELOC_PJ_CODE_LO16;
77       input_line_pointer += 5;
78       expression (op);
79     }
80 }
81 
82 /* Parse an expression and then restore the input line pointer.  */
83 
84 static char *
85 parse_exp_save_ilp (char *s, expressionS *op)
86 {
87   char *save = input_line_pointer;
88 
89   input_line_pointer = s;
90   expression (op);
91   s = input_line_pointer;
92   input_line_pointer = save;
93   return s;
94 }
95 
96 /* This is called by emit_expr via TC_CONS_FIX_NEW when creating a
97    reloc for a cons.  We could use the definition there, except that
98    we want to handle magic pending reloc expressions specially.  */
99 
100 void
101 pj_cons_fix_new_pj (fragS *frag, int where, int nbytes, expressionS *exp)
102 {
103   static int rv[5][2] =
104   { { 0, 0 },
105     { BFD_RELOC_8, BFD_RELOC_8 },
106     { BFD_RELOC_PJ_CODE_DIR16, BFD_RELOC_16 },
107     { 0, 0 },
108     { BFD_RELOC_PJ_CODE_DIR32, BFD_RELOC_32 }};
109 
110   fix_new_exp (frag, where, nbytes, exp, 0,
111 	       pending_reloc ? pending_reloc
112 	       : rv[nbytes][(now_seg->flags & SEC_CODE) ? 0 : 1]);
113 
114   pending_reloc = 0;
115 }
116 
117 /* Turn a reloc description character from the pj-opc.h table into
118    code which BFD can handle.  */
119 
120 static int
121 c_to_r (int x)
122 {
123   switch (x)
124     {
125     case O_R8:
126       return BFD_RELOC_8_PCREL;
127     case O_U8:
128     case O_8:
129       return BFD_RELOC_8;
130     case O_R16:
131       return BFD_RELOC_PJ_CODE_REL16;
132     case O_U16:
133     case O_16:
134       return BFD_RELOC_PJ_CODE_DIR16;
135     case O_R32:
136       return BFD_RELOC_PJ_CODE_REL32;
137     case O_32:
138       return BFD_RELOC_PJ_CODE_DIR32;
139     }
140   abort ();
141   return 0;
142 }
143 
144 /* Handler for the ipush fake opcode,
145    turns ipush <foo> into sipush lo16<foo>, sethi hi16<foo>.  */
146 
147 static void
148 ipush_code (pj_opc_info_t *opcode ATTRIBUTE_UNUSED, char *str)
149 {
150   char *b = frag_more (6);
151   expressionS arg;
152 
153   b[0] = 0x11;
154   b[3] = 0xed;
155   parse_exp_save_ilp (str + 1, &arg);
156   if (pending_reloc)
157     {
158       as_bad (_("can't have relocation for ipush"));
159       pending_reloc = 0;
160     }
161 
162   fix_new_exp (frag_now, b - frag_now->fr_literal + 1, 2,
163 	       &arg, 0, BFD_RELOC_PJ_CODE_DIR16);
164   fix_new_exp (frag_now, b - frag_now->fr_literal + 4, 2,
165 	       &arg, 0, BFD_RELOC_PJ_CODE_HI16);
166 }
167 
168 /* Insert names into the opcode table which are really mini macros,
169    not opcodes.  The fakeness is indicated with an opcode of -1.  */
170 
171 static void
172 fake_opcode (const char *name,
173 	     void (*func) (struct pj_opc_info_t *, char *))
174 {
175   pj_opc_info_t * fake = xmalloc (sizeof (pj_opc_info_t));
176 
177   fake->opcode = -1;
178   fake->opcode_next = -1;
179   fake->u.func = func;
180   hash_insert (opcode_hash_control, name, (char *) fake);
181 }
182 
183 /* Enter another entry into the opcode hash table so the same opcode
184    can have another name.  */
185 
186 static void
187 alias (const char *new, const char *old)
188 {
189   hash_insert (opcode_hash_control, new,
190 	       (char *) hash_find (opcode_hash_control, old));
191 }
192 
193 /* This function is called once, at assembler startup time.  It sets
194    up the hash table with all the opcodes in it, and also initializes
195    some aliases for compatibility with other assemblers.  */
196 
197 void
198 md_begin (void)
199 {
200   const pj_opc_info_t *opcode;
201   opcode_hash_control = hash_new ();
202 
203   /* Insert names into hash table.  */
204   for (opcode = pj_opc_info; opcode->u.name; opcode++)
205     hash_insert (opcode_hash_control, opcode->u.name, (char *) opcode);
206 
207   /* Insert the only fake opcode.  */
208   fake_opcode ("ipush", ipush_code);
209 
210   /* Add some aliases for opcode names.  */
211   alias ("ifeq_s", "ifeq");
212   alias ("ifne_s", "ifne");
213   alias ("if_icmpge_s", "if_icmpge");
214   alias ("if_icmpne_s", "if_icmpne");
215   alias ("if_icmpeq_s", "if_icmpeq");
216   alias ("if_icmpgt_s", "if_icmpgt");
217   alias ("goto_s", "goto");
218 
219   bfd_set_arch_mach (stdoutput, TARGET_ARCH, 0);
220 }
221 
222 /* This is the guts of the machine-dependent assembler.  STR points to
223    a machine dependent instruction.  This function is supposed to emit
224    the frags/bytes it assembles to.  */
225 
226 void
227 md_assemble (char *str)
228 {
229   char *op_start;
230   char *op_end;
231 
232   pj_opc_info_t *opcode;
233   char *output;
234   int idx = 0;
235   char pend;
236 
237   int nlen = 0;
238 
239   /* Drop leading whitespace.  */
240   while (*str == ' ')
241     str++;
242 
243   /* Find the op code end.  */
244   op_start = str;
245   for (op_end = str;
246        *op_end && !is_end_of_line[*op_end & 0xff] && *op_end != ' ';
247        op_end++)
248     nlen++;
249 
250   pend = *op_end;
251   *op_end = 0;
252 
253   if (nlen == 0)
254     as_bad (_("can't find opcode "));
255 
256   opcode = (pj_opc_info_t *) hash_find (opcode_hash_control, op_start);
257   *op_end = pend;
258 
259   if (opcode == NULL)
260     {
261       as_bad (_("unknown opcode %s"), op_start);
262       return;
263     }
264 
265   if (opcode->opcode == -1)
266     {
267       /* It's a fake opcode.  Dig out the args and pretend that was
268          what we were passed.  */
269       (*opcode->u.func) (opcode, op_end);
270     }
271   else
272     {
273       int an;
274 
275       output = frag_more (opcode->len);
276       output[idx++] = opcode->opcode;
277 
278       if (opcode->opcode_next != -1)
279 	output[idx++] = opcode->opcode_next;
280 
281       for (an = 0; opcode->arg[an]; an++)
282 	{
283 	  expressionS arg;
284 
285 	  if (*op_end == ',' && an != 0)
286 	    op_end++;
287 
288 	  if (*op_end == 0)
289 	    as_bad ("expected expresssion");
290 
291 	  op_end = parse_exp_save_ilp (op_end, &arg);
292 
293 	  fix_new_exp (frag_now,
294 		       output - frag_now->fr_literal + idx,
295 		       ASIZE (opcode->arg[an]),
296 		       &arg,
297 		       PCREL (opcode->arg[an]),
298 		       pending_reloc ? pending_reloc : c_to_r (opcode->arg[an]));
299 
300 	  idx += ASIZE (opcode->arg[an]);
301 	  pending_reloc = 0;
302 	}
303 
304       while (ISSPACE (*op_end))
305 	op_end++;
306 
307       if (*op_end != 0)
308 	as_warn ("extra stuff on line ignored");
309 
310     }
311 
312   if (pending_reloc)
313     as_bad ("Something forgot to clean up\n");
314 
315 }
316 
317 /* Turn a string in input_line_pointer into a floating point constant
318    of type type, and store the appropriate bytes in *LITP.  The number
319    of LITTLENUMS emitted is stored in *SIZEP .  An error message is
320    returned, or NULL on OK.  */
321 
322 char *
323 md_atof (int type, char *litP, int *sizeP)
324 {
325   int prec;
326   LITTLENUM_TYPE words[4];
327   char *t;
328   int i;
329 
330   switch (type)
331     {
332     case 'f':
333       prec = 2;
334       break;
335 
336     case 'd':
337       prec = 4;
338       break;
339 
340     default:
341       *sizeP = 0;
342       return _("bad call to md_atof");
343     }
344 
345   t = atof_ieee (input_line_pointer, type, words);
346   if (t)
347     input_line_pointer = t;
348 
349   *sizeP = prec * 2;
350 
351   if (!target_big_endian)
352     {
353       for (i = prec - 1; i >= 0; i--)
354 	{
355 	  md_number_to_chars (litP, (valueT) words[i], 2);
356 	  litP += 2;
357 	}
358     }
359   else
360     {
361       for (i = 0; i < prec; i++)
362 	{
363 	  md_number_to_chars (litP, (valueT) words[i], 2);
364 	  litP += 2;
365 	}
366     }
367 
368   return NULL;
369 }
370 
371 const char *md_shortopts = "";
372 
373 struct option md_longopts[] =
374 {
375 #define OPTION_LITTLE (OPTION_MD_BASE)
376 #define OPTION_BIG    (OPTION_LITTLE + 1)
377 
378   {"little", no_argument, NULL, OPTION_LITTLE},
379   {"big", no_argument, NULL, OPTION_BIG},
380   {NULL, no_argument, NULL, 0}
381 };
382 size_t md_longopts_size = sizeof (md_longopts);
383 
384 int
385 md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
386 {
387   switch (c)
388     {
389     case OPTION_LITTLE:
390       little (0);
391       break;
392     case OPTION_BIG:
393       big (0);
394       break;
395     default:
396       return 0;
397     }
398   return 1;
399 }
400 
401 void
402 md_show_usage (FILE *stream)
403 {
404   fprintf (stream, _("\
405 PJ options:\n\
406 -little			generate little endian code\n\
407 -big			generate big endian code\n"));
408 }
409 
410 /* Apply a fixup to the object file.  */
411 
412 void
413 md_apply_fix (fixS *fixP, valueT * valP, segT seg ATTRIBUTE_UNUSED)
414 {
415   char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
416   long val = *valP;
417   long max, min;
418   int shift;
419 
420   max = min = 0;
421   shift = 0;
422   switch (fixP->fx_r_type)
423     {
424     case BFD_RELOC_VTABLE_INHERIT:
425     case BFD_RELOC_VTABLE_ENTRY:
426       fixP->fx_done = 0;
427       return;
428 
429     case BFD_RELOC_PJ_CODE_REL16:
430       if (val < -0x8000 || val >= 0x7fff)
431 	as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far"));
432       buf[0] |= (val >> 8) & 0xff;
433       buf[1] = val & 0xff;
434       break;
435 
436     case BFD_RELOC_PJ_CODE_HI16:
437       *buf++ = val >> 24;
438       *buf++ = val >> 16;
439       fixP->fx_addnumber = val & 0xffff;
440       break;
441 
442     case BFD_RELOC_PJ_CODE_DIR16:
443     case BFD_RELOC_PJ_CODE_LO16:
444       *buf++ = val >> 8;
445       *buf++ = val >> 0;
446 
447       max = 0xffff;
448       min = -0xffff;
449       break;
450 
451     case BFD_RELOC_8:
452       max = 0xff;
453       min = -0xff;
454       *buf++ = val;
455       break;
456 
457     case BFD_RELOC_PJ_CODE_DIR32:
458       *buf++ = val >> 24;
459       *buf++ = val >> 16;
460       *buf++ = val >> 8;
461       *buf++ = val >> 0;
462       break;
463 
464     case BFD_RELOC_32:
465       if (target_big_endian)
466 	{
467 	  *buf++ = val >> 24;
468 	  *buf++ = val >> 16;
469 	  *buf++ = val >> 8;
470 	  *buf++ = val >> 0;
471 	}
472       else
473 	{
474 	  *buf++ = val >> 0;
475 	  *buf++ = val >> 8;
476 	  *buf++ = val >> 16;
477 	  *buf++ = val >> 24;
478 	}
479       break;
480 
481     case BFD_RELOC_16:
482       if (target_big_endian)
483 	{
484 	  *buf++ = val >> 8;
485 	  *buf++ = val >> 0;
486 	}
487       else
488 	{
489 	  *buf++ = val >> 0;
490 	  *buf++ = val >> 8;
491 	}
492       break;
493 
494     default:
495       abort ();
496     }
497 
498   if (max != 0 && (val < min || val > max))
499     as_bad_where (fixP->fx_file, fixP->fx_line, _("offset out of range"));
500 
501   if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
502     fixP->fx_done = 1;
503 }
504 
505 /* Put number into target byte order.  Always put values in an
506    executable section into big endian order.  */
507 
508 void
509 md_number_to_chars (char *ptr, valueT use, int nbytes)
510 {
511   if (target_big_endian || now_seg->flags & SEC_CODE)
512     number_to_chars_bigendian (ptr, use, nbytes);
513   else
514     number_to_chars_littleendian (ptr, use, nbytes);
515 }
516 
517 /* Translate internal representation of relocation info to BFD target
518    format.  */
519 
520 arelent *
521 tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
522 {
523   arelent *rel;
524   bfd_reloc_code_real_type r_type;
525 
526   rel = xmalloc (sizeof (arelent));
527   rel->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
528   *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
529   rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
530 
531   r_type = fixp->fx_r_type;
532   rel->addend = fixp->fx_addnumber;
533   rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
534 
535   if (rel->howto == NULL)
536     {
537       as_bad_where (fixp->fx_file, fixp->fx_line,
538 		    _("Cannot represent relocation type %s"),
539 		    bfd_get_reloc_code_name (r_type));
540       /* Set howto to a garbage value so that we can keep going.  */
541       rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
542       assert (rel->howto != NULL);
543     }
544 
545   return rel;
546 }
547