1/* M32R opcode support. -*- C -*- 2 3 Copyright 1998, 1999, 2000, 2001, 2004, 2005, 2007, 2009 4 Free Software Foundation, Inc. 5 6 Contributed by Red Hat Inc; developed under contract from 7 Mitsubishi Electric Corporation. 8 9 This file is part of the GNU Binutils. 10 11 This program is free software; you can redistribute it and/or modify 12 it under the terms of the GNU General Public License as published by 13 the Free Software Foundation; either version 3 of the License, or 14 (at your option) any later version. 15 16 This program is distributed in the hope that it will be useful, 17 but WITHOUT ANY WARRANTY; without even the implied warranty of 18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 GNU General Public License for more details. 20 21 You should have received a copy of the GNU General Public License 22 along with this program; if not, write to the Free Software 23 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, 24 MA 02110-1301, USA. */ 25 26 27/* This file is an addendum to m32r.cpu. Heavy use of C code isn't 28 appropriate in .cpu files, so it resides here. This especially applies 29 to assembly/disassembly where parsing/printing can be quite involved. 30 Such things aren't really part of the specification of the cpu, per se, 31 so .cpu files provide the general framework and .opc files handle the 32 nitty-gritty details as necessary. 33 34 Each section is delimited with start and end markers. 35 36 <arch>-opc.h additions use: "-- opc.h" 37 <arch>-opc.c additions use: "-- opc.c" 38 <arch>-asm.c additions use: "-- asm.c" 39 <arch>-dis.c additions use: "-- dis.c" 40 <arch>-ibd.h additions use: "-- ibd.h" */ 41 42/* -- opc.h */ 43 44#undef CGEN_DIS_HASH_SIZE 45#define CGEN_DIS_HASH_SIZE 256 46#undef CGEN_DIS_HASH 47#if 0 48#define X(b) (((unsigned char *) (b))[0] & 0xf0) 49#define CGEN_DIS_HASH(buffer, value) \ 50(X (buffer) | \ 51 (X (buffer) == 0x40 || X (buffer) == 0xe0 || X (buffer) == 0x60 || X (buffer) == 0x50 ? 0 \ 52 : X (buffer) == 0x70 || X (buffer) == 0xf0 ? (((unsigned char *) (buffer))[0] & 0xf) \ 53 : X (buffer) == 0x30 ? ((((unsigned char *) (buffer))[1] & 0x70) >> 4) \ 54 : ((((unsigned char *) (buffer))[1] & 0xf0) >> 4))) 55#else 56#define CGEN_DIS_HASH(buffer, value) m32r_cgen_dis_hash (buffer, value) 57extern unsigned int m32r_cgen_dis_hash (const char *, CGEN_INSN_INT); 58#endif 59 60/* -- */ 61 62/* -- opc.c */ 63unsigned int 64m32r_cgen_dis_hash (const char * buf ATTRIBUTE_UNUSED, CGEN_INSN_INT value) 65{ 66 unsigned int x; 67 68 if (value & 0xffff0000) /* 32bit instructions. */ 69 value = (value >> 16) & 0xffff; 70 71 x = (value >> 8) & 0xf0; 72 if (x == 0x40 || x == 0xe0 || x == 0x60 || x == 0x50) 73 return x; 74 75 if (x == 0x70 || x == 0xf0) 76 return x | ((value >> 8) & 0x0f); 77 78 if (x == 0x30) 79 return x | ((value & 0x70) >> 4); 80 else 81 return x | ((value & 0xf0) >> 4); 82} 83 84/* -- */ 85 86/* -- asm.c */ 87static const char * MISSING_CLOSING_PARENTHESIS = N_("missing `)'"); 88 89/* Handle '#' prefixes (i.e. skip over them). */ 90 91static const char * 92parse_hash (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, 93 const char **strp, 94 int opindex ATTRIBUTE_UNUSED, 95 long *valuep ATTRIBUTE_UNUSED) 96{ 97 if (**strp == '#') 98 ++*strp; 99 return NULL; 100} 101 102/* Handle shigh(), high(). */ 103 104static const char * 105parse_hi16 (CGEN_CPU_DESC cd, 106 const char **strp, 107 int opindex, 108 unsigned long *valuep) 109{ 110 const char *errmsg; 111 enum cgen_parse_operand_result result_type; 112 bfd_vma value; 113 114 if (**strp == '#') 115 ++*strp; 116 117 if (strncasecmp (*strp, "high(", 5) == 0) 118 { 119 *strp += 5; 120 errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_M32R_HI16_ULO, 121 & result_type, & value); 122 if (**strp != ')') 123 return MISSING_CLOSING_PARENTHESIS; 124 ++*strp; 125 if (errmsg == NULL 126 && result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER) 127 { 128 value >>= 16; 129 value &= 0xffff; 130 } 131 *valuep = value; 132 return errmsg; 133 } 134 else if (strncasecmp (*strp, "shigh(", 6) == 0) 135 { 136 *strp += 6; 137 errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_M32R_HI16_SLO, 138 & result_type, & value); 139 if (**strp != ')') 140 return MISSING_CLOSING_PARENTHESIS; 141 ++*strp; 142 if (errmsg == NULL 143 && result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER) 144 { 145 value += 0x8000; 146 value >>= 16; 147 value &= 0xffff; 148 } 149 *valuep = value; 150 return errmsg; 151 } 152 153 return cgen_parse_unsigned_integer (cd, strp, opindex, valuep); 154} 155 156/* Handle low() in a signed context. Also handle sda(). 157 The signedness of the value doesn't matter to low(), but this also 158 handles the case where low() isn't present. */ 159 160static const char * 161parse_slo16 (CGEN_CPU_DESC cd, 162 const char ** strp, 163 int opindex, 164 long * valuep) 165{ 166 const char *errmsg; 167 enum cgen_parse_operand_result result_type; 168 bfd_vma value; 169 170 if (**strp == '#') 171 ++*strp; 172 173 if (strncasecmp (*strp, "low(", 4) == 0) 174 { 175 *strp += 4; 176 errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_M32R_LO16, 177 & result_type, & value); 178 if (**strp != ')') 179 return MISSING_CLOSING_PARENTHESIS; 180 ++*strp; 181 if (errmsg == NULL 182 && result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER) 183 value = ((value & 0xffff) ^ 0x8000) - 0x8000; 184 *valuep = value; 185 return errmsg; 186 } 187 188 if (strncasecmp (*strp, "sda(", 4) == 0) 189 { 190 *strp += 4; 191 errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_M32R_SDA16, 192 NULL, & value); 193 if (**strp != ')') 194 return MISSING_CLOSING_PARENTHESIS; 195 ++*strp; 196 *valuep = value; 197 return errmsg; 198 } 199 200 return cgen_parse_signed_integer (cd, strp, opindex, valuep); 201} 202 203/* Handle low() in an unsigned context. 204 The signedness of the value doesn't matter to low(), but this also 205 handles the case where low() isn't present. */ 206 207static const char * 208parse_ulo16 (CGEN_CPU_DESC cd, 209 const char **strp, 210 int opindex, 211 unsigned long *valuep) 212{ 213 const char *errmsg; 214 enum cgen_parse_operand_result result_type; 215 bfd_vma value; 216 217 if (**strp == '#') 218 ++*strp; 219 220 if (strncasecmp (*strp, "low(", 4) == 0) 221 { 222 *strp += 4; 223 errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_M32R_LO16, 224 & result_type, & value); 225 if (**strp != ')') 226 return MISSING_CLOSING_PARENTHESIS; 227 ++*strp; 228 if (errmsg == NULL 229 && result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER) 230 value &= 0xffff; 231 *valuep = value; 232 return errmsg; 233 } 234 235 return cgen_parse_unsigned_integer (cd, strp, opindex, valuep); 236} 237 238/* -- */ 239 240/* -- dis.c */ 241 242/* Print signed operands with '#' prefixes. */ 243 244static void 245print_signed_with_hash_prefix (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, 246 void * dis_info, 247 long value, 248 unsigned int attrs ATTRIBUTE_UNUSED, 249 bfd_vma pc ATTRIBUTE_UNUSED, 250 int length ATTRIBUTE_UNUSED) 251{ 252 disassemble_info *info = (disassemble_info *) dis_info; 253 254 (*info->fprintf_func) (info->stream, "#"); 255 (*info->fprintf_func) (info->stream, "%ld", value); 256} 257 258/* Print unsigned operands with '#' prefixes. */ 259 260static void 261print_unsigned_with_hash_prefix (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, 262 void * dis_info, 263 long value, 264 unsigned int attrs ATTRIBUTE_UNUSED, 265 bfd_vma pc ATTRIBUTE_UNUSED, 266 int length ATTRIBUTE_UNUSED) 267{ 268 disassemble_info *info = (disassemble_info *) dis_info; 269 270 (*info->fprintf_func) (info->stream, "#"); 271 (*info->fprintf_func) (info->stream, "0x%lx", value); 272} 273 274/* Handle '#' prefixes as operands. */ 275 276static void 277print_hash (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, 278 void * dis_info, 279 long value ATTRIBUTE_UNUSED, 280 unsigned int attrs ATTRIBUTE_UNUSED, 281 bfd_vma pc ATTRIBUTE_UNUSED, 282 int length ATTRIBUTE_UNUSED) 283{ 284 disassemble_info *info = (disassemble_info *) dis_info; 285 286 (*info->fprintf_func) (info->stream, "#"); 287} 288 289#undef CGEN_PRINT_INSN 290#define CGEN_PRINT_INSN my_print_insn 291 292static int 293my_print_insn (CGEN_CPU_DESC cd, 294 bfd_vma pc, 295 disassemble_info *info) 296{ 297 bfd_byte buffer[CGEN_MAX_INSN_SIZE]; 298 bfd_byte *buf = buffer; 299 int status; 300 int buflen = (pc & 3) == 0 ? 4 : 2; 301 int big_p = CGEN_CPU_INSN_ENDIAN (cd) == CGEN_ENDIAN_BIG; 302 bfd_byte *x; 303 304 /* Read the base part of the insn. */ 305 306 status = (*info->read_memory_func) (pc - ((!big_p && (pc & 3) != 0) ? 2 : 0), 307 buf, buflen, info); 308 if (status != 0) 309 { 310 (*info->memory_error_func) (status, pc, info); 311 return -1; 312 } 313 314 /* 32 bit insn? */ 315 x = (big_p ? &buf[0] : &buf[3]); 316 if ((pc & 3) == 0 && (*x & 0x80) != 0) 317 return print_insn (cd, pc, info, buf, buflen); 318 319 /* Print the first insn. */ 320 if ((pc & 3) == 0) 321 { 322 buf += (big_p ? 0 : 2); 323 if (print_insn (cd, pc, info, buf, 2) == 0) 324 (*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG); 325 buf += (big_p ? 2 : -2); 326 } 327 328 x = (big_p ? &buf[0] : &buf[1]); 329 if (*x & 0x80) 330 { 331 /* Parallel. */ 332 (*info->fprintf_func) (info->stream, " || "); 333 *x &= 0x7f; 334 } 335 else 336 (*info->fprintf_func) (info->stream, " -> "); 337 338 /* The "& 3" is to pass a consistent address. 339 Parallel insns arguably both begin on the word boundary. 340 Also, branch insns are calculated relative to the word boundary. */ 341 if (print_insn (cd, pc & ~ (bfd_vma) 3, info, buf, 2) == 0) 342 (*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG); 343 344 return (pc & 3) ? 2 : 4; 345} 346 347/* -- */ 348