| /* tc-xgate.c -- Assembler code for Freescale XGATE |
| Copyright (C) 2010-2021 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 htab_t 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); |
| |
| const char * |
| md_atof (int type, char *litP, int *sizeP) |
| { |
| return ieee_md_atof (type, litP, sizeP, true); |
| } |
| |
| int |
| md_parse_option (int c, const 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; |
| const 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 = XNEWVEC (struct xgate_opcode, xgate_num_opcodes); |
| |
| 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 = XNEWVEC (struct xgate_opcode_handle, number_of_op_handles); |
| |
| /* Insert unique opcode names into hash table, aliasing duplicates. */ |
| xgate_hash = str_htab_create (); |
| |
| 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; |
| str_hash_insert (xgate_hash, op_handles[j].name, &op_handles[j], 0); |
| } |
| 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 NULL; |
| } |
| |
| /* 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_section_alignment (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")); |
| |
| opcode_handle = (struct xgate_opcode_handle *) str_hash_find (xgate_hash, |
| op_name); |
| if (!opcode_handle) |
| 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); |
| |
| opcode_handle |
| = (struct xgate_opcode_handle *) str_hash_find (xgate_hash, |
| op_name); |
| if (!opcode_handle) |
| { |
| 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 = XNEW (arelent); |
| reloc->sym_ptr_ptr = XNEW (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_subtract (fixP); |
| |
| where = fixP->fx_frag->fr_literal + fixP->fx_where; |
| opcode = bfd_getl16 (where); |
| int mask = 0; |
| |
| switch (fixP->fx_r_type) |
| { |
| case BFD_RELOC_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 BFD_RELOC_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 (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, |
| BFD_RELOC_XGATE_PCREL_9); |
| else if (*op_constraint == 'a') |
| fix_new_exp (frag_now, where, 2, &operand.exp, true, |
| BFD_RELOC_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; |
| } |