Copyright 2005, 2007, 2009, 2010 Free Software Foundation, Inc.
Contributed by Red Hat Inc; developed under contract from Renesas
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. */
/* This file is an addendum to m32c.cpu. Heavy use of C code isn't
appropriate in .cpu files, so it resides here. This especially applies
to assembly/disassembly where parsing/printing can be quite involved.
Such things aren't really part of the specification of the cpu, per se,
so .cpu files provide the general framework and .opc files handle the
nitty-gritty details as necessary.
Each section is delimited with start and end markers.
/* Needed for RTL's 'ext' and 'trunc' operators. */
#include "cgen/basic-modes.h"
#include "cgen/basic-ops.h"
/* We can't use the default hash size because many bits are used by
operands. */
#define CGEN_DIS_HASH_SIZE 1
#define CGEN_DIS_HASH(buf, value) 0
#define CGEN_VERBOSE_ASSEMBLER_ERRORS
#define CGEN_VALIDATE_INSN_SUPPORTED
extern int m32c_cgen_insn_supported (CGEN_CPU_DESC, const CGEN_INSN *);
if (value > 0xff)
return _("dsp:8 immediate is out of range");
/* If this field may require a relocation then use larger dsp16. */
if (! have_zero && value == 0)
return _("dsp:8 immediate is out of range");
*valuep = value;
return 0;
}
static const char *
parse_signed4 (CGEN_CPU_DESC cd, const char **strp,
int opindex, signed long *valuep)
{
const char *errmsg = 0;
signed long value;
long have_zero = 0;
if (value < -8 || value > 7)
return _("Immediate is out of range -8 to 7");
/* If this field may require a relocation then use larger dsp16. */
if (! have_zero && value == 0)
return _("Immediate is out of range -8 to 7");
*valuep = value;
return 0;
}
static const char *
parse_signed4n (CGEN_CPU_DESC cd, const char **strp,
int opindex, signed long *valuep)
{
const char *errmsg = 0;
signed long value;
long have_zero = 0;
if (value < -7 || value > 8)
return _("Immediate is out of range -7 to 8");
/* If this field may require a relocation then use larger dsp16. */
if (! have_zero && value == 0)
return _("Immediate is out of range -7 to 8");
*valuep = -value;
return 0;
}
static const char *
parse_signed8 (CGEN_CPU_DESC cd, const char **strp,
int opindex, signed long *valuep)
{
const char *errmsg = 0;
signed long value = 0;
if (value > 0xffff)
return _("dsp:16 immediate is out of range");
/* If this field may require a relocation then use larger dsp24. */
if (cd->machs == MACH_M32C && ! have_zero && value == 0
&& (strncmp (*strp, "[a", 2) == 0
|| **strp == ','
|| **strp == 0))
return _("dsp:16 immediate is out of range");
*valuep = value;
return 0;
}
static const char *
parse_signed16 (CGEN_CPU_DESC cd, const char **strp,
int opindex, signed long *valuep)
{
const char *errmsg = 0;
signed long value = 0;
if (value > 0xffffff)
return _("dsp:24 immediate is out of range");
*valuep = value;
return 0;
}
/* This should only be used for #imm->reg. */
static const char *
parse_signed24 (CGEN_CPU_DESC cd, const char **strp,
int opindex, signed long *valuep)
{
const char *errmsg = 0;
signed long value;
PARSE_SIGNED;
if (value <= 0xffffff && value > 0x7fffff)
value -= 0x1000000;
if (value > 0xffffff)
return _("dsp:24 immediate is out of range");
*valuep = value;
return 0;
}
static const char *
parse_signed32 (CGEN_CPU_DESC cd, const char **strp,
int opindex, signed long *valuep)
{
const char *errmsg = 0;
signed long value;
if (op_res == CGEN_PARSE_OPERAND_RESULT_QUEUED)
{
/* This is a hack; the field cannot handle near-zero signed
offsets that CGEN wants to put in to indicate an "empty"
operand at first. */
*valuep = 2;
return 0;
}
if (errmsg)
return errmsg;
if (value < 2 || value > 9)
return _("immediate is out of range 2-9");
*valuep = value;
return 0;
}
static const char *
parse_Bitno16R (CGEN_CPU_DESC cd, const char **strp,
int opindex, unsigned long *valuep)
{
const char *errmsg = 0;
unsigned long value;
bitbase = (unsigned long long) bit + ((unsigned long long) base * 8);
if (bitbase >= (1ull << bits))
return _("bit,base is out of range");
/* If this field may require a relocation then use larger displacement. */
if (! have_zero && base == 0)
{
switch (allow_syms) {
case 0:
return _("bit,base out of range for symbol");
case 1:
break;
case 2:
if (strncmp (newp, "[sb]", 4) != 0)
return _("bit,base out of range for symbol");
break;
}
}
*valuep = bitbase;
*strp = newp;
return 0;
}
static const char *
parse_signed_bitbase (CGEN_CPU_DESC cd, const char **strp,
int opindex, signed long *valuep,
unsigned bits, int allow_syms)
{
const char *errmsg = 0;
unsigned long bit;
signed long base;
const char *newp = *strp;
long long bitbase;
long long limit;
long have_zero = 0;
limit = 1ll << (bits - 1);
if (bitbase < -limit || bitbase >= limit)
return _("bit,base is out of range");
/* If this field may require a relocation then use larger displacement. */
if (! have_zero && base == 0 && ! allow_syms)
return _("bit,base out of range for symbol");
return "Invalid suffix"; /* Anything -- will not be seen. */
}
static const char *
parse_S (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, const char **strp,
int opindex ATTRIBUTE_UNUSED, signed long *valuep ATTRIBUTE_UNUSED)
{
return parse_suffix (strp, 's');
}
static const char *
parse_G (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, const char **strp,
int opindex ATTRIBUTE_UNUSED, signed long *valuep ATTRIBUTE_UNUSED)
{
return parse_suffix (strp, 'g');
}
static const char *
parse_Q (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, const char **strp,
int opindex ATTRIBUTE_UNUSED, signed long *valuep ATTRIBUTE_UNUSED)
{
return parse_suffix (strp, 'q');
}
static const char *
parse_Z (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, const char **strp,
int opindex ATTRIBUTE_UNUSED, signed long *valuep ATTRIBUTE_UNUSED)
{
return parse_suffix (strp, 'z');
}
/* Parse an empty suffix. Fail if the next char is ':'. */
static const char *
parse_X (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED, const char **strp,
int opindex ATTRIBUTE_UNUSED, signed long *valuep ATTRIBUTE_UNUSED)
{
if (**strp == ':')
return "Unexpected suffix";
return 0;
}
static const char *
parse_r0l_r0h (CGEN_CPU_DESC cd, const char **strp,
int opindex ATTRIBUTE_UNUSED, signed long *valuep)
{
const char *errmsg;
signed long value;
signed long junk;
const char *newp = *strp;
/* Parse a set of registers, R0,R1,A0,A1,SB,FB. */
static const char *
parse_regset (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
const char **strp,
int opindex ATTRIBUTE_UNUSED,
unsigned long *valuep,
int push)
{
const char *errmsg = 0;
int regno = 0;
*valuep = 0;
while (**strp && **strp != ')')
{
if (**strp == 'r' || **strp == 'R')
{
++*strp;
regno = **strp - '0';
if (regno > 4)
errmsg = _("Register number is not valid");
}
else if (**strp == 'a' || **strp == 'A')
{
++*strp;
regno = **strp - '0';
if (regno > 2)
errmsg = _("Register number is not valid");
regno = **strp - '0' + 4;
}
static void
print_S (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
void * dis_info,
long value ATTRIBUTE_UNUSED,
unsigned int attrs ATTRIBUTE_UNUSED,
bfd_vma pc ATTRIBUTE_UNUSED,
int length ATTRIBUTE_UNUSED)
{
print_suffix (dis_info, 's');
}
static void
print_G (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
void * dis_info,
long value ATTRIBUTE_UNUSED,
unsigned int attrs ATTRIBUTE_UNUSED,
bfd_vma pc ATTRIBUTE_UNUSED,
int length ATTRIBUTE_UNUSED)
{
print_suffix (dis_info, 'g');
}
static void
print_Q (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
void * dis_info,
long value ATTRIBUTE_UNUSED,
unsigned int attrs ATTRIBUTE_UNUSED,
bfd_vma pc ATTRIBUTE_UNUSED,
int length ATTRIBUTE_UNUSED)
{
print_suffix (dis_info, 'q');
}
static void
print_Z (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
void * dis_info,
long value ATTRIBUTE_UNUSED,
unsigned int attrs ATTRIBUTE_UNUSED,
bfd_vma pc ATTRIBUTE_UNUSED,
int length ATTRIBUTE_UNUSED)
{
print_suffix (dis_info, 'z');
}
/* Print the empty suffix. */
static void
print_X (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
void * dis_info ATTRIBUTE_UNUSED,
long value ATTRIBUTE_UNUSED,
unsigned int attrs ATTRIBUTE_UNUSED,
bfd_vma pc ATTRIBUTE_UNUSED,
int length ATTRIBUTE_UNUSED)
{
return;
}
static void
print_r0l_r0h (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
void * dis_info,
long value,
unsigned int attrs ATTRIBUTE_UNUSED,
bfd_vma pc ATTRIBUTE_UNUSED,
int length ATTRIBUTE_UNUSED)
{
disassemble_info *info = dis_info;
static void
print_unsigned_bitbase (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
void * dis_info,
unsigned long value,
unsigned int attrs ATTRIBUTE_UNUSED,
bfd_vma pc ATTRIBUTE_UNUSED,
int length ATTRIBUTE_UNUSED)
{
disassemble_info *info = dis_info;
(*info->fprintf_func) (info->stream, "%ld,0x%lx", value & 0x7, value >> 3);
}
static void
print_signed_bitbase (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
void * dis_info,
signed long value,
unsigned int attrs ATTRIBUTE_UNUSED,
bfd_vma pc ATTRIBUTE_UNUSED,
int length ATTRIBUTE_UNUSED)
{
disassemble_info *info = dis_info;
(*info->fprintf_func) (info->stream, "%ld,%ld", value & 0x7, value >> 3);
}
static void
print_size (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
void * dis_info,
long value ATTRIBUTE_UNUSED,
unsigned int attrs ATTRIBUTE_UNUSED,
bfd_vma pc ATTRIBUTE_UNUSED,
int length ATTRIBUTE_UNUSED)
{
/* Always print the size as '.w'. */
disassemble_info *info = dis_info;
static void
print_pop_regset (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
void * dis_info,
long value,
unsigned int attrs ATTRIBUTE_UNUSED,
bfd_vma pc ATTRIBUTE_UNUSED,
int length ATTRIBUTE_UNUSED)
{
print_regset (cd, dis_info, value, attrs, pc, length, POP);
}
static void
print_push_regset (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
void * dis_info,
long value,
unsigned int attrs ATTRIBUTE_UNUSED,
bfd_vma pc ATTRIBUTE_UNUSED,
int length ATTRIBUTE_UNUSED)
{
print_regset (cd, dis_info, value, attrs, pc, length, PUSH);
}
static void
print_signed4n (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
void * dis_info,
signed long value,
unsigned int attrs ATTRIBUTE_UNUSED,
bfd_vma pc ATTRIBUTE_UNUSED,
int length ATTRIBUTE_UNUSED)
{
disassemble_info *info = dis_info;