diff options
Diffstat (limited to 'binutils-2.25/gas/config/tc-xgate.c')
-rw-r--r-- | binutils-2.25/gas/config/tc-xgate.c | 1353 |
1 files changed, 1353 insertions, 0 deletions
diff --git a/binutils-2.25/gas/config/tc-xgate.c b/binutils-2.25/gas/config/tc-xgate.c new file mode 100644 index 00000000..7ed1ef62 --- /dev/null +++ b/binutils-2.25/gas/config/tc-xgate.c @@ -0,0 +1,1353 @@ +/* tc-xgate.c -- Assembler code for Freescale XGATE + Copyright 2010, 2011, 2012 + Free Software Foundation, Inc. + Contributed by Sean Keys <skeys@ipdatasys.com> + + 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 3, 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, 51 Franklin Street - Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#include "as.h" +#include "safe-ctype.h" +#include "subsegs.h" +#include "opcode/xgate.h" +#include "dwarf2dbg.h" +#include "elf/xgate.h" + +const char comment_chars[] = ";!"; +const char line_comment_chars[] = "#*"; +const char line_separator_chars[] = ""; +const char EXP_CHARS[] = "eE"; +const char FLT_CHARS[] = "dD"; + +/* Max opcodes per opcode handle. */ +#define MAX_OPCODES 0x05 + +#define SIXTEENTH_BIT 0x8000 +#define N_BITS_IN_WORD 16 +#define MAX_NUM_OPERANDS 3 + +/* #define STATE_CONDITIONAL_BRANCH (1) */ +#define STATE_PC_RELATIVE (2) +#define REGISTER_P(ptr) (ptr == 'r') +#define INCREMENT 01 +#define DECREMENT 02 +#define MAXREGISTER 07 +#define MINREGISTER 00 + +#define OPTION_MMCU 'm' + +/* This macro has no side-effects. */ +#define ENCODE_RELAX(what,length) (((what) << 2) + (length)) + +/* Each unique opcode name has a handle. That handle may + contain pointers to opcodes with the same name but + different address modes. */ +struct xgate_opcode_handle +{ + int number_of_modes; + char *name; + struct xgate_opcode *opc0[MAX_OPCODES]; +}; + +/* XGATE's registers all are 16-bit general purpose. + They are numbered according to the specifications. */ +typedef enum register_id +{ + REG_NONE = -1, + REG_R0 = 0, + REG_R1 = 1, + REG_R2 = 2, + REG_R3 = 3, + REG_R4 = 4, + REG_R5 = 5, + REG_R6 = 6, + REG_R7 = 7, + REG_PC = 8, + REG_CCR = 9 +} register_id; + +/* Operand Modifiers */ +typedef enum op_modifiers +{ + MOD_NONE = -1, + MOD_POSTINC = 1, + MOD_PREDEC = 2, + MOD_CONSTANT = 3, + MOD_LOAD_HIGH = 4, + MOD_LOAD_LOW = 5 +}op_modifiers; + +typedef struct s_operand +{ + expressionS exp; + register_id reg; + op_modifiers mod; +} s_operand; + + +/* Forward declarations. */ +static inline char *skip_whitespace (char *); +static void get_default_target (void); +static char *extract_word (char *, char *, int); +static struct xgate_opcode *xgate_find_match (struct xgate_opcode_handle *, + int, s_operand [], unsigned int); +static int cmp_opcode (struct xgate_opcode *, struct xgate_opcode *); +static void xgate_print_table (void); +static unsigned int xgate_get_operands (char *, s_operand []); +static register_id reg_name_search (char *); +static op_modifiers xgate_determine_modifiers(char **); +static void xgate_scan_operands (struct xgate_opcode *opcode, s_operand []); +static unsigned int xgate_parse_operand (struct xgate_opcode *, int *, int, + char **, s_operand); + +static struct hash_control *xgate_hash; + +/* Previous opcode. */ +static unsigned int prev = 0; + +static unsigned char fixup_required = 0; + +/* Used to enable clipping of 16 bit operands into 8 bit constraints. */ +static unsigned char autoHiLo = 0; + +static char oper_check; +static char flag_print_insn_syntax = 0; +static char flag_print_opcodes = 0; + +static int current_architecture; +static const char *default_cpu; + +/* ELF flags to set in the output file header. */ +static int elf_flags = E_XGATE_F64; + +/* This table describes how you change sizes for the various types of variable + size expressions. This version only supports two kinds. */ + +/* The fields are: + How far Forward this mode will reach. + How far Backward this mode will reach. + How many bytes this mode will add to the size of the frag. + Which mode to go to if the offset won't fit in this one. */ + +relax_typeS md_relax_table[] = +{ + {1, 1, 0, 0}, /* First entries aren't used. */ + {1, 1, 0, 0}, /* For no good reason except. */ + {1, 1, 0, 0}, /* that the VAX doesn't either. */ + {1, 1, 0, 0}, + /* XGATE 9 and 10 bit pc rel todo complete and test */ +/*{(511), (-512), 0, ENCODE_RELAX (STATE_PC_RELATIVE, STATE_WORD)}, + {(1023), (-1024), 0, ENCODE_RELAX (STATE_PC_RELATIVE, STATE_WORD)}, */ + {0, 0, 0, 0} +}; + +/* This table describes all the machine specific pseudo-ops the assembler + has to support. The fields are: pseudo-op name without dot function to + call to execute this pseudo-op Integer arg to pass to the function. */ +const pseudo_typeS md_pseudo_table[] = +{ + /* The following pseudo-ops are supported for MRI compatibility. */ + {0, 0, 0} +}; + +const char *md_shortopts = "m:"; + +struct option md_longopts[] = +{ +#define OPTION_PRINT_INSN_SYNTAX (OPTION_MD_BASE + 0) + { "print-insn-syntax", no_argument, NULL, OPTION_PRINT_INSN_SYNTAX }, + +#define OPTION_PRINT_OPCODES (OPTION_MD_BASE + 1) + { "print-opcodes", no_argument, NULL, OPTION_PRINT_OPCODES }, + +#define OPTION_GENERATE_EXAMPLE (OPTION_MD_BASE + 2) + { "generate-example", no_argument, NULL, OPTION_GENERATE_EXAMPLE }, + +#define OPTION_MSHORT (OPTION_MD_BASE + 3) + { "mshort", no_argument, NULL, OPTION_MSHORT }, + +#define OPTION_MLONG (OPTION_MD_BASE + 4) + { "mlong", no_argument, NULL, OPTION_MLONG }, + +#define OPTION_MSHORT_DOUBLE (OPTION_MD_BASE + 5) + { "mshort-double", no_argument, NULL, OPTION_MSHORT_DOUBLE }, + +#define OPTION_MLONG_DOUBLE (OPTION_MD_BASE + 6) + { "mlong-double", no_argument, NULL, OPTION_MLONG_DOUBLE }, + + { NULL, no_argument, NULL, 0 } +}; + +size_t md_longopts_size = sizeof(md_longopts); + +char * +md_atof (int type, char *litP, int *sizeP) +{ + return ieee_md_atof (type, litP, sizeP, TRUE); +} + +int +md_parse_option (int c, char *arg) +{ + switch (c) + { + case OPTION_MMCU: + if (strcasecmp (arg, "v1") == 0) + current_architecture = XGATE_V1; + else if (strcasecmp (arg, "v2") == 0) + current_architecture = XGATE_V2; + else if (strcasecmp (arg, "v3") == 0) + current_architecture = XGATE_V3; + else + as_bad (_(" architecture variant invalid")); + break; + + case OPTION_PRINT_INSN_SYNTAX: + flag_print_insn_syntax = 1; + break; + + case OPTION_PRINT_OPCODES: + flag_print_opcodes = 1; + break; + + case OPTION_GENERATE_EXAMPLE: + flag_print_opcodes = 2; + break; + + case OPTION_MSHORT: + elf_flags &= ~E_XGATE_I32; + break; + + case OPTION_MLONG: + elf_flags |= E_XGATE_I32; + break; + + case OPTION_MSHORT_DOUBLE: + elf_flags &= ~E_XGATE_F64; + break; + + case OPTION_MLONG_DOUBLE: + elf_flags |= E_XGATE_F64; + break; + + default: + return 0; + } + return 1; +} + +const char * +xgate_arch_format (void) +{ + get_default_target (); + + if (current_architecture & cpuxgate) + return "elf32-xgate"; + + return "error"; +} + +static void +get_default_target (void) +{ + const bfd_target *target; + bfd abfd; + + if (current_architecture != 0) + return; + + default_cpu = "unknown"; + target = bfd_find_target (0, &abfd); + + if (target && target->name) + { + if (strcmp (target->name, "elf32-xgate") == 0) + { + current_architecture = cpuxgate; + default_cpu = "XGATE V1"; + return; + } + + as_bad (_("Default target `%s' is not supported."), target->name); + } +} + +void +md_begin (void) +{ + struct xgate_opcode *xgate_opcode_ptr = NULL; + struct xgate_opcode *xgate_op_table = NULL; + struct xgate_opcode_handle *op_handles = 0; + char *prev_op_name = 0; + int handle_enum = 0; + int number_of_op_handles = 0; + int i, j = 0; + + /* Create a local copy of our opcode table + including an extra line for NULL termination. */ + xgate_op_table = (struct xgate_opcode *) + xmalloc ((xgate_num_opcodes) * sizeof (struct xgate_opcode)); + + memset (xgate_op_table, 0, + sizeof(struct xgate_opcode) * (xgate_num_opcodes)); + + for (xgate_opcode_ptr = (struct xgate_opcode*) xgate_opcodes, i = 0; + i < xgate_num_opcodes; i++) + xgate_op_table[i] = xgate_opcode_ptr[i]; + + qsort (xgate_op_table, xgate_num_opcodes, sizeof(struct xgate_opcode), + (int (*)(const void *, const void *)) cmp_opcode); + + /* Calculate number of handles since this will be + smaller than the raw number of opcodes in the table. */ + prev_op_name = ""; + for (xgate_opcode_ptr = xgate_op_table, i = 0; i < xgate_num_opcodes; + xgate_opcode_ptr++, i++) + { + if (strcmp (prev_op_name, xgate_opcode_ptr->name)) + number_of_op_handles++; + prev_op_name = xgate_opcode_ptr->name; + } + + op_handles = (struct xgate_opcode_handle *) + xmalloc (sizeof(struct xgate_opcode_handle) * (number_of_op_handles)); + + /* Insert unique opcode names into hash table, aliasing duplicates. */ + xgate_hash = hash_new (); + + prev_op_name = ""; + for (xgate_opcode_ptr = xgate_op_table, i = 0, j = 0; i < xgate_num_opcodes; + i++, xgate_opcode_ptr++) + { + if (!strcmp (prev_op_name, xgate_opcode_ptr->name)) + { + handle_enum++; + op_handles[j].opc0[handle_enum] = xgate_opcode_ptr; + } + else + { + handle_enum = 0; + if (i) + j++; + op_handles[j].name = xgate_opcode_ptr->name; + op_handles[j].opc0[0] = xgate_opcode_ptr; + hash_insert (xgate_hash, (char *) op_handles[j].name, + (char *) &(op_handles[j])); + } + op_handles[j].number_of_modes = handle_enum; + prev_op_name = op_handles[j].name; + } + + if (flag_print_opcodes) + { + xgate_print_table (); + exit (EXIT_SUCCESS); + } +} + +void +xgate_init_after_args (void) +{ +} + +void +md_show_usage (FILE * stream) +{ + get_default_target (); + + fprintf (stream, + _("\ +Freescale XGATE co-processor options:\n\ + -mshort use 16-bit int ABI (default)\n\ + -mlong use 32-bit int ABI\n\ + -mshort-double use 32-bit double ABI\n\ + -mlong-double use 64-bit double ABI (default)\n\ + --mxgate specify the processor variant[default %s]\n\ + --print-insn-syntax print the syntax of instruction in case of error\n\ + --print-opcodes print the list of instructions with syntax\n\ + --generate-example generate an example of each instruction"), + default_cpu); +} + +enum bfd_architecture +xgate_arch (void) +{ + get_default_target (); + return bfd_arch_xgate; +} + +int +xgate_mach (void) +{ + return 0; +} + +static void +xgate_print_syntax (char *name) +{ + int i; + + for (i = 0; i < xgate_num_opcodes; i++) + { + if (!strcmp (xgate_opcodes[i].name, name)) + { + if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_IDR)) + printf ("\tFormat is %s\tRx, Rx, Rx+|-Rx|Rx\n", + xgate_opcodes[i].name); + if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_INH)) + printf ("\tFormat is %s\n", xgate_opcodes[i].name); + if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_TRI)) + printf ("\tFormat is %s\tRx, Rx, Rx\n", xgate_opcodes[i].name); + if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_DYA)) + printf ("\tFormat is %s\tRx, Rx\n", xgate_opcodes[i].name); + if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_IMM3)) + printf ("\tFormat is %s\t<3-bit value>\n", xgate_opcodes[i].name); + if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_IMM4)) + printf ("\tFormat is %s\t<4 -bit value>\n", xgate_opcodes[i].name); + if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_IMM8)) + printf ("\tFormat is %s\tRx, <8-bit value>\n", + xgate_opcodes[i].name); + if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_IMM16)) + printf ("\tFormat is %s\tRx, <16-bit value>\n", + xgate_opcodes[i].name); + if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_MON_R_C)) + printf ("\tFormat is %s\tRx, CCR\n", xgate_opcodes[i].name); + if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_MON_C_R)) + printf ("\tFormat is %s\tCCR, Rx\n", xgate_opcodes[i].name); + if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_MON_R_P)) + printf ("\tFormat is %s\tRx, PC\n", xgate_opcodes[i].name); + if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_IMM16mLDW)) + printf ("\tFormat is %s\tRx, <16-bit value>\n", + xgate_opcodes[i].name); + } + } +} + +static void +xgate_print_table (void) +{ + int i; + + for (i = 0; i < xgate_num_opcodes; i++) + xgate_print_syntax (xgate_opcodes[i].name); + + return; +} + +const char * +xgate_listing_header (void) +{ + if (current_architecture & cpuxgate) + return "XGATE GAS "; + + return "ERROR MC9S12X GAS "; +} + +symbolS * +md_undefined_symbol (char *name ATTRIBUTE_UNUSED) +{ + return 0; +} + +/* GAS will call this function for each section at the end of the assembly, + to permit the CPU backend to adjust the alignment of a section. */ + +valueT +md_section_align (asection * seg, valueT addr) +{ + int align = bfd_get_section_alignment (stdoutput, seg); + return ((addr + (1 << align) - 1) & (-1 << align)); +} + +void +md_assemble (char *input_line) +{ + struct xgate_opcode *opcode = 0; + struct xgate_opcode *macro_opcode = 0; + struct xgate_opcode_handle *opcode_handle = 0; + /* Caller expects it to be returned as it was passed. */ + char *saved_input_line = input_line; + char op_name[9] = { 0 }; + unsigned int operandCount = 0; + char *p = 0; + + s_operand new_operands[MAX_NUM_OPERANDS]; + + fixup_required = 0; + oper_check = 0; /* set error flags */ + input_line = extract_word (input_line, op_name, sizeof(op_name)); + + /* Check to make sure we are not reading a bogus line. */ + if (!op_name[0]) + as_bad (_("opcode missing or not found on input line")); + + if (!(opcode_handle = (struct xgate_opcode_handle *) hash_find (xgate_hash, + op_name))) + { + as_bad (_("opcode %s not found in opcode hash table"), op_name); + } + else + { + /* Parse operands so we can find the proper opcode bin. */ + + operandCount = xgate_get_operands (input_line, new_operands); + + opcode = xgate_find_match (opcode_handle, opcode_handle->number_of_modes, + new_operands, operandCount); + + if (!opcode) + { + as_bad (_("matching operands to opcode ")); + xgate_print_syntax (opcode_handle->opc0[0]->name); + } + else if (opcode->size == 2) + { + /* Size is one word - assemble that native insn. */ + xgate_scan_operands (opcode, new_operands); + } + else + { + /* Insn is a simplified instruction - expand it out. */ + autoHiLo = 1; + unsigned int i; + + /* skip past our ';' separator. */ + for (i = strlen (opcode->constraints), p = opcode->constraints; i > 0; + i--, p++) + { + if (*p == ';') + { + p++; + break; + } + } + input_line = skip_whitespace (input_line); + char *macro_inline = input_line; + + /* Loop though the macro's opcode list and apply operands to + each real opcode. */ + for (i = 0; *p && i < (opcode->size / 2); i++) + { + /* Loop though macro operand list. */ + input_line = macro_inline; /* Rewind. */ + p = extract_word (p, op_name, 10); + + if (!(opcode_handle = (struct xgate_opcode_handle *) + hash_find (xgate_hash, op_name))) + { + as_bad (_(": processing macro, real opcode handle" + " not found in hash")); + break; + } + else + { + operandCount = xgate_get_operands(input_line, new_operands); + macro_opcode = xgate_find_match (opcode_handle, + opcode_handle->number_of_modes, new_operands, + operandCount); + xgate_scan_operands (macro_opcode, new_operands); + } + } + } + } + autoHiLo = 0; + input_line = saved_input_line; +} + +/* Force truly undefined symbols to their maximum size, and generally set up + the frag list to be relaxed. */ + +int +md_estimate_size_before_relax (fragS *fragp, asection *seg) +{ + /* If symbol is undefined or located in a different section, + select the largest supported relocation. */ + relax_substateT subtype; + relax_substateT rlx_state[] = { 0, 2 }; + + for (subtype = 0; subtype < ARRAY_SIZE (rlx_state); subtype += 2) + { + if (fragp->fr_subtype == rlx_state[subtype] + && (!S_IS_DEFINED (fragp->fr_symbol) + || seg != S_GET_SEGMENT (fragp->fr_symbol))) + { + fragp->fr_subtype = rlx_state[subtype + 1]; + break; + } + } + + if (fragp->fr_subtype >= ARRAY_SIZE (md_relax_table)) + abort (); + + return md_relax_table[fragp->fr_subtype].rlx_length; +} + + +/* Relocation, relaxation and frag conversions. */ + +/* PC-relative offsets are relative to the start of the + next instruction. That is, the address of the offset, plus its + size, since the offset is always the last part of the insn. */ + +long +md_pcrel_from (fixS * fixP) +{ + return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address; +} + +/* If while processing a fixup, a reloc really needs to be created + then it is done here. */ + +arelent * +tc_gen_reloc (asection * section ATTRIBUTE_UNUSED, fixS * fixp) +{ + arelent * reloc; + + reloc = (arelent *) xmalloc (sizeof(arelent)); + reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof(asymbol *)); + *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); + reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; + + if (fixp->fx_r_type == 0) + reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_16); + else + reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type); + + if (reloc->howto == (reloc_howto_type *) NULL) + { + as_bad_where (fixp->fx_file, fixp->fx_line, _ + ("Relocation %d is not supported by object file format."), + (int) fixp->fx_r_type); + return NULL; + } + + /* Since we use Rel instead of Rela, encode the vtable entry to be + used in the relocation's section offset. */ + if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + reloc->address = fixp->fx_offset; + reloc->addend = 0; + return reloc; +} + +/* Patch the instruction with the resolved operand. Elf relocation + info will also be generated to take care of linker/loader fixups. + The XGATE addresses only 16-bit addresses.The BFD_RELOC_32 is necessary + for the support of --gstabs. */ + +void +md_apply_fix (fixS * fixP, valueT * valP, segT seg ATTRIBUTE_UNUSED) +{ + char *where; + long value = *valP; + int opcode = 0; + ldiv_t result; + + /* If the fixup is done mark it done so no further symbol resolution + will take place. */ + 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.")); + + where = fixP->fx_frag->fr_literal + fixP->fx_where; + opcode = bfd_getl16 (where); + int mask = 0; + + switch (fixP->fx_r_type) + { + case R_XGATE_PCREL_9: + if (value < -512 || value > 511) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Value %ld too large for 9-bit PC-relative branch."), + value); + result = ldiv (value, 2); /* from bytes to words */ + value = result.quot; + if (result.rem) + as_bad_where (fixP->fx_file, fixP->fx_line, _ + ("Value %ld not aligned by 2 for 9-bit" + " PC-relative branch."), value); + /* Clip into 8-bit field. + FIXME I'm sure there is a more proper place for this. */ + mask = 0x1FF; + value &= mask; + number_to_chars_bigendian (where, (opcode | value), 2); + break; + case R_XGATE_PCREL_10: + if (value < -1024 || value > 1023) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Value %ld too large for 10-bit PC-relative branch."), + value); + result = ldiv (value, 2); /* from bytes to words */ + value = result.quot; + if (result.rem) + as_bad_where (fixP->fx_file, fixP->fx_line, _ + ("Value %ld not aligned by 2 for 10-bit" + " PC-relative branch."), value); + /* Clip into 9-bit field. + FIXME I'm sure there is a more proper place for this. */ + mask = 0x3FF; + value &= mask; + number_to_chars_bigendian (where, (opcode | value), 2); + break; + case BFD_RELOC_XGATE_IMM8_HI: + if (value < -65537 || value > 65535) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Value out of 16-bit range.")); + value >>= 8; + value &= 0x00ff; + bfd_putb16 ((bfd_vma) value | opcode, (void *) where); + break; + case BFD_RELOC_XGATE_24: + case BFD_RELOC_XGATE_IMM8_LO: + if (value < -65537 || value > 65535) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Value out of 16-bit range.")); + value &= 0x00ff; + bfd_putb16 ((bfd_vma) value | opcode, (void *) where); + break; + case BFD_RELOC_XGATE_IMM3: + if (value < 0 || value > 7) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Value out of 3-bit range.")); + value <<= 8; /* make big endian */ + number_to_chars_bigendian (where, (opcode | value), 2); + break; + case BFD_RELOC_XGATE_IMM4: + if (value < 0 || value > 15) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Value out of 4-bit range.")); + value <<= 4; /* align the operand bits */ + number_to_chars_bigendian (where, (opcode | value), 2); + break; + case BFD_RELOC_XGATE_IMM5: + if (value < 0 || value > 31) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Value out of 5-bit range.")); + value <<= 5; /* align the operand bits */ + number_to_chars_bigendian (where, (opcode | value), 2); + break; + case BFD_RELOC_8: + ((bfd_byte *) where)[0] = (bfd_byte) value; + break; + case BFD_RELOC_32: + bfd_putb32 ((bfd_vma) value, (unsigned char *) where); + break; + case BFD_RELOC_16: + bfd_putb16 ((bfd_vma) value, (unsigned char *) where); + break; + default: + as_fatal (_("Line %d: unknown relocation type: 0x%x."), fixP->fx_line, + fixP->fx_r_type); + break; + } +} + +/* See whether we need to force a relocation into the output file. */ + +int +tc_xgate_force_relocation (fixS * fixP) +{ + if (fixP->fx_r_type == BFD_RELOC_XGATE_RL_GROUP) + return 1; + return generic_force_reloc (fixP); +} + +/* Here we decide which fixups can be adjusted to make them relative + to the beginning of the section instead of the symbol. Basically + we need to make sure that the linker relaxation is done + correctly, so in some cases we force the original symbol to be + used. */ + +int +tc_xgate_fix_adjustable (fixS * fixP) +{ + switch (fixP->fx_r_type) + { + /* For the linker relaxation to work correctly, these relocs + need to be on the symbol itself. */ + case BFD_RELOC_16: + case BFD_RELOC_XGATE_RL_JUMP: + case BFD_RELOC_XGATE_RL_GROUP: + case BFD_RELOC_VTABLE_INHERIT: + case BFD_RELOC_VTABLE_ENTRY: + case BFD_RELOC_32: + return 0; + default: + return 1; + } +} + +void +md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED, + asection * sec ATTRIBUTE_UNUSED, + fragS * fragP ATTRIBUTE_UNUSED) +{ + as_bad (("md_convert_frag not implemented yet")); + abort (); +} + +/* Set the ELF specific flags. */ + +void +xgate_elf_final_processing (void) +{ + elf_flags |= EF_XGATE_MACH; + elf_elfheader (stdoutput)->e_flags &= ~EF_XGATE_ABI; + elf_elfheader (stdoutput)->e_flags |= elf_flags; +} + +static inline char * +skip_whitespace (char *s) +{ + while (*s == ' ' || *s == '\t' || *s == '(' || *s == ')') + s++; + + return s; +} + +/* Extract a word (continuous alpha-numeric chars) from the input line. */ + +static char * +extract_word (char *from, char *to, int limit) +{ + char *op_end; + int size = 0; + + /* Drop leading whitespace. */ + from = skip_whitespace (from); + *to = 0; + /* Find the op code end. */ + for (op_end = from; *op_end != 0 && is_part_of_name (*op_end);) + { + to[size++] = *op_end++; + if (size + 1 >= limit) + break; + } + to[size] = 0; + return op_end; +} + +static char * +xgate_new_instruction (int size) +{ + char *f = frag_more (size); + dwarf2_emit_insn (size); + return f; +} + +static unsigned short +xgate_apply_operand (unsigned short new_mask, + unsigned short *availiable_mask_bits, + unsigned short mask, + unsigned char n_bits) +{ + unsigned short n_shifts; + unsigned int n_drop_bits; + + /* Shift until you find an available operand bit "1" and record + the number of shifts. */ + for (n_shifts = 0; + !(*availiable_mask_bits & SIXTEENTH_BIT) && n_shifts < 16; + n_shifts++) + *availiable_mask_bits <<= 1; + + /* Shift for the number of bits your operand requires while bits + are available. */ + for (n_drop_bits = n_bits; + n_drop_bits && (*availiable_mask_bits & SIXTEENTH_BIT); + --n_drop_bits) + *availiable_mask_bits <<= 1; + + if (n_drop_bits) + as_bad (_(":operand has too many bits")); + *availiable_mask_bits >>= n_shifts + n_bits; + if ((n_drop_bits == 0) && (*availiable_mask_bits == 0)) + { + oper_check = 1; /* flag operand check as good */ + } + new_mask <<= N_BITS_IN_WORD - (n_shifts + n_bits); + mask |= new_mask; + return mask; +} + +/* Parse ordinary expression. */ + +static char * +xgate_parse_exp (char *s, expressionS * op) +{ + input_line_pointer = s; + expression(op); + if (op->X_op == O_absent) + as_bad (_("missing operand")); + return input_line_pointer; +} + +static int +cmp_opcode (struct xgate_opcode *op1, struct xgate_opcode *op2) +{ + return strcmp (op1->name, op2->name); +} + +static struct xgate_opcode * +xgate_find_match (struct xgate_opcode_handle *opcode_handle, + int numberOfModes, s_operand oprs[], unsigned int operandCount) +{ + int i; + + if (numberOfModes == 0) + return opcode_handle->opc0[0]; + + for (i = 0; i <= numberOfModes; i++) + { + switch (operandCount) + { + case 0: + if (!strcmp(opcode_handle->opc0[i]->constraints, XGATE_OP_INH)) + return opcode_handle->opc0[i]; + break; + case 1: + if (oprs[0].reg >= REG_R0 && oprs[0].reg <= REG_R7) + if (!strcmp(opcode_handle->opc0[i]->constraints, XGATE_OP_MON)) + return opcode_handle->opc0[i]; + if (!strcmp(opcode_handle->opc0[i]->constraints, XGATE_OP_DYA_MON)) + return opcode_handle->opc0[i]; + if (oprs[0].reg == REG_NONE) + if (!strcmp(opcode_handle->opc0[i]->constraints, XGATE_OP_IMM3)) + return opcode_handle->opc0[i]; + break; + case 2: + if (oprs[0].reg >= REG_R0 && oprs[0].reg <= REG_R7) + { + if (oprs[1].reg >= REG_R0 && oprs[1].reg <= REG_R7) + if (!strcmp(opcode_handle->opc0[i]->constraints, XGATE_OP_DYA)) + return opcode_handle->opc0[i]; + if (oprs[1].reg == REG_CCR) + if (!strcmp(opcode_handle->opc0[i]->constraints, + XGATE_OP_MON_R_C)) + return opcode_handle->opc0[i]; + if (oprs[1].reg == REG_PC) + if (!strcmp(opcode_handle->opc0[i]->constraints, + XGATE_OP_MON_R_P)) + return opcode_handle->opc0[i]; + if (oprs[1].reg == REG_NONE) + if (!strcmp(opcode_handle->opc0[i]->constraints, XGATE_OP_IMM16) + || !strcmp(opcode_handle->opc0[i]->constraints, XGATE_OP_IMM8) + || !strcmp(opcode_handle->opc0[i]->constraints, XGATE_OP_IMM4) + || !strcmp(opcode_handle->opc0[i]->constraints, + XGATE_OP_IMM16mADD) + || !strcmp(opcode_handle->opc0[i]->constraints, + XGATE_OP_IMM16mAND) + || !strcmp(opcode_handle->opc0[i]->constraints, + XGATE_OP_IMM16mCPC) + || !strcmp(opcode_handle->opc0[i]->constraints, + XGATE_OP_IMM16mSUB) + || !strcmp(opcode_handle->opc0[i]->constraints, + XGATE_OP_IMM16mLDW)) + return opcode_handle->opc0[i]; + } + if (oprs[0].reg == REG_CCR) + if (!strcmp(opcode_handle->opc0[i]->constraints, XGATE_OP_MON_C_R)) + return opcode_handle->opc0[i]; + break; + case 3: + if (oprs[0].reg >= REG_R0 && oprs[0].reg <= REG_R7) + { + if (oprs[1].reg >= REG_R0 && oprs[1].reg <= REG_R7) + { + if (oprs[2].reg >= REG_R0 && oprs[2].reg <= REG_R7) + { + if (!strcmp(opcode_handle->opc0[i]->constraints, + XGATE_OP_IDR) + || !strcmp(opcode_handle->opc0[i]->constraints, + XGATE_OP_TRI)) + return opcode_handle->opc0[i]; + } + + if (oprs[2].reg == REG_NONE) + if (!strcmp(opcode_handle->opc0[i]->constraints, + XGATE_OP_IDO5)) + return opcode_handle->opc0[i]; + } + } + break; + default: + as_bad(_("unknown operand count")); + break; + } + } + return NULL ; +} + +/* Because we are dealing with two different core that view the system + memory with different offsets, we must differentiate what core a + symbol belongs to, in order for the linker to cross-link. */ + +int +xgate_frob_symbol (symbolS *sym) +{ + asymbol *bfdsym; + elf_symbol_type *elfsym; + + bfdsym = symbol_get_bfdsym (sym); + elfsym = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym); + + gas_assert(elfsym); + + /* Mark the symbol as being *from XGATE */ + elfsym->internal_elf_sym.st_target_internal = 1; + + return 0; +} + +static unsigned int +xgate_get_operands (char *line, s_operand oprs[]) +{ + int num_operands; + + /* If there are no operands, then it must be inherent. */ + if (*line == 0 || *line == '\n' || *line == '\r') + return 0; + + for (num_operands = 0; strlen (line) && (num_operands < MAX_NUM_OPERANDS); + num_operands++) + { + line = skip_whitespace (line); + if (*line == '#') + line++; + + oprs[num_operands].mod = xgate_determine_modifiers (&line); + + if ((oprs[num_operands].reg = reg_name_search (line)) == REG_NONE) + line = xgate_parse_exp (line, &oprs[num_operands].exp); + + /* skip to next operand */ + while (*line != 0) + { + if (*line == ',') + { + line++; + break; + } + line++; + } + } + if (num_operands > MAX_NUM_OPERANDS) + return 0; + return num_operands; +} + +/* reg_name_search() finds the register number given its name. + Returns the register number or REG_NONE on failure. */ +static register_id +reg_name_search (char *name) +{ + if (strncasecmp (name, "r0", 2) == 0) + return REG_R0; + if (strncasecmp (name, "r1", 2) == 0) + return REG_R1; + if (strncasecmp (name, "r2", 2) == 0) + return REG_R2; + if (strncasecmp (name, "r3", 2) == 0) + return REG_R3; + if (strncasecmp (name, "r4", 2) == 0) + return REG_R4; + if (strncasecmp (name, "r5", 2) == 0) + return REG_R5; + if (strncasecmp (name, "r6", 2) == 0) + return REG_R6; + if (strncasecmp (name, "r7", 2) == 0) + return REG_R7; + if (strncasecmp (name, "pc", 2) == 0) + return REG_PC; + if (strncasecmp (name, "ccr", 3) == 0) + return REG_CCR; + return REG_NONE; +} + +/* Parse operand modifiers such as inc/dec/hi/low. */ + +static op_modifiers +xgate_determine_modifiers(char **line) +{ + char *local_line = line[0]; + + if (strncasecmp (local_line, "%hi", 3) == 0) + { + *line += 3; + return MOD_LOAD_HIGH; + } + if (strncasecmp (local_line, "%lo", 3) == 0) + { + *line += 3; + return MOD_LOAD_LOW; + } + if (*(local_line + 2) == '+') + return MOD_POSTINC; + if (strncasecmp (local_line, "-r", 2) == 0) + { + *line += 1; + return MOD_PREDEC; + } + return MOD_NONE; +} + +/* Parse instruction operands. */ + +static void +xgate_scan_operands (struct xgate_opcode *opcode, s_operand oprs[]) +{ + char *frag = xgate_new_instruction (opcode->size); + int where = frag - frag_now->fr_literal; + char *op = opcode->constraints; + unsigned int bin = (int) opcode->bin_opcode; + unsigned short oper_mask = 0; + int operand_bit_length = 0; + unsigned int operand = 0; + char n_operand_bits = 0; + char first_operand_equals_second = 0; + int i = 0; + char c = 0; + + /* Generate available operand bits mask. */ + for (i = 0; (c = opcode->format[i]); i++) + { + if (ISDIGIT (c) || (c == 's')) + { + oper_mask <<= 1; + } + else + { + oper_mask <<= 1; + oper_mask += 1; + n_operand_bits++; + } + } + + /* Parse first operand. */ + if (*op) + { + if (*op == '=') + { + first_operand_equals_second = 1; + ++op; + } + operand = xgate_parse_operand (opcode, &operand_bit_length, where, + &op, oprs[0]); + ++op; + bin = xgate_apply_operand (operand, &oper_mask, bin, operand_bit_length); + + if(first_operand_equals_second) + bin = xgate_apply_operand (operand, &oper_mask, bin, + operand_bit_length); + /* Parse second operand. */ + if (*op) + { + if (*op == ',') + ++op; + if (first_operand_equals_second) + { + bin = xgate_apply_operand (operand, &oper_mask, bin, + operand_bit_length); + ++op; + } + else + { + operand = xgate_parse_operand (opcode, &operand_bit_length, where, + &op, oprs[1]); + bin = xgate_apply_operand (operand, &oper_mask, bin, + operand_bit_length); + ++op; + } + } + /* Parse the third register. */ + if (*op) + { + if (*op == ',') + ++op; + operand = xgate_parse_operand (opcode, &operand_bit_length, where, + &op, oprs[2]); + bin = xgate_apply_operand (operand, &oper_mask, bin, + operand_bit_length); + } + } + if (opcode->size == 2 && fixup_required) + { + bfd_putl16 (bin, frag); + } + else if ( !strcmp (opcode->constraints, XGATE_OP_REL9) + || !strcmp (opcode->constraints, XGATE_OP_REL10)) + { + /* Write our data to a frag for further processing. */ + bfd_putl16 (opcode->bin_opcode, frag); + } + else + { + /* Apply operand mask(s)to bin opcode and write the output. */ + /* Since we are done write this frag in xgate BE format. */ + number_to_chars_bigendian (frag, bin, opcode->size); + } + prev = bin; + return; +} + +static unsigned int +xgate_parse_operand (struct xgate_opcode *opcode, + int *bit_width, + int where, + char **op_con, + s_operand operand) +{ + char *op_constraint = *op_con; + unsigned int op_mask = 0; + unsigned int pp_fix = 0; + unsigned short max_size = 0; + int i; + + *bit_width = 0; + /* Reset. */ + + switch (*op_constraint) + { + case '+': /* Indexed register operand +/- or plain r. */ + /* Default to neither inc or dec. */ + pp_fix = 0; + *bit_width = 5; + + if (operand.reg == REG_NONE) + as_bad (_(": expected register name r0-r7 ") ); + op_mask = operand.reg; + if(operand.mod == MOD_POSTINC) + pp_fix = INCREMENT; + if(operand.mod == MOD_PREDEC) + pp_fix = DECREMENT; + op_mask <<= 2; + op_mask |= pp_fix; + break; + + case 'r': /* Register operand. */ + if (operand.reg == REG_NONE) + as_bad (_(": expected register name r0-r7 ")); + + *bit_width = 3; + + op_mask = operand.reg; + break; + + case 'i': /* Immediate value or expression expected. */ + /* Advance the original format pointer. */ + (*op_con)++; + op_constraint++; + if (ISDIGIT (*op_constraint)) + *bit_width = (int) *op_constraint - '0'; + else if (*op_constraint == 'a') + *bit_width = 0x0A; + else if (*op_constraint == 'f') + *bit_width = 0x0F; + + /* http://tigcc.ticalc.org/doc/gnuasm.html#SEC31 */ + if (operand.exp.X_op == O_constant) + { + op_mask = operand.exp.X_add_number; + if (((opcode->name[strlen (opcode->name) - 1] == 'l') && autoHiLo) + || operand.mod == MOD_LOAD_LOW) + op_mask &= 0x00FF; + else if (((opcode->name[strlen (opcode->name) - 1]) == 'h' + && autoHiLo) || operand.mod == MOD_LOAD_HIGH) + op_mask >>= 8; + + /* Make sure it fits. */ + for (i = *bit_width; i; i--) + { + max_size <<= 1; + max_size += 1; + } + if (op_mask > max_size) + as_bad (_(":operand value(%d) too big for constraint"), op_mask); + } + else + { + /* Should be BFD_RELOC_XGATE_IMM8_LO instead of BFD_RELOC_XGATE_24 + TODO fix. */ + fixup_required = 1; + if (*op_constraint == '8') + { + if (((opcode->name[strlen (opcode->name) - 1] == 'l') + && autoHiLo) || operand.mod == MOD_LOAD_LOW) + fix_new_exp (frag_now, where, 2, &operand.exp, FALSE, + BFD_RELOC_XGATE_24); + else if (((opcode->name[strlen (opcode->name) - 1]) == 'h' + && autoHiLo) || operand.mod == MOD_LOAD_HIGH ) + fix_new_exp (frag_now, where, 2, &operand.exp, FALSE, + BFD_RELOC_XGATE_IMM8_HI); + else + as_bad (_("you must use a hi/lo directive or 16-bit macro " + "to load a 16-bit value.")); + } + else if (*op_constraint == '5') + fix_new_exp (frag_now, where, 2, &operand.exp, FALSE, + BFD_RELOC_XGATE_IMM5); + else if (*op_constraint == '4') + fix_new_exp (frag_now, where, 2, &operand.exp, FALSE, + BFD_RELOC_XGATE_IMM4); + else if (*op_constraint == '3') + fix_new_exp (frag_now, where, 2, &operand.exp, FALSE, + BFD_RELOC_XGATE_IMM3); + else + as_bad (_(":unknown relocation constraint size")); + } + break; + + case 'c': /* CCR register expected. */ + *bit_width = 0; + if (operand.reg != REG_CCR) + as_bad (_(": expected register name ccr ")); + break; + + case 'p': /* PC register expected. */ + *bit_width = 0; + if (operand.reg != REG_PC) + as_bad (_(": expected register name pc ")); + break; + + case 'b': /* Branch expected. */ + (*op_con)++; + op_constraint++; + + if (operand.exp.X_op != O_register) + { + if (*op_constraint == '9') + fix_new_exp (frag_now, where, 2, &operand.exp, TRUE, + R_XGATE_PCREL_9); + else if (*op_constraint == 'a') + fix_new_exp (frag_now, where, 2, &operand.exp, TRUE, + R_XGATE_PCREL_10); + } + else + as_fatal (_("Operand `%x' not recognized in fixup8."), + operand.exp.X_op); + break; + case '?': + break; + + default: + as_bad (_("unknown constraint `%c'"), *op_constraint); + break; + } + return op_mask; +} |