18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* Disassemble SPU instructions
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci   Copyright 2006 Free Software Foundation, Inc.
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci   This file is part of GDB, GAS, and the GNU binutils.
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/string.h>
118c2ecf20Sopenharmony_ci#include "nonstdio.h"
128c2ecf20Sopenharmony_ci#include "ansidecl.h"
138c2ecf20Sopenharmony_ci#include "spu.h"
148c2ecf20Sopenharmony_ci#include "dis-asm.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/* This file provides a disassembler function which uses
178c2ecf20Sopenharmony_ci   the disassembler interface defined in dis-asm.h.   */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ciextern const struct spu_opcode spu_opcodes[];
208c2ecf20Sopenharmony_ciextern const int spu_num_opcodes;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define SPU_DISASM_TBL_SIZE (1 << 11)
238c2ecf20Sopenharmony_cistatic const struct spu_opcode *spu_disassemble_table[SPU_DISASM_TBL_SIZE];
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic void
268c2ecf20Sopenharmony_ciinit_spu_disassemble (void)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci  int i;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci  /* If two instructions have the same opcode then we prefer the first
318c2ecf20Sopenharmony_ci   * one.  In most cases it is just an alternate mnemonic. */
328c2ecf20Sopenharmony_ci  for (i = 0; i < spu_num_opcodes; i++)
338c2ecf20Sopenharmony_ci    {
348c2ecf20Sopenharmony_ci      int o = spu_opcodes[i].opcode;
358c2ecf20Sopenharmony_ci      if (o >= SPU_DISASM_TBL_SIZE)
368c2ecf20Sopenharmony_ci	continue; /* abort (); */
378c2ecf20Sopenharmony_ci      if (spu_disassemble_table[o] == 0)
388c2ecf20Sopenharmony_ci	spu_disassemble_table[o] = &spu_opcodes[i];
398c2ecf20Sopenharmony_ci    }
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* Determine the instruction from the 10 least significant bits. */
438c2ecf20Sopenharmony_cistatic const struct spu_opcode *
448c2ecf20Sopenharmony_ciget_index_for_opcode (unsigned int insn)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci  const struct spu_opcode *index;
478c2ecf20Sopenharmony_ci  unsigned int opcode = insn >> (32-11);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci  /* Init the table.  This assumes that element 0/opcode 0 (currently
508c2ecf20Sopenharmony_ci   * NOP) is always used */
518c2ecf20Sopenharmony_ci  if (spu_disassemble_table[0] == 0)
528c2ecf20Sopenharmony_ci    init_spu_disassemble ();
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci  if ((index = spu_disassemble_table[opcode & 0x780]) != 0
558c2ecf20Sopenharmony_ci      && index->insn_type == RRR)
568c2ecf20Sopenharmony_ci    return index;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci  if ((index = spu_disassemble_table[opcode & 0x7f0]) != 0
598c2ecf20Sopenharmony_ci      && (index->insn_type == RI18 || index->insn_type == LBT))
608c2ecf20Sopenharmony_ci    return index;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci  if ((index = spu_disassemble_table[opcode & 0x7f8]) != 0
638c2ecf20Sopenharmony_ci      && index->insn_type == RI10)
648c2ecf20Sopenharmony_ci    return index;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci  if ((index = spu_disassemble_table[opcode & 0x7fc]) != 0
678c2ecf20Sopenharmony_ci      && (index->insn_type == RI16))
688c2ecf20Sopenharmony_ci    return index;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci  if ((index = spu_disassemble_table[opcode & 0x7fe]) != 0
718c2ecf20Sopenharmony_ci      && (index->insn_type == RI8))
728c2ecf20Sopenharmony_ci    return index;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci  if ((index = spu_disassemble_table[opcode & 0x7ff]) != 0)
758c2ecf20Sopenharmony_ci    return index;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci  return NULL;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/* Print a Spu instruction.  */
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ciint
838c2ecf20Sopenharmony_ciprint_insn_spu (unsigned long insn, unsigned long memaddr)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci  int value;
868c2ecf20Sopenharmony_ci  int hex_value;
878c2ecf20Sopenharmony_ci  const struct spu_opcode *index;
888c2ecf20Sopenharmony_ci  enum spu_insns tag;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci  index = get_index_for_opcode (insn);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci  if (index == 0)
938c2ecf20Sopenharmony_ci    {
948c2ecf20Sopenharmony_ci      printf(".long 0x%lx", insn);
958c2ecf20Sopenharmony_ci    }
968c2ecf20Sopenharmony_ci  else
978c2ecf20Sopenharmony_ci    {
988c2ecf20Sopenharmony_ci      int i;
998c2ecf20Sopenharmony_ci      int paren = 0;
1008c2ecf20Sopenharmony_ci      tag = (enum spu_insns)(index - spu_opcodes);
1018c2ecf20Sopenharmony_ci      printf("%s", index->mnemonic);
1028c2ecf20Sopenharmony_ci      if (tag == M_BI || tag == M_BISL || tag == M_IRET || tag == M_BISLED
1038c2ecf20Sopenharmony_ci	  || tag == M_BIHNZ || tag == M_BIHZ || tag == M_BINZ || tag == M_BIZ
1048c2ecf20Sopenharmony_ci          || tag == M_SYNC || tag == M_HBR)
1058c2ecf20Sopenharmony_ci	{
1068c2ecf20Sopenharmony_ci	  int fb = (insn >> (32-18)) & 0x7f;
1078c2ecf20Sopenharmony_ci	  if (fb & 0x40)
1088c2ecf20Sopenharmony_ci	    printf(tag == M_SYNC ? "c" : "p");
1098c2ecf20Sopenharmony_ci	  if (fb & 0x20)
1108c2ecf20Sopenharmony_ci	    printf("d");
1118c2ecf20Sopenharmony_ci	  if (fb & 0x10)
1128c2ecf20Sopenharmony_ci	    printf("e");
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci      if (index->arg[0] != 0)
1158c2ecf20Sopenharmony_ci	printf("\t");
1168c2ecf20Sopenharmony_ci      hex_value = 0;
1178c2ecf20Sopenharmony_ci      for (i = 1;  i <= index->arg[0]; i++)
1188c2ecf20Sopenharmony_ci	{
1198c2ecf20Sopenharmony_ci	  int arg = index->arg[i];
1208c2ecf20Sopenharmony_ci	  if (arg != A_P && !paren && i > 1)
1218c2ecf20Sopenharmony_ci	    printf(",");
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	  switch (arg)
1248c2ecf20Sopenharmony_ci	    {
1258c2ecf20Sopenharmony_ci	    case A_T:
1268c2ecf20Sopenharmony_ci	      printf("$%lu",
1278c2ecf20Sopenharmony_ci				     DECODE_INSN_RT (insn));
1288c2ecf20Sopenharmony_ci	      break;
1298c2ecf20Sopenharmony_ci	    case A_A:
1308c2ecf20Sopenharmony_ci	      printf("$%lu",
1318c2ecf20Sopenharmony_ci				     DECODE_INSN_RA (insn));
1328c2ecf20Sopenharmony_ci	      break;
1338c2ecf20Sopenharmony_ci	    case A_B:
1348c2ecf20Sopenharmony_ci	      printf("$%lu",
1358c2ecf20Sopenharmony_ci				     DECODE_INSN_RB (insn));
1368c2ecf20Sopenharmony_ci	      break;
1378c2ecf20Sopenharmony_ci	    case A_C:
1388c2ecf20Sopenharmony_ci	      printf("$%lu",
1398c2ecf20Sopenharmony_ci				     DECODE_INSN_RC (insn));
1408c2ecf20Sopenharmony_ci	      break;
1418c2ecf20Sopenharmony_ci	    case A_S:
1428c2ecf20Sopenharmony_ci	      printf("$sp%lu",
1438c2ecf20Sopenharmony_ci				     DECODE_INSN_RA (insn));
1448c2ecf20Sopenharmony_ci	      break;
1458c2ecf20Sopenharmony_ci	    case A_H:
1468c2ecf20Sopenharmony_ci	      printf("$ch%lu",
1478c2ecf20Sopenharmony_ci				     DECODE_INSN_RA (insn));
1488c2ecf20Sopenharmony_ci	      break;
1498c2ecf20Sopenharmony_ci	    case A_P:
1508c2ecf20Sopenharmony_ci	      paren++;
1518c2ecf20Sopenharmony_ci	      printf("(");
1528c2ecf20Sopenharmony_ci	      break;
1538c2ecf20Sopenharmony_ci	    case A_U7A:
1548c2ecf20Sopenharmony_ci	      printf("%lu",
1558c2ecf20Sopenharmony_ci				     173 - DECODE_INSN_U8 (insn));
1568c2ecf20Sopenharmony_ci	      break;
1578c2ecf20Sopenharmony_ci	    case A_U7B:
1588c2ecf20Sopenharmony_ci	      printf("%lu",
1598c2ecf20Sopenharmony_ci				     155 - DECODE_INSN_U8 (insn));
1608c2ecf20Sopenharmony_ci	      break;
1618c2ecf20Sopenharmony_ci	    case A_S3:
1628c2ecf20Sopenharmony_ci	    case A_S6:
1638c2ecf20Sopenharmony_ci	    case A_S7:
1648c2ecf20Sopenharmony_ci	    case A_S7N:
1658c2ecf20Sopenharmony_ci	    case A_U3:
1668c2ecf20Sopenharmony_ci	    case A_U5:
1678c2ecf20Sopenharmony_ci	    case A_U6:
1688c2ecf20Sopenharmony_ci	    case A_U7:
1698c2ecf20Sopenharmony_ci	      hex_value = DECODE_INSN_I7 (insn);
1708c2ecf20Sopenharmony_ci	      printf("%d", hex_value);
1718c2ecf20Sopenharmony_ci	      break;
1728c2ecf20Sopenharmony_ci	    case A_S11:
1738c2ecf20Sopenharmony_ci	      print_address(memaddr + DECODE_INSN_I9a (insn) * 4);
1748c2ecf20Sopenharmony_ci	      break;
1758c2ecf20Sopenharmony_ci	    case A_S11I:
1768c2ecf20Sopenharmony_ci	      print_address(memaddr + DECODE_INSN_I9b (insn) * 4);
1778c2ecf20Sopenharmony_ci	      break;
1788c2ecf20Sopenharmony_ci	    case A_S10:
1798c2ecf20Sopenharmony_ci	    case A_S10B:
1808c2ecf20Sopenharmony_ci	      hex_value = DECODE_INSN_I10 (insn);
1818c2ecf20Sopenharmony_ci	      printf("%d", hex_value);
1828c2ecf20Sopenharmony_ci	      break;
1838c2ecf20Sopenharmony_ci	    case A_S14:
1848c2ecf20Sopenharmony_ci	      hex_value = DECODE_INSN_I10 (insn) * 16;
1858c2ecf20Sopenharmony_ci	      printf("%d", hex_value);
1868c2ecf20Sopenharmony_ci	      break;
1878c2ecf20Sopenharmony_ci	    case A_S16:
1888c2ecf20Sopenharmony_ci	      hex_value = DECODE_INSN_I16 (insn);
1898c2ecf20Sopenharmony_ci	      printf("%d", hex_value);
1908c2ecf20Sopenharmony_ci	      break;
1918c2ecf20Sopenharmony_ci	    case A_X16:
1928c2ecf20Sopenharmony_ci	      hex_value = DECODE_INSN_U16 (insn);
1938c2ecf20Sopenharmony_ci	      printf("%u", hex_value);
1948c2ecf20Sopenharmony_ci	      break;
1958c2ecf20Sopenharmony_ci	    case A_R18:
1968c2ecf20Sopenharmony_ci	      value = DECODE_INSN_I16 (insn) * 4;
1978c2ecf20Sopenharmony_ci	      if (value == 0)
1988c2ecf20Sopenharmony_ci		printf("%d", value);
1998c2ecf20Sopenharmony_ci	      else
2008c2ecf20Sopenharmony_ci		{
2018c2ecf20Sopenharmony_ci		  hex_value = memaddr + value;
2028c2ecf20Sopenharmony_ci		  print_address(hex_value & 0x3ffff);
2038c2ecf20Sopenharmony_ci		}
2048c2ecf20Sopenharmony_ci	      break;
2058c2ecf20Sopenharmony_ci	    case A_S18:
2068c2ecf20Sopenharmony_ci	      value = DECODE_INSN_U16 (insn) * 4;
2078c2ecf20Sopenharmony_ci	      if (value == 0)
2088c2ecf20Sopenharmony_ci		printf("%d", value);
2098c2ecf20Sopenharmony_ci	      else
2108c2ecf20Sopenharmony_ci		print_address(value);
2118c2ecf20Sopenharmony_ci	      break;
2128c2ecf20Sopenharmony_ci	    case A_U18:
2138c2ecf20Sopenharmony_ci	      value = DECODE_INSN_U18 (insn);
2148c2ecf20Sopenharmony_ci	      if (value == 0 || 1)
2158c2ecf20Sopenharmony_ci		{
2168c2ecf20Sopenharmony_ci		  hex_value = value;
2178c2ecf20Sopenharmony_ci		  printf("%u", value);
2188c2ecf20Sopenharmony_ci		}
2198c2ecf20Sopenharmony_ci	      else
2208c2ecf20Sopenharmony_ci		print_address(value);
2218c2ecf20Sopenharmony_ci	      break;
2228c2ecf20Sopenharmony_ci	    case A_U14:
2238c2ecf20Sopenharmony_ci	      hex_value = DECODE_INSN_U14 (insn);
2248c2ecf20Sopenharmony_ci	      printf("%u", hex_value);
2258c2ecf20Sopenharmony_ci	      break;
2268c2ecf20Sopenharmony_ci	    }
2278c2ecf20Sopenharmony_ci	  if (arg != A_P && paren)
2288c2ecf20Sopenharmony_ci	    {
2298c2ecf20Sopenharmony_ci	      printf(")");
2308c2ecf20Sopenharmony_ci	      paren--;
2318c2ecf20Sopenharmony_ci	    }
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci      if (hex_value > 16)
2348c2ecf20Sopenharmony_ci	printf("\t# %x", hex_value);
2358c2ecf20Sopenharmony_ci    }
2368c2ecf20Sopenharmony_ci  return 4;
2378c2ecf20Sopenharmony_ci}
238