/* tc-xstormy16.c -- Assembler for the Sanyo XSTORMY16. Copyright (C) 2000, 2001, 2002 Free Software Foundation. This file is part of GAS, the GNU Assembler. GAS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GAS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GAS; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "as.h" #include "subsegs.h" #include "symcat.h" #include "opcodes/xstormy16-desc.h" #include "opcodes/xstormy16-opc.h" #include "cgen.h" /* Structure to hold all of the different components describing an individual instruction. */ typedef struct { const CGEN_INSN * insn; const CGEN_INSN * orig_insn; CGEN_FIELDS fields; #if CGEN_INT_INSN_P CGEN_INSN_INT buffer [1]; #define INSN_VALUE(buf) (*(buf)) #else unsigned char buffer [CGEN_MAX_INSN_SIZE]; #define INSN_VALUE(buf) (buf) #endif char * addr; fragS * frag; int num_fixups; fixS * fixups [GAS_CGEN_MAX_FIXUPS]; int indices [MAX_OPERAND_INSTANCES]; } xstormy16_insn; const char comment_chars[] = ";"; const char line_comment_chars[] = "#"; const char line_separator_chars[] = "|"; const char EXP_CHARS[] = "eE"; const char FLT_CHARS[] = "dD"; #define O_fptr_symbol (O_max + 1) #define XSTORMY16_SHORTOPTS "" const char * md_shortopts = XSTORMY16_SHORTOPTS; struct option md_longopts[] = { {NULL, no_argument, NULL, 0} }; size_t md_longopts_size = sizeof (md_longopts); int md_parse_option (c, arg) int c ATTRIBUTE_UNUSED; char * arg ATTRIBUTE_UNUSED; { return 0; } void md_show_usage (stream) FILE * stream; { fprintf (stream, _(" XSTORMY16 specific command line options:\n")); } /* The target specific pseudo-ops which we support. */ const pseudo_typeS md_pseudo_table[] = { { "word", cons, 4 }, { NULL, NULL, 0 } }; void md_begin () { /* Initialize the `cgen' interface. */ /* Set the machine number and endian. */ gas_cgen_cpu_desc = xstormy16_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, 0, CGEN_CPU_OPEN_ENDIAN, CGEN_ENDIAN_LITTLE, CGEN_CPU_OPEN_END); xstormy16_cgen_init_asm (gas_cgen_cpu_desc); /* This is a callback from cgen to gas to parse operands. */ cgen_set_parse_operand_fn (gas_cgen_cpu_desc, gas_cgen_parse_operand); } void md_assemble (str) char * str; { xstormy16_insn insn; char * errmsg; /* Initialize GAS's cgen interface for a new instruction. */ gas_cgen_init_parse (); insn.insn = xstormy16_cgen_assemble_insn (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg); if (!insn.insn) { as_bad (errmsg); return; } /* Doesn't really matter what we pass for RELAX_P here. */ gas_cgen_finish_insn (insn.insn, insn.buffer, CGEN_FIELDS_BITSIZE (& insn.fields), 0, NULL); } void md_operand (e) expressionS * e; { if (*input_line_pointer != '@') return; if (strncmp (input_line_pointer+1, "fptr", 4) == 0) { input_line_pointer += 5; SKIP_WHITESPACE (); if (*input_line_pointer != '(') { as_bad ("Expected '('"); goto err; } input_line_pointer++; expression (e); if (*input_line_pointer != ')') { as_bad ("Missing ')'"); goto err; } input_line_pointer++; if (e->X_op != O_symbol) as_bad ("Not a symbolic expression"); else e->X_op = O_fptr_symbol; } return; err: ignore_rest_of_line (); } /* Called while parsing data to create a fixup. Create BFD_RELOC_XSTORMY16_FPTR16 relocations. */ void xstormy16_cons_fix_new (f, where, nbytes, exp) fragS *f; int where; int nbytes; expressionS *exp; { bfd_reloc_code_real_type code; fixS *fix; if (exp->X_op == O_fptr_symbol) { if (nbytes != 2) { as_bad ("unsupported fptr fixup size %d", nbytes); return; } exp->X_op = O_symbol; code = BFD_RELOC_XSTORMY16_FPTR16; } else if (nbytes == 1) code = BFD_RELOC_8; else if (nbytes == 2) code = BFD_RELOC_16; else if (nbytes == 4) code = BFD_RELOC_32; else { as_bad ("unsupported fixup size %d", nbytes); return; } fix = fix_new_exp (f, where, nbytes, exp, 0, code); } /* Called while parsing an instruction to create a fixup. Create BFD_RELOC_XSTORMY16_FPTR16 relocations. */ fixS * xstormy16_cgen_record_fixup_exp (frag, where, insn, length, operand, opinfo, exp) fragS * frag; int where; const CGEN_INSN * insn; int length; const CGEN_OPERAND * operand; int opinfo; expressionS * exp; { fixS *fixP; operatorT op = exp->X_op; if (op == O_fptr_symbol) exp->X_op = O_symbol; fixP = gas_cgen_record_fixup_exp (frag, where, insn, length, operand, opinfo, exp); if (op == O_fptr_symbol) { if (operand->type != XSTORMY16_OPERAND_IMM16) as_bad ("unsupported fptr fixup"); else { fixP->fx_r_type = BFD_RELOC_XSTORMY16_FPTR16; fixP->fx_where += 2; } } return fixP; } valueT md_section_align (segment, size) segT segment; valueT size; { int align = bfd_get_section_alignment (stdoutput, segment); return ((size + (1 << align) - 1) & (-1 << align)); } symbolS * md_undefined_symbol (name) char * name ATTRIBUTE_UNUSED; { return 0; } /* Return an initial guess of the length by which a fragment must grow to hold a branch to reach its destination. Also updates fr_type/fr_subtype as necessary. Called just before doing relaxation. Any symbol that is now undefined will not become defined. The guess for fr_var is ACTUALLY the growth beyond fr_fix. Whatever we do to grow fr_fix or fr_var contributes to our returned value. Although it may not be explicit in the frag, pretend fr_var starts with a 0 value. */ int md_estimate_size_before_relax (fragP, segment) fragS * fragP ATTRIBUTE_UNUSED; segT segment ATTRIBUTE_UNUSED; { /* No assembler relaxation is defined (or necessary) for this port. */ abort (); } /* *fragP has been relaxed to its final size, and now needs to have the bytes inside it modified to conform to the new size. Called after relaxation is finished. fragP->fr_type == rs_machine_dependent. fragP->fr_subtype is the subtype of what the address relaxed to. */ void md_convert_frag (abfd, sec, fragP) bfd * abfd ATTRIBUTE_UNUSED; segT sec ATTRIBUTE_UNUSED; fragS * fragP ATTRIBUTE_UNUSED; { /* No assembler relaxation is defined (or necessary) for this port. */ abort (); } /* Functions concerning relocs. */ /* The location from which a PC relative jump should be calculated, given a PC relative reloc. */ long md_pcrel_from_section (fixP, sec) fixS * fixP; segT sec; { if (fixP->fx_addsy != (symbolS *) NULL && (! S_IS_DEFINED (fixP->fx_addsy) || S_GET_SEGMENT (fixP->fx_addsy) != sec)) { /* The symbol is undefined (or is defined but not in this section). Let the linker figure it out. */ return 0; } return fixP->fx_frag->fr_address + fixP->fx_where; } /* Return the bfd reloc type for OPERAND of INSN at fixup FIXP. Returns BFD_RELOC_NONE if no reloc type can be found. *FIXP may be modified if desired. */ bfd_reloc_code_real_type md_cgen_lookup_reloc (insn, operand, fixP) const CGEN_INSN * insn ATTRIBUTE_UNUSED; const CGEN_OPERAND * operand; fixS * fixP; { switch (operand->type) { case XSTORMY16_OPERAND_IMM2: case XSTORMY16_OPERAND_IMM3: case XSTORMY16_OPERAND_IMM3B: case XSTORMY16_OPERAND_IMM4: case XSTORMY16_OPERAND_IMM12: case XSTORMY16_OPERAND_HMEM8: return BFD_RELOC_NONE; case XSTORMY16_OPERAND_IMM8: case XSTORMY16_OPERAND_LMEM8: return fixP->fx_pcrel ? BFD_RELOC_8_PCREL : BFD_RELOC_8; case XSTORMY16_OPERAND_IMM16: fixP->fx_where += 2; return fixP->fx_pcrel ? BFD_RELOC_16_PCREL : BFD_RELOC_16; case XSTORMY16_OPERAND_ABS24: return BFD_RELOC_XSTORMY16_24; case XSTORMY16_OPERAND_REL8_2: case XSTORMY16_OPERAND_REL8_4: fixP->fx_pcrel = 1; return BFD_RELOC_8_PCREL; case XSTORMY16_OPERAND_REL12: fixP->fx_where += 2; /* Fall through... */ case XSTORMY16_OPERAND_REL12A: fixP->fx_pcrel = 1; return BFD_RELOC_XSTORMY16_REL_12; default : /* avoid -Wall warning */ abort (); } } /* See whether we need to force a relocation into the output file. This is used to force out switch and PC relative relocations when relaxing. */ int xstormy16_force_relocation (fix) fixS * fix; { switch (fix->fx_r_type) { case BFD_RELOC_XSTORMY16_FPTR16: case BFD_RELOC_VTABLE_INHERIT: case BFD_RELOC_VTABLE_ENTRY: return 1; default: break; } return S_FORCE_RELOC (fix->fx_addsy); } /* Return true if a relocation against a symbol may be replaced with a relocation against section+offset. */ boolean xstormy16_fix_adjustable (fixP) fixS * fixP; { /* We need the symbol name for the VTABLE entries. */ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY) return 0; if (fixP->fx_r_type == BFD_RELOC_XSTORMY16_FPTR16) return 0; return 1; } /* This is a copy of gas_cgen_md_apply_fix3, with some enhancements to do various things that would not be valid for all ports. */ void xstormy16_md_apply_fix3 (fixP, valueP, seg) fixS * fixP; valueT * valueP; segT seg ATTRIBUTE_UNUSED; { char *where = fixP->fx_frag->fr_literal + fixP->fx_where; valueT value = *valueP; /* Canonical name, since used a lot. */ CGEN_CPU_DESC cd = gas_cgen_cpu_desc; /* This port has pc-relative relocs and DIFF_EXPR_OK defined, so it must deal with turning a BFD_RELOC_{8,16,32,64} into a BFD_RELOC_*_PCREL for the case of .word something-. */ if (fixP->fx_pcrel) switch (fixP->fx_r_type) { case BFD_RELOC_8: fixP->fx_r_type = BFD_RELOC_8_PCREL; break; case BFD_RELOC_16: fixP->fx_r_type = BFD_RELOC_16_PCREL; break; case BFD_RELOC_32: fixP->fx_r_type = BFD_RELOC_32_PCREL; break; case BFD_RELOC_64: fixP->fx_r_type = BFD_RELOC_64_PCREL; break; default: break; } if (fixP->fx_addsy == (symbolS *) NULL) fixP->fx_done = 1; /* We don't actually support subtracting a symbol. */ if (fixP->fx_subsy != (symbolS *) NULL) as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex")); if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED) { int opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED; const CGEN_OPERAND *operand = cgen_operand_lookup_by_num (cd, opindex); const char *errmsg; bfd_reloc_code_real_type reloc_type; CGEN_FIELDS *fields = alloca (CGEN_CPU_SIZEOF_FIELDS (cd)); const CGEN_INSN *insn = fixP->fx_cgen.insn; /* If the reloc has been fully resolved finish the operand here. */ /* FIXME: This duplicates the capabilities of code in BFD. */ if (fixP->fx_done) { CGEN_CPU_SET_FIELDS_BITSIZE (cd) (fields, CGEN_INSN_BITSIZE (insn)); CGEN_CPU_SET_VMA_OPERAND (cd) (cd, opindex, fields, (bfd_vma) value); #if CGEN_INT_INSN_P { CGEN_INSN_INT insn_value = cgen_get_insn_value (cd, where, CGEN_INSN_BITSIZE (insn)); /* ??? 0 is passed for `pc'. */ errmsg = CGEN_CPU_INSERT_OPERAND (cd) (cd, opindex, fields, &insn_value, (bfd_vma) 0); cgen_put_insn_value (cd, where, CGEN_INSN_BITSIZE (insn), insn_value); } #else /* ??? 0 is passed for `pc'. */ errmsg = CGEN_CPU_INSERT_OPERAND (cd) (cd, opindex, fields, where, (bfd_vma) 0); #endif if (errmsg) as_bad_where (fixP->fx_file, fixP->fx_line, "%s", errmsg); } if (fixP->fx_done) return; /* The operand isn't fully resolved. Determine a BFD reloc value based on the operand information and leave it to bfd_install_relocation. Note that this doesn't work when partial_inplace == false. */ reloc_type = md_cgen_lookup_reloc (insn, operand, fixP); if (reloc_type != BFD_RELOC_NONE) { fixP->fx_r_type = reloc_type; } else { as_bad_where (fixP->fx_file, fixP->fx_line, _("unresolved expression that must be resolved")); fixP->fx_done = 1; return; } } else if (fixP->fx_done) { /* We're finished with this fixup. Install it because bfd_install_relocation won't be called to do it. */ switch (fixP->fx_r_type) { case BFD_RELOC_8: md_number_to_chars (where, value, 1); break; case BFD_RELOC_16: md_number_to_chars (where, value, 2); break; case BFD_RELOC_32: md_number_to_chars (where, value, 4); break; case BFD_RELOC_64: md_number_to_chars (where, value, 8); break; default: as_bad_where (fixP->fx_file, fixP->fx_line, _("internal error: can't install fix for reloc type %d (`%s')"), fixP->fx_r_type, bfd_get_reloc_code_name (fixP->fx_r_type)); break; } } else { /* bfd_install_relocation will be called to finish things up. */ } /* This is a RELA port. Thus, it does not need to store a value if it is going to make a reloc. What's more, when assembling a line like .byte global-0x7f00 we'll get a spurious error message if we try to stuff 0x7f00 into the byte. */ if (! fixP->fx_done) *valueP = 0; /* Tuck `value' away for use by tc_gen_reloc. See the comment describing fx_addnumber in write.h. This field is misnamed (or misused :-). */ fixP->fx_addnumber = value; } /* Write a value out to the object file, using the appropriate endianness. */ void md_number_to_chars (buf, val, n) char * buf; valueT val; int n; { number_to_chars_littleendian (buf, val, n); } /* Turn a string in input_line_pointer into a floating point constant of type type, and store the appropriate bytes in *litP. The number of LITTLENUMS emitted is stored in *sizeP . An error message is returned, or NULL on OK. */ /* Equal to MAX_PRECISION in atof-ieee.c */ #define MAX_LITTLENUMS 6 char * md_atof (type, litP, sizeP) char type; char * litP; int * sizeP; { int prec; LITTLENUM_TYPE words [MAX_LITTLENUMS]; LITTLENUM_TYPE *wordP; char * t; switch (type) { case 'f': case 'F': prec = 2; break; case 'd': case 'D': prec = 4; break; /* FIXME: Some targets allow other format chars for bigger sizes here. */ default: * sizeP = 0; return _("Bad call to md_atof()"); } t = atof_ieee (input_line_pointer, type, words); if (t) input_line_pointer = t; * sizeP = prec * sizeof (LITTLENUM_TYPE); *sizeP = prec * sizeof (LITTLENUM_TYPE); /* This loops outputs the LITTLENUMs in REVERSE order; in accord with the littleendianness of the processor. */ for (wordP = words + prec - 1; prec--;) { md_number_to_chars (litP, (valueT) (*wordP--), sizeof (LITTLENUM_TYPE)); litP += sizeof (LITTLENUM_TYPE); } return 0; }