/* MeP opcode support. -*- C -*-
Copyright 2011 Free Software Foundation, Inc.
Contributed by Red Hat Inc;
This file is part of the GNU Binutils.
This program 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 of the License, or
(at your option) any later version.
This program 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. */
extern int mep_insn_supported_by_isa (const CGEN_INSN *, CGEN_ATTR_VALUE_BITSET_TYPE *);
/* A mask for all ISAs executed by the core. */
#define MEP_ALL_CORE_ISAS_MASK mep_all_core_isas_mask
extern CGEN_ATTR_VALUE_BITSET_TYPE mep_all_core_isas_mask;
/* A mask for all ISAs executed by a VLIW coprocessor. */
#define MEP_ALL_COP_ISAS_MASK mep_all_cop_isas_mask
extern CGEN_ATTR_VALUE_BITSET_TYPE mep_all_cop_isas_mask;
switch (type)
{
case MEP_OPERAND_PCREL8A2:
case MEP_OPERAND_PCREL12A2:
case MEP_OPERAND_PCREL17A2:
case MEP_OPERAND_PCREL24A2:
err = cgen_parse_signed_integer (cd, strp, type, field);
break;
case MEP_OPERAND_PCABS24A2:
case MEP_OPERAND_UDISP7:
case MEP_OPERAND_UDISP7A2:
case MEP_OPERAND_UDISP7A4:
case MEP_OPERAND_UIMM7A4:
case MEP_OPERAND_ADDR24A4:
err = cgen_parse_unsigned_integer (cd, strp, type, (unsigned long *) field);
break;
default:
abort();
}
if (err)
return err;
switch (type)
{
case MEP_OPERAND_UDISP7:
lsbs = 0;
break;
case MEP_OPERAND_PCREL8A2:
case MEP_OPERAND_PCREL12A2:
case MEP_OPERAND_PCREL17A2:
case MEP_OPERAND_PCREL24A2:
case MEP_OPERAND_PCABS24A2:
case MEP_OPERAND_UDISP7A2:
lsbs = *field & 1;
break;
case MEP_OPERAND_UDISP7A4:
case MEP_OPERAND_UIMM7A4:
case MEP_OPERAND_ADDR24A4:
lsbs = *field & 3;
break;
lsbs = *field & 7;
break;
default:
/* Safe assumption? */
abort ();
}
if (lsbs)
return "Value is not aligned enough";
return NULL;
}
static const char *
parse_signed16_range (CGEN_CPU_DESC cd,
const char **strp,
int opindex,
signed long *valuep)
{
const char *errmsg = 0;
signed long value;
/*fprintf(stderr, "dj: signed parse opindex `%s'\n", *strp);*/
/* Prevent ($ry) from being attempted as an expression on 'sw $rx,($ry)'.
It will fail and cause ry to be listed as an undefined symbol in the
listing. */
if (strncmp (*strp, "($", 2) == 0)
return "not zero"; /* any string will do -- will never be seen. */
if (strncasecmp (*strp, "%lo(", 4) == 0)
{
*strp += 4;
errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_MEP_LOW16,
&result_type, &value);
if (**strp != ')')
return "missing `)'";
++*strp;
if (errmsg == NULL
&& (result_type != CGEN_PARSE_OPERAND_RESULT_NUMBER || value != 0))
return "not zero"; /* any string will do -- will never be seen. */
*valuep = value;
return errmsg;
}
if (strncasecmp (*strp, "%hi(", 4) == 0)
{
*strp += 4;
errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_MEP_HI16S,
&result_type, &value);
if (**strp != ')')
return "missing `)'";
++*strp;
if (errmsg == NULL
&& (result_type != CGEN_PARSE_OPERAND_RESULT_NUMBER || value != 0))
return "not zero"; /* any string will do -- will never be seen. */
*valuep = value;
return errmsg;
}
if (strncasecmp (*strp, "%uhi(", 5) == 0)
{
*strp += 5;
errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_MEP_HI16U,
&result_type, &value);
if (**strp != ')')
return "missing `)'";
++*strp;
if (errmsg == NULL
&& (result_type != CGEN_PARSE_OPERAND_RESULT_NUMBER || value != 0))
return "not zero"; /* any string will do -- will never be seen. */
*valuep = value;
return errmsg;
}
if (strncasecmp (*strp, "%sdaoff(", 8) == 0)
{
*strp += 8;
errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_MEP_GPREL,
&result_type, &value);
if (**strp != ')')
return "missing `)'";
++*strp;
if (errmsg == NULL
&& (result_type != CGEN_PARSE_OPERAND_RESULT_NUMBER || value != 0))
return "not zero"; /* any string will do -- will never be seen. */
*valuep = value;
return errmsg;
}
if (strncasecmp (*strp, "%tpoff(", 7) == 0)
{
*strp += 7;
errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_MEP_TPREL,
&result_type, &value);
if (**strp != ')')
return "missing `)'";
++*strp;
if (errmsg == NULL
&& (result_type != CGEN_PARSE_OPERAND_RESULT_NUMBER || value != 0))
return "not zero"; /* any string will do -- will never be seen. */
*valuep = value;
return errmsg;
}
if (**strp == '%')
return "invalid %function() here";
errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_NONE,
&result_type, &value);
if (errmsg == NULL
&& (result_type != CGEN_PARSE_OPERAND_RESULT_NUMBER || value != 0))
return "not zero"; /* any string will do -- will never be seen. */
static ATTRIBUTE_UNUSED const char *
parse_cdisp10 (CGEN_CPU_DESC cd,
const char **strp,
int opindex,
long *valuep)
{
const char *errmsg = 0;
signed long value;
long have_zero = 0;
int wide = 0;
int alignment;
switch (opindex)
{
case MEP_OPERAND_CDISP10A4:
alignment = 2;
break;
case MEP_OPERAND_CDISP10A2:
alignment = 1;
break;
case MEP_OPERAND_CDISP10:
default:
alignment = 0;
break;
}
if ((MEP_CPU & EF_MEP_CPU_MASK) == EF_MEP_CPU_C5)
wide = 1;
if (wide)
{
if (value < -512 || value > 511)
return _("Immediate is out of range -512 to 511");
}
else
{
if (value < -128 || value > 127)
return _("Immediate is out of range -128 to 127");
}
if (value & ((1<<alignment)-1))
return _("Value is not aligned enough");
/* If this field may require a relocation then use larger dsp16. */
if (! have_zero && value == 0)
return (wide ? _("Immediate is out of range -512 to 511")
: _("Immediate is out of range -128 to 127"));
/* Set up a new pointer to macro-expanded string. */
str = expand_string (*strp_in, 1);
/* fprintf (stderr, " expanded <<%s>> to <<%s>>\n", *strp_in, str); */
/* Now work out the advance. */
if (strlen (str) == 0)
*strp_in += strlen (*strp_in);
else
{
if (strstr (*strp_in, str))
/* A macro-expansion was pulled off the front. */
*strp_in = strstr (*strp_in, str);
else
/* A non-macro-expansion was pulled off the front. */
*strp_in += (str - hold);
}
static int
mep_print_vliw_insns (CGEN_CPU_DESC cd, bfd_vma pc, disassemble_info *info,
bfd_byte *buf, int corelength, int copro1length,
int copro2length ATTRIBUTE_UNUSED)
{
int i;
int status = 0;
/* char insnbuf[CGEN_MAX_INSN_SIZE]; */
bfd_byte insnbuf[64];
/* If corelength > 0 then there is a core insn present. It
will be at the beginning of the buffer. After printing
the core insn, we need to print the + on the next line. */
if (corelength > 0)
{
int my_status = 0;
for (i = 0; i < corelength; i++ )
insnbuf[i] = buf[i];
cd->isas = & MEP_CORE_ISA;
/* Print the + to indicate that the following copro insn is
part of a vliw group. */
if (copro1length > 0)
(*info->fprintf_func) (info->stream, " + ");
}
/* Now all that is left to be processed is the coprocessor insns
In vliw mode, there will always be one. Its positioning will
be from byte corelength to byte corelength+copro1length -1.
No need to check for existence. Also, the first vliw insn,
will, as spec'd, always be at least as long as the core insn
so we don't need to flush the buffer. */
if (copro1length > 0)
{
int my_status = 0;
for (i = corelength; i < corelength + copro1length; i++ )
insnbuf[i - corelength] = buf[i];
switch (copro1length)
{
case 0:
break;
case 2:
cd->isas = & MEP_COP16_ISA;
break;
case 4:
cd->isas = & MEP_COP32_ISA;
break;
case 6:
cd->isas = & MEP_COP48_ISA;
break;
case 8:
cd->isas = & MEP_COP64_ISA;
break;
default:
/* Shouldn't be anything but 16,32,48,64. */
break;
}
if (my_status != copro1length)
{
(*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG);
my_status = copro1length;
}
status += my_status;
}
#if 0
/* Now we need to process the second copro insn if it exists. We
have no guarantee that the second copro insn will be longer
than the first, so we have to flush the buffer if we are have
a second copro insn to process. If present, this insn will
be in the position from byte corelength+copro1length to byte
corelength+copro1length+copro2length-1 (which better equal 8
or else we're in big trouble. */
if (copro2length > 0)
{
int my_status = 0;
for (i = 0; i < 64 ; i++)
insnbuf[i] = 0;
for (i = corelength + copro1length; i < 64; i++)
insnbuf[i - (corelength + copro1length)] = buf[i];
switch (copro2length)
{
case 2:
cd->isas = 1 << ISA_EXT_COP1_16;
break;
case 4:
cd->isas = 1 << ISA_EXT_COP1_32;
break;
case 6:
cd->isas = 1 << ISA_EXT_COP1_48;
break;
case 8:
cd->isas = 1 << ISA_EXT_COP1_64;
break;
default:
/* Shouldn't be anything but 16,32,48,64. */
break;
}
/* The two functions mep_examine_vliw[32,64]_insns are used find out
which vliw combinaion (16 bit core with 48 bit copro, 32 bit core
with 32 bit copro, etc.) is present. Later on, when internally
parallel coprocessors are handled, only these functions should
need to be changed.
At this time only the following combinations are supported:
VLIW32 Mode:
16 bit core insn (core) and 16 bit coprocessor insn (cop1)
32 bit core insn (core)
32 bit coprocessor insn (cop1)
Note: As of this time, I do not believe we have enough information
to distinguish a 32 bit core insn from a 32 bit cop insn. Also,
no 16 bit coprocessor insns have been specified.
VLIW64 Mode:
16 bit core insn (core) and 48 bit coprocessor insn (cop1)
32 bit core insn (core) and 32 bit coprocessor insn (cop1)
64 bit coprocessor insn (cop1)
The framework for an internally parallel coprocessor is also
present (2nd coprocessor insn is cop2), but at this time it
is not used. This only appears to be valid in VLIW64 mode. */
static int
mep_examine_vliw32_insns (CGEN_CPU_DESC cd, bfd_vma pc, disassemble_info *info)
{
int status;
int buflength;
int corebuflength;
int cop1buflength;
int cop2buflength;
bfd_byte buf[CGEN_MAX_INSN_SIZE];
char indicator16[1];
char indicatorcop32[2];
/* At this time we're not supporting internally parallel coprocessors,
so cop2buflength will always be 0. */
cop2buflength = 0;
/* If the two high order bits are 00, 01 or 10, we have a 16 bit
core insn and a 48 bit copro insn. */
if ((indicator16[0] & 0x80) && (indicator16[0] & 0x40))
{
if ((indicatorcop32[0] & 0xf0) == 0xf0 && (indicatorcop32[1] & 0x07) == 0x07)
{
/* We have a 32 bit copro insn. */
corebuflength = 0;
/* All 4 4ytes are one copro insn. */
cop1buflength = 4;
}
else
{
/* We have a 32 bit core. */
corebuflength = 4;
cop1buflength = 0;
}
}
else
{
/* We have a 16 bit core insn and a 16 bit copro insn. */
corebuflength = 2;
cop1buflength = 2;
}
/* Now we have the distrubution set. Print them out. */
status = mep_print_vliw_insns (cd, pc, info, buf, corebuflength,
cop1buflength, cop2buflength);
return status;
}
static int
mep_examine_vliw64_insns (CGEN_CPU_DESC cd, bfd_vma pc, disassemble_info *info)
{
int status;
int buflength;
int corebuflength;
int cop1buflength;
int cop2buflength;
bfd_byte buf[CGEN_MAX_INSN_SIZE];
char indicator16[1];
char indicator64[4];
/* At this time we're not supporting internally parallel
coprocessors, so cop2buflength will always be 0. */
cop2buflength = 0;
/* We have all 64 bits in the buffer now. We have to figure out
what combination of instruction sizes are present. The two
high order bits will indicate whether or not we have a 16 bit
core insn or not. If not, then we have to look at the 7,8th
bytes to tell whether we have 64 bit copro insn or a 32 bit
core insn with a 32 bit copro insn. Endianness will make a
difference here. */
/* Put the big endian representation of the bytes to be examined
in the temporary buffers for examination. */
/* If the two high order bits are 00, 01 or 10, we have a 16 bit
core insn and a 48 bit copro insn. */
if ((indicator16[0] & 0x80) && (indicator16[0] & 0x40))
{
if ((indicator64[0] & 0xf0) == 0xf0 && (indicator64[1] & 0x07) == 0x07
&& ((indicator64[2] & 0xfe) != 0xf0 || (indicator64[3] & 0xf4) != 0))
{
/* We have a 64 bit copro insn. */
corebuflength = 0;
/* All 8 bytes are one copro insn. */
cop1buflength = 8;
}
else
{
/* We have a 32 bit core insn and a 32 bit copro insn. */
corebuflength = 4;
cop1buflength = 4;
}
}
else
{
/* We have a 16 bit core insn and a 48 bit copro insn. */
corebuflength = 2;
cop1buflength = 6;
}
/* Now we have the distrubution set. Print them out. */
status = mep_print_vliw_insns (cd, pc, info, buf, corebuflength,
cop1buflength, cop2buflength);
/* Fill in ex_info fields like read_insn would. Don't actually call
read_insn, since the incoming buffer is already read (and possibly
modified a la m32r). */
ex_info.valid = (1 << 8) - 1;
ex_info.dis_info = info;
ex_info.insn_bytes = buf;
/* The instructions are stored in hash lists.
Pick the first one and keep trying until we find the right one. */
if ((insn_value & CGEN_INSN_BASE_MASK (insn))
== CGEN_INSN_BASE_VALUE (insn))
{
/* Printing is handled in two passes. The first pass parses the
machine insn and extracts the fields. The second pass prints
them. */
/* Length < 0 -> error. */
if (length < 0)
return length;
if (length > 0)
{
CGEN_PRINT_FN (cd, insn) (cd, info, insn, &fields, pc, length);
/* Length is in bits, result is in bytes. */
return length / 8;
}
}
insn_list = CGEN_DIS_NEXT_INSN (insn_list);
}
if (slot == SLOTS_P0S)
(*info->fprintf_func) (info->stream, "*unknown-p0s*");
else if (slot == SLOTS_P0)
(*info->fprintf_func) (info->stream, "*unknown-p0*");
else if (slot == SLOTS_P1)
(*info->fprintf_func) (info->stream, "*unknown-p1*");
else if (slot == SLOTS_C3)
(*info->fprintf_func) (info->stream, "*unknown-c3*");
return 0;
}
static int
mep_examine_ivc2_insns (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, bfd_vma pc ATTRIBUTE_UNUSED, disassemble_info *info ATTRIBUTE_UNUSED)
{
int status;
int buflength;
bfd_byte buf[8];
bfd_byte insn[8];
int e;
/* This is a hack. SID calls this to update the disassembler as the
CPU changes modes. */
static int mep_ivc2_disassemble_p = 0;
static int mep_ivc2_vliw_disassemble_p = 0;
void
mep_print_insn_set_ivc2_mode (int ivc2_p, int vliw_p, int cfg_idx);
void
mep_print_insn_set_ivc2_mode (int ivc2_p, int vliw_p, int cfg_idx)
{
mep_ivc2_disassemble_p = ivc2_p;
mep_ivc2_vliw_disassemble_p = vliw_p;
mep_config_index = cfg_idx;
}
static int
mep_print_insn (CGEN_CPU_DESC cd, bfd_vma pc, disassemble_info *info)
{
int status;
int cop_type;
int ivc2 = 0;
static CGEN_ATTR_VALUE_BITSET_TYPE *ivc2_core_isa = NULL;
if (ivc2_core_isa == NULL)
{
/* IVC2 has some core-only coprocessor instructions. We
use COP32 to flag those, and COP64 for the VLIW ones,
since they have the same names. */
ivc2_core_isa = cgen_bitset_create (MAX_ISAS);
}
/* Extract and adapt to configuration number, if available. */
if (info->section && info->section->owner)
{
bfd *abfd = info->section->owner;
if (bfd_get_flavour (abfd) == bfd_target_elf_flavour)
{
mep_config_index = abfd->tdata.elf_obj_data->elf_header->e_flags & EF_MEP_INDEX_MASK;
/* This instantly redefines MEP_CONFIG, MEP_OMASK, .... MEP_VLIW64 */
/* mep_config_map is a variable sized array, so we do not know how big it is.
The only safe way to check the index therefore is to iterate over the array.
We do know that the last entry is all null. */
int i;
for (i = 0; i <= mep_config_index; i++)
if (mep_config_map[i].name == NULL)
break;
if (i < mep_config_index)
{
opcodes_error_handler (_("illegal MEP INDEX setting '%x' in ELF header e_flags field"), mep_config_index);
mep_config_index = 0;
}
/* Picking the right ISA bitmask for the current context is tricky. */
if (info->section)
{
if (info->section->flags & SEC_MEP_VLIW)
{
#ifdef MEP_IVC2_SUPPORTED
if (ivc2)
{
/* ivc2 has its own way of selecting its functions. */
cd->isas = & MEP_CORE_ISA;
status = mep_examine_ivc2_insns (cd, pc, info);
}
else
#endif
/* Are we in 32 or 64 bit vliw mode? */
if (MEP_VLIW64)
status = mep_examine_vliw64_insns (cd, pc, info);
else
status = mep_examine_vliw32_insns (cd, pc, info);
/* Both the above branches set their own isa bitmasks. */
}
else
{
if (ivc2)
{
cgen_bitset_clear (ivc2_core_isa);
cgen_bitset_union (ivc2_core_isa, &MEP_CORE_ISA, ivc2_core_isa);
cgen_bitset_union (ivc2_core_isa, &MEP_COP32_ISA, ivc2_core_isa);
cd->isas = ivc2_core_isa;
}
else
cd->isas = & MEP_CORE_ISA;
status = default_print_insn (cd, pc, info);
}
}
else /* sid or gdb */
{
#ifdef MEP_IVC2_SUPPORTED
if (mep_ivc2_disassemble_p)
{
if (mep_ivc2_vliw_disassemble_p)
{
cd->isas = & MEP_CORE_ISA;
status = mep_examine_ivc2_insns (cd, pc, info);
return status;
}
else
{
if (ivc2)
cd->isas = ivc2_core_isa;
}
}
#endif
status = default_print_insn (cd, pc, info);
}
return status;
}
/* -- opc.c */
#include "elf/mep.h"
/* A mask for all ISAs executed by the core. */
CGEN_ATTR_VALUE_BITSET_TYPE mep_all_core_isas_mask = {0, 0};
static int
check_configured_mach (int machs)
{
/* All base insns are supported. */
int mach = 1 << MACH_BASE;
switch (MEP_CPU & EF_MEP_CPU_MASK)
{
case EF_MEP_CPU_C2:
case EF_MEP_CPU_C3:
mach |= (1 << MACH_MEP);
break;
case EF_MEP_CPU_H1:
mach |= (1 << MACH_H1);
break;
case EF_MEP_CPU_C5:
mach |= (1 << MACH_MEP);
mach |= (1 << MACH_C5);
break;
default:
break;
}
return machs & mach;
}
int
mep_cgen_insn_supported (CGEN_CPU_DESC cd, const CGEN_INSN *insn)
{
int iconfig = CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_CONFIG);
int machs = CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_MACH);
CGEN_BITSET isas = CGEN_INSN_BITSET_ATTR_VALUE (insn, CGEN_INSN_ISA);
int ok1;
int ok2;
int ok3;
/* If the insn has an option bit set that we don't want,
reject it. */
if (CGEN_INSN_ATTRS (insn)->bool_ & OPTION_MASK & ~MEP_OMASK)
return 0;
/* If attributes are absent, assume no restriction. */
if (machs == 0)
machs = ~0;
ok1 = ((machs & cd->machs) && cgen_bitset_intersect_p (& isas, cd->isas));
/* If the insn is config-specific, make sure it matches. */
ok2 = (iconfig == 0 || iconfig == MEP_CONFIG);
/* Make sure the insn is supported by the configured mach */
ok3 = check_configured_mach (machs);
return (ok1 && ok2 && ok3);
}
int
mep_cgen_insn_supported_asm (CGEN_CPU_DESC cd, const CGEN_INSN *insn)
{
#ifdef MEP_IVC2_SUPPORTED
/* If we're assembling VLIW packets, ignore the 12-bit BSR as we
can't relax that. The 24-bit BSR is matched instead. */
if (insn->base->num == MEP_INSN_BSR12
&& cgen_bitset_contains (cd->isas, ISA_EXT_COP1_64))
return 0;
#endif