1 /* Print TI TMS320C80 (MVP) instructions 2 Copyright (C) 1996-2016 Free Software Foundation, Inc. 3 4 This file is part of the GNU opcodes library. 5 6 This library is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 3, or (at your option) 9 any later version. 10 11 It is distributed in the hope that it will be useful, but WITHOUT 12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 14 License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, 19 MA 02110-1301, USA. */ 20 21 #include "sysdep.h" 22 #include <stdio.h> 23 #include "opcode/tic80.h" 24 #include "dis-asm.h" 25 26 static int length; 27 28 /* Print an integer operand. Try to be somewhat smart about the 30 format by assuming that small positive or negative integers are 31 probably loop increment values, structure offsets, or similar 32 values that are more meaningful printed as signed decimal values. 33 Larger numbers are probably better printed as hex values. */ 34 35 static void 36 print_operand_integer (struct disassemble_info *info, long value) 37 { 38 if ((value > 9999 || value < -9999)) 39 (*info->fprintf_func) (info->stream, "%#lx", value); 40 else 41 (*info->fprintf_func) (info->stream, "%ld", value); 42 } 43 44 /* FIXME: depends upon sizeof (long) == sizeof (float) and 46 also upon host floating point format matching target 47 floating point format. */ 48 49 static void 50 print_operand_float (struct disassemble_info *info, long value) 51 { 52 union { float f; long l; } fval; 53 54 fval.l = value; 55 (*info->fprintf_func) (info->stream, "%g", fval.f); 56 } 57 58 static void 59 print_operand_control_register (struct disassemble_info *info, long value) 60 { 61 const char *tmp; 62 63 tmp = tic80_value_to_symbol (value, TIC80_OPERAND_CR); 64 if (tmp != NULL) 65 (*info->fprintf_func) (info->stream, "%s", tmp); 66 else 67 (*info->fprintf_func) (info->stream, "%#lx", value); 68 } 69 70 static void 71 print_operand_condition_code (struct disassemble_info *info, long value) 72 { 73 const char *tmp; 74 75 tmp = tic80_value_to_symbol (value, TIC80_OPERAND_CC); 76 if (tmp != NULL) 77 (*info->fprintf_func) (info->stream, "%s", tmp); 78 else 79 (*info->fprintf_func) (info->stream, "%ld", value); 80 } 81 82 static void 83 print_operand_bitnum (struct disassemble_info *info, long value) 84 { 85 int bitnum; 86 const char *tmp; 87 88 bitnum = ~value & 0x1F; 89 tmp = tic80_value_to_symbol (bitnum, TIC80_OPERAND_BITNUM); 90 if (tmp != NULL) 91 (*info->fprintf_func) (info->stream, "%s", tmp); 92 else 93 (*info->fprintf_func) (info->stream, "%d", bitnum); 94 } 95 96 /* Print the operand as directed by the flags. */ 98 99 #define M_SI(insn,op) ((((op)->flags & TIC80_OPERAND_M_SI) != 0) && ((insn) & (1 << 17))) 100 #define M_LI(insn,op) ((((op)->flags & TIC80_OPERAND_M_LI) != 0) && ((insn) & (1 << 15))) 101 #define R_SCALED(insn,op) ((((op)->flags & TIC80_OPERAND_SCALED) != 0) && ((insn) & (1 << 11))) 102 103 static void 104 print_operand (struct disassemble_info *info, 105 long value, 106 unsigned long insn, 107 const struct tic80_operand *operand, 108 bfd_vma memaddr) 109 { 110 if ((operand->flags & TIC80_OPERAND_GPR) != 0) 111 { 112 (*info->fprintf_func) (info->stream, "r%ld", value); 113 if (M_SI (insn, operand) || M_LI (insn, operand)) 114 { 115 (*info->fprintf_func) (info->stream, ":m"); 116 } 117 } 118 else if ((operand->flags & TIC80_OPERAND_FPA) != 0) 119 (*info->fprintf_func) (info->stream, "a%ld", value); 120 121 else if ((operand->flags & TIC80_OPERAND_PCREL) != 0) 122 (*info->print_address_func) (memaddr + 4 * value, info); 123 124 else if ((operand->flags & TIC80_OPERAND_BASEREL) != 0) 125 (*info->print_address_func) (value, info); 126 127 else if ((operand->flags & TIC80_OPERAND_BITNUM) != 0) 128 print_operand_bitnum (info, value); 129 130 else if ((operand->flags & TIC80_OPERAND_CC) != 0) 131 print_operand_condition_code (info, value); 132 133 else if ((operand->flags & TIC80_OPERAND_CR) != 0) 134 print_operand_control_register (info, value); 135 136 else if ((operand->flags & TIC80_OPERAND_FLOAT) != 0) 137 print_operand_float (info, value); 138 139 else if ((operand->flags & TIC80_OPERAND_BITFIELD)) 140 (*info->fprintf_func) (info->stream, "%#lx", value); 141 142 else 143 print_operand_integer (info, value); 144 145 /* If this is a scaled operand, then print the modifier. */ 146 if (R_SCALED (insn, operand)) 147 (*info->fprintf_func) (info->stream, ":s"); 148 } 149 150 /* Get the next 32 bit word from the instruction stream and convert it 152 into internal format in the unsigned long INSN, for which we are 153 passed the address. Return 0 on success, -1 on error. */ 154 155 static int 156 fill_instruction (struct disassemble_info *info, 157 bfd_vma memaddr, 158 unsigned long *insnp) 159 { 160 bfd_byte buffer[4]; 161 int status; 162 163 /* Get the bits for the next 32 bit word and put in buffer. */ 164 status = (*info->read_memory_func) (memaddr + length, buffer, 4, info); 165 if (status != 0) 166 { 167 (*info->memory_error_func) (status, memaddr, info); 168 return -1; 169 } 170 171 /* Read was successful, so increment count of bytes read and convert 172 the bits into internal format. */ 173 174 length += 4; 175 if (info->endian == BFD_ENDIAN_LITTLE) 176 *insnp = bfd_getl32 (buffer); 177 178 else if (info->endian == BFD_ENDIAN_BIG) 179 *insnp = bfd_getb32 (buffer); 180 181 else 182 /* FIXME: Should probably just default to one or the other. */ 183 abort (); 184 185 return 0; 186 } 187 188 /* We have chosen an opcode table entry. */ 189 190 static int 191 print_one_instruction (struct disassemble_info *info, 192 bfd_vma memaddr, 193 unsigned long insn, 194 const struct tic80_opcode *opcode) 195 { 196 const struct tic80_operand *operand; 197 long value; 198 int status; 199 const unsigned char *opindex; 200 int close_paren; 201 202 (*info->fprintf_func) (info->stream, "%-10s", opcode->name); 203 204 for (opindex = opcode->operands; *opindex != 0; opindex++) 205 { 206 operand = tic80_operands + *opindex; 207 208 /* Extract the value from the instruction. */ 209 if (operand->extract) 210 value = (*operand->extract) (insn, NULL); 211 212 else if (operand->bits == 32) 213 { 214 status = fill_instruction (info, memaddr, (unsigned long *) &value); 215 if (status == -1) 216 return status; 217 } 218 else 219 { 220 value = (insn >> operand->shift) & ((1 << operand->bits) - 1); 221 222 if ((operand->flags & TIC80_OPERAND_SIGNED) != 0 223 && (value & (1 << (operand->bits - 1))) != 0) 224 value -= 1 << operand->bits; 225 } 226 227 /* If this operand is enclosed in parenthesis, then print 228 the open paren, otherwise just print the regular comma 229 separator, except for the first operand. */ 230 if ((operand->flags & TIC80_OPERAND_PARENS) == 0) 231 { 232 close_paren = 0; 233 if (opindex != opcode->operands) 234 (*info->fprintf_func) (info->stream, ","); 235 } 236 else 237 { 238 close_paren = 1; 239 (*info->fprintf_func) (info->stream, "("); 240 } 241 242 print_operand (info, value, insn, operand, memaddr); 243 244 /* If we printed an open paren before printing this operand, close 245 it now. The flag gets reset on each loop. */ 246 if (close_paren) 247 (*info->fprintf_func) (info->stream, ")"); 248 } 249 250 return length; 251 } 252 253 /* There are no specific bits that tell us for certain whether a vector 255 instruction opcode contains one or two instructions. However since 256 a destination register of r0 is illegal, we can check for nonzero 257 values in both destination register fields. Only opcodes that have 258 two valid instructions will have non-zero in both. */ 259 260 #define TWO_INSN(insn) ((((insn) & (0x1F << 27)) != 0) && (((insn) & (0x1F << 22)) != 0)) 261 262 static int 263 print_instruction (struct disassemble_info *info, 264 bfd_vma memaddr, 265 unsigned long insn, 266 const struct tic80_opcode *vec_opcode) 267 { 268 const struct tic80_opcode *opcode; 269 const struct tic80_opcode *opcode_end; 270 271 /* Find the first opcode match in the opcodes table. For vector 272 opcodes (vec_opcode != NULL) find the first match that is not the 273 previously found match. FIXME: there should be faster ways to 274 search (hash table or binary search), but don't worry too much 275 about it until other TIc80 support is finished. */ 276 277 opcode_end = tic80_opcodes + tic80_num_opcodes; 278 for (opcode = tic80_opcodes; opcode < opcode_end; opcode++) 279 { 280 if ((insn & opcode->mask) == opcode->opcode && 281 opcode != vec_opcode) 282 break; 283 } 284 285 if (opcode == opcode_end) 286 { 287 /* No match found, just print the bits as a .word directive. */ 288 (*info->fprintf_func) (info->stream, ".word %#08lx", insn); 289 } 290 else 291 { 292 /* Match found, decode the instruction. */ 293 length = print_one_instruction (info, memaddr, insn, opcode); 294 if (opcode->flags & TIC80_VECTOR && vec_opcode == NULL && TWO_INSN (insn)) 295 { 296 /* There is another instruction to print from the same opcode. 297 Print the separator and then find and print the other 298 instruction. */ 299 (*info->fprintf_func) (info->stream, " || "); 300 length = print_instruction (info, memaddr, insn, opcode); 301 } 302 } 303 304 return length; 305 } 306 307 int 309 print_insn_tic80 (bfd_vma memaddr, struct disassemble_info *info) 310 { 311 unsigned long insn; 312 int status; 313 314 length = 0; 315 info->bytes_per_line = 8; 316 status = fill_instruction (info, memaddr, &insn); 317 if (status != -1) 318 status = print_instruction (info, memaddr, insn, NULL); 319 320 return status; 321 } 322