2000-08-04 Ben Elliston <bje@redhat.com>
* cgen-dis.in, cgen-asm.in, cgen-ibld.in: New files. * cgen.sh: Likewise.
This commit is contained in:
parent
ab3e48dc5d
commit
f6e6b40ff5
|
@ -1,3 +1,8 @@
|
|||
2000-08-04 Ben Elliston <bje@redhat.com>
|
||||
|
||||
* cgen-dis.in, cgen-asm.in, cgen-ibld.in: New files.
|
||||
* cgen.sh: Likewise.
|
||||
|
||||
2000-08-02 Jim Wilson <wilson@cygnus.com>
|
||||
|
||||
* ia64-dis.c (print_insn_ia64): Call ia64_free_opcode at end.
|
||||
|
|
|
@ -0,0 +1,309 @@
|
|||
/* Assembler interface for targets using CGEN. -*- C -*-
|
||||
CGEN: Cpu tools GENerator
|
||||
|
||||
THIS FILE IS MACHINE GENERATED WITH CGEN.
|
||||
- the resultant file is machine generated, cgen-asm.in isn't
|
||||
|
||||
Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of the GNU Binutils and GDB, the GNU debugger.
|
||||
|
||||
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 2, 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.,
|
||||
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* ??? Eventually more and more of this stuff can go to cpu-independent files.
|
||||
Keep that in mind. */
|
||||
|
||||
#include "sysdep.h"
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include "ansidecl.h"
|
||||
#include "bfd.h"
|
||||
#include "symcat.h"
|
||||
#include "@prefix@-desc.h"
|
||||
#include "@prefix@-opc.h"
|
||||
#include "opintl.h"
|
||||
|
||||
#undef min
|
||||
#define min(a,b) ((a) < (b) ? (a) : (b))
|
||||
#undef max
|
||||
#define max(a,b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
static const char * parse_insn_normal
|
||||
PARAMS ((CGEN_CPU_DESC, const CGEN_INSN *, const char **, CGEN_FIELDS *));
|
||||
|
||||
/* -- assembler routines inserted here */
|
||||
|
||||
/* Default insn parser.
|
||||
|
||||
The syntax string is scanned and operands are parsed and stored in FIELDS.
|
||||
Relocs are queued as we go via other callbacks.
|
||||
|
||||
??? Note that this is currently an all-or-nothing parser. If we fail to
|
||||
parse the instruction, we return 0 and the caller will start over from
|
||||
the beginning. Backtracking will be necessary in parsing subexpressions,
|
||||
but that can be handled there. Not handling backtracking here may get
|
||||
expensive in the case of the m68k. Deal with later.
|
||||
|
||||
Returns NULL for success, an error message for failure.
|
||||
*/
|
||||
|
||||
static const char *
|
||||
parse_insn_normal (cd, insn, strp, fields)
|
||||
CGEN_CPU_DESC cd;
|
||||
const CGEN_INSN *insn;
|
||||
const char **strp;
|
||||
CGEN_FIELDS *fields;
|
||||
{
|
||||
/* ??? Runtime added insns not handled yet. */
|
||||
const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
|
||||
const char *str = *strp;
|
||||
const char *errmsg;
|
||||
const char *p;
|
||||
const unsigned char * syn;
|
||||
#ifdef CGEN_MNEMONIC_OPERANDS
|
||||
/* FIXME: wip */
|
||||
int past_opcode_p;
|
||||
#endif
|
||||
|
||||
/* For now we assume the mnemonic is first (there are no leading operands).
|
||||
We can parse it without needing to set up operand parsing.
|
||||
GAS's input scrubber will ensure mnemonics are lowercase, but we may
|
||||
not be called from GAS. */
|
||||
p = CGEN_INSN_MNEMONIC (insn);
|
||||
while (*p && tolower (*p) == tolower (*str))
|
||||
++p, ++str;
|
||||
|
||||
if (* p)
|
||||
return _("unrecognized instruction");
|
||||
|
||||
#ifndef CGEN_MNEMONIC_OPERANDS
|
||||
if (* str && !isspace (* str))
|
||||
return _("unrecognized instruction");
|
||||
#endif
|
||||
|
||||
CGEN_INIT_PARSE (cd);
|
||||
cgen_init_parse_operand (cd);
|
||||
#ifdef CGEN_MNEMONIC_OPERANDS
|
||||
past_opcode_p = 0;
|
||||
#endif
|
||||
|
||||
/* We don't check for (*str != '\0') here because we want to parse
|
||||
any trailing fake arguments in the syntax string. */
|
||||
syn = CGEN_SYNTAX_STRING (syntax);
|
||||
|
||||
/* Mnemonics come first for now, ensure valid string. */
|
||||
if (! CGEN_SYNTAX_MNEMONIC_P (* syn))
|
||||
abort ();
|
||||
|
||||
++syn;
|
||||
|
||||
while (* syn != 0)
|
||||
{
|
||||
/* Non operand chars must match exactly. */
|
||||
if (CGEN_SYNTAX_CHAR_P (* syn))
|
||||
{
|
||||
/* FIXME: While we allow for non-GAS callers above, we assume the
|
||||
first char after the mnemonic part is a space. */
|
||||
/* FIXME: We also take inappropriate advantage of the fact that
|
||||
GAS's input scrubber will remove extraneous blanks. */
|
||||
if (tolower (*str) == tolower (CGEN_SYNTAX_CHAR (* syn)))
|
||||
{
|
||||
#ifdef CGEN_MNEMONIC_OPERANDS
|
||||
if (* syn == ' ')
|
||||
past_opcode_p = 1;
|
||||
#endif
|
||||
++ syn;
|
||||
++ str;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Syntax char didn't match. Can't be this insn. */
|
||||
static char msg [80];
|
||||
/* xgettext:c-format */
|
||||
sprintf (msg, _("syntax error (expected char `%c', found `%c')"),
|
||||
*syn, *str);
|
||||
return msg;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We have an operand of some sort. */
|
||||
errmsg = @arch@_cgen_parse_operand (cd, CGEN_SYNTAX_FIELD (*syn),
|
||||
&str, fields);
|
||||
if (errmsg)
|
||||
return errmsg;
|
||||
|
||||
/* Done with this operand, continue with next one. */
|
||||
++ syn;
|
||||
}
|
||||
|
||||
/* If we're at the end of the syntax string, we're done. */
|
||||
if (* syn == '\0')
|
||||
{
|
||||
/* FIXME: For the moment we assume a valid `str' can only contain
|
||||
blanks now. IE: We needn't try again with a longer version of
|
||||
the insn and it is assumed that longer versions of insns appear
|
||||
before shorter ones (eg: lsr r2,r3,1 vs lsr r2,r3). */
|
||||
while (isspace (* str))
|
||||
++ str;
|
||||
|
||||
if (* str != '\0')
|
||||
return _("junk at end of line"); /* FIXME: would like to include `str' */
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* We couldn't parse it. */
|
||||
return _("unrecognized instruction");
|
||||
}
|
||||
|
||||
/* Main entry point.
|
||||
This routine is called for each instruction to be assembled.
|
||||
STR points to the insn to be assembled.
|
||||
We assume all necessary tables have been initialized.
|
||||
The assembled instruction, less any fixups, is stored in BUF.
|
||||
Remember that if CGEN_INT_INSN_P then BUF is an int and thus the value
|
||||
still needs to be converted to target byte order, otherwise BUF is an array
|
||||
of bytes in target byte order.
|
||||
The result is a pointer to the insn's entry in the opcode table,
|
||||
or NULL if an error occured (an error message will have already been
|
||||
printed).
|
||||
|
||||
Note that when processing (non-alias) macro-insns,
|
||||
this function recurses.
|
||||
|
||||
??? It's possible to make this cpu-independent.
|
||||
One would have to deal with a few minor things.
|
||||
At this point in time doing so would be more of a curiosity than useful
|
||||
[for example this file isn't _that_ big], but keeping the possibility in
|
||||
mind helps keep the design clean. */
|
||||
|
||||
const CGEN_INSN *
|
||||
@arch@_cgen_assemble_insn (cd, str, fields, buf, errmsg)
|
||||
CGEN_CPU_DESC cd;
|
||||
const char *str;
|
||||
CGEN_FIELDS *fields;
|
||||
CGEN_INSN_BYTES_PTR buf;
|
||||
char **errmsg;
|
||||
{
|
||||
const char *start;
|
||||
CGEN_INSN_LIST *ilist;
|
||||
const char *tmp_errmsg = NULL;
|
||||
|
||||
/* Skip leading white space. */
|
||||
while (isspace (* str))
|
||||
++ str;
|
||||
|
||||
/* The instructions are stored in hashed lists.
|
||||
Get the first in the list. */
|
||||
ilist = CGEN_ASM_LOOKUP_INSN (cd, str);
|
||||
|
||||
/* Keep looking until we find a match. */
|
||||
|
||||
start = str;
|
||||
for ( ; ilist != NULL ; ilist = CGEN_ASM_NEXT_INSN (ilist))
|
||||
{
|
||||
const CGEN_INSN *insn = ilist->insn;
|
||||
|
||||
#ifdef CGEN_VALIDATE_INSN_SUPPORTED
|
||||
/* not usually needed as unsupported opcodes shouldn't be in the hash lists */
|
||||
/* Is this insn supported by the selected cpu? */
|
||||
if (! @arch@_cgen_insn_supported (cd, insn))
|
||||
continue;
|
||||
#endif
|
||||
|
||||
/* If the RELAX attribute is set, this is an insn that shouldn't be
|
||||
chosen immediately. Instead, it is used during assembler/linker
|
||||
relaxation if possible. */
|
||||
if (CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_RELAX) != 0)
|
||||
continue;
|
||||
|
||||
str = start;
|
||||
|
||||
/* Allow parse/insert handlers to obtain length of insn. */
|
||||
CGEN_FIELDS_BITSIZE (fields) = CGEN_INSN_BITSIZE (insn);
|
||||
|
||||
tmp_errmsg = CGEN_PARSE_FN (cd, insn) (cd, insn, & str, fields);
|
||||
if (tmp_errmsg != NULL)
|
||||
continue;
|
||||
|
||||
/* ??? 0 is passed for `pc' */
|
||||
tmp_errmsg = CGEN_INSERT_FN (cd, insn) (cd, insn, fields, buf,
|
||||
(bfd_vma) 0);
|
||||
if (tmp_errmsg != NULL)
|
||||
continue;
|
||||
|
||||
/* It is up to the caller to actually output the insn and any
|
||||
queued relocs. */
|
||||
return insn;
|
||||
}
|
||||
|
||||
/* Make sure we leave this with something at this point. */
|
||||
if (tmp_errmsg == NULL)
|
||||
tmp_errmsg = "unknown mnemonic";
|
||||
|
||||
{
|
||||
static char errbuf[150];
|
||||
|
||||
#ifdef CGEN_VERBOSE_ASSEMBLER_ERRORS
|
||||
/* if verbose error messages, use errmsg from CGEN_PARSE_FN */
|
||||
if (strlen (start) > 50)
|
||||
/* xgettext:c-format */
|
||||
sprintf (errbuf, "%s `%.50s...'", tmp_errmsg, start);
|
||||
else
|
||||
/* xgettext:c-format */
|
||||
sprintf (errbuf, "%s `%.50s'", tmp_errmsg, start);
|
||||
#else
|
||||
if (strlen (start) > 50)
|
||||
/* xgettext:c-format */
|
||||
sprintf (errbuf, _("bad instruction `%.50s...'"), start);
|
||||
else
|
||||
/* xgettext:c-format */
|
||||
sprintf (errbuf, _("bad instruction `%.50s'"), start);
|
||||
#endif
|
||||
|
||||
*errmsg = errbuf;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 /* This calls back to GAS which we can't do without care. */
|
||||
|
||||
/* Record each member of OPVALS in the assembler's symbol table.
|
||||
This lets GAS parse registers for us.
|
||||
??? Interesting idea but not currently used. */
|
||||
|
||||
/* Record each member of OPVALS in the assembler's symbol table.
|
||||
FIXME: Not currently used. */
|
||||
|
||||
void
|
||||
@arch@_cgen_asm_hash_keywords (cd, opvals)
|
||||
CGEN_CPU_DESC cd;
|
||||
CGEN_KEYWORD *opvals;
|
||||
{
|
||||
CGEN_KEYWORD_SEARCH search = cgen_keyword_search_init (opvals, NULL);
|
||||
const CGEN_KEYWORD_ENTRY * ke;
|
||||
|
||||
while ((ke = cgen_keyword_search_next (& search)) != NULL)
|
||||
{
|
||||
#if 0 /* Unnecessary, should be done in the search routine. */
|
||||
if (! @arch@_cgen_opval_supported (ke))
|
||||
continue;
|
||||
#endif
|
||||
cgen_asm_record_register (cd, ke->name, ke->value);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* 0 */
|
|
@ -0,0 +1,388 @@
|
|||
/* Disassembler interface for targets using CGEN. -*- C -*-
|
||||
CGEN: Cpu tools GENerator
|
||||
|
||||
THIS FILE IS MACHINE GENERATED WITH CGEN.
|
||||
- the resultant file is machine generated, cgen-dis.in isn't
|
||||
|
||||
Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of the GNU Binutils and GDB, the GNU debugger.
|
||||
|
||||
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 2, 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.,
|
||||
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* ??? Eventually more and more of this stuff can go to cpu-independent files.
|
||||
Keep that in mind. */
|
||||
|
||||
#include "sysdep.h"
|
||||
#include <stdio.h>
|
||||
#include "ansidecl.h"
|
||||
#include "dis-asm.h"
|
||||
#include "bfd.h"
|
||||
#include "symcat.h"
|
||||
#include "@prefix@-desc.h"
|
||||
#include "@prefix@-opc.h"
|
||||
#include "opintl.h"
|
||||
|
||||
/* Default text to print if an instruction isn't recognized. */
|
||||
#define UNKNOWN_INSN_MSG _("*unknown*")
|
||||
|
||||
static void print_normal
|
||||
PARAMS ((CGEN_CPU_DESC, PTR, long, unsigned int, bfd_vma, int));
|
||||
static void print_address
|
||||
PARAMS ((CGEN_CPU_DESC, PTR, bfd_vma, unsigned int, bfd_vma, int));
|
||||
static void print_keyword
|
||||
PARAMS ((CGEN_CPU_DESC, PTR, CGEN_KEYWORD *, long, unsigned int));
|
||||
static void print_insn_normal
|
||||
PARAMS ((CGEN_CPU_DESC, PTR, const CGEN_INSN *, CGEN_FIELDS *,
|
||||
bfd_vma, int));
|
||||
static int print_insn PARAMS ((CGEN_CPU_DESC, bfd_vma,
|
||||
disassemble_info *, char *, int));
|
||||
static int default_print_insn
|
||||
PARAMS ((CGEN_CPU_DESC, bfd_vma, disassemble_info *));
|
||||
|
||||
/* -- disassembler routines inserted here */
|
||||
|
||||
/* Default print handler. */
|
||||
|
||||
static void
|
||||
print_normal (cd, dis_info, value, attrs, pc, length)
|
||||
#ifdef CGEN_PRINT_NORMAL
|
||||
CGEN_CPU_DESC cd;
|
||||
#else
|
||||
CGEN_CPU_DESC cd ATTRIBUTE_UNUSED;
|
||||
#endif
|
||||
PTR dis_info;
|
||||
long value;
|
||||
unsigned int attrs;
|
||||
#ifdef CGEN_PRINT_NORMAL
|
||||
bfd_vma pc;
|
||||
int length;
|
||||
#else
|
||||
bfd_vma pc ATTRIBUTE_UNUSED;
|
||||
int length ATTRIBUTE_UNUSED;
|
||||
#endif
|
||||
{
|
||||
disassemble_info *info = (disassemble_info *) dis_info;
|
||||
|
||||
#ifdef CGEN_PRINT_NORMAL
|
||||
CGEN_PRINT_NORMAL (cd, info, value, attrs, pc, length);
|
||||
#endif
|
||||
|
||||
/* Print the operand as directed by the attributes. */
|
||||
if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
|
||||
; /* nothing to do */
|
||||
else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
|
||||
(*info->fprintf_func) (info->stream, "%ld", value);
|
||||
else
|
||||
(*info->fprintf_func) (info->stream, "0x%lx", value);
|
||||
}
|
||||
|
||||
/* Default address handler. */
|
||||
|
||||
static void
|
||||
print_address (cd, dis_info, value, attrs, pc, length)
|
||||
#ifdef CGEN_PRINT_NORMAL
|
||||
CGEN_CPU_DESC cd;
|
||||
#else
|
||||
CGEN_CPU_DESC cd ATTRIBUTE_UNUSED;
|
||||
#endif
|
||||
PTR dis_info;
|
||||
bfd_vma value;
|
||||
unsigned int attrs;
|
||||
#ifdef CGEN_PRINT_NORMAL
|
||||
bfd_vma pc;
|
||||
int length;
|
||||
#else
|
||||
bfd_vma pc ATTRIBUTE_UNUSED;
|
||||
int length ATTRIBUTE_UNUSED;
|
||||
#endif
|
||||
{
|
||||
disassemble_info *info = (disassemble_info *) dis_info;
|
||||
|
||||
#ifdef CGEN_PRINT_ADDRESS
|
||||
CGEN_PRINT_ADDRESS (cd, info, value, attrs, pc, length);
|
||||
#endif
|
||||
|
||||
/* Print the operand as directed by the attributes. */
|
||||
if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
|
||||
; /* nothing to do */
|
||||
else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_PCREL_ADDR))
|
||||
(*info->print_address_func) (value, info);
|
||||
else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_ABS_ADDR))
|
||||
(*info->print_address_func) (value, info);
|
||||
else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
|
||||
(*info->fprintf_func) (info->stream, "%ld", (long) value);
|
||||
else
|
||||
(*info->fprintf_func) (info->stream, "0x%lx", (long) value);
|
||||
}
|
||||
|
||||
/* Keyword print handler. */
|
||||
|
||||
static void
|
||||
print_keyword (cd, dis_info, keyword_table, value, attrs)
|
||||
CGEN_CPU_DESC cd ATTRIBUTE_UNUSED;
|
||||
PTR dis_info;
|
||||
CGEN_KEYWORD *keyword_table;
|
||||
long value;
|
||||
unsigned int attrs ATTRIBUTE_UNUSED;
|
||||
{
|
||||
disassemble_info *info = (disassemble_info *) dis_info;
|
||||
const CGEN_KEYWORD_ENTRY *ke;
|
||||
|
||||
ke = cgen_keyword_lookup_value (keyword_table, value);
|
||||
if (ke != NULL)
|
||||
(*info->fprintf_func) (info->stream, "%s", ke->name);
|
||||
else
|
||||
(*info->fprintf_func) (info->stream, "???");
|
||||
}
|
||||
|
||||
/* Default insn printer.
|
||||
|
||||
DIS_INFO is defined as `PTR' so the disassembler needn't know anything
|
||||
about disassemble_info. */
|
||||
|
||||
static void
|
||||
print_insn_normal (cd, dis_info, insn, fields, pc, length)
|
||||
CGEN_CPU_DESC cd;
|
||||
PTR dis_info;
|
||||
const CGEN_INSN *insn;
|
||||
CGEN_FIELDS *fields;
|
||||
bfd_vma pc;
|
||||
int length;
|
||||
{
|
||||
const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
|
||||
disassemble_info *info = (disassemble_info *) dis_info;
|
||||
const unsigned char *syn;
|
||||
|
||||
CGEN_INIT_PRINT (cd);
|
||||
|
||||
for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn)
|
||||
{
|
||||
if (CGEN_SYNTAX_MNEMONIC_P (*syn))
|
||||
{
|
||||
(*info->fprintf_func) (info->stream, "%s", CGEN_INSN_MNEMONIC (insn));
|
||||
continue;
|
||||
}
|
||||
if (CGEN_SYNTAX_CHAR_P (*syn))
|
||||
{
|
||||
(*info->fprintf_func) (info->stream, "%c", CGEN_SYNTAX_CHAR (*syn));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We have an operand. */
|
||||
@arch@_cgen_print_operand (cd, CGEN_SYNTAX_FIELD (*syn), info,
|
||||
fields, CGEN_INSN_ATTRS (insn), pc, length);
|
||||
}
|
||||
}
|
||||
|
||||
/* Utility to print an insn.
|
||||
BUF is the base part of the insn, target byte order, BUFLEN bytes long.
|
||||
The result is the size of the insn in bytes or zero for an unknown insn
|
||||
or -1 if an error occurs fetching data (memory_error_func will have
|
||||
been called). */
|
||||
|
||||
static int
|
||||
print_insn (cd, pc, info, buf, buflen)
|
||||
CGEN_CPU_DESC cd;
|
||||
bfd_vma pc;
|
||||
disassemble_info *info;
|
||||
char *buf;
|
||||
int buflen;
|
||||
{
|
||||
unsigned long insn_value;
|
||||
const CGEN_INSN_LIST *insn_list;
|
||||
CGEN_EXTRACT_INFO ex_info;
|
||||
|
||||
ex_info.dis_info = info;
|
||||
ex_info.valid = (1 << (cd->base_insn_bitsize / 8)) - 1;
|
||||
ex_info.insn_bytes = buf;
|
||||
|
||||
switch (buflen)
|
||||
{
|
||||
case 1:
|
||||
insn_value = buf[0];
|
||||
break;
|
||||
case 2:
|
||||
insn_value = info->endian == BFD_ENDIAN_BIG ? bfd_getb16 (buf) : bfd_getl16 (buf);
|
||||
break;
|
||||
case 4:
|
||||
insn_value = info->endian == BFD_ENDIAN_BIG ? bfd_getb32 (buf) : bfd_getl32 (buf);
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
|
||||
/* The instructions are stored in hash lists.
|
||||
Pick the first one and keep trying until we find the right one. */
|
||||
|
||||
insn_list = CGEN_DIS_LOOKUP_INSN (cd, buf, insn_value);
|
||||
while (insn_list != NULL)
|
||||
{
|
||||
const CGEN_INSN *insn = insn_list->insn;
|
||||
CGEN_FIELDS fields;
|
||||
int length;
|
||||
|
||||
#ifdef CGEN_VALIDATE_INSN_SUPPORTED
|
||||
/* not needed as insn shouldn't be in hash lists if not supported */
|
||||
/* Supported by this cpu? */
|
||||
if (! @arch@_cgen_insn_supported (cd, insn))
|
||||
{
|
||||
insn_list = CGEN_DIS_NEXT_INSN (insn_list);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Basic bit mask must be correct. */
|
||||
/* ??? May wish to allow target to defer this check until the extract
|
||||
handler. */
|
||||
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 = CGEN_EXTRACT_FN (cd, insn)
|
||||
(cd, insn, &ex_info, insn_value, &fields, pc);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Default value for CGEN_PRINT_INSN.
|
||||
The result is the size of the insn in bytes or zero for an unknown insn
|
||||
or -1 if an error occured fetching bytes. */
|
||||
|
||||
#ifndef CGEN_PRINT_INSN
|
||||
#define CGEN_PRINT_INSN default_print_insn
|
||||
#endif
|
||||
|
||||
static int
|
||||
default_print_insn (cd, pc, info)
|
||||
CGEN_CPU_DESC cd;
|
||||
bfd_vma pc;
|
||||
disassemble_info *info;
|
||||
{
|
||||
char buf[CGEN_MAX_INSN_SIZE];
|
||||
int status;
|
||||
|
||||
/* Read the base part of the insn. */
|
||||
|
||||
status = (*info->read_memory_func) (pc, buf, cd->base_insn_bitsize / 8, info);
|
||||
if (status != 0)
|
||||
{
|
||||
(*info->memory_error_func) (status, pc, info);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return print_insn (cd, pc, info, buf, cd->base_insn_bitsize / 8);
|
||||
}
|
||||
|
||||
/* Main entry point.
|
||||
Print one instruction from PC on INFO->STREAM.
|
||||
Return the size of the instruction (in bytes). */
|
||||
|
||||
int
|
||||
print_insn_@arch@ (pc, info)
|
||||
bfd_vma pc;
|
||||
disassemble_info *info;
|
||||
{
|
||||
static CGEN_CPU_DESC cd = 0;
|
||||
static int prev_isa;
|
||||
static int prev_mach;
|
||||
static int prev_endian;
|
||||
int length;
|
||||
int isa,mach;
|
||||
int endian = (info->endian == BFD_ENDIAN_BIG
|
||||
? CGEN_ENDIAN_BIG
|
||||
: CGEN_ENDIAN_LITTLE);
|
||||
enum bfd_architecture arch;
|
||||
|
||||
/* ??? gdb will set mach but leave the architecture as "unknown" */
|
||||
#ifndef CGEN_BFD_ARCH
|
||||
#define CGEN_BFD_ARCH bfd_arch_@arch@
|
||||
#endif
|
||||
arch = info->arch;
|
||||
if (arch == bfd_arch_unknown)
|
||||
arch = CGEN_BFD_ARCH;
|
||||
|
||||
/* There's no standard way to compute the isa number (e.g. for arm thumb)
|
||||
so we leave it to the target. */
|
||||
#ifdef CGEN_COMPUTE_ISA
|
||||
isa = CGEN_COMPUTE_ISA (info);
|
||||
#else
|
||||
isa = 0;
|
||||
#endif
|
||||
|
||||
mach = info->mach;
|
||||
|
||||
/* If we've switched cpu's, close the current table and open a new one. */
|
||||
if (cd
|
||||
&& (isa != prev_isa
|
||||
|| mach != prev_mach
|
||||
|| endian != prev_endian))
|
||||
{
|
||||
@arch@_cgen_cpu_close (cd);
|
||||
cd = 0;
|
||||
}
|
||||
|
||||
/* If we haven't initialized yet, initialize the opcode table. */
|
||||
if (! cd)
|
||||
{
|
||||
const bfd_arch_info_type *arch_type = bfd_lookup_arch (arch, mach);
|
||||
const char *mach_name;
|
||||
|
||||
if (!arch_type)
|
||||
abort ();
|
||||
mach_name = arch_type->printable_name;
|
||||
|
||||
prev_isa = isa;
|
||||
prev_mach = mach;
|
||||
prev_endian = endian;
|
||||
cd = @arch@_cgen_cpu_open (CGEN_CPU_OPEN_ISAS, prev_isa,
|
||||
CGEN_CPU_OPEN_BFDMACH, mach_name,
|
||||
CGEN_CPU_OPEN_ENDIAN, prev_endian,
|
||||
CGEN_CPU_OPEN_END);
|
||||
if (!cd)
|
||||
abort ();
|
||||
@arch@_cgen_init_dis (cd);
|
||||
}
|
||||
|
||||
/* We try to have as much common code as possible.
|
||||
But at this point some targets need to take over. */
|
||||
/* ??? Some targets may need a hook elsewhere. Try to avoid this,
|
||||
but if not possible try to move this hook elsewhere rather than
|
||||
have two hooks. */
|
||||
length = CGEN_PRINT_INSN (cd, pc, info);
|
||||
if (length > 0)
|
||||
return length;
|
||||
if (length < 0)
|
||||
return -1;
|
||||
|
||||
(*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG);
|
||||
return cd->default_insn_bitsize / 8;
|
||||
}
|
|
@ -0,0 +1,570 @@
|
|||
/* Instruction building/extraction support for @arch@. -*- C -*-
|
||||
|
||||
THIS FILE IS MACHINE GENERATED WITH CGEN: Cpu tools GENerator.
|
||||
- the resultant file is machine generated, cgen-ibld.in isn't
|
||||
|
||||
Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of the GNU Binutils and GDB, the GNU debugger.
|
||||
|
||||
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 2, 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.,
|
||||
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* ??? Eventually more and more of this stuff can go to cpu-independent files.
|
||||
Keep that in mind. */
|
||||
|
||||
#include "sysdep.h"
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include "ansidecl.h"
|
||||
#include "dis-asm.h"
|
||||
#include "bfd.h"
|
||||
#include "symcat.h"
|
||||
#include "@prefix@-desc.h"
|
||||
#include "@prefix@-opc.h"
|
||||
#include "opintl.h"
|
||||
|
||||
#undef min
|
||||
#define min(a,b) ((a) < (b) ? (a) : (b))
|
||||
#undef max
|
||||
#define max(a,b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
/* Used by the ifield rtx function. */
|
||||
#define FLD(f) (fields->f)
|
||||
|
||||
static const char * insert_normal
|
||||
PARAMS ((CGEN_CPU_DESC, long, unsigned int, unsigned int, unsigned int,
|
||||
unsigned int, unsigned int, unsigned int, CGEN_INSN_BYTES_PTR));
|
||||
static const char * insert_insn_normal
|
||||
PARAMS ((CGEN_CPU_DESC, const CGEN_INSN *,
|
||||
CGEN_FIELDS *, CGEN_INSN_BYTES_PTR, bfd_vma));
|
||||
|
||||
static int extract_normal
|
||||
PARAMS ((CGEN_CPU_DESC, CGEN_EXTRACT_INFO *, CGEN_INSN_INT,
|
||||
unsigned int, unsigned int, unsigned int, unsigned int,
|
||||
unsigned int, unsigned int, bfd_vma, long *));
|
||||
static int extract_insn_normal
|
||||
PARAMS ((CGEN_CPU_DESC, const CGEN_INSN *, CGEN_EXTRACT_INFO *,
|
||||
CGEN_INSN_INT, CGEN_FIELDS *, bfd_vma));
|
||||
|
||||
/* Operand insertion. */
|
||||
|
||||
#if ! CGEN_INT_INSN_P
|
||||
|
||||
/* Subroutine of insert_normal. */
|
||||
|
||||
static CGEN_INLINE void
|
||||
insert_1 (cd, value, start, length, word_length, bufp)
|
||||
CGEN_CPU_DESC cd;
|
||||
unsigned long value;
|
||||
int start,length,word_length;
|
||||
unsigned char *bufp;
|
||||
{
|
||||
unsigned long x,mask;
|
||||
int shift;
|
||||
int big_p = CGEN_CPU_INSN_ENDIAN (cd) == CGEN_ENDIAN_BIG;
|
||||
|
||||
switch (word_length)
|
||||
{
|
||||
case 8:
|
||||
x = *bufp;
|
||||
break;
|
||||
case 16:
|
||||
if (big_p)
|
||||
x = bfd_getb16 (bufp);
|
||||
else
|
||||
x = bfd_getl16 (bufp);
|
||||
break;
|
||||
case 24:
|
||||
/* ??? This may need reworking as these cases don't necessarily
|
||||
want the first byte and the last two bytes handled like this. */
|
||||
if (big_p)
|
||||
x = (bufp[0] << 16) | bfd_getb16 (bufp + 1);
|
||||
else
|
||||
x = bfd_getl16 (bufp) | (bufp[2] << 16);
|
||||
break;
|
||||
case 32:
|
||||
if (big_p)
|
||||
x = bfd_getb32 (bufp);
|
||||
else
|
||||
x = bfd_getl32 (bufp);
|
||||
break;
|
||||
default :
|
||||
abort ();
|
||||
}
|
||||
|
||||
/* Written this way to avoid undefined behaviour. */
|
||||
mask = (((1L << (length - 1)) - 1) << 1) | 1;
|
||||
if (CGEN_INSN_LSB0_P)
|
||||
shift = (start + 1) - length;
|
||||
else
|
||||
shift = (word_length - (start + length));
|
||||
x = (x & ~(mask << shift)) | ((value & mask) << shift);
|
||||
|
||||
switch (word_length)
|
||||
{
|
||||
case 8:
|
||||
*bufp = x;
|
||||
break;
|
||||
case 16:
|
||||
if (big_p)
|
||||
bfd_putb16 (x, bufp);
|
||||
else
|
||||
bfd_putl16 (x, bufp);
|
||||
break;
|
||||
case 24:
|
||||
/* ??? This may need reworking as these cases don't necessarily
|
||||
want the first byte and the last two bytes handled like this. */
|
||||
if (big_p)
|
||||
{
|
||||
bufp[0] = x >> 16;
|
||||
bfd_putb16 (x, bufp + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
bfd_putl16 (x, bufp);
|
||||
bufp[2] = x >> 16;
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
if (big_p)
|
||||
bfd_putb32 (x, bufp);
|
||||
else
|
||||
bfd_putl32 (x, bufp);
|
||||
break;
|
||||
default :
|
||||
abort ();
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ! CGEN_INT_INSN_P */
|
||||
|
||||
/* Default insertion routine.
|
||||
|
||||
ATTRS is a mask of the boolean attributes.
|
||||
WORD_OFFSET is the offset in bits from the start of the insn of the value.
|
||||
WORD_LENGTH is the length of the word in bits in which the value resides.
|
||||
START is the starting bit number in the word, architecture origin.
|
||||
LENGTH is the length of VALUE in bits.
|
||||
TOTAL_LENGTH is the total length of the insn in bits.
|
||||
|
||||
The result is an error message or NULL if success. */
|
||||
|
||||
/* ??? This duplicates functionality with bfd's howto table and
|
||||
bfd_install_relocation. */
|
||||
/* ??? This doesn't handle bfd_vma's. Create another function when
|
||||
necessary. */
|
||||
|
||||
static const char *
|
||||
insert_normal (cd, value, attrs, word_offset, start, length, word_length,
|
||||
total_length, buffer)
|
||||
CGEN_CPU_DESC cd;
|
||||
long value;
|
||||
unsigned int attrs;
|
||||
unsigned int word_offset, start, length, word_length, total_length;
|
||||
CGEN_INSN_BYTES_PTR buffer;
|
||||
{
|
||||
static char errbuf[100];
|
||||
/* Written this way to avoid undefined behaviour. */
|
||||
unsigned long mask = (((1L << (length - 1)) - 1) << 1) | 1;
|
||||
|
||||
/* If LENGTH is zero, this operand doesn't contribute to the value. */
|
||||
if (length == 0)
|
||||
return NULL;
|
||||
|
||||
if (CGEN_INT_INSN_P
|
||||
&& word_offset != 0)
|
||||
abort ();
|
||||
|
||||
if (word_length > 32)
|
||||
abort ();
|
||||
|
||||
/* For architectures with insns smaller than the base-insn-bitsize,
|
||||
word_length may be too big. */
|
||||
if (cd->min_insn_bitsize < cd->base_insn_bitsize)
|
||||
{
|
||||
if (word_offset == 0
|
||||
&& word_length > total_length)
|
||||
word_length = total_length;
|
||||
}
|
||||
|
||||
/* Ensure VALUE will fit. */
|
||||
if (! CGEN_BOOL_ATTR (attrs, CGEN_IFLD_SIGNED))
|
||||
{
|
||||
unsigned long maxval = mask;
|
||||
|
||||
if ((unsigned long) value > maxval)
|
||||
{
|
||||
/* xgettext:c-format */
|
||||
sprintf (errbuf,
|
||||
_("operand out of range (%lu not between 0 and %lu)"),
|
||||
value, maxval);
|
||||
return errbuf;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! cgen_signed_overflow_ok_p (cd))
|
||||
{
|
||||
long minval = - (1L << (length - 1));
|
||||
long maxval = (1L << (length - 1)) - 1;
|
||||
|
||||
if (value < minval || value > maxval)
|
||||
{
|
||||
sprintf
|
||||
/* xgettext:c-format */
|
||||
(errbuf, _("operand out of range (%ld not between %ld and %ld)"),
|
||||
value, minval, maxval);
|
||||
return errbuf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if CGEN_INT_INSN_P
|
||||
|
||||
{
|
||||
int shift;
|
||||
|
||||
if (CGEN_INSN_LSB0_P)
|
||||
shift = (start + 1) - length;
|
||||
else
|
||||
shift = word_length - (start + length);
|
||||
*buffer = (*buffer & ~(mask << shift)) | ((value & mask) << shift);
|
||||
}
|
||||
|
||||
#else /* ! CGEN_INT_INSN_P */
|
||||
|
||||
{
|
||||
unsigned char *bufp = (unsigned char *) buffer + word_offset / 8;
|
||||
|
||||
insert_1 (cd, value, start, length, word_length, bufp);
|
||||
}
|
||||
|
||||
#endif /* ! CGEN_INT_INSN_P */
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Default insn builder (insert handler).
|
||||
The instruction is recorded in CGEN_INT_INSN_P byte order
|
||||
(meaning that if CGEN_INT_INSN_P BUFFER is an int * and thus the value is
|
||||
recorded in host byte order, otherwise BUFFER is an array of bytes and the
|
||||
value is recorded in target byte order).
|
||||
The result is an error message or NULL if success. */
|
||||
|
||||
static const char *
|
||||
insert_insn_normal (cd, insn, fields, buffer, pc)
|
||||
CGEN_CPU_DESC cd;
|
||||
const CGEN_INSN * insn;
|
||||
CGEN_FIELDS * fields;
|
||||
CGEN_INSN_BYTES_PTR buffer;
|
||||
bfd_vma pc;
|
||||
{
|
||||
const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
|
||||
unsigned long value;
|
||||
const unsigned char * syn;
|
||||
|
||||
CGEN_INIT_INSERT (cd);
|
||||
value = CGEN_INSN_BASE_VALUE (insn);
|
||||
|
||||
/* If we're recording insns as numbers (rather than a string of bytes),
|
||||
target byte order handling is deferred until later. */
|
||||
|
||||
#if CGEN_INT_INSN_P
|
||||
|
||||
*buffer = value;
|
||||
|
||||
#else
|
||||
|
||||
cgen_put_insn_value (cd, buffer, min (cd->base_insn_bitsize,
|
||||
CGEN_FIELDS_BITSIZE (fields)),
|
||||
value);
|
||||
|
||||
#endif /* ! CGEN_INT_INSN_P */
|
||||
|
||||
/* ??? It would be better to scan the format's fields.
|
||||
Still need to be able to insert a value based on the operand though;
|
||||
e.g. storing a branch displacement that got resolved later.
|
||||
Needs more thought first. */
|
||||
|
||||
for (syn = CGEN_SYNTAX_STRING (syntax); * syn != '\0'; ++ syn)
|
||||
{
|
||||
const char *errmsg;
|
||||
|
||||
if (CGEN_SYNTAX_CHAR_P (* syn))
|
||||
continue;
|
||||
|
||||
errmsg = (* cd->insert_operand) (cd, CGEN_SYNTAX_FIELD (*syn),
|
||||
fields, buffer, pc);
|
||||
if (errmsg)
|
||||
return errmsg;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Operand extraction. */
|
||||
|
||||
#if ! CGEN_INT_INSN_P
|
||||
|
||||
/* Subroutine of extract_normal.
|
||||
Ensure sufficient bytes are cached in EX_INFO.
|
||||
OFFSET is the offset in bytes from the start of the insn of the value.
|
||||
BYTES is the length of the needed value.
|
||||
Returns 1 for success, 0 for failure. */
|
||||
|
||||
static CGEN_INLINE int
|
||||
fill_cache (cd, ex_info, offset, bytes, pc)
|
||||
CGEN_CPU_DESC cd;
|
||||
CGEN_EXTRACT_INFO *ex_info;
|
||||
int offset, bytes;
|
||||
bfd_vma pc;
|
||||
{
|
||||
/* It's doubtful that the middle part has already been fetched so
|
||||
we don't optimize that case. kiss. */
|
||||
int mask;
|
||||
disassemble_info *info = (disassemble_info *) ex_info->dis_info;
|
||||
|
||||
/* First do a quick check. */
|
||||
mask = (1 << bytes) - 1;
|
||||
if (((ex_info->valid >> offset) & mask) == mask)
|
||||
return 1;
|
||||
|
||||
/* Search for the first byte we need to read. */
|
||||
for (mask = 1 << offset; bytes > 0; --bytes, ++offset, mask <<= 1)
|
||||
if (! (mask & ex_info->valid))
|
||||
break;
|
||||
|
||||
if (bytes)
|
||||
{
|
||||
int status;
|
||||
|
||||
pc += offset;
|
||||
status = (*info->read_memory_func)
|
||||
(pc, ex_info->insn_bytes + offset, bytes, info);
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
(*info->memory_error_func) (status, pc, info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ex_info->valid |= ((1 << bytes) - 1) << offset;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Subroutine of extract_normal. */
|
||||
|
||||
static CGEN_INLINE long
|
||||
extract_1 (cd, ex_info, start, length, word_length, bufp, pc)
|
||||
CGEN_CPU_DESC cd;
|
||||
CGEN_EXTRACT_INFO *ex_info;
|
||||
int start,length,word_length;
|
||||
unsigned char *bufp;
|
||||
bfd_vma pc;
|
||||
{
|
||||
unsigned long x,mask;
|
||||
int shift;
|
||||
int big_p = CGEN_CPU_INSN_ENDIAN (cd) == CGEN_ENDIAN_BIG;
|
||||
|
||||
switch (word_length)
|
||||
{
|
||||
case 8:
|
||||
x = *bufp;
|
||||
break;
|
||||
case 16:
|
||||
if (big_p)
|
||||
x = bfd_getb16 (bufp);
|
||||
else
|
||||
x = bfd_getl16 (bufp);
|
||||
break;
|
||||
case 24:
|
||||
/* ??? This may need reworking as these cases don't necessarily
|
||||
want the first byte and the last two bytes handled like this. */
|
||||
if (big_p)
|
||||
x = (bufp[0] << 16) | bfd_getb16 (bufp + 1);
|
||||
else
|
||||
x = bfd_getl16 (bufp) | (bufp[2] << 16);
|
||||
break;
|
||||
case 32:
|
||||
if (big_p)
|
||||
x = bfd_getb32 (bufp);
|
||||
else
|
||||
x = bfd_getl32 (bufp);
|
||||
break;
|
||||
default :
|
||||
abort ();
|
||||
}
|
||||
|
||||
/* Written this way to avoid undefined behaviour. */
|
||||
mask = (((1L << (length - 1)) - 1) << 1) | 1;
|
||||
if (CGEN_INSN_LSB0_P)
|
||||
shift = (start + 1) - length;
|
||||
else
|
||||
shift = (word_length - (start + length));
|
||||
return (x >> shift) & mask;
|
||||
}
|
||||
|
||||
#endif /* ! CGEN_INT_INSN_P */
|
||||
|
||||
/* Default extraction routine.
|
||||
|
||||
INSN_VALUE is the first base_insn_bitsize bits of the insn in host order,
|
||||
or sometimes less for cases like the m32r where the base insn size is 32
|
||||
but some insns are 16 bits.
|
||||
ATTRS is a mask of the boolean attributes. We only need `SIGNED',
|
||||
but for generality we take a bitmask of all of them.
|
||||
WORD_OFFSET is the offset in bits from the start of the insn of the value.
|
||||
WORD_LENGTH is the length of the word in bits in which the value resides.
|
||||
START is the starting bit number in the word, architecture origin.
|
||||
LENGTH is the length of VALUE in bits.
|
||||
TOTAL_LENGTH is the total length of the insn in bits.
|
||||
|
||||
Returns 1 for success, 0 for failure. */
|
||||
|
||||
/* ??? The return code isn't properly used. wip. */
|
||||
|
||||
/* ??? This doesn't handle bfd_vma's. Create another function when
|
||||
necessary. */
|
||||
|
||||
static int
|
||||
extract_normal (cd, ex_info, insn_value, attrs, word_offset, start, length,
|
||||
word_length, total_length, pc, valuep)
|
||||
CGEN_CPU_DESC cd;
|
||||
#if ! CGEN_INT_INSN_P
|
||||
CGEN_EXTRACT_INFO *ex_info;
|
||||
#else
|
||||
CGEN_EXTRACT_INFO *ex_info ATTRIBUTE_UNUSED;
|
||||
#endif
|
||||
CGEN_INSN_INT insn_value;
|
||||
unsigned int attrs;
|
||||
unsigned int word_offset, start, length, word_length, total_length;
|
||||
#if ! CGEN_INT_INSN_P
|
||||
bfd_vma pc;
|
||||
#else
|
||||
bfd_vma pc ATTRIBUTE_UNUSED;
|
||||
#endif
|
||||
long *valuep;
|
||||
{
|
||||
CGEN_INSN_INT value;
|
||||
|
||||
/* If LENGTH is zero, this operand doesn't contribute to the value
|
||||
so give it a standard value of zero. */
|
||||
if (length == 0)
|
||||
{
|
||||
*valuep = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (CGEN_INT_INSN_P
|
||||
&& word_offset != 0)
|
||||
abort ();
|
||||
|
||||
if (word_length > 32)
|
||||
abort ();
|
||||
|
||||
/* For architectures with insns smaller than the insn-base-bitsize,
|
||||
word_length may be too big. */
|
||||
if (cd->min_insn_bitsize < cd->base_insn_bitsize)
|
||||
{
|
||||
if (word_offset == 0
|
||||
&& word_length > total_length)
|
||||
word_length = total_length;
|
||||
}
|
||||
|
||||
/* Does the value reside in INSN_VALUE? */
|
||||
|
||||
if (word_offset == 0)
|
||||
{
|
||||
/* Written this way to avoid undefined behaviour. */
|
||||
CGEN_INSN_INT mask = (((1L << (length - 1)) - 1) << 1) | 1;
|
||||
|
||||
if (CGEN_INSN_LSB0_P)
|
||||
value = insn_value >> ((start + 1) - length);
|
||||
else
|
||||
value = insn_value >> (word_length - (start + length));
|
||||
value &= mask;
|
||||
/* sign extend? */
|
||||
if (CGEN_BOOL_ATTR (attrs, CGEN_IFLD_SIGNED)
|
||||
&& (value & (1L << (length - 1))))
|
||||
value |= ~mask;
|
||||
}
|
||||
|
||||
#if ! CGEN_INT_INSN_P
|
||||
|
||||
else
|
||||
{
|
||||
unsigned char *bufp = ex_info->insn_bytes + word_offset / 8;
|
||||
|
||||
if (word_length > 32)
|
||||
abort ();
|
||||
|
||||
if (fill_cache (cd, ex_info, word_offset / 8, word_length / 8, pc) == 0)
|
||||
return 0;
|
||||
|
||||
value = extract_1 (cd, ex_info, start, length, word_length, bufp, pc);
|
||||
}
|
||||
|
||||
#endif /* ! CGEN_INT_INSN_P */
|
||||
|
||||
*valuep = value;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Default insn extractor.
|
||||
|
||||
INSN_VALUE is the first base_insn_bitsize bits, translated to host order.
|
||||
The extracted fields are stored in FIELDS.
|
||||
EX_INFO is used to handle reading variable length insns.
|
||||
Return the length of the insn in bits, or 0 if no match,
|
||||
or -1 if an error occurs fetching data (memory_error_func will have
|
||||
been called). */
|
||||
|
||||
static int
|
||||
extract_insn_normal (cd, insn, ex_info, insn_value, fields, pc)
|
||||
CGEN_CPU_DESC cd;
|
||||
const CGEN_INSN *insn;
|
||||
CGEN_EXTRACT_INFO *ex_info;
|
||||
CGEN_INSN_INT insn_value;
|
||||
CGEN_FIELDS *fields;
|
||||
bfd_vma pc;
|
||||
{
|
||||
const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
|
||||
const unsigned char *syn;
|
||||
|
||||
CGEN_FIELDS_BITSIZE (fields) = CGEN_INSN_BITSIZE (insn);
|
||||
|
||||
CGEN_INIT_EXTRACT (cd);
|
||||
|
||||
for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn)
|
||||
{
|
||||
int length;
|
||||
|
||||
if (CGEN_SYNTAX_CHAR_P (*syn))
|
||||
continue;
|
||||
|
||||
length = (* cd->extract_operand) (cd, CGEN_SYNTAX_FIELD (*syn),
|
||||
ex_info, insn_value, fields, pc);
|
||||
if (length <= 0)
|
||||
return length;
|
||||
}
|
||||
|
||||
/* We recognized and successfully extracted this insn. */
|
||||
return CGEN_INSN_BITSIZE (insn);
|
||||
}
|
||||
|
||||
/* machine generated code added here */
|
|
@ -0,0 +1,134 @@
|
|||
#! /bin/sh
|
||||
# Generate CGEN opcode files: arch-desc.[ch], arch-opc.[ch],
|
||||
# arch-asm.c, arch-dis.c, arch-opinst.c, arch-ibld.[ch].
|
||||
#
|
||||
# Usage:
|
||||
# cgen.sh action srcdir cgen cgendir cgenflags arch prefix options
|
||||
#
|
||||
# ACTION is currently always "opcodes". It exists to be consistent with the
|
||||
# simulator.
|
||||
# OPTIONS is comma separated list of options:
|
||||
# - opinst - arch-opinst.c is being made, causes semantic analysis
|
||||
#
|
||||
# We store the generated files in the source directory until we decide to
|
||||
# ship a Scheme interpreter (or other implementation) with gdb/binutils.
|
||||
# Maybe we never will.
|
||||
|
||||
# We want to behave like make, any error forces us to stop.
|
||||
set -e
|
||||
|
||||
action=$1
|
||||
srcdir=$2
|
||||
cgen=$3
|
||||
cgendir=$4
|
||||
cgenflags=$5
|
||||
arch=$6
|
||||
prefix=$7
|
||||
options=$8
|
||||
|
||||
# List of extra files to build.
|
||||
# Values: opinst (only 1 extra file at present)
|
||||
extrafiles=$9
|
||||
|
||||
rootdir=${srcdir}/..
|
||||
|
||||
# $arch is $6, as passed on the command line.
|
||||
# $ARCH is the same argument but in all uppercase.
|
||||
# Both forms are used in this script.
|
||||
|
||||
lowercase='abcdefghijklmnopqrstuvwxyz'
|
||||
uppercase='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
ARCH=`echo ${arch} | tr "${lowercase}" "${uppercase}"`
|
||||
|
||||
extrafile_args=""
|
||||
for ef in .. $extrafiles
|
||||
do
|
||||
case $ef in
|
||||
..) ;;
|
||||
opinst) extrafile_args="-Q tmp-opinst.c1 $extrafile_args" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
case $action in
|
||||
opcodes)
|
||||
# Remove residual working files.
|
||||
rm -f tmp-desc.h tmp-desc.h1
|
||||
rm -f tmp-desc.c tmp-desc.c1
|
||||
rm -f tmp-opc.h tmp-opc.h1
|
||||
rm -f tmp-opc.c tmp-opc.c1
|
||||
rm -f tmp-opinst.c tmp-opinst.c1
|
||||
rm -f tmp-ibld.h tmp-ibld.h1
|
||||
rm -f tmp-ibld.c tmp-ibld.in1
|
||||
rm -f tmp-asm.c tmp-asm.in1
|
||||
rm -f tmp-dis.c tmp-dis.in1
|
||||
|
||||
# Run CGEN.
|
||||
${cgen} -s ${cgendir}/cgen-opc.scm \
|
||||
-s ${cgendir} \
|
||||
${cgenflags} \
|
||||
-f "${options}" \
|
||||
-m all \
|
||||
-a ${arch} \
|
||||
-H tmp-desc.h1 \
|
||||
-C tmp-desc.c1 \
|
||||
-O tmp-opc.h1 \
|
||||
-P tmp-opc.c1 \
|
||||
-L tmp-ibld.in1 \
|
||||
-A tmp-asm.in1 \
|
||||
-D tmp-dis.in1 \
|
||||
${extrafile_args}
|
||||
|
||||
# Customise generated files for the particular architecture.
|
||||
sed -e "s/@ARCH@/${ARCH}/g" -e "s/@arch@/${arch}/g" < tmp-desc.h1 > tmp-desc.h
|
||||
${rootdir}/move-if-change tmp-desc.h ${srcdir}/${prefix}-desc.h
|
||||
|
||||
sed -e "s/@ARCH@/${ARCH}/g" -e "s/@arch@/${arch}/g" \
|
||||
-e "s/@prefix@/${prefix}/" < tmp-desc.c1 > tmp-desc.c
|
||||
${rootdir}/move-if-change tmp-desc.c ${srcdir}/${prefix}-desc.c
|
||||
|
||||
sed -e "s/@ARCH@/${ARCH}/g" -e "s/@arch@/${arch}/g" < tmp-opc.h1 > tmp-opc.h
|
||||
${rootdir}/move-if-change tmp-opc.h ${srcdir}/${prefix}-opc.h
|
||||
|
||||
sed -e "s/@ARCH@/${ARCH}/g" -e "s/@arch@/${arch}/g" \
|
||||
-e "s/@prefix@/${prefix}/" < tmp-opc.c1 > tmp-opc.c
|
||||
${rootdir}/move-if-change tmp-opc.c ${srcdir}/${prefix}-opc.c
|
||||
|
||||
case $extrafiles in
|
||||
*opinst*)
|
||||
sed -e "s/@ARCH@/${ARCH}/g" -e "s/@arch@/${arch}/g" \
|
||||
-e "s/@prefix@/${prefix}/" < tmp-opinst.c1 >tmp-opinst.c
|
||||
${rootdir}/move-if-change tmp-opinst.c ${srcdir}/${prefix}-opinst.c
|
||||
;;
|
||||
esac
|
||||
|
||||
cat ${srcdir}/cgen-ibld.in tmp-ibld.in1 | \
|
||||
sed -e "s/@ARCH@/${ARCH}/g" -e "s/@arch@/${arch}/g" \
|
||||
-e "s/@prefix@/${prefix}/" > tmp-ibld.c
|
||||
${rootdir}/move-if-change tmp-ibld.c ${srcdir}/${prefix}-ibld.c
|
||||
|
||||
sed -e "/ -- assembler routines/ r tmp-asm.in1" ${srcdir}/cgen-asm.in \
|
||||
| sed -e "s/@ARCH@/${ARCH}/g" -e "s/@arch@/${arch}/g" \
|
||||
-e "s/@prefix@/${prefix}/" > tmp-asm.c
|
||||
${rootdir}/move-if-change tmp-asm.c ${srcdir}/${prefix}-asm.c
|
||||
|
||||
sed -e "/ -- disassembler routines/ r tmp-dis.in1" ${srcdir}/cgen-dis.in \
|
||||
| sed -e "s/@ARCH@/${ARCH}/g" -e "s/@arch@/${arch}/g" \
|
||||
-e "s/@prefix@/${prefix}/" > tmp-dis.c
|
||||
${rootdir}/move-if-change tmp-dis.c ${srcdir}/${prefix}-dis.c
|
||||
|
||||
# Remove temporary files.
|
||||
rm -f tmp-desc.h1 tmp-desc.c1
|
||||
rm -f tmp-opc.h1 tmp-opc.c1
|
||||
rm -f tmp-opinst.c1
|
||||
rm -f tmp-ibld.h1 tmp-ibld.in1
|
||||
rm -f tmp-asm.in1 tmp-dis.in1
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "$0: bad action: ${action}" >&2
|
||||
exit 1
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
exit 0
|
Loading…
Reference in New Issue