/* Adapteva epiphany opcode support. -*- C -*- Copyright 2011 Free Software Foundation, Inc. Contributed by Embecosm on behalf of Adapteva, Inc. This file is part of the GNU Binutils and of GDB. This program 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 of the License, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ /* Each section is delimited with start and end markers. -opc.h additions use: "-- opc.h" -opc.c additions use: "-- opc.c" -asm.c additions use: "-- asm.c" -dis.c additions use: "-- dis.c" -ibd.h additions use: "-- ibd.h". */ /* -- opc.h */ /* enumerate relaxation types for gas. */ typedef enum epiphany_relax_types { EPIPHANY_RELAX_NONE=0, EPIPHANY_RELAX_NEED_RELAXING, EPIPHANY_RELAX_BRANCH_SHORT, /* Fits into +127..-128 */ EPIPHANY_RELAX_BRANCH_LONG, /* b/bl/b +-2*16 */ EPIPHANY_RELAX_ARITH_SIMM3, /* add/sub -7..3 */ EPIPHANY_RELAX_ARITH_SIMM11, /* add/sub -2**11-1 .. 2**10-1 */ EPIPHANY_RELAX_MOV_IMM8, /* mov r,imm8 */ EPIPHANY_RELAX_MOV_IMM16, /* mov r,imm16 */ EPIPHANY_RELAX_LDST_IMM3, /* (ldr|str)* r,[r,disp3] */ EPIPHANY_RELAX_LDST_IMM11 /* (ldr|str)* r,[r,disp11] */ } EPIPHANY_RELAX_TYPES; /* Override disassembly hashing... */ /* Can only depend on instruction having 4 decode bits which gets us to the major groups of 16/32 instructions. */ #undef CGEN_DIS_HASH_SIZE #if 1 /* hash code on the 4 LSBs */ #define CGEN_DIS_HASH_SIZE 16 #define CGEN_DIS_HASH(buf, value) ((*buf) & 0xf) #else #define CGEN_DIS_HASH_SIZE 1 #define CGEN_DIS_HASH(buf, value) 0 #endif extern const char * parse_shortregs (CGEN_CPU_DESC cd, const char ** strp, CGEN_KEYWORD * keywords, long * valuep); extern const char * parse_branch_addr (CGEN_CPU_DESC cd, const char ** strp, int opindex, int opinfo, enum cgen_parse_operand_result * resultp, bfd_vma *valuep); /* Allows reason codes to be output when assembler errors occur. */ #define CGEN_VERBOSE_ASSEMBLER_ERRORS /* -- opc.c */ /* -- asm.c */ const char * parse_shortregs (CGEN_CPU_DESC cd, const char ** strp, CGEN_KEYWORD * keywords, long * regno) { const char * errmsg; /* Parse register. */ errmsg = cgen_parse_keyword (cd, strp, keywords, regno); if (errmsg) return errmsg; if (*regno > 7) errmsg = _("register unavailable for short instructions"); return errmsg; } static const char * parse_simm_not_reg (CGEN_CPU_DESC, const char **, int, long *); static const char * parse_uimm_not_reg (CGEN_CPU_DESC cd, const char ** strp, int opindex, unsigned long * valuep) { long * svalp = (void *) valuep; return parse_simm_not_reg (cd, strp, opindex, svalp); } /* Handle simm3/simm11/imm3/imm12. */ static const char * parse_simm_not_reg (CGEN_CPU_DESC cd, const char ** strp, int opindex, long * valuep) { const char * errmsg; int sign = 0; int bits = 0; switch (opindex) { case EPIPHANY_OPERAND_SIMM3: sign = 1; bits = 3; break; case EPIPHANY_OPERAND_SIMM11: sign = 1; bits = 11; break; case EPIPHANY_OPERAND_DISP3: sign = 0; bits = 3; break; case EPIPHANY_OPERAND_DISP11: /* Load/store displacement is a sign-magnitude 12 bit value. */ sign = 0; bits = 11; break; } /* First try to parse as a register name and reject the operand. */ errmsg = cgen_parse_keyword (cd, strp, & epiphany_cgen_opval_gr_names,valuep); if (!errmsg) return _("register name used as immediate value"); errmsg = (sign ? cgen_parse_signed_integer (cd, strp, opindex, valuep) : cgen_parse_unsigned_integer (cd, strp, opindex, (unsigned long *) valuep)); if (errmsg) return errmsg; if (sign) errmsg = cgen_validate_signed_integer (*valuep, -((1L << bits) - 1), (1 << (bits - 1)) - 1); else errmsg = cgen_validate_unsigned_integer (*valuep, 0, (1L << bits) - 1); return errmsg; } static const char * parse_postindex (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, const char ** strp, int opindex ATTRIBUTE_UNUSED, unsigned long *valuep) { if (**strp == '#') ++*strp; /* Skip leading hashes. */ if (**strp == '-') { *valuep = 1; ++*strp; } else if (**strp == '+') { *valuep = 0; ++*strp; } else *valuep = 0; return NULL; } static const char * parse_imm8 (CGEN_CPU_DESC cd, const char ** strp, int opindex, bfd_reloc_code_real_type code, enum cgen_parse_operand_result * result_type, bfd_vma * valuep) { const char * errmsg; enum cgen_parse_operand_result rt; long dummyval; if (!result_type) result_type = &rt; code = BFD_RELOC_NONE; if (!cgen_parse_keyword (cd, strp, &epiphany_cgen_opval_gr_names, &dummyval) || !cgen_parse_keyword (cd, strp, &epiphany_cgen_opval_cr_names, &dummyval)) /* Don't treat "mov ip,ip" as a move-immediate. */ return _("register source in immediate move"); errmsg = cgen_parse_address (cd, strp, opindex, code, result_type, valuep); if (errmsg) return errmsg; if (*result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER) errmsg = cgen_validate_unsigned_integer (*valuep, 0, 0xff); else errmsg = _("byte relocation unsupported"); *valuep &= 0xff; return errmsg; } static const char * MISSING_CLOSE_PARENTHESIS = N_("missing `)'"); static const char * parse_imm16 (CGEN_CPU_DESC cd, const char ** strp, int opindex, bfd_reloc_code_real_type code ATTRIBUTE_UNUSED, enum cgen_parse_operand_result * result_type, bfd_vma * valuep) { const char * errmsg; enum cgen_parse_operand_result rt; long dummyval; if (!result_type) result_type = &rt; if (strncasecmp (*strp, "%high(", 6) == 0) { *strp += 6; errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_EPIPHANY_HIGH, result_type, valuep); if (**strp != ')') return MISSING_CLOSE_PARENTHESIS; ++*strp; *valuep >>= 16; } else if (strncasecmp (*strp, "%low(", 5) == 0) { *strp += 5; errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_EPIPHANY_LOW, result_type, valuep); if (**strp != ')') return MISSING_CLOSE_PARENTHESIS; ++*strp; } else if (!cgen_parse_keyword (cd, strp, &epiphany_cgen_opval_gr_names, &dummyval) || !cgen_parse_keyword (cd, strp, &epiphany_cgen_opval_cr_names, &dummyval)) /* Don't treat "mov ip,ip" as a move-immediate. */ return _("register source in immediate move"); else errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_16, result_type, valuep); if (!errmsg && result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER) errmsg = cgen_validate_unsigned_integer (*valuep, 0, 0xffff); *valuep &= 0xffff; return errmsg; } const char * parse_branch_addr (CGEN_CPU_DESC cd, const char ** strp, int opindex, int opinfo ATTRIBUTE_UNUSED, enum cgen_parse_operand_result * resultp ATTRIBUTE_UNUSED, bfd_vma *valuep ATTRIBUTE_UNUSED) { const char * errmsg; enum cgen_parse_operand_result result_type; bfd_reloc_code_real_type code = BFD_RELOC_NONE; bfd_vma value; switch (opindex) { case EPIPHANY_OPERAND_SIMM24: code = BFD_RELOC_EPIPHANY_SIMM24; break; case EPIPHANY_OPERAND_SIMM8: code = BFD_RELOC_EPIPHANY_SIMM8; break; default: errmsg = _("ABORT: unknown operand"); return errmsg; } errmsg = cgen_parse_address (cd, strp, opindex, code, &result_type, &value); if (errmsg == NULL) { if (result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER) { /* Act as if we had done a PC-relative branch, ala .+num. */ char buf[20]; const char * bufp = (const char *) buf; sprintf (buf, ".+%ld", (long) value); errmsg = cgen_parse_address (cd, &bufp, opindex, code, &result_type, &value); } if (result_type == CGEN_PARSE_OPERAND_RESULT_QUEUED) { /* This will happen for things like (s2-s1) where s2 and s1 are labels. */ /* Nothing further to be done. */ } else errmsg = _("Not a pc-relative address."); } return errmsg; } /* -- dis.c */ #define CGEN_PRINT_INSN epiphany_print_insn static int epiphany_print_insn (CGEN_CPU_DESC cd, bfd_vma pc, disassemble_info *info) { bfd_byte buf[CGEN_MAX_INSN_SIZE]; int buflen; int status; info->bytes_per_chunk = 2; /* Attempt to read the base part of the insn. */ info->bytes_per_line = buflen = cd->base_insn_bitsize / 8; status = (*info->read_memory_func) (pc, buf, buflen, info); /* Try again with the minimum part, if min < base. */ if (status != 0 && (cd->min_insn_bitsize < cd->base_insn_bitsize)) { info->bytes_per_line = buflen = cd->min_insn_bitsize / 8; status = (*info->read_memory_func) (pc, buf, buflen, info); } if (status != 0) { (*info->memory_error_func) (status, pc, info); return -1; } return print_insn (cd, pc, info, buf, buflen); } static void print_postindex (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, void * dis_info, long value, unsigned int attrs ATTRIBUTE_UNUSED, bfd_vma pc ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED) { disassemble_info *info = (disassemble_info *) dis_info; (*info->fprintf_func) (info->stream, value ? "-" : "+"); } static void print_simm_not_reg (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, void * dis_info, long value, unsigned int attrs ATTRIBUTE_UNUSED, bfd_vma pc ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED) { print_address (cd, dis_info, value, attrs, pc, length); } static void print_uimm_not_reg (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, void * dis_info, unsigned long value, unsigned int attrs ATTRIBUTE_UNUSED, bfd_vma pc ATTRIBUTE_UNUSED, int length ATTRIBUTE_UNUSED) { disassemble_info *info = (disassemble_info *)dis_info; if (value & 0x800) (*info->fprintf_func) (info->stream, "-"); value &= 0x7ff; print_address (cd, dis_info, value, attrs, pc, length); } /* -- */