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