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