|  | /* tc-xgate.c -- Assembler code for Freescale XGATE | 
|  | Copyright (C) 2010-2017 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); | 
|  |  | 
|  | 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 = 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 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_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 = 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_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; | 
|  | } |