diff options
author | Doug Evans <dje@google.com> | 1998-11-10 19:11:04 +0000 |
---|---|---|
committer | Doug Evans <dje@google.com> | 1998-11-10 19:11:04 +0000 |
commit | 1c8f439ec6f8f43fa3a6e1987fb5ea23ebd910f8 (patch) | |
tree | b143242fc2138714d5178c04cd452ff1a675a286 /opcodes/cgen-dis.in | |
parent | a3606134a2986d6b28f7c0dd86b35d8be775765f (diff) | |
download | gdb-1c8f439ec6f8f43fa3a6e1987fb5ea23ebd910f8.zip gdb-1c8f439ec6f8f43fa3a6e1987fb5ea23ebd910f8.tar.gz gdb-1c8f439ec6f8f43fa3a6e1987fb5ea23ebd910f8.tar.bz2 |
* cgen-dis.in (print_normal): CGEN_OPERAND_FAKE renamed to
CGEN_OPERAND_SEM_ONLY.
* m32r-dis.c,m32r-opc.c,m32r-opc.h: Rebuild.
* fr30-dis.c,fr30-opc.c,fr30-opc.h: Rebuild.
Diffstat (limited to 'opcodes/cgen-dis.in')
-rw-r--r-- | opcodes/cgen-dis.in | 449 |
1 files changed, 333 insertions, 116 deletions
diff --git a/opcodes/cgen-dis.in b/opcodes/cgen-dis.in index 06e22eb..77edb9a 100644 --- a/opcodes/cgen-dis.in +++ b/opcodes/cgen-dis.in @@ -1,9 +1,9 @@ /* Disassembler interface for targets using CGEN. -*- C -*- CGEN: Cpu tools GENerator -This file is used to generate @arch@-dis.c. +THIS FILE IS USED TO GENERATE @prefix@-dis.c. -Copyright (C) 1996, 1997 Free Software Foundation, Inc. +Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. This file is part of the GNU Binutils and GDB, the GNU debugger. @@ -18,8 +18,8 @@ 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. */ +along with this program; if not, write to the Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "sysdep.h" #include <stdio.h> @@ -27,65 +27,215 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "dis-asm.h" #include "bfd.h" #include "symcat.h" -#include "@arch@-opc.h" +#include "@prefix@-opc.h" +#include "opintl.h" -/* ??? The layout of this stuff is still work in progress. - For speed in assembly/disassembly, we use inline functions. That of course - will only work for GCC. When this stuff is finished, we can decide whether - to keep the inline functions (and only get the performance increase when - compiled with GCC), or switch to macros, or use something else. -*/ - -/* Default text to print if an instruction isn't recognized. */ -#define UNKNOWN_INSN_MSG "*unknown*" - -/* FIXME: Machine generate. */ -#ifndef CGEN_PCREL_OFFSET -#define CGEN_PCREL_OFFSET 0 +#undef INLINE +#ifdef __GNUC__ +#define INLINE __inline__ +#else +#define INLINE #endif -static int print_insn PARAMS ((bfd_vma, disassemble_info *, char *, int)); - +/* Default text to print if an instruction isn't recognized. */ +#define UNKNOWN_INSN_MSG _("*unknown*") + +static int extract_normal + PARAMS ((CGEN_OPCODE_DESC, CGEN_EXTRACT_INFO *, CGEN_INSN_BYTES, + unsigned int, int, int, int, long *)); +static void print_normal + PARAMS ((CGEN_OPCODE_DESC, PTR, long, unsigned int, bfd_vma, int)); +static void print_address + PARAMS ((CGEN_OPCODE_DESC, PTR, bfd_vma, unsigned int, bfd_vma, int)); +static void print_keyword + PARAMS ((CGEN_OPCODE_DESC, PTR, CGEN_KEYWORD *, long, unsigned int)); static int extract_insn_normal - PARAMS ((const CGEN_INSN *, void *, cgen_insn_t, CGEN_FIELDS *)); + PARAMS ((CGEN_OPCODE_DESC, const CGEN_INSN *, CGEN_EXTRACT_INFO *, + unsigned long, CGEN_FIELDS *, bfd_vma)); static void print_insn_normal - PARAMS ((void *, const CGEN_INSN *, CGEN_FIELDS *, bfd_vma, int)); + PARAMS ((CGEN_OPCODE_DESC, PTR, const CGEN_INSN *, CGEN_FIELDS *, + bfd_vma, int)); +static int print_insn PARAMS ((CGEN_OPCODE_DESC, bfd_vma, + disassemble_info *, char *, int)); +static int default_print_insn + PARAMS ((CGEN_OPCODE_DESC, bfd_vma, disassemble_info *)); + +/* -- disassembler routines inserted here */ +#if ! CGEN_INT_INSN_P + +/* Subroutine of extract_normal. */ + +static INLINE long +extract_1 (od, ex_info, start, length, word_length, bufp) + CGEN_OPCODE_DESC od; + CGEN_EXTRACT_INFO *info; + int start,length,word_length; + unsigned char *bufp; +{ + unsigned long x,mask; + int shift; + int big_p = CGEN_OPCODE_INSN_ENDIAN (od) == CGEN_ENDIAN_BIG; + + /* FIXME: Need to use ex_info to ensure bytes have been fetched. */ + + 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 = (bfd_getb8 (bufp) << 16) | bfd_getb16 (bufp + 1); + else + x = bfd_getl16 (bufp) | (bfd_getb8 (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; + else + shift = (word_length - (start + length)); + return (x >> shift) & mask; +} + +#endif /* ! CGEN_INT_INSN_P */ + /* Default extraction routine. ATTRS is a mask of the boolean attributes. We only need `unsigned', but for generality we take a bitmask of all of them. */ +/* ??? This doesn't handle bfd_vma's. Create another function when + necessary. */ + static int -extract_normal (buf_ctrl, insn_value, attrs, start, length, shift, total_length, valuep) - PTR buf_ctrl; - cgen_insn_t insn_value; +extract_normal (od, ex_info, insn_value, attrs, start, length, total_length, valuep) + CGEN_OPCODE_DESC od; + CGEN_EXTRACT_INFO *ex_info; + CGEN_INSN_BYTES insn_value; unsigned int attrs; - int start, length, shift, total_length; + int start, length, total_length; long *valuep; { - long value; + unsigned long 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 + + { + /* Written this way to avoid undefined behaviour. */ + unsigned long mask = (((1L << (length - 1)) - 1) << 1) | 1; + + if (CGEN_INSN_LSB0_P) + value = insn_value >> start; + else + value = insn_value >> (total_length - (start + length)); + value &= mask; + /* sign extend? */ + if (! (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_UNSIGNED)) + && (value & (1L << (length - 1)))) + value |= ~mask; + } -#ifdef CGEN_INT_INSN -#if 0 - value = ((insn_value >> (CGEN_BASE_INSN_BITSIZE - (start + length))) - & ((1 << length) - 1)); -#else - value = ((insn_value >> (total_length - (start + length))) - & ((1 << length) - 1)); -#endif - if (! (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_UNSIGNED)) - && (value & (1 << (length - 1)))) - value -= 1 << length; #else - /* FIXME: unfinished */ -#endif - /* This is backwards as we undo the effects of insert_normal. */ - if (shift < 0) - value >>= -shift; + /* The hard case is probably too slow for the normal cases. + It's certainly more difficult to understand than the normal case. + Thus this is split into two. Keep it that way. The hard case is defined + to be when a field straddles a (loosely defined) word boundary + (??? which may require target specific help to determine). */ + +#if 0 /*wip*/ + +#define HARD_CASE_P 0 /* FIXME:wip */ + + if (HARD_CASE_P) + { + } +#endif else - value <<= shift; + { + unsigned char *bufp = (unsigned char *) insn_value; + + if (length > 32) + abort (); + + /* Adjust start,total_length,bufp to point to the pseudo-word that holds + the value. For example in a 48 bit insn where the value to insert + (say an immediate value) is the last 16 bits then word_length here + would be 16. To handle a 24 bit insn with an 18 bit immediate, + extract_1 handles 24 bits (using a combination of bfd_get8,16). */ + + if (total_length > 32) + { + int needed_width = start % 8 + length; + int fetch_length = (needed_width <= 8 ? 8 + : needed_width <= 16 ? 16 + : 32); + + if (CGEN_INSN_LSB0_P) + { + if (CGEN_INSN_WORD_ENDIAN (od) == CGEN_ENDIAN_BIG) + { + abort (); /* wip */ + } + else + { + int offset = start & ~7; + + bufp += offset / 8; + start -= offset; + total_length -= offset; + } + } + else + { + if (CGEN_INSN_WORD_ENDIAN (od) == CGEN_ENDIAN_BIG) + { + int offset = start & ~7; + + bufp += offset / 8; + start -= offset; + total_length -= offset; + } + else + { + abort (); /* wip */ + } + } + } + + /* FIXME: which bytes are being extracted have been lost. */ + value = extract_1 (od, ex_info, start, length, total_length, bufp); + } + +#endif /* ! CGEN_INT_INSN_P */ *valuep = value; @@ -96,40 +246,70 @@ extract_normal (buf_ctrl, insn_value, attrs, start, length, shift, total_length, /* Default print handler. */ static void -print_normal (dis_info, value, attrs, pc, length) +print_normal (od, dis_info, value, attrs, pc, length) + CGEN_OPCODE_DESC od; PTR dis_info; long value; unsigned int attrs; - unsigned long pc; /* FIXME: should be bfd_vma */ + bfd_vma pc; int length; { - disassemble_info *info = dis_info; + disassemble_info *info = (disassemble_info *) dis_info; + +#ifdef CGEN_PRINT_NORMAL + CGEN_PRINT_NORMAL (od, info, value, attrs, pc, length); +#endif /* Print the operand as directed by the attributes. */ - if (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_FAKE)) - ; /* nothing to do (??? at least not yet) */ - else if (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_PCREL_ADDR)) - (*info->print_address_func) (pc + CGEN_PCREL_OFFSET + value, info); - /* ??? Not all cases of this are currently caught. */ - else if (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_ABS_ADDR)) - /* FIXME: Why & 0xffffffff? */ - (*info->print_address_func) ((bfd_vma) value & 0xffffffff, info); - else if (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_UNSIGNED)) + if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY)) + ; /* nothing to do */ + else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_UNSIGNED)) (*info->fprintf_func) (info->stream, "0x%lx", value); else (*info->fprintf_func) (info->stream, "%ld", value); } +/* Default address handler. */ + +static void +print_address (od, dis_info, value, attrs, pc, length) + CGEN_OPCODE_DESC od; + PTR dis_info; + bfd_vma value; + unsigned int attrs; + bfd_vma pc; + int length; +{ + disassemble_info *info = (disassemble_info *) dis_info; + +#ifdef CGEN_PRINT_ADDRESS + CGEN_PRINT_ADDRESS (od, 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_UNSIGNED)) + (*info->fprintf_func) (info->stream, "0x%lx", (long) value); + else + (*info->fprintf_func) (info->stream, "%ld", (long) value); +} + /* Keyword print handler. */ static void -print_keyword (dis_info, keyword_table, value, attrs) +print_keyword (od, dis_info, keyword_table, value, attrs) + CGEN_OPCODE_DESC od; PTR dis_info; CGEN_KEYWORD *keyword_table; long value; - CGEN_ATTR *attrs; + unsigned int attrs; { - disassemble_info *info = dis_info; + disassemble_info *info = (disassemble_info *) dis_info; const CGEN_KEYWORD_ENTRY *ke; ke = cgen_keyword_lookup_value (keyword_table, value); @@ -139,27 +319,30 @@ print_keyword (dis_info, keyword_table, value, attrs) (*info->fprintf_func) (info->stream, "???"); } -/* -- disassembler routines inserted here */ - /* Default insn extractor. - The extracted fields are stored in DIS_FLDS. - BUF_CTRL is used to handle reading variable length insns (FIXME: not done). - Return the length of the insn in bits, or 0 if no match. */ + INSN_VALUE is the first CGEN_BASE_INSN_SIZE bytes, 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 (insn, buf_ctrl, insn_value, fields) +extract_insn_normal (od, insn, ex_info, insn_value, fields, pc) + CGEN_OPCODE_DESC od; const CGEN_INSN *insn; - PTR buf_ctrl; - cgen_insn_t insn_value; + CGEN_EXTRACT_INFO *ex_info; + unsigned long 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 (); + CGEN_INIT_EXTRACT (od); for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn) { @@ -168,10 +351,10 @@ extract_insn_normal (insn, buf_ctrl, insn_value, fields) if (CGEN_SYNTAX_CHAR_P (*syn)) continue; - length = @arch@_cgen_extract_operand (CGEN_SYNTAX_FIELD (*syn), - buf_ctrl, insn_value, fields); - if (length == 0) - return 0; + length = @arch@_cgen_extract_operand (od, CGEN_SYNTAX_FIELD (*syn), + ex_info, insn_value, fields, pc); + if (length <= 0) + return length; } /* We recognized and successfully extracted this insn. */ @@ -181,11 +364,11 @@ extract_insn_normal (insn, buf_ctrl, insn_value, fields) /* Default insn printer. DIS_INFO is defined as `PTR' so the disassembler needn't know anything - about disassemble_info. -*/ + about disassemble_info. */ static void -print_insn_normal (dis_info, insn, fields, pc, length) +print_insn_normal (od, dis_info, insn, fields, pc, length) + CGEN_OPCODE_DESC od; PTR dis_info; const CGEN_INSN *insn; CGEN_FIELDS *fields; @@ -193,10 +376,10 @@ print_insn_normal (dis_info, insn, fields, pc, length) int length; { const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn); - disassemble_info *info = dis_info; + disassemble_info *info = (disassemble_info *) dis_info; const unsigned char *syn; - CGEN_INIT_PRINT (); + CGEN_INIT_PRINT (od); for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn) { @@ -212,23 +395,20 @@ print_insn_normal (dis_info, insn, fields, pc, length) } /* We have an operand. */ - @arch@_cgen_print_operand (CGEN_SYNTAX_FIELD (*syn), info, - fields, CGEN_INSN_ATTRS (insn), pc, length); + @arch@_cgen_print_operand (od, CGEN_SYNTAX_FIELD (*syn), info, + fields, CGEN_INSN_ATTRS (insn), pc, length); } } -/* Default value for CGEN_PRINT_INSN. - Given BUFLEN bits (target byte order) read into BUF, look up the - insn in the instruction table and disassemble it. - - The result is the size of the insn in bytes. */ - -#ifndef CGEN_PRINT_INSN -#define CGEN_PRINT_INSN print_insn -#endif +/* 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 (pc, info, buf, buflen) +print_insn (od, pc, info, buf, buflen) + CGEN_OPCODE_DESC od; bfd_vma pc; disassemble_info *info; char *buf; @@ -236,16 +416,21 @@ print_insn (pc, info, buf, 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 << CGEN_BASE_INSN_SIZE) - 1; + ex_info.bytes = buf; switch (buflen) { - case 8: + case 1: insn_value = buf[0]; break; - case 16: + case 2: insn_value = info->endian == BFD_ENDIAN_BIG ? bfd_getb16 (buf) : bfd_getl16 (buf); break; - case 32: + case 4: insn_value = info->endian == BFD_ENDIAN_BIG ? bfd_getb32 (buf) : bfd_getl32 (buf); break; default: @@ -255,7 +440,7 @@ print_insn (pc, info, buf, buflen) /* 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 (buf, insn_value); + insn_list = CGEN_DIS_LOOKUP_INSN (od, buf, insn_value); while (insn_list != NULL) { const CGEN_INSN *insn = insn_list->insn; @@ -264,7 +449,7 @@ print_insn (pc, info, buf, buflen) #if 0 /* not needed as insn shouldn't be in hash lists if not supported */ /* Supported by this cpu? */ - if (! @arch@_cgen_insn_supported (insn)) + if (! @arch@_cgen_insn_supported (od, insn)) continue; #endif @@ -277,10 +462,14 @@ print_insn (pc, info, buf, buflen) machine insn and extracts the fields. The second pass prints them. */ - length = (*CGEN_EXTRACT_FN (insn)) (insn, NULL, insn_value, &fields); + length = (*CGEN_EXTRACT_FN (insn)) (od, insn, &ex_info, insn_value, + &fields, pc); + /* length < 0 -> error */ + if (length < 0) + return length; if (length > 0) { - (*CGEN_PRINT_FN (insn)) (info, insn, &fields, pc, length); + (*CGEN_PRINT_FN (insn)) (od, info, insn, &fields, pc, length); /* length is in bits, result is in bytes */ return length / 8; } @@ -292,6 +481,35 @@ print_insn (pc, info, buf, buflen) 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 (od, pc, info) + CGEN_OPCODE_DESC od; + 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, CGEN_BASE_INSN_SIZE, info); + if (status != 0) + { + (*info->memory_error_func) (status, pc, info); + return -1; + } + + return print_insn (od, pc, info, buf, CGEN_BASE_INSN_SIZE); +} + /* Main entry point. Print one instruction from PC on INFO->STREAM. Return the size of the instruction (in bytes). */ @@ -301,30 +519,27 @@ print_insn_@arch@ (pc, info) bfd_vma pc; disassemble_info *info; { - char buffer[CGEN_MAX_INSN_SIZE]; - int status, length; - static int initialized = 0; - static int current_mach = 0; - static int current_big_p = 0; + int length; + static CGEN_OPCODE_DESC od = 0; int mach = info->mach; int big_p = info->endian == BFD_ENDIAN_BIG; - /* If we haven't initialized yet, or if we've switched cpu's, initialize. */ - if (!initialized || mach != current_mach || big_p != current_big_p) + /* If we haven't initialized yet, initialize the opcode table. */ + if (! od) { - initialized = 1; - current_mach = mach; - current_big_p = big_p; - @arch@_cgen_init_dis (mach, big_p ? CGEN_ENDIAN_BIG : CGEN_ENDIAN_LITTLE); + od = @arch@_cgen_opcode_open (mach, + big_p ? + CGEN_ENDIAN_BIG + : CGEN_ENDIAN_LITTLE); + @arch@_cgen_init_dis (od); } - - /* Read enough of the insn so we can look it up in the hash lists. */ - - status = (*info->read_memory_func) (pc, buffer, CGEN_BASE_INSN_SIZE, info); - if (status != 0) + /* If we've switched cpu's, re-initialize. */ + /* ??? Perhaps we should use BFD_ENDIAN. */ + else if (mach != CGEN_OPCODE_MACH (od) + || (CGEN_OPCODE_ENDIAN (od) + != (big_p ? CGEN_ENDIAN_BIG : CGEN_ENDIAN_LITTLE))) { - (*info->memory_error_func) (status, pc, info); - return -1; + cgen_set_cpu (od, mach, big_p ? CGEN_ENDIAN_BIG : CGEN_ENDIAN_LITTLE); } /* We try to have as much common code as possible. @@ -332,9 +547,11 @@ print_insn_@arch@ (pc, info) /* ??? 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 (pc, info, buffer, CGEN_BASE_INSN_BITSIZE); - if (length) + length = CGEN_PRINT_INSN (od, pc, info); + if (length > 0) return length; + if (length < 0) + return -1; (*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG); return CGEN_DEFAULT_INSN_SIZE; |