blob: dfb03df601cc5b2af31be68459ec14ae3c325dc7 [file] [log] [blame]
/* Single instruction disassembler for the Visium.
Copyright (C) 2002-2023 Free Software Foundation, Inc.
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"
#include "disassemble.h"
#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 = ((ins & 0xffff) ^ 0x8000) - 0x8000;
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 %d,r%d", indx, source_a);
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 = &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 = (unsigned) 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;
}