162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* Disassemble SPU instructions
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci   Copyright 2006 Free Software Foundation, Inc.
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci   This file is part of GDB, GAS, and the GNU binutils.
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/string.h>
1162306a36Sopenharmony_ci#include "nonstdio.h"
1262306a36Sopenharmony_ci#include "ansidecl.h"
1362306a36Sopenharmony_ci#include "spu.h"
1462306a36Sopenharmony_ci#include "dis-asm.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/* This file provides a disassembler function which uses
1762306a36Sopenharmony_ci   the disassembler interface defined in dis-asm.h.   */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ciextern const struct spu_opcode spu_opcodes[];
2062306a36Sopenharmony_ciextern const int spu_num_opcodes;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define SPU_DISASM_TBL_SIZE (1 << 11)
2362306a36Sopenharmony_cistatic const struct spu_opcode *spu_disassemble_table[SPU_DISASM_TBL_SIZE];
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic void
2662306a36Sopenharmony_ciinit_spu_disassemble (void)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci  int i;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci  /* If two instructions have the same opcode then we prefer the first
3162306a36Sopenharmony_ci   * one.  In most cases it is just an alternate mnemonic. */
3262306a36Sopenharmony_ci  for (i = 0; i < spu_num_opcodes; i++)
3362306a36Sopenharmony_ci    {
3462306a36Sopenharmony_ci      int o = spu_opcodes[i].opcode;
3562306a36Sopenharmony_ci      if (o >= SPU_DISASM_TBL_SIZE)
3662306a36Sopenharmony_ci	continue; /* abort (); */
3762306a36Sopenharmony_ci      if (spu_disassemble_table[o] == 0)
3862306a36Sopenharmony_ci	spu_disassemble_table[o] = &spu_opcodes[i];
3962306a36Sopenharmony_ci    }
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* Determine the instruction from the 10 least significant bits. */
4362306a36Sopenharmony_cistatic const struct spu_opcode *
4462306a36Sopenharmony_ciget_index_for_opcode (unsigned int insn)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci  const struct spu_opcode *index;
4762306a36Sopenharmony_ci  unsigned int opcode = insn >> (32-11);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci  /* Init the table.  This assumes that element 0/opcode 0 (currently
5062306a36Sopenharmony_ci   * NOP) is always used */
5162306a36Sopenharmony_ci  if (spu_disassemble_table[0] == 0)
5262306a36Sopenharmony_ci    init_spu_disassemble ();
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci  if ((index = spu_disassemble_table[opcode & 0x780]) != 0
5562306a36Sopenharmony_ci      && index->insn_type == RRR)
5662306a36Sopenharmony_ci    return index;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci  if ((index = spu_disassemble_table[opcode & 0x7f0]) != 0
5962306a36Sopenharmony_ci      && (index->insn_type == RI18 || index->insn_type == LBT))
6062306a36Sopenharmony_ci    return index;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci  if ((index = spu_disassemble_table[opcode & 0x7f8]) != 0
6362306a36Sopenharmony_ci      && index->insn_type == RI10)
6462306a36Sopenharmony_ci    return index;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci  if ((index = spu_disassemble_table[opcode & 0x7fc]) != 0
6762306a36Sopenharmony_ci      && (index->insn_type == RI16))
6862306a36Sopenharmony_ci    return index;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci  if ((index = spu_disassemble_table[opcode & 0x7fe]) != 0
7162306a36Sopenharmony_ci      && (index->insn_type == RI8))
7262306a36Sopenharmony_ci    return index;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci  if ((index = spu_disassemble_table[opcode & 0x7ff]) != 0)
7562306a36Sopenharmony_ci    return index;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci  return NULL;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/* Print a Spu instruction.  */
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ciint
8362306a36Sopenharmony_ciprint_insn_spu (unsigned long insn, unsigned long memaddr)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci  int value;
8662306a36Sopenharmony_ci  int hex_value;
8762306a36Sopenharmony_ci  const struct spu_opcode *index;
8862306a36Sopenharmony_ci  enum spu_insns tag;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci  index = get_index_for_opcode (insn);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci  if (index == 0)
9362306a36Sopenharmony_ci    {
9462306a36Sopenharmony_ci      printf(".long 0x%lx", insn);
9562306a36Sopenharmony_ci    }
9662306a36Sopenharmony_ci  else
9762306a36Sopenharmony_ci    {
9862306a36Sopenharmony_ci      int i;
9962306a36Sopenharmony_ci      int paren = 0;
10062306a36Sopenharmony_ci      tag = (enum spu_insns)(index - spu_opcodes);
10162306a36Sopenharmony_ci      printf("%s", index->mnemonic);
10262306a36Sopenharmony_ci      if (tag == M_BI || tag == M_BISL || tag == M_IRET || tag == M_BISLED
10362306a36Sopenharmony_ci	  || tag == M_BIHNZ || tag == M_BIHZ || tag == M_BINZ || tag == M_BIZ
10462306a36Sopenharmony_ci          || tag == M_SYNC || tag == M_HBR)
10562306a36Sopenharmony_ci	{
10662306a36Sopenharmony_ci	  int fb = (insn >> (32-18)) & 0x7f;
10762306a36Sopenharmony_ci	  if (fb & 0x40)
10862306a36Sopenharmony_ci	    printf(tag == M_SYNC ? "c" : "p");
10962306a36Sopenharmony_ci	  if (fb & 0x20)
11062306a36Sopenharmony_ci	    printf("d");
11162306a36Sopenharmony_ci	  if (fb & 0x10)
11262306a36Sopenharmony_ci	    printf("e");
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci      if (index->arg[0] != 0)
11562306a36Sopenharmony_ci	printf("\t");
11662306a36Sopenharmony_ci      hex_value = 0;
11762306a36Sopenharmony_ci      for (i = 1;  i <= index->arg[0]; i++)
11862306a36Sopenharmony_ci	{
11962306a36Sopenharmony_ci	  int arg = index->arg[i];
12062306a36Sopenharmony_ci	  if (arg != A_P && !paren && i > 1)
12162306a36Sopenharmony_ci	    printf(",");
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	  switch (arg)
12462306a36Sopenharmony_ci	    {
12562306a36Sopenharmony_ci	    case A_T:
12662306a36Sopenharmony_ci	      printf("$%lu",
12762306a36Sopenharmony_ci				     DECODE_INSN_RT (insn));
12862306a36Sopenharmony_ci	      break;
12962306a36Sopenharmony_ci	    case A_A:
13062306a36Sopenharmony_ci	      printf("$%lu",
13162306a36Sopenharmony_ci				     DECODE_INSN_RA (insn));
13262306a36Sopenharmony_ci	      break;
13362306a36Sopenharmony_ci	    case A_B:
13462306a36Sopenharmony_ci	      printf("$%lu",
13562306a36Sopenharmony_ci				     DECODE_INSN_RB (insn));
13662306a36Sopenharmony_ci	      break;
13762306a36Sopenharmony_ci	    case A_C:
13862306a36Sopenharmony_ci	      printf("$%lu",
13962306a36Sopenharmony_ci				     DECODE_INSN_RC (insn));
14062306a36Sopenharmony_ci	      break;
14162306a36Sopenharmony_ci	    case A_S:
14262306a36Sopenharmony_ci	      printf("$sp%lu",
14362306a36Sopenharmony_ci				     DECODE_INSN_RA (insn));
14462306a36Sopenharmony_ci	      break;
14562306a36Sopenharmony_ci	    case A_H:
14662306a36Sopenharmony_ci	      printf("$ch%lu",
14762306a36Sopenharmony_ci				     DECODE_INSN_RA (insn));
14862306a36Sopenharmony_ci	      break;
14962306a36Sopenharmony_ci	    case A_P:
15062306a36Sopenharmony_ci	      paren++;
15162306a36Sopenharmony_ci	      printf("(");
15262306a36Sopenharmony_ci	      break;
15362306a36Sopenharmony_ci	    case A_U7A:
15462306a36Sopenharmony_ci	      printf("%lu",
15562306a36Sopenharmony_ci				     173 - DECODE_INSN_U8 (insn));
15662306a36Sopenharmony_ci	      break;
15762306a36Sopenharmony_ci	    case A_U7B:
15862306a36Sopenharmony_ci	      printf("%lu",
15962306a36Sopenharmony_ci				     155 - DECODE_INSN_U8 (insn));
16062306a36Sopenharmony_ci	      break;
16162306a36Sopenharmony_ci	    case A_S3:
16262306a36Sopenharmony_ci	    case A_S6:
16362306a36Sopenharmony_ci	    case A_S7:
16462306a36Sopenharmony_ci	    case A_S7N:
16562306a36Sopenharmony_ci	    case A_U3:
16662306a36Sopenharmony_ci	    case A_U5:
16762306a36Sopenharmony_ci	    case A_U6:
16862306a36Sopenharmony_ci	    case A_U7:
16962306a36Sopenharmony_ci	      hex_value = DECODE_INSN_I7 (insn);
17062306a36Sopenharmony_ci	      printf("%d", hex_value);
17162306a36Sopenharmony_ci	      break;
17262306a36Sopenharmony_ci	    case A_S11:
17362306a36Sopenharmony_ci	      print_address(memaddr + DECODE_INSN_I9a (insn) * 4);
17462306a36Sopenharmony_ci	      break;
17562306a36Sopenharmony_ci	    case A_S11I:
17662306a36Sopenharmony_ci	      print_address(memaddr + DECODE_INSN_I9b (insn) * 4);
17762306a36Sopenharmony_ci	      break;
17862306a36Sopenharmony_ci	    case A_S10:
17962306a36Sopenharmony_ci	    case A_S10B:
18062306a36Sopenharmony_ci	      hex_value = DECODE_INSN_I10 (insn);
18162306a36Sopenharmony_ci	      printf("%d", hex_value);
18262306a36Sopenharmony_ci	      break;
18362306a36Sopenharmony_ci	    case A_S14:
18462306a36Sopenharmony_ci	      hex_value = DECODE_INSN_I10 (insn) * 16;
18562306a36Sopenharmony_ci	      printf("%d", hex_value);
18662306a36Sopenharmony_ci	      break;
18762306a36Sopenharmony_ci	    case A_S16:
18862306a36Sopenharmony_ci	      hex_value = DECODE_INSN_I16 (insn);
18962306a36Sopenharmony_ci	      printf("%d", hex_value);
19062306a36Sopenharmony_ci	      break;
19162306a36Sopenharmony_ci	    case A_X16:
19262306a36Sopenharmony_ci	      hex_value = DECODE_INSN_U16 (insn);
19362306a36Sopenharmony_ci	      printf("%u", hex_value);
19462306a36Sopenharmony_ci	      break;
19562306a36Sopenharmony_ci	    case A_R18:
19662306a36Sopenharmony_ci	      value = DECODE_INSN_I16 (insn) * 4;
19762306a36Sopenharmony_ci	      if (value == 0)
19862306a36Sopenharmony_ci		printf("%d", value);
19962306a36Sopenharmony_ci	      else
20062306a36Sopenharmony_ci		{
20162306a36Sopenharmony_ci		  hex_value = memaddr + value;
20262306a36Sopenharmony_ci		  print_address(hex_value & 0x3ffff);
20362306a36Sopenharmony_ci		}
20462306a36Sopenharmony_ci	      break;
20562306a36Sopenharmony_ci	    case A_S18:
20662306a36Sopenharmony_ci	      value = DECODE_INSN_U16 (insn) * 4;
20762306a36Sopenharmony_ci	      if (value == 0)
20862306a36Sopenharmony_ci		printf("%d", value);
20962306a36Sopenharmony_ci	      else
21062306a36Sopenharmony_ci		print_address(value);
21162306a36Sopenharmony_ci	      break;
21262306a36Sopenharmony_ci	    case A_U18:
21362306a36Sopenharmony_ci	      value = DECODE_INSN_U18 (insn);
21462306a36Sopenharmony_ci	      if (value == 0 || 1)
21562306a36Sopenharmony_ci		{
21662306a36Sopenharmony_ci		  hex_value = value;
21762306a36Sopenharmony_ci		  printf("%u", value);
21862306a36Sopenharmony_ci		}
21962306a36Sopenharmony_ci	      else
22062306a36Sopenharmony_ci		print_address(value);
22162306a36Sopenharmony_ci	      break;
22262306a36Sopenharmony_ci	    case A_U14:
22362306a36Sopenharmony_ci	      hex_value = DECODE_INSN_U14 (insn);
22462306a36Sopenharmony_ci	      printf("%u", hex_value);
22562306a36Sopenharmony_ci	      break;
22662306a36Sopenharmony_ci	    }
22762306a36Sopenharmony_ci	  if (arg != A_P && paren)
22862306a36Sopenharmony_ci	    {
22962306a36Sopenharmony_ci	      printf(")");
23062306a36Sopenharmony_ci	      paren--;
23162306a36Sopenharmony_ci	    }
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci      if (hex_value > 16)
23462306a36Sopenharmony_ci	printf("\t# %x", hex_value);
23562306a36Sopenharmony_ci    }
23662306a36Sopenharmony_ci  return 4;
23762306a36Sopenharmony_ci}
238