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