2014-12-06 16:25:55 +01:00
|
|
|
/* Single instruction disassembler for the Visium.
|
|
|
|
|
2017-01-02 04:36:43 +01:00
|
|
|
Copyright (C) 2002-2017 Free Software Foundation, Inc.
|
2014-12-06 16:25:55 +01:00
|
|
|
|
|
|
|
This file is part of the GNU opcodes library.
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
It is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
|
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
|
|
|
License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
|
|
|
MA 02110-1301, USA. */
|
|
|
|
|
|
|
|
#include "sysdep.h"
|
Move print_insn_XXX to an opcodes internal header
With the changes done in previous patches, print_insn_XXX functions
don't have to be external visible out of opcodes, because both gdb
and objdump select disassemblers through a single interface.
This patch moves these print_insn_XXX declarations from
include/dis-asm.h to opcodes/disassemble.h, which is a new header
added by this patch.
include:
2017-05-24 Yao Qi <yao.qi@linaro.org>
* dis-asm.h: Move some function declarations to
opcodes/disassemble.h.
opcodes:
2017-05-24 Yao Qi <yao.qi@linaro.org>
* alpha-dis.c: Include disassemble.h, don't include
dis-asm.h.
* avr-dis.c, bfin-dis.c, cr16-dis.c: Likewise.
* crx-dis.c, d10v-dis.c, d30v-dis.c: Likewise.
* disassemble.c, dlx-dis.c, epiphany-dis.c: Likewise.
* fr30-dis.c, ft32-dis.c, h8300-dis.c, h8500-dis.c: Likewise.
* hppa-dis.c, i370-dis.c, i386-dis.c: Likewise.
* i860-dis.c, i960-dis.c, ip2k-dis.c: Likewise.
* iq2000-dis.c, lm32-dis.c, m10200-dis.c: Likewise.
* m10300-dis.c, m32r-dis.c, m68hc11-dis.c: Likewise.
* m68k-dis.c, m88k-dis.c, mcore-dis.c: Likewise.
* metag-dis.c, microblaze-dis.c, mmix-dis.c: Likewise.
* moxie-dis.c, msp430-dis.c, mt-dis.c:
* nds32-dis.c, nios2-dis.c, ns32k-dis.c: Likewise.
* or1k-dis.c, pdp11-dis.c, pj-dis.c: Likewise.
* ppc-dis.c, pru-dis.c, riscv-dis.c: Likewise.
* rl78-dis.c, s390-dis.c, score-dis.c: Likewise.
* sh-dis.c, sh64-dis.c, tic30-dis.c: Likewise.
* tic4x-dis.c, tic54x-dis.c, tic6x-dis.c: Likewise.
* tic80-dis.c, tilegx-dis.c, tilepro-dis.c: Likewise.
* v850-dis.c, vax-dis.c, visium-dis.c: Likewise.
* w65-dis.c, wasm32-dis.c, xc16x-dis.c: Likewise.
* xgate-dis.c, xstormy16-dis.c, xtensa-dis.c: Likewise.
* z80-dis.c, z8k-dis.c: Likewise.
* disassemble.h: New file.
2017-05-24 18:23:52 +02:00
|
|
|
#include "disassemble.h"
|
2014-12-06 16:25:55 +01:00
|
|
|
#include "opcode/visium.h"
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <setjmp.h>
|
|
|
|
|
|
|
|
/* Maximum length of an instruction. */
|
|
|
|
#define MAXLEN 4
|
|
|
|
|
|
|
|
struct private
|
|
|
|
{
|
|
|
|
/* Points to first byte not fetched. */
|
|
|
|
bfd_byte *max_fetched;
|
|
|
|
bfd_byte the_buffer[MAXLEN];
|
|
|
|
bfd_vma insn_start;
|
|
|
|
jmp_buf bailout;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive)
|
|
|
|
to ADDR (exclusive) are valid. Returns 1 for success, longjmps
|
|
|
|
on error. */
|
|
|
|
#define FETCH_DATA(info, addr) \
|
|
|
|
((addr) <= ((struct private *)(info->private_data))->max_fetched \
|
|
|
|
? 1 : fetch_data ((info), (addr)))
|
|
|
|
|
|
|
|
static int fetch_data (struct disassemble_info *info, bfd_byte * addr);
|
|
|
|
|
|
|
|
static int
|
|
|
|
fetch_data (struct disassemble_info *info, bfd_byte *addr)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
struct private *priv = (struct private *) info->private_data;
|
|
|
|
bfd_vma start = priv->insn_start + (priv->max_fetched - priv->the_buffer);
|
|
|
|
|
|
|
|
status = (*info->read_memory_func) (start,
|
|
|
|
priv->max_fetched,
|
|
|
|
addr - priv->max_fetched, info);
|
|
|
|
if (status != 0)
|
|
|
|
{
|
|
|
|
(*info->memory_error_func) (status, start, info);
|
|
|
|
longjmp (priv->bailout, 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
priv->max_fetched = addr;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *size_names[] = { "?", "b", "w", "?", "l", "?", "?", "?" };
|
|
|
|
|
|
|
|
static char *cc_names[] =
|
|
|
|
{
|
|
|
|
"fa", "eq", "cs", "os", "ns", "ne", "cc", "oc",
|
|
|
|
"nc", "ge", "gt", "hi", "le", "ls", "lt", "tr"
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Disassemble non-storage relative instructions. */
|
|
|
|
|
|
|
|
static int
|
|
|
|
disassem_class0 (disassemble_info *info, unsigned int ins)
|
|
|
|
{
|
|
|
|
int opcode = (ins >> 21) & 0x000f;
|
|
|
|
|
|
|
|
if (ins & CLASS0_UNUSED_MASK)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
switch (opcode)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
/* BRR instruction. */
|
|
|
|
{
|
|
|
|
unsigned cbf = (ins >> 27) & 0x000f;
|
|
|
|
int displacement = ((int) (ins << 16)) >> 16;
|
|
|
|
|
|
|
|
if (ins == 0)
|
|
|
|
(*info->fprintf_func) (info->stream, "nop");
|
|
|
|
else
|
|
|
|
(*info->fprintf_func) (info->stream, "brr %s,%+d",
|
|
|
|
cc_names[cbf], displacement);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 11:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 13:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 14:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 15:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
illegal_opcode:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Disassemble non-storage register class instructions. */
|
|
|
|
|
|
|
|
static int
|
|
|
|
disassem_class1 (disassemble_info *info, unsigned int ins)
|
|
|
|
{
|
|
|
|
int opcode = (ins >> 21) & 0xf;
|
|
|
|
int source_a = (ins >> 16) & 0x1f;
|
|
|
|
int source_b = (ins >> 4) & 0x1f;
|
|
|
|
int indx = (ins >> 10) & 0x1f;
|
|
|
|
|
|
|
|
int size = ins & 0x7;
|
|
|
|
|
|
|
|
if (ins & CLASS1_UNUSED_MASK)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
switch (opcode)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
/* Stop. */
|
|
|
|
(*info->fprintf_func) (info->stream, "stop");
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
/* BMI - Block Move Indirect. */
|
|
|
|
if (ins != BMI)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
(*info->fprintf_func) (info->stream, "bmi r1,r2,r3");
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
/* BMD - Block Move Direct. */
|
|
|
|
if (ins != BMD)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
(*info->fprintf_func) (info->stream, "bmd r1,r2,r3");
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
/* DSI - Disable Interrupts. */
|
|
|
|
if (ins != DSI)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
(*info->fprintf_func) (info->stream, "dsi");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 5:
|
|
|
|
/* ENI - Enable Interrupts. */
|
|
|
|
if (ins != ENI)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
(*info->fprintf_func) (info->stream, "eni");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 6:
|
|
|
|
/* Illegal opcode (was EUT). */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
/* RFI - Return from Interrupt. */
|
|
|
|
if (ins != RFI)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
(*info->fprintf_func) (info->stream, "rfi");
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 11:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 13:
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 14:
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 15:
|
|
|
|
if (ins & EAM_SELECT_MASK)
|
|
|
|
{
|
|
|
|
/* Extension arithmetic module write */
|
|
|
|
int fp_ins = (ins >> 27) & 0xf;
|
|
|
|
|
|
|
|
if (size != 4)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
if (ins & FP_SELECT_MASK)
|
|
|
|
{
|
|
|
|
/* Which floating point instructions don't need a fsrcB
|
|
|
|
register. */
|
|
|
|
const int no_fsrcb[16] = { 1, 0, 0, 0, 0, 1, 1, 1,
|
|
|
|
1, 1, 0, 0, 1, 0, 0, 0
|
|
|
|
};
|
|
|
|
if (no_fsrcb[fp_ins] && source_b)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
/* Check that none of the floating register register numbers
|
|
|
|
is higher than 15. (If this is fload, then srcA is a
|
|
|
|
general register. */
|
|
|
|
if (ins & ((1 << 14) | (1 << 8)) || (fp_ins && ins & (1 << 20)))
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
switch (fp_ins)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
(*info->fprintf_func) (info->stream, "fload f%d,r%d",
|
|
|
|
indx, source_a);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
(*info->fprintf_func) (info->stream, "fadd f%d,f%d,f%d",
|
|
|
|
indx, source_a, source_b);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
(*info->fprintf_func) (info->stream, "fsub f%d,f%d,f%d",
|
|
|
|
indx, source_a, source_b);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
(*info->fprintf_func) (info->stream, "fmult f%d,f%d,f%d",
|
|
|
|
indx, source_a, source_b);
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
(*info->fprintf_func) (info->stream, "fdiv f%d,f%d,f%d",
|
|
|
|
indx, source_a, source_b);
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
(*info->fprintf_func) (info->stream, "fsqrt f%d,f%d",
|
|
|
|
indx, source_a);
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
(*info->fprintf_func) (info->stream, "fneg f%d,f%d",
|
|
|
|
indx, source_a);
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
(*info->fprintf_func) (info->stream, "fabs f%d,f%d",
|
|
|
|
indx, source_a);
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
(*info->fprintf_func) (info->stream, "ftoi f%d,f%d",
|
|
|
|
indx, source_a);
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
(*info->fprintf_func) (info->stream, "itof f%d,f%d",
|
|
|
|
indx, source_a);
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
(*info->fprintf_func) (info->stream, "fmove f%d,f%d",
|
|
|
|
indx, source_a);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
(*info->fprintf_func) (info->stream,
|
|
|
|
"fpinst %d,f%d,f%d,f%d", fp_ins,
|
|
|
|
indx, source_a, source_b);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Which EAM operations do not need a srcB register. */
|
|
|
|
const int no_srcb[32] =
|
|
|
|
{ 0, 0, 1, 1, 0, 1, 1, 1,
|
|
|
|
0, 1, 1, 1, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0
|
|
|
|
};
|
|
|
|
|
|
|
|
if (no_srcb[indx] && source_b)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
if (fp_ins)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
switch (indx)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
(*info->fprintf_func) (info->stream, "mults r%d,r%d",
|
|
|
|
source_a, source_b);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
(*info->fprintf_func) (info->stream, "multu r%d,r%d",
|
|
|
|
source_a, source_b);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
(*info->fprintf_func) (info->stream, "divs r%d",
|
|
|
|
source_a);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
(*info->fprintf_func) (info->stream, "divu r%d",
|
|
|
|
source_a);
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
(*info->fprintf_func) (info->stream, "writemd r%d,r%d",
|
|
|
|
source_a, source_b);
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
(*info->fprintf_func) (info->stream, "writemdc r%d",
|
|
|
|
source_a);
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
(*info->fprintf_func) (info->stream, "divds r%d",
|
|
|
|
source_a);
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
(*info->fprintf_func) (info->stream, "divdu r%d",
|
|
|
|
source_a);
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
(*info->fprintf_func) (info->stream, "asrd r%d",
|
|
|
|
source_a);
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
(*info->fprintf_func) (info->stream, "lsrd r%d",
|
|
|
|
source_a);
|
|
|
|
break;
|
|
|
|
case 11:
|
|
|
|
(*info->fprintf_func) (info->stream, "asld r%d",
|
|
|
|
source_a);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
(*info->fprintf_func) (info->stream,
|
|
|
|
"eamwrite %d,r%d,r%d", indx,
|
|
|
|
source_a, source_b);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* WRITE - write to memory. */
|
|
|
|
(*info->fprintf_func) (info->stream, "write.%s %d(r%d),r%d",
|
|
|
|
size_names[size], indx, source_a, source_b);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
illegal_opcode:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Disassemble storage immediate class instructions. */
|
|
|
|
|
|
|
|
static int
|
|
|
|
disassem_class2 (disassemble_info *info, unsigned int ins)
|
|
|
|
{
|
|
|
|
int opcode = (ins >> 21) & 0xf;
|
|
|
|
int source_a = (ins >> 16) & 0x1f;
|
|
|
|
unsigned immediate = ins & 0x0000ffff;
|
|
|
|
|
|
|
|
if (ins & CC_MASK)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
switch (opcode)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
/* ADDI instruction. */
|
|
|
|
(*info->fprintf_func) (info->stream, "addi r%d,%d", source_a,
|
|
|
|
immediate);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
/* SUBI instruction. */
|
|
|
|
(*info->fprintf_func) (info->stream, "subi r%d,%d", source_a,
|
|
|
|
immediate);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
/* MOVIL instruction. */
|
|
|
|
(*info->fprintf_func) (info->stream, "movil r%d,0x%04X", source_a,
|
|
|
|
immediate);
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
/* MOVIU instruction. */
|
|
|
|
(*info->fprintf_func) (info->stream, "moviu r%d,0x%04X", source_a,
|
|
|
|
immediate);
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
/* MOVIQ instruction. */
|
|
|
|
(*info->fprintf_func) (info->stream, "moviq r%d,%u", source_a,
|
|
|
|
immediate);
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
/* WRTL instruction. */
|
|
|
|
if (source_a != 0)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
(*info->fprintf_func) (info->stream, "wrtl 0x%04X", immediate);
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
/* WRTU instruction. */
|
|
|
|
if (source_a != 0)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
(*info->fprintf_func) (info->stream, "wrtu 0x%04X", immediate);
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 11:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 13:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 14:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
case 15:
|
|
|
|
/* Illegal opcode. */
|
|
|
|
goto illegal_opcode;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
illegal_opcode:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Disassemble storage register class instructions. */
|
|
|
|
|
|
|
|
static int
|
|
|
|
disassem_class3 (disassemble_info *info, unsigned int ins)
|
|
|
|
{
|
|
|
|
int opcode = (ins >> 21) & 0xf;
|
|
|
|
int source_b = (ins >> 4) & 0x1f;
|
|
|
|
int source_a = (ins >> 16) & 0x1f;
|
|
|
|
int size = ins & 0x7;
|
|
|
|
int dest = (ins >> 10) & 0x1f;
|
|
|
|
|
|
|
|
/* Those instructions that don't have a srcB register. */
|
|
|
|
const int no_srcb[16] =
|
|
|
|
{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0 };
|
|
|
|
|
|
|
|
/* These are instructions which can take an immediate srcB value. */
|
|
|
|
const int srcb_immed[16] =
|
|
|
|
{ 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1 };
|
|
|
|
|
|
|
|
/* User opcodes should not provide a non-zero srcB register
|
|
|
|
when none is required. Only a BRA or floating point
|
|
|
|
instruction should have a non-zero condition code field.
|
|
|
|
Only a WRITE or EAMWRITE (opcode 15) should select an EAM
|
|
|
|
or floating point operation. Note that FP_SELECT_MASK is
|
|
|
|
the same bit (bit 3) as the interrupt bit which
|
|
|
|
distinguishes SYS1 from BRA and SYS2 from RFLAG. */
|
|
|
|
if ((no_srcb[opcode] && source_b)
|
|
|
|
|| (!srcb_immed[opcode] && ins & CLASS3_SOURCEB_IMMED)
|
|
|
|
|| (opcode != 12 && opcode != 15 && ins & CC_MASK)
|
|
|
|
|| (opcode != 15 && ins & (EAM_SELECT_MASK | FP_SELECT_MASK)))
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
|
|
|
|
switch (opcode)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
/* ADD instruction. */
|
|
|
|
(*info->fprintf_func) (info->stream, "add.%s r%d,r%d,r%d",
|
|
|
|
size_names[size], dest, source_a, source_b);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
/* ADC instruction. */
|
|
|
|
(*info->fprintf_func) (info->stream, "adc.%s r%d,r%d,r%d",
|
|
|
|
size_names[size], dest, source_a, source_b);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
/* SUB instruction. */
|
|
|
|
if (dest == 0)
|
|
|
|
(*info->fprintf_func) (info->stream, "cmp.%s r%d,r%d",
|
|
|
|
size_names[size], source_a, source_b);
|
|
|
|
else
|
|
|
|
(*info->fprintf_func) (info->stream, "sub.%s r%d,r%d,r%d",
|
|
|
|
size_names[size], dest, source_a, source_b);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
/* SUBC instruction. */
|
|
|
|
if (dest == 0)
|
|
|
|
(*info->fprintf_func) (info->stream, "cmpc.%s r%d,r%d",
|
|
|
|
size_names[size], source_a, source_b);
|
|
|
|
else
|
|
|
|
(*info->fprintf_func) (info->stream, "subc.%s r%d,r%d,r%d",
|
|
|
|
size_names[size], dest, source_a, source_b);
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
/* EXTW instruction. */
|
|
|
|
if (size == 1)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
(*info->fprintf_func) (info->stream, "extw.%s r%d,r%d",
|
|
|
|
size_names[size], dest, source_a);
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
/* ASR instruction. */
|
|
|
|
if (ins & CLASS3_SOURCEB_IMMED)
|
|
|
|
(*info->fprintf_func) (info->stream, "asr.%s r%d,r%d,%d",
|
|
|
|
size_names[size], dest, source_a, source_b);
|
|
|
|
else
|
|
|
|
(*info->fprintf_func) (info->stream, "asr.%s r%d,r%d,r%d",
|
|
|
|
size_names[size], dest, source_a, source_b);
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
/* LSR instruction. */
|
|
|
|
if (ins & CLASS3_SOURCEB_IMMED)
|
|
|
|
(*info->fprintf_func) (info->stream, "lsr.%s r%d,r%d,%d",
|
|
|
|
size_names[size], dest, source_a, source_b);
|
|
|
|
else
|
|
|
|
(*info->fprintf_func) (info->stream, "lsr.%s r%d,r%d,r%d",
|
|
|
|
size_names[size], dest, source_a, source_b);
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
/* ASL instruction. */
|
|
|
|
if (ins & CLASS3_SOURCEB_IMMED)
|
|
|
|
(*info->fprintf_func) (info->stream, "asl.%s r%d,r%d,%d",
|
|
|
|
size_names[size], dest, source_a, source_b);
|
|
|
|
else
|
|
|
|
(*info->fprintf_func) (info->stream, "asl.%s r%d,r%d,r%d",
|
|
|
|
size_names[size], dest, source_a, source_b);
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
/* XOR instruction. */
|
|
|
|
(*info->fprintf_func) (info->stream, "xor.%s r%d,r%d,r%d",
|
|
|
|
size_names[size], dest, source_a, source_b);
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
/* OR instruction. */
|
|
|
|
if (source_b == 0)
|
|
|
|
(*info->fprintf_func) (info->stream, "move.%s r%d,r%d",
|
|
|
|
size_names[size], dest, source_a);
|
|
|
|
else
|
|
|
|
(*info->fprintf_func) (info->stream, "or.%s r%d,r%d,r%d",
|
|
|
|
size_names[size], dest, source_a, source_b);
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
/* AND instruction. */
|
|
|
|
(*info->fprintf_func) (info->stream, "and.%s r%d,r%d,r%d",
|
|
|
|
size_names[size], dest, source_a, source_b);
|
|
|
|
break;
|
|
|
|
case 11:
|
|
|
|
/* NOT instruction. */
|
|
|
|
(*info->fprintf_func) (info->stream, "not.%s r%d,r%d",
|
|
|
|
size_names[size], dest, source_a);
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
/* BRA instruction. */
|
|
|
|
{
|
|
|
|
unsigned cbf = (ins >> 27) & 0x000f;
|
|
|
|
|
|
|
|
if (size != 4)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
(*info->fprintf_func) (info->stream, "bra %s,r%d,r%d",
|
|
|
|
cc_names[cbf], source_a, dest);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 13:
|
|
|
|
/* RFLAG instruction. */
|
|
|
|
if (source_a || size != 4)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
(*info->fprintf_func) (info->stream, "rflag r%d", dest);
|
|
|
|
break;
|
|
|
|
case 14:
|
|
|
|
/* EXTB instruction. */
|
|
|
|
(*info->fprintf_func) (info->stream, "extb.%s r%d,r%d",
|
|
|
|
size_names[size], dest, source_a);
|
|
|
|
break;
|
|
|
|
case 15:
|
|
|
|
if (!(ins & CLASS3_SOURCEB_IMMED))
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
if (ins & EAM_SELECT_MASK)
|
|
|
|
{
|
|
|
|
/* Extension arithmetic module read. */
|
|
|
|
int fp_ins = (ins >> 27) & 0xf;
|
|
|
|
|
|
|
|
if (size != 4)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
if (ins & FP_SELECT_MASK)
|
|
|
|
{
|
|
|
|
/* Check fsrcA <= 15 and fsrcB <= 15. */
|
|
|
|
if (ins & ((1 << 20) | (1 << 8)))
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
switch (fp_ins)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
if (source_b)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
(*info->fprintf_func) (info->stream, "fstore r%d,f%d",
|
|
|
|
dest, source_a);
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
(*info->fprintf_func) (info->stream, "fcmp r%d,f%d,f%d",
|
|
|
|
dest, source_a, source_b);
|
|
|
|
break;
|
|
|
|
case 11:
|
|
|
|
(*info->fprintf_func) (info->stream, "fcmpe r%d,f%d,f%d",
|
|
|
|
dest, source_a, source_b);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
(*info->fprintf_func) (info->stream,
|
|
|
|
"fpuread %d,r%d,f%d,f%d", fp_ins,
|
|
|
|
dest, source_a, source_b);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (fp_ins || source_a)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
switch (source_b)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
(*info->fprintf_func) (info->stream, "readmda r%d", dest);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
(*info->fprintf_func) (info->stream, "readmdb r%d", dest);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
(*info->fprintf_func) (info->stream, "readmdc r%d", dest);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
(*info->fprintf_func) (info->stream, "eamread r%d,%d",
|
|
|
|
dest, source_b);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (ins & FP_SELECT_MASK)
|
|
|
|
goto illegal_opcode;
|
|
|
|
|
|
|
|
/* READ instruction. */
|
|
|
|
(*info->fprintf_func) (info->stream, "read.%s r%d,%d(r%d)",
|
|
|
|
size_names[size], dest, source_b, source_a);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
illegal_opcode:
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Print the visium instruction at address addr in debugged memory,
|
|
|
|
on info->stream. Return length of the instruction, in bytes. */
|
|
|
|
|
|
|
|
int
|
|
|
|
print_insn_visium (bfd_vma addr, disassemble_info *info)
|
|
|
|
{
|
|
|
|
unsigned ins;
|
|
|
|
unsigned p1, p2;
|
|
|
|
int ans;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Stuff copied from m68k-dis.c. */
|
|
|
|
struct private priv;
|
|
|
|
bfd_byte *buffer = priv.the_buffer;
|
|
|
|
info->private_data = (PTR) & priv;
|
|
|
|
priv.max_fetched = priv.the_buffer;
|
|
|
|
priv.insn_start = addr;
|
|
|
|
if (setjmp (priv.bailout) != 0)
|
|
|
|
{
|
|
|
|
/* Error return. */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We do return this info. */
|
|
|
|
info->insn_info_valid = 1;
|
|
|
|
|
|
|
|
/* Assume non branch insn. */
|
|
|
|
info->insn_type = dis_nonbranch;
|
|
|
|
|
|
|
|
/* Assume no delay. */
|
|
|
|
info->branch_delay_insns = 0;
|
|
|
|
|
|
|
|
/* Assume no target known. */
|
|
|
|
info->target = 0;
|
|
|
|
|
|
|
|
/* Get 32-bit instruction word. */
|
|
|
|
FETCH_DATA (info, buffer + 4);
|
|
|
|
ins = buffer[0] << 24;
|
|
|
|
ins |= buffer[1] << 16;
|
|
|
|
ins |= buffer[2] << 8;
|
|
|
|
ins |= buffer[3];
|
|
|
|
|
|
|
|
ans = 0;
|
|
|
|
|
|
|
|
p1 = buffer[0] ^ buffer[1] ^ buffer[2] ^ buffer[3];
|
|
|
|
p2 = 0;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
p2 += p1 & 1;
|
|
|
|
p1 >>= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decode the instruction. */
|
|
|
|
if (p2 & 1)
|
|
|
|
ans = -1;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch ((ins >> 25) & 0x3)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
ans = disassem_class0 (info, ins);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
ans = disassem_class1 (info, ins);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
ans = disassem_class2 (info, ins);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
ans = disassem_class3 (info, ins);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ans != 0)
|
|
|
|
(*info->fprintf_func) (info->stream, "err");
|
|
|
|
|
|
|
|
/* Return number of bytes consumed (always 4 for the Visium). */
|
|
|
|
return 4;
|
|
|
|
}
|