#include <stdio.h>
#include "dis-asm.h"
#include "sysdep.h"
#include "sh64-opc.h"
#include "libiberty.h"
#include "elf-bfd.h"
#include "elf/sh.h"
#include "elf32-sh64.h"
#define ELF_MODE32_CODE_LABEL_P(SYM) \
(((elf_symbol_type *) (SYM))->internal_elf_sym.st_other & STO_SH5_ISA32)
#define SAVED_MOVI_R(INFO) \
(((struct sh64_disassemble_info *) ((INFO)->private_data))->address_reg)
#define SAVED_MOVI_IMM(INFO) \
(((struct sh64_disassemble_info *) ((INFO)->private_data))->built_address)
struct sh64_disassemble_info
{
unsigned int address_reg;
bfd_signed_vma built_address;
sh64_elf_crange crange;
};
static unsigned long *shmedia_opcode_mask_table;
static void
initialize_shmedia_opcode_mask_table (void)
{
int n_opc;
int n;
for (n_opc = 0; shmedia_table[n_opc].name != NULL; n_opc++)
;
shmedia_opcode_mask_table
= xmalloc (sizeof (shmedia_opcode_mask_table[0]) * n_opc);
for (n = 0; n < n_opc; n++)
{
int i;
unsigned long mask = 0;
for (i = 0; shmedia_table[n].arg[i] != A_NONE; i++)
{
int offset = shmedia_table[n].nibbles[i];
int length;
switch (shmedia_table[n].arg[i])
{
case A_GREG_M:
case A_GREG_N:
case A_GREG_D:
case A_CREG_K:
case A_CREG_J:
case A_FREG_G:
case A_FREG_H:
case A_FREG_F:
case A_DREG_G:
case A_DREG_H:
case A_DREG_F:
case A_FMREG_G:
case A_FMREG_H:
case A_FMREG_F:
case A_FPREG_G:
case A_FPREG_H:
case A_FPREG_F:
case A_FVREG_G:
case A_FVREG_H:
case A_FVREG_F:
case A_REUSE_PREV:
length = 6;
break;
case A_TREG_A:
case A_TREG_B:
length = 3;
break;
case A_IMMM:
abort ();
break;
case A_IMMU5:
length = 5;
break;
case A_IMMS6:
case A_IMMU6:
case A_IMMS6BY32:
length = 6;
break;
case A_IMMS10:
case A_IMMS10BY1:
case A_IMMS10BY2:
case A_IMMS10BY4:
case A_IMMS10BY8:
length = 10;
break;
case A_IMMU16:
case A_IMMS16:
case A_PCIMMS16BY4:
case A_PCIMMS16BY4_PT:
length = 16;
break;
default:
abort ();
length = 0;
break;
}
if (length != 0)
mask |= (0xffffffff >> (32 - length)) << offset;
}
shmedia_opcode_mask_table[n] = 0xffffffff & ~mask;
}
}
static const char *
creg_name (int cregno)
{
const shmedia_creg_info *cregp;
for (cregp = shmedia_creg_table; cregp->name != NULL; cregp++)
if (cregp->cregno == cregno)
return cregp->name;
return NULL;
}
static int
print_insn_shmedia (bfd_vma memaddr, struct disassemble_info *info)
{
fprintf_ftype fprintf_fn = info->fprintf_func;
void *stream = info->stream;
unsigned char insn[4];
unsigned long instruction;
int status;
int n;
const shmedia_opcode_info *op;
int i;
unsigned int r = 0;
long imm = 0;
bfd_vma disp_pc_addr;
status = info->read_memory_func (memaddr, insn, 4, info);
if (status != 0)
{
int i;
for (i = 0; i < 3; i++)
{
status = info->read_memory_func (memaddr + i, insn, 1, info);
if (status != 0)
break;
(*fprintf_fn) (stream, "%s0x%02x",
i == 0 ? ".byte " : ", ",
insn[0]);
}
return i ? i : -1;
}
if (info->endian == BFD_ENDIAN_LITTLE)
instruction = bfd_getl32 (insn);
else
instruction = bfd_getb32 (insn);
for (n = 0, op = shmedia_table;
op->name != NULL
&& ((instruction & shmedia_opcode_mask_table[n]) != op->opcode_base);
n++, op++)
;
if (op->name == NULL)
{
fprintf_fn (stream, ".long 0x%08lx", instruction);
return 4;
}
fprintf_fn (stream, "%s\t", op->name);
for (i = 0; i < 3 && op->arg[i] != A_NONE; i++)
{
unsigned long temp = instruction >> op->nibbles[i];
int by_number = 0;
if (i > 0 && op->arg[i] != A_REUSE_PREV)
fprintf_fn (stream, ",");
switch (op->arg[i])
{
case A_REUSE_PREV:
continue;
case A_GREG_M:
case A_GREG_N:
case A_GREG_D:
r = temp & 0x3f;
fprintf_fn (stream, "r%d", r);
break;
case A_FVREG_F:
case A_FVREG_G:
case A_FVREG_H:
r = temp & 0x3f;
fprintf_fn (stream, "fv%d", r);
break;
case A_FPREG_F:
case A_FPREG_G:
case A_FPREG_H:
r = temp & 0x3f;
fprintf_fn (stream, "fp%d", r);
break;
case A_FMREG_F:
case A_FMREG_G:
case A_FMREG_H:
r = temp & 0x3f;
fprintf_fn (stream, "mtrx%d", r);
break;
case A_CREG_K:
case A_CREG_J:
{
const char *name;
r = temp & 0x3f;
name = creg_name (r);
if (name != NULL)
fprintf_fn (stream, "%s", name);
else
fprintf_fn (stream, "cr%d", r);
}
break;
case A_FREG_G:
case A_FREG_H:
case A_FREG_F:
r = temp & 0x3f;
fprintf_fn (stream, "fr%d", r);
break;
case A_DREG_G:
case A_DREG_H:
case A_DREG_F:
r = temp & 0x3f;
fprintf_fn (stream, "dr%d", r);
break;
case A_TREG_A:
case A_TREG_B:
r = temp & 0x7;
fprintf_fn (stream, "tr%d", r);
break;
case A_IMMS6:
imm = temp & 0x3f;
if (imm & (unsigned long) 0x20)
imm |= ~(unsigned long) 0x3f;
fprintf_fn (stream, "%ld", imm);
break;
case A_IMMS6BY32:
imm = temp & 0x3f;
if (imm & (unsigned long) 0x20)
imm |= ~(unsigned long) 0x3f;
fprintf_fn (stream, "%ld", imm * 32);
break;
case A_IMMS10BY8:
by_number++;
case A_IMMS10BY4:
by_number++;
case A_IMMS10BY2:
by_number++;
case A_IMMS10:
case A_IMMS10BY1:
imm = temp & 0x3ff;
if (imm & (unsigned long) 0x200)
imm |= ~(unsigned long) 0x3ff;
imm <<= by_number;
fprintf_fn (stream, "%ld", imm);
break;
case A_IMMS16:
imm = temp & 0xffff;
if (imm & (unsigned long) 0x8000)
imm |= ~((unsigned long) 0xffff);
fprintf_fn (stream, "%ld", imm);
break;
case A_PCIMMS16BY4:
imm = temp & 0xffff;
if (imm & (unsigned long) 0x8000)
imm |= ~(unsigned long) 0xffff;
imm <<= 2;
disp_pc_addr = (bfd_vma) imm + memaddr;
(*info->print_address_func) (disp_pc_addr, info);
break;
case A_IMMU5:
imm = temp & 0x1f;
fprintf_fn (stream, "%ld", imm);
break;
case A_IMMU6:
imm = temp & 0x3f;
fprintf_fn (stream, "%ld", imm);
break;
case A_IMMU16:
imm = temp & 0xffff;
fprintf_fn (stream, "%ld", imm);
break;
default:
abort ();
break;
}
}
if (op->opcode_base == (unsigned long) SHMEDIA_SHORI_OPC
&& SAVED_MOVI_R (info) == r)
{
asection *section = info->section;
if (section == NULL
&& info->symbols != NULL
&& bfd_asymbol_flavour (info->symbols[0]) == bfd_target_elf_flavour
&& ! bfd_is_und_section (bfd_get_section (info->symbols[0]))
&& ! bfd_is_abs_section (bfd_get_section (info->symbols[0])))
section = bfd_get_section (info->symbols[0]);
if (section == NULL
|| section->owner == NULL
|| elf_elfheader (section->owner)->e_type == ET_EXEC)
{
bfd_signed_vma shori_addr;
shori_addr = SAVED_MOVI_IMM (info) << 16;
shori_addr |= imm;
fprintf_fn (stream, "\t! 0x");
(*info->print_address_func) (shori_addr, info);
}
}
if (op->opcode_base == SHMEDIA_MOVI_OPC)
{
SAVED_MOVI_IMM (info) = imm;
SAVED_MOVI_R (info) = r;
}
else
{
SAVED_MOVI_IMM (info) = 0;
SAVED_MOVI_R (info) = 255;
}
return 4;
}
static enum sh64_elf_cr_type
sh64_get_contents_type_disasm (bfd_vma memaddr, struct disassemble_info *info)
{
struct sh64_disassemble_info *sh64_infop = info->private_data;
if (sh64_infop->crange.cr_type != CRT_NONE
&& memaddr >= sh64_infop->crange.cr_addr
&& memaddr < sh64_infop->crange.cr_addr + sh64_infop->crange.cr_size)
return sh64_infop->crange.cr_type;
if (info->section
&& bfd_get_flavour (info->section->owner) == bfd_target_elf_flavour)
{
enum sh64_elf_cr_type cr_type
= sh64_get_contents_type (info->section, memaddr,
&sh64_infop->crange);
if (cr_type != CRT_NONE)
return cr_type;
}
if (info->symbols != NULL
&& bfd_asymbol_flavour (info->symbols[0]) == bfd_target_elf_flavour
&& ! bfd_is_und_section (bfd_get_section (info->symbols[0]))
&& ! bfd_is_abs_section (bfd_get_section (info->symbols[0])))
{
enum sh64_elf_cr_type cr_type
= sh64_get_contents_type (bfd_get_section (info->symbols[0]),
memaddr, &sh64_infop->crange);
if (cr_type != CRT_NONE)
return cr_type;
}
if (info->symbols
&& bfd_asymbol_flavour (info->symbols[0]) == bfd_target_elf_flavour
&& elf_symbol_from (bfd_asymbol_bfd (info->symbols[0]),
info->symbols[0])->internal_elf_sym.st_other
== STO_SH5_ISA32)
return CRT_SH5_ISA32;
return (memaddr & 1) == 1 ? CRT_SH5_ISA32 : CRT_SH5_ISA16;
}
static bfd_boolean
init_sh64_disasm_info (struct disassemble_info *info)
{
struct sh64_disassemble_info *sh64_infop
= calloc (sizeof (*sh64_infop), 1);
if (sh64_infop == NULL)
return FALSE;
info->private_data = sh64_infop;
SAVED_MOVI_IMM (info) = 0;
SAVED_MOVI_R (info) = 255;
if (shmedia_opcode_mask_table == NULL)
initialize_shmedia_opcode_mask_table ();
return TRUE;
}
int
print_insn_sh64x_media (bfd_vma memaddr, struct disassemble_info *info)
{
if (info->private_data == NULL && ! init_sh64_disasm_info (info))
return -1;
info->bytes_per_line = 4;
info->bytes_per_chunk = 4;
return print_insn_shmedia (memaddr, info);
}
int
print_insn_sh64 (bfd_vma memaddr, struct disassemble_info *info)
{
enum bfd_endian endian = info->endian;
enum sh64_elf_cr_type cr_type;
if (info->private_data == NULL && ! init_sh64_disasm_info (info))
return -1;
cr_type = sh64_get_contents_type_disasm (memaddr, info);
if (cr_type != CRT_SH5_ISA16)
{
int length = 4 - (memaddr % 4);
info->display_endian = endian;
if (cr_type == CRT_SH5_ISA32 && length == 3)
memaddr--, length = 4;
if (cr_type == CRT_SH5_ISA32 && length == 4)
return print_insn_sh64x_media (memaddr, info);
if (cr_type == CRT_DATA || length != 4)
{
int status;
unsigned char data[4];
struct sh64_disassemble_info *sh64_infop = info->private_data;
if (length == 4
&& sh64_infop->crange.cr_type != CRT_NONE
&& memaddr >= sh64_infop->crange.cr_addr
&& memaddr < (sh64_infop->crange.cr_addr
+ sh64_infop->crange.cr_size))
length
= (sh64_infop->crange.cr_addr
+ sh64_infop->crange.cr_size - memaddr);
status
= (*info->read_memory_func) (memaddr, data,
length >= 4 ? 4 : length, info);
if (status == 0 && length >= 4)
{
(*info->fprintf_func) (info->stream, ".long 0x%08lx",
endian == BFD_ENDIAN_BIG
? (long) (bfd_getb32 (data))
: (long) (bfd_getl32 (data)));
return 4;
}
else
{
int i;
for (i = 0; i < length; i++)
{
status = info->read_memory_func (memaddr + i, data, 1, info);
if (status != 0)
break;
(*info->fprintf_func) (info->stream, "%s0x%02x",
i == 0 ? ".byte " : ", ",
data[0]);
}
return i ? i : -1;
}
}
}
return -2;
}