mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-06-20 18:50:21 +02:00
initial import
(automatically generated log message) git-archimport-id: bonzini@gnu.org--2004b/lightning--stable--1.2--base-0
This commit is contained in:
commit
3b4c061913
79 changed files with 26993 additions and 0 deletions
7
opcode/Makefile.am
Normal file
7
opcode/Makefile.am
Normal file
|
@ -0,0 +1,7 @@
|
|||
EXTRA_LIBRARIES = libdisass.a
|
||||
noinst_LIBRARIES = @LIBDISASS@
|
||||
|
||||
libdisass_a_SOURCES = dis-buf.c i386-dis.c ppc-dis.c ppc-opc.c sparc-dis.c \
|
||||
sparc-opc.c disass.c
|
||||
|
||||
noinst_HEADERS = ansidecl.h bfd.h dis-asm.h i386.h ppc.h sparc.h sysdep.h
|
13
opcode/ansidecl.h
Normal file
13
opcode/ansidecl.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef __ANSIDECL_H_SEEN
|
||||
#define __ANSIDECL_H_SEEN
|
||||
|
||||
#ifdef __STDC__
|
||||
#define PARAMS(x) x
|
||||
typedef void *PTR;
|
||||
#else
|
||||
#define CONST const
|
||||
#define PARAMS(x) ()
|
||||
typedef char *PTR;
|
||||
#endif
|
||||
|
||||
#endif
|
185
opcode/bfd.h
Normal file
185
opcode/bfd.h
Normal file
|
@ -0,0 +1,185 @@
|
|||
/* Main header file for the bfd library -- portable access to object files.
|
||||
Copyright 1990, 91, 92, 93, 94, 95, 1996 Free Software Foundation, Inc.
|
||||
Contributed by Cygnus Support.
|
||||
|
||||
This file is part of BFD, the Binary File Descriptor library.
|
||||
(Simplified and modified for GNU lightning)
|
||||
|
||||
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 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* bfd.h -- The only header file required by users of the bfd library
|
||||
|
||||
The bfd.h file is generated from bfd-in.h and various .c files; if you
|
||||
change it, your changes will probably be lost.
|
||||
|
||||
All the prototypes and definitions following the comment "THE FOLLOWING
|
||||
IS EXTRACTED FROM THE SOURCE" are extracted from the source files for
|
||||
BFD. If you change it, someone oneday will extract it from the source
|
||||
again, and your changes will be lost. To save yourself from this bind,
|
||||
change the definitions in the source in the bfd directory. Type "make
|
||||
docs" and then "make headers" in that directory, and magically this file
|
||||
will change to reflect your changes.
|
||||
|
||||
If you don't have the tools to perform the extraction, then you are
|
||||
safe from someone on your system trampling over your header files.
|
||||
You should still maintain the equivalence between the source and this
|
||||
file though; every change you make to the .c file should be reflected
|
||||
here. */
|
||||
|
||||
#ifndef __BFD_H_SEEN__
|
||||
#define __BFD_H_SEEN__
|
||||
|
||||
#include "ansidecl.h"
|
||||
|
||||
#ifndef INLINE
|
||||
#if __GNUC__ >= 2
|
||||
#define INLINE __inline__
|
||||
#else
|
||||
#define INLINE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* To squelch erroneous compiler warnings ("illegal pointer
|
||||
combination") from the SVR3 compiler, we would like to typedef
|
||||
boolean to int (it doesn't like functions which return boolean.
|
||||
Making sure they are never implicitly declared to return int
|
||||
doesn't seem to help). But this file is not configured based on
|
||||
the host. */
|
||||
/* General rules: functions which are boolean return true on success
|
||||
and false on failure (unless they're a predicate). -- bfd.doc */
|
||||
/* I'm sure this is going to break something and someone is going to
|
||||
force me to change it. */
|
||||
/* typedef enum boolean {false, true} boolean; */
|
||||
/* Yup, SVR4 has a "typedef enum boolean" in <sys/types.h> -fnf */
|
||||
/* It gets worse if the host also defines a true/false enum... -sts */
|
||||
/* And even worse if your compiler has built-in boolean types... -law */
|
||||
#if defined (__GNUG__) && (__GNUC_MINOR__ > 5)
|
||||
#define TRUE_FALSE_ALREADY_DEFINED
|
||||
#endif
|
||||
#ifdef MPW
|
||||
/* Pre-emptive strike - get the file with the enum. */
|
||||
#include <Types.h>
|
||||
#define TRUE_FALSE_ALREADY_DEFINED
|
||||
#endif /* MPW */
|
||||
#ifndef TRUE_FALSE_ALREADY_DEFINED
|
||||
typedef enum bfd_boolean {false, true} boolean;
|
||||
#define BFD_TRUE_FALSE
|
||||
#else
|
||||
/* Use enum names that will appear nowhere else. */
|
||||
typedef enum bfd_boolean {bfd_fffalse, bfd_tttrue} boolean;
|
||||
#endif
|
||||
|
||||
/* A pointer to a position in a file. */
|
||||
/* FIXME: This should be using off_t from <sys/types.h>.
|
||||
For now, try to avoid breaking stuff by not including <sys/types.h> here.
|
||||
This will break on systems with 64-bit file offsets (e.g. 4.4BSD).
|
||||
Probably the best long-term answer is to avoid using file_ptr AND off_t
|
||||
in this header file, and to handle this in the BFD implementation
|
||||
rather than in its interface. */
|
||||
/* typedef off_t file_ptr; */
|
||||
typedef long int file_ptr;
|
||||
|
||||
/* Represent a target address. Also used as a generic unsigned type
|
||||
which is guaranteed to be big enough to hold any arithmetic types
|
||||
we need to deal with. */
|
||||
typedef unsigned long bfd_vma;
|
||||
|
||||
/* A generic signed type which is guaranteed to be big enough to hold any
|
||||
arithmetic types we need to deal with. Can be assumed to be compatible
|
||||
with bfd_vma in the same way that signed and unsigned ints are compatible
|
||||
(as parameters, in assignment, etc). */
|
||||
typedef long bfd_signed_vma;
|
||||
|
||||
typedef unsigned long symvalue;
|
||||
typedef unsigned long bfd_size_type;
|
||||
|
||||
/* Print a bfd_vma x on stream s. */
|
||||
#define fprintf_vma(s,x) fprintf(s, "%08lx", x)
|
||||
#define sprintf_vma(s,x) sprintf(s, "%08lx", x)
|
||||
#define printf_vma(x) fprintf_vma(stdout,x)
|
||||
|
||||
typedef unsigned int flagword; /* 32 bits of flags */
|
||||
typedef unsigned char bfd_byte;
|
||||
|
||||
enum bfd_architecture
|
||||
{
|
||||
bfd_arch_unknown, /* File arch not known */
|
||||
bfd_arch_obscure, /* Arch known, not one of these */
|
||||
bfd_arch_m68k, /* Motorola 68xxx */
|
||||
bfd_arch_vax, /* DEC Vax */
|
||||
bfd_arch_i960, /* Intel 960 */
|
||||
/* The order of the following is important.
|
||||
lower number indicates a machine type that
|
||||
only accepts a subset of the instructions
|
||||
available to machines with higher numbers.
|
||||
The exception is the "ca", which is
|
||||
incompatible with all other machines except
|
||||
"core". */
|
||||
|
||||
#define bfd_mach_i960_core 1
|
||||
#define bfd_mach_i960_ka_sa 2
|
||||
#define bfd_mach_i960_kb_sb 3
|
||||
#define bfd_mach_i960_mc 4
|
||||
#define bfd_mach_i960_xa 5
|
||||
#define bfd_mach_i960_ca 6
|
||||
#define bfd_mach_i960_jx 7
|
||||
#define bfd_mach_i960_hx 8
|
||||
|
||||
bfd_arch_a29k, /* AMD 29000 */
|
||||
bfd_arch_sparc, /* SPARC */
|
||||
#define bfd_mach_sparc 1
|
||||
/* The difference between v8plus and v9 is that v9 is a true 64 bit env. */
|
||||
#define bfd_mach_sparc_v8plus 2
|
||||
#define bfd_mach_sparc_v8plusa 3 /* with ultrasparc add'ns */
|
||||
#define bfd_mach_sparc_v9 4
|
||||
#define bfd_mach_sparc_v9a 5 /* with ultrasparc add'ns */
|
||||
/* Nonzero if MACH has the v9 instruction set. */
|
||||
#define bfd_mach_sparc_v9_p(mach) ((mach) != bfd_mach_sparc)
|
||||
bfd_arch_mips, /* MIPS Rxxxx */
|
||||
bfd_arch_i386, /* Intel 386 */
|
||||
bfd_arch_we32k, /* AT&T WE32xxx */
|
||||
bfd_arch_tahoe, /* CCI/Harris Tahoe */
|
||||
bfd_arch_i860, /* Intel 860 */
|
||||
bfd_arch_romp, /* IBM ROMP PC/RT */
|
||||
bfd_arch_alliant, /* Alliant */
|
||||
bfd_arch_convex, /* Convex */
|
||||
bfd_arch_m88k, /* Motorola 88xxx */
|
||||
bfd_arch_pyramid, /* Pyramid Technology */
|
||||
bfd_arch_h8300, /* Hitachi H8/300 */
|
||||
#define bfd_mach_h8300 1
|
||||
#define bfd_mach_h8300h 2
|
||||
bfd_arch_powerpc, /* PowerPC */
|
||||
bfd_arch_rs6000, /* IBM RS/6000 */
|
||||
bfd_arch_hppa, /* HP PA RISC */
|
||||
bfd_arch_z8k, /* Zilog Z8000 */
|
||||
#define bfd_mach_z8001 1
|
||||
#define bfd_mach_z8002 2
|
||||
bfd_arch_h8500, /* Hitachi H8/500 */
|
||||
bfd_arch_sh, /* Hitachi SH */
|
||||
bfd_arch_alpha, /* Dec Alpha */
|
||||
bfd_arch_arm, /* Advanced Risc Machines ARM */
|
||||
bfd_arch_ns32k, /* National Semiconductors ns32000 */
|
||||
bfd_arch_w65, /* WDC 65816 */
|
||||
bfd_arch_last
|
||||
};
|
||||
|
||||
enum bfd_endian { BFD_ENDIAN_UNKNOWN };
|
||||
|
||||
typedef struct bfd bfd;
|
||||
|
||||
#define bfd_getb32(x) *((int *)(x))
|
||||
#define bfd_getl32(x) *((int *)(x))
|
||||
|
||||
#endif
|
175
opcode/dis-asm.h
Normal file
175
opcode/dis-asm.h
Normal file
|
@ -0,0 +1,175 @@
|
|||
/* Interface between the opcode library and its callers.
|
||||
Written by Cygnus Support, 1993.
|
||||
|
||||
The opcode library (libopcodes.a) provides instruction decoders for
|
||||
a large variety of instruction sets, callable with an identical
|
||||
interface, for making instruction-processing programs more independent
|
||||
of the instruction set being processed. */
|
||||
|
||||
#ifndef DIS_ASM_H
|
||||
#define DIS_ASM_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "bfd.h"
|
||||
|
||||
typedef int (*fprintf_ftype) PARAMS((FILE*, const char*, ...));
|
||||
|
||||
enum dis_insn_type {
|
||||
dis_noninsn, /* Not a valid instruction */
|
||||
dis_nonbranch, /* Not a branch instruction */
|
||||
dis_branch, /* Unconditional branch */
|
||||
dis_condbranch, /* Conditional branch */
|
||||
dis_jsr, /* Jump to subroutine */
|
||||
dis_condjsr, /* Conditional jump to subroutine */
|
||||
dis_dref, /* Data reference instruction */
|
||||
dis_dref2 /* Two data references in instruction */
|
||||
};
|
||||
|
||||
/* This struct is passed into the instruction decoding routine,
|
||||
and is passed back out into each callback. The various fields are used
|
||||
for conveying information from your main routine into your callbacks,
|
||||
for passing information into the instruction decoders (such as the
|
||||
addresses of the callback functions), or for passing information
|
||||
back from the instruction decoders to their callers.
|
||||
|
||||
It must be initialized before it is first passed; this can be done
|
||||
by hand, or using one of the initialization macros below. */
|
||||
|
||||
typedef struct disassemble_info {
|
||||
fprintf_ftype fprintf_func;
|
||||
FILE *stream;
|
||||
PTR application_data;
|
||||
|
||||
/* Target description. We could replace this with a pointer to the bfd,
|
||||
but that would require one. There currently isn't any such requirement
|
||||
so to avoid introducing one we record these explicitly. */
|
||||
/* The bfd_arch value. */
|
||||
enum bfd_architecture arch;
|
||||
/* The bfd_mach value. */
|
||||
unsigned long mach;
|
||||
/* Endianness (for bi-endian cpus). Mono-endian cpus can ignore this. */
|
||||
enum bfd_endian endian;
|
||||
|
||||
/* For use by the disassembler.
|
||||
The top 16 bits are reserved for public use (and are documented here).
|
||||
The bottom 16 bits are for the internal use of the disassembler. */
|
||||
unsigned long flags;
|
||||
PTR private_data;
|
||||
|
||||
/* Function used to get bytes to disassemble. MEMADDR is the
|
||||
address of the stuff to be disassembled, MYADDR is the address to
|
||||
put the bytes in, and LENGTH is the number of bytes to read.
|
||||
INFO is a pointer to this struct.
|
||||
Returns an errno value or 0 for success. */
|
||||
int (*read_memory_func)
|
||||
PARAMS ((bfd_vma memaddr, bfd_byte *myaddr, int length,
|
||||
struct disassemble_info *info));
|
||||
|
||||
/* Function which should be called if we get an error that we can't
|
||||
recover from. STATUS is the errno value from read_memory_func and
|
||||
MEMADDR is the address that we were trying to read. INFO is a
|
||||
pointer to this struct. */
|
||||
void (*memory_error_func)
|
||||
PARAMS ((int status, bfd_vma memaddr, struct disassemble_info *info));
|
||||
|
||||
/* Function called to print ADDR. */
|
||||
void (*print_address_func)
|
||||
PARAMS ((bfd_vma addr, struct disassemble_info *info));
|
||||
|
||||
/* These are for buffer_read_memory. */
|
||||
bfd_byte *buffer;
|
||||
bfd_vma buffer_vma;
|
||||
int buffer_length;
|
||||
|
||||
/* Results from instruction decoders. Not all decoders yet support
|
||||
this information. This info is set each time an instruction is
|
||||
decoded, and is only valid for the last such instruction.
|
||||
|
||||
To determine whether this decoder supports this information, set
|
||||
insn_info_valid to 0, decode an instruction, then check it. */
|
||||
|
||||
char insn_info_valid; /* Branch info has been set. */
|
||||
char branch_delay_insns; /* How many sequential insn's will run before
|
||||
a branch takes effect. (0 = normal) */
|
||||
char data_size; /* Size of data reference in insn, in bytes */
|
||||
enum dis_insn_type insn_type; /* Type of instruction */
|
||||
bfd_vma target; /* Target address of branch or dref, if known;
|
||||
zero if unknown. */
|
||||
bfd_vma target2; /* Second target address for dref2 */
|
||||
|
||||
} disassemble_info;
|
||||
|
||||
|
||||
/* Standard disassemblers. Disassemble one instruction at the given
|
||||
target address. Return number of bytes processed. */
|
||||
typedef int (*disassembler_ftype)
|
||||
PARAMS((bfd_vma, disassemble_info *));
|
||||
|
||||
extern int print_insn_big_mips PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_little_mips PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_i386 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_m68k PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_z8001 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_z8002 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_h8300 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_h8300h PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_h8500 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_alpha PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_big_arm PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_little_arm PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_sparc PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_sparc64 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_big_a29k PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_little_a29k PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_i960 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_sh PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_shl PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_hppa PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_m88k PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_ns32k PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_big_powerpc PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_little_powerpc PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_rs6000 PARAMS ((bfd_vma, disassemble_info*));
|
||||
extern int print_insn_w65 PARAMS ((bfd_vma, disassemble_info*));
|
||||
|
||||
/* Fetch the disassembler for a given BFD, if that support is available. */
|
||||
extern disassembler_ftype disassembler PARAMS ((bfd *));
|
||||
|
||||
|
||||
/* This block of definitions is for particular callers who read instructions
|
||||
into a buffer before calling the instruction decoder. */
|
||||
|
||||
/* Here is a function which callers may wish to use for read_memory_func.
|
||||
It gets bytes from a buffer. */
|
||||
extern int buffer_read_memory
|
||||
PARAMS ((bfd_vma, bfd_byte *, int, struct disassemble_info *));
|
||||
|
||||
/* This function goes with buffer_read_memory.
|
||||
It prints a message using info->fprintf_func and info->stream. */
|
||||
extern void perror_memory PARAMS ((int, bfd_vma, struct disassemble_info *));
|
||||
|
||||
|
||||
/* Just print the address in hex. This is included for completeness even
|
||||
though both GDB and objdump provide their own (to print symbolic
|
||||
addresses). */
|
||||
extern void generic_print_address
|
||||
PARAMS ((bfd_vma, struct disassemble_info *));
|
||||
|
||||
/* Macro to initialize a disassemble_info struct. This should be called
|
||||
by all applications creating such a struct. */
|
||||
#define INIT_DISASSEMBLE_INFO(INFO, STREAM, FPRINTF_FUNC) \
|
||||
(INFO).fprintf_func = (FPRINTF_FUNC), \
|
||||
(INFO).stream = (STREAM), \
|
||||
(INFO).buffer = NULL, \
|
||||
(INFO).buffer_vma = 0, \
|
||||
(INFO).buffer_length = 0, \
|
||||
(INFO).read_memory_func = buffer_read_memory, \
|
||||
(INFO).memory_error_func = perror_memory, \
|
||||
(INFO).print_address_func = generic_print_address, \
|
||||
(INFO).arch = bfd_arch_unknown, \
|
||||
(INFO).mach = 0, \
|
||||
(INFO).endian = BFD_ENDIAN_UNKNOWN, \
|
||||
(INFO).flags = 0, \
|
||||
(INFO).insn_info_valid = 0
|
||||
|
||||
#endif /* ! defined (DIS_ASM_H) */
|
70
opcode/dis-buf.c
Normal file
70
opcode/dis-buf.c
Normal file
|
@ -0,0 +1,70 @@
|
|||
/* Disassemble from a buffer, for GNU.
|
||||
Copyright (C) 1993, 1994 Free Software Foundation, Inc.
|
||||
|
||||
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 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
#include "sysdep.h"
|
||||
#include "dis-asm.h"
|
||||
#include <errno.h>
|
||||
|
||||
/* Get LENGTH bytes from info's buffer, at target address memaddr.
|
||||
Transfer them to myaddr. */
|
||||
int
|
||||
buffer_read_memory (memaddr, myaddr, length, info)
|
||||
bfd_vma memaddr;
|
||||
bfd_byte *myaddr;
|
||||
int length;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
if (memaddr < info->buffer_vma
|
||||
|| memaddr + length > info->buffer_vma + info->buffer_length)
|
||||
/* Out of bounds. Use EIO because GDB uses it. */
|
||||
return EIO;
|
||||
memcpy (myaddr, info->buffer + (memaddr - info->buffer_vma), length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Print an error message. We can assume that this is in response to
|
||||
an error return from buffer_read_memory. */
|
||||
void
|
||||
perror_memory (status, memaddr, info)
|
||||
int status;
|
||||
bfd_vma memaddr;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
if (status != EIO)
|
||||
/* Can't happen. */
|
||||
(*info->fprintf_func) (info->stream, "Unknown error %d\n", status);
|
||||
else
|
||||
/* Actually, address between memaddr and memaddr + len was
|
||||
out of bounds. */
|
||||
(*info->fprintf_func) (info->stream,
|
||||
"Address 0x%x is out of bounds.\n", memaddr);
|
||||
}
|
||||
|
||||
/* This could be in a separate file, to save miniscule amounts of space
|
||||
in statically linked executables. */
|
||||
|
||||
/* Just print the address is hex. This is included for completeness even
|
||||
though both GDB and objdump provide their own (to print symbolic
|
||||
addresses). */
|
||||
|
||||
void
|
||||
generic_print_address (addr, info)
|
||||
bfd_vma addr;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
(*info->fprintf_func) (info->stream, "0x%x", addr);
|
||||
}
|
78
opcode/disass.c
Normal file
78
opcode/disass.c
Normal file
|
@ -0,0 +1,78 @@
|
|||
/******************************** -*- C -*- ****************************
|
||||
*
|
||||
* lightning disassembling support
|
||||
*
|
||||
***********************************************************************/
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* Copyright 2000 Free Software Foundation, Inc.
|
||||
* Written by Paolo Bonzini.
|
||||
*
|
||||
* This file is part of GNU lightning.
|
||||
*
|
||||
* GNU lightning is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published
|
||||
* by the Free Software Foundation; either version 2.1, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* GNU lightning 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 Lesser General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with GNU lightning; see the file COPYING.LESSER; if not, write to the
|
||||
* Free Software Foundation, 59 Temple Place - Suite 330, Boston,
|
||||
* MA 02111-1307, USA.
|
||||
*
|
||||
***********************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "lightning.h"
|
||||
#include "dis-asm.h"
|
||||
|
||||
void disassemble(stream, from, to)
|
||||
FILE *stream;
|
||||
char *from, *to;
|
||||
{
|
||||
disassemble_info info;
|
||||
bfd_vma pc = (bfd_vma) from;
|
||||
bfd_vma end = (bfd_vma) to;
|
||||
|
||||
INIT_DISASSEMBLE_INFO(info, stream, fprintf);
|
||||
info.buffer = NULL;
|
||||
info.buffer_vma = 0;
|
||||
info.buffer_length = end;
|
||||
|
||||
while (pc < end) {
|
||||
fprintf_vma(stream, pc);
|
||||
putc('\t', stream);
|
||||
#ifdef LIGHTNING_I386
|
||||
pc += print_insn_i386(pc, &info);
|
||||
#endif
|
||||
#ifdef LIGHTNING_PPC
|
||||
pc += print_insn_big_powerpc(pc, &info);
|
||||
#endif
|
||||
#ifdef LIGHTNING_SPARC
|
||||
pc += print_insn_sparc(pc, &info);
|
||||
#endif
|
||||
putc('\n', stream);
|
||||
}
|
||||
}
|
||||
|
||||
/* Panic on failing malloc */
|
||||
PTR
|
||||
xmalloc(size)
|
||||
size_t size;
|
||||
{
|
||||
PTR ret = malloc(size ? size : 1);
|
||||
if (!ret) {
|
||||
fprintf(stderr, "Couldn't allocate memory\n");
|
||||
exit(1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
2031
opcode/i386-dis.c
Normal file
2031
opcode/i386-dis.c
Normal file
File diff suppressed because it is too large
Load diff
898
opcode/i386.h
Normal file
898
opcode/i386.h
Normal file
|
@ -0,0 +1,898 @@
|
|||
/* i386-opcode.h -- Intel 80386 opcode table
|
||||
Copyright 1989, 1991, 1992, 1995 Free Software Foundation.
|
||||
|
||||
This file is part of GAS, the GNU Assembler, 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 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
static const template i386_optab[] = {
|
||||
|
||||
#define _ None
|
||||
/* move instructions */
|
||||
#define MOV_AX_DISP32 0xa0
|
||||
{ "mov", 2, 0xa0, _, DW|NoModrm, { Disp32, Acc, 0 } },
|
||||
{ "mov", 2, 0x88, _, DW|Modrm, { Reg, Reg|Mem, 0 } },
|
||||
{ "mov", 2, 0xb0, _, ShortFormW, { Imm, Reg, 0 } },
|
||||
{ "mov", 2, 0xc6, _, W|Modrm, { Imm, Reg|Mem, 0 } },
|
||||
{ "mov", 2, 0x8c, _, D|Modrm, { SReg3|SReg2, Reg16|Mem, 0 } },
|
||||
/* move to/from control debug registers */
|
||||
{ "mov", 2, 0x0f20, _, D|Modrm, { Control, Reg32, 0} },
|
||||
{ "mov", 2, 0x0f21, _, D|Modrm, { Debug, Reg32, 0} },
|
||||
{ "mov", 2, 0x0f24, _, D|Modrm, { Test, Reg32, 0} },
|
||||
|
||||
/* move with sign extend */
|
||||
/* "movsbl" & "movsbw" must not be unified into "movsb" to avoid
|
||||
conflict with the "movs" string move instruction. Thus,
|
||||
{"movsb", 2, 0x0fbe, _, ReverseRegRegmem|Modrm, { Reg8|Mem, Reg16|Reg32, 0} },
|
||||
is not kosher; we must seperate the two instructions. */
|
||||
{"movsbl", 2, 0x0fbe, _, ReverseRegRegmem|Modrm|Data32, { Reg8|Mem, Reg32, 0} },
|
||||
{"movsbw", 2, 0x0fbe, _, ReverseRegRegmem|Modrm|Data16, { Reg8|Mem, Reg16, 0} },
|
||||
{"movswl", 2, 0x0fbf, _, ReverseRegRegmem|Modrm, { Reg16|Mem, Reg32, 0} },
|
||||
|
||||
/* move with zero extend */
|
||||
{"movzb", 2, 0x0fb6, _, ReverseRegRegmem|Modrm, { Reg8|Mem, Reg16|Reg32, 0} },
|
||||
{"movzwl", 2, 0x0fb7, _, ReverseRegRegmem|Modrm, { Reg16|Mem, Reg32, 0} },
|
||||
|
||||
/* push instructions */
|
||||
{"push", 1, 0x50, _, ShortForm, { WordReg,0,0 } },
|
||||
{"push", 1, 0xff, 0x6, Modrm, { WordReg|WordMem, 0, 0 } },
|
||||
{"push", 1, 0x6a, _, NoModrm, { Imm8S, 0, 0} },
|
||||
{"push", 1, 0x68, _, NoModrm, { Imm16|Imm32, 0, 0} },
|
||||
{"push", 1, 0x06, _, Seg2ShortForm, { SReg2,0,0 } },
|
||||
{"push", 1, 0x0fa0, _, Seg3ShortForm, { SReg3,0,0 } },
|
||||
/* push all */
|
||||
{"pusha", 0, 0x60, _, NoModrm, { 0, 0, 0 } },
|
||||
|
||||
/* pop instructions */
|
||||
{"pop", 1, 0x58, _, ShortForm, { WordReg,0,0 } },
|
||||
{"pop", 1, 0x8f, 0x0, Modrm, { WordReg|WordMem, 0, 0 } },
|
||||
#define POP_SEG_SHORT 0x7
|
||||
{"pop", 1, 0x07, _, Seg2ShortForm, { SReg2,0,0 } },
|
||||
{"pop", 1, 0x0fa1, _, Seg3ShortForm, { SReg3,0,0 } },
|
||||
/* pop all */
|
||||
{"popa", 0, 0x61, _, NoModrm, { 0, 0, 0 } },
|
||||
|
||||
/* xchg exchange instructions
|
||||
xchg commutes: we allow both operand orders */
|
||||
{"xchg", 2, 0x90, _, ShortForm, { WordReg, Acc, 0 } },
|
||||
{"xchg", 2, 0x90, _, ShortForm, { Acc, WordReg, 0 } },
|
||||
{"xchg", 2, 0x86, _, W|Modrm, { Reg, Reg|Mem, 0 } },
|
||||
{"xchg", 2, 0x86, _, W|Modrm, { Reg|Mem, Reg, 0 } },
|
||||
|
||||
/* in/out from ports */
|
||||
{"in", 2, 0xe4, _, W|NoModrm, { Imm8, Acc, 0 } },
|
||||
{"in", 2, 0xec, _, W|NoModrm, { InOutPortReg, Acc, 0 } },
|
||||
{"in", 1, 0xe4, _, W|NoModrm, { Imm8, 0, 0 } },
|
||||
{"in", 1, 0xec, _, W|NoModrm, { InOutPortReg, 0, 0 } },
|
||||
{"out", 2, 0xe6, _, W|NoModrm, { Acc, Imm8, 0 } },
|
||||
{"out", 2, 0xee, _, W|NoModrm, { Acc, InOutPortReg, 0 } },
|
||||
{"out", 1, 0xe6, _, W|NoModrm, { Imm8, 0, 0 } },
|
||||
{"out", 1, 0xee, _, W|NoModrm, { InOutPortReg, 0, 0 } },
|
||||
|
||||
/* load effective address */
|
||||
{"lea", 2, 0x8d, _, Modrm, { WordMem, WordReg, 0 } },
|
||||
|
||||
/* load segment registers from memory */
|
||||
{"lds", 2, 0xc5, _, Modrm, { Mem, Reg32, 0} },
|
||||
{"les", 2, 0xc4, _, Modrm, { Mem, Reg32, 0} },
|
||||
{"lfs", 2, 0x0fb4, _, Modrm, { Mem, Reg32, 0} },
|
||||
{"lgs", 2, 0x0fb5, _, Modrm, { Mem, Reg32, 0} },
|
||||
{"lss", 2, 0x0fb2, _, Modrm, { Mem, Reg32, 0} },
|
||||
|
||||
/* flags register instructions */
|
||||
{"clc", 0, 0xf8, _, NoModrm, { 0, 0, 0} },
|
||||
{"cld", 0, 0xfc, _, NoModrm, { 0, 0, 0} },
|
||||
{"cli", 0, 0xfa, _, NoModrm, { 0, 0, 0} },
|
||||
{"clts", 0, 0x0f06, _, NoModrm, { 0, 0, 0} },
|
||||
{"cmc", 0, 0xf5, _, NoModrm, { 0, 0, 0} },
|
||||
{"lahf", 0, 0x9f, _, NoModrm, { 0, 0, 0} },
|
||||
{"sahf", 0, 0x9e, _, NoModrm, { 0, 0, 0} },
|
||||
{"pushfl", 0, 0x9c, _, NoModrm|Data32, { 0, 0, 0} },
|
||||
{"popfl", 0, 0x9d, _, NoModrm|Data32, { 0, 0, 0} },
|
||||
{"pushfw", 0, 0x9c, _, NoModrm|Data16, { 0, 0, 0} },
|
||||
{"popfw", 0, 0x9d, _, NoModrm|Data16, { 0, 0, 0} },
|
||||
{"pushf", 0, 0x9c, _, NoModrm, { 0, 0, 0} },
|
||||
{"popf", 0, 0x9d, _, NoModrm, { 0, 0, 0} },
|
||||
{"stc", 0, 0xf9, _, NoModrm, { 0, 0, 0} },
|
||||
{"std", 0, 0xfd, _, NoModrm, { 0, 0, 0} },
|
||||
{"sti", 0, 0xfb, _, NoModrm, { 0, 0, 0} },
|
||||
|
||||
{"add", 2, 0x0, _, DW|Modrm, { Reg, Reg|Mem, 0} },
|
||||
{"add", 2, 0x83, 0, Modrm, { Imm8S, WordReg|WordMem, 0} },
|
||||
{"add", 2, 0x4, _, W|NoModrm, { Imm, Acc, 0} },
|
||||
{"add", 2, 0x80, 0, W|Modrm, { Imm, Reg|Mem, 0} },
|
||||
|
||||
{"inc", 1, 0x40, _, ShortForm, { WordReg, 0, 0} },
|
||||
{"inc", 1, 0xfe, 0, W|Modrm, { Reg|Mem, 0, 0} },
|
||||
|
||||
{"sub", 2, 0x28, _, DW|Modrm, { Reg, Reg|Mem, 0} },
|
||||
{"sub", 2, 0x83, 5, Modrm, { Imm8S, WordReg|WordMem, 0} },
|
||||
{"sub", 2, 0x2c, _, W|NoModrm, { Imm, Acc, 0} },
|
||||
{"sub", 2, 0x80, 5, W|Modrm, { Imm, Reg|Mem, 0} },
|
||||
|
||||
{"dec", 1, 0x48, _, ShortForm, { WordReg, 0, 0} },
|
||||
{"dec", 1, 0xfe, 1, W|Modrm, { Reg|Mem, 0, 0} },
|
||||
|
||||
{"sbb", 2, 0x18, _, DW|Modrm, { Reg, Reg|Mem, 0} },
|
||||
{"sbb", 2, 0x83, 3, Modrm, { Imm8S, WordReg|WordMem, 0} },
|
||||
{"sbb", 2, 0x1c, _, W|NoModrm, { Imm, Acc, 0} },
|
||||
{"sbb", 2, 0x80, 3, W|Modrm, { Imm, Reg|Mem, 0} },
|
||||
|
||||
{"cmp", 2, 0x38, _, DW|Modrm, { Reg, Reg|Mem, 0} },
|
||||
{"cmp", 2, 0x83, 7, Modrm, { Imm8S, WordReg|WordMem, 0} },
|
||||
{"cmp", 2, 0x3c, _, W|NoModrm, { Imm, Acc, 0} },
|
||||
{"cmp", 2, 0x80, 7, W|Modrm, { Imm, Reg|Mem, 0} },
|
||||
|
||||
{"test", 2, 0x84, _, W|Modrm, { Reg|Mem, Reg, 0} },
|
||||
{"test", 2, 0x84, _, W|Modrm, { Reg, Reg|Mem, 0} },
|
||||
{"test", 2, 0xa8, _, W|NoModrm, { Imm, Acc, 0} },
|
||||
{"test", 2, 0xf6, 0, W|Modrm, { Imm, Reg|Mem, 0} },
|
||||
|
||||
{"and", 2, 0x20, _, DW|Modrm, { Reg, Reg|Mem, 0} },
|
||||
{"and", 2, 0x83, 4, Modrm, { Imm8S, WordReg|WordMem, 0} },
|
||||
{"and", 2, 0x24, _, W|NoModrm, { Imm, Acc, 0} },
|
||||
{"and", 2, 0x80, 4, W|Modrm, { Imm, Reg|Mem, 0} },
|
||||
|
||||
{"or", 2, 0x08, _, DW|Modrm, { Reg, Reg|Mem, 0} },
|
||||
{"or", 2, 0x83, 1, Modrm, { Imm8S, WordReg|WordMem, 0} },
|
||||
{"or", 2, 0x0c, _, W|NoModrm, { Imm, Acc, 0} },
|
||||
{"or", 2, 0x80, 1, W|Modrm, { Imm, Reg|Mem, 0} },
|
||||
|
||||
{"xor", 2, 0x30, _, DW|Modrm, { Reg, Reg|Mem, 0} },
|
||||
{"xor", 2, 0x83, 6, Modrm, { Imm8S, WordReg|WordMem, 0} },
|
||||
{"xor", 2, 0x34, _, W|NoModrm, { Imm, Acc, 0} },
|
||||
{"xor", 2, 0x80, 6, W|Modrm, { Imm, Reg|Mem, 0} },
|
||||
|
||||
{"adc", 2, 0x10, _, DW|Modrm, { Reg, Reg|Mem, 0} },
|
||||
{"adc", 2, 0x83, 2, Modrm, { Imm8S, WordReg|WordMem, 0} },
|
||||
{"adc", 2, 0x14, _, W|NoModrm, { Imm, Acc, 0} },
|
||||
{"adc", 2, 0x80, 2, W|Modrm, { Imm, Reg|Mem, 0} },
|
||||
|
||||
{"neg", 1, 0xf6, 3, W|Modrm, { Reg|Mem, 0, 0} },
|
||||
{"not", 1, 0xf6, 2, W|Modrm, { Reg|Mem, 0, 0} },
|
||||
|
||||
{"aaa", 0, 0x37, _, NoModrm, { 0, 0, 0} },
|
||||
{"aas", 0, 0x3f, _, NoModrm, { 0, 0, 0} },
|
||||
{"daa", 0, 0x27, _, NoModrm, { 0, 0, 0} },
|
||||
{"das", 0, 0x2f, _, NoModrm, { 0, 0, 0} },
|
||||
{"aad", 0, 0xd50a, _, NoModrm, { 0, 0, 0} },
|
||||
{"aam", 0, 0xd40a, _, NoModrm, { 0, 0, 0} },
|
||||
|
||||
/* conversion insns */
|
||||
/* conversion: intel naming */
|
||||
{"cbw", 0, 0x98, _, NoModrm|Data16, { 0, 0, 0} },
|
||||
{"cwd", 0, 0x99, _, NoModrm|Data16, { 0, 0, 0} },
|
||||
{"cwde", 0, 0x98, _, NoModrm|Data32, { 0, 0, 0} },
|
||||
{"cdq", 0, 0x99, _, NoModrm|Data32, { 0, 0, 0} },
|
||||
/* att naming */
|
||||
{"cbtw", 0, 0x98, _, NoModrm|Data16, { 0, 0, 0} },
|
||||
{"cwtl", 0, 0x98, _, NoModrm|Data32, { 0, 0, 0} },
|
||||
{"cwtd", 0, 0x99, _, NoModrm|Data16, { 0, 0, 0} },
|
||||
{"cltd", 0, 0x99, _, NoModrm|Data32, { 0, 0, 0} },
|
||||
|
||||
/* Warning! the mul/imul (opcode 0xf6) must only have 1 operand! They are
|
||||
expanding 64-bit multiplies, and *cannot* be selected to accomplish
|
||||
'imul %ebx, %eax' (opcode 0x0faf must be used in this case)
|
||||
These multiplies can only be selected with single operand forms. */
|
||||
{"mul", 1, 0xf6, 4, W|Modrm, { Reg|Mem, 0, 0} },
|
||||
{"imul", 1, 0xf6, 5, W|Modrm, { Reg|Mem, 0, 0} },
|
||||
|
||||
|
||||
|
||||
|
||||
/* imulKludge here is needed to reverse the i.rm.reg & i.rm.regmem fields.
|
||||
These instructions are exceptions: 'imul $2, %eax, %ecx' would put
|
||||
'%eax' in the reg field and '%ecx' in the regmem field if we did not
|
||||
switch them. */
|
||||
{"imul", 2, 0x0faf, _, Modrm|ReverseRegRegmem, { WordReg|Mem, WordReg, 0} },
|
||||
{"imul", 3, 0x6b, _, Modrm|ReverseRegRegmem, { Imm8S, WordReg|Mem, WordReg} },
|
||||
{"imul", 3, 0x69, _, Modrm|ReverseRegRegmem, { Imm16|Imm32, WordReg|Mem, WordReg} },
|
||||
/*
|
||||
imul with 2 operands mimicks imul with 3 by puting register both
|
||||
in i.rm.reg & i.rm.regmem fields
|
||||
*/
|
||||
{"imul", 2, 0x6b, _, Modrm|imulKludge, { Imm8S, WordReg, 0} },
|
||||
{"imul", 2, 0x69, _, Modrm|imulKludge, { Imm16|Imm32, WordReg, 0} },
|
||||
{"div", 1, 0xf6, 6, W|Modrm, { Reg|Mem, 0, 0} },
|
||||
{"div", 2, 0xf6, 6, W|Modrm, { Reg|Mem, Acc, 0} },
|
||||
{"idiv", 1, 0xf6, 7, W|Modrm, { Reg|Mem, 0, 0} },
|
||||
{"idiv", 2, 0xf6, 7, W|Modrm, { Reg|Mem, Acc, 0} },
|
||||
|
||||
{"rol", 2, 0xd0, 0, W|Modrm, { Imm1, Reg|Mem, 0} },
|
||||
{"rol", 2, 0xc0, 0, W|Modrm, { Imm8, Reg|Mem, 0} },
|
||||
{"rol", 2, 0xd2, 0, W|Modrm, { ShiftCount, Reg|Mem, 0} },
|
||||
{"rol", 1, 0xd0, 0, W|Modrm, { Reg|Mem, 0, 0} },
|
||||
|
||||
{"ror", 2, 0xd0, 1, W|Modrm, { Imm1, Reg|Mem, 0} },
|
||||
{"ror", 2, 0xc0, 1, W|Modrm, { Imm8, Reg|Mem, 0} },
|
||||
{"ror", 2, 0xd2, 1, W|Modrm, { ShiftCount, Reg|Mem, 0} },
|
||||
{"ror", 1, 0xd0, 1, W|Modrm, { Reg|Mem, 0, 0} },
|
||||
|
||||
{"rcl", 2, 0xd0, 2, W|Modrm, { Imm1, Reg|Mem, 0} },
|
||||
{"rcl", 2, 0xc0, 2, W|Modrm, { Imm8, Reg|Mem, 0} },
|
||||
{"rcl", 2, 0xd2, 2, W|Modrm, { ShiftCount, Reg|Mem, 0} },
|
||||
{"rcl", 1, 0xd0, 2, W|Modrm, { Reg|Mem, 0, 0} },
|
||||
|
||||
{"rcr", 2, 0xd0, 3, W|Modrm, { Imm1, Reg|Mem, 0} },
|
||||
{"rcr", 2, 0xc0, 3, W|Modrm, { Imm8, Reg|Mem, 0} },
|
||||
{"rcr", 2, 0xd2, 3, W|Modrm, { ShiftCount, Reg|Mem, 0} },
|
||||
{"rcr", 1, 0xd0, 3, W|Modrm, { Reg|Mem, 0, 0} },
|
||||
|
||||
{"sal", 2, 0xd0, 4, W|Modrm, { Imm1, Reg|Mem, 0} },
|
||||
{"sal", 2, 0xc0, 4, W|Modrm, { Imm8, Reg|Mem, 0} },
|
||||
{"sal", 2, 0xd2, 4, W|Modrm, { ShiftCount, Reg|Mem, 0} },
|
||||
{"sal", 1, 0xd0, 4, W|Modrm, { Reg|Mem, 0, 0} },
|
||||
{"shl", 2, 0xd0, 4, W|Modrm, { Imm1, Reg|Mem, 0} },
|
||||
{"shl", 2, 0xc0, 4, W|Modrm, { Imm8, Reg|Mem, 0} },
|
||||
{"shl", 2, 0xd2, 4, W|Modrm, { ShiftCount, Reg|Mem, 0} },
|
||||
{"shl", 1, 0xd0, 4, W|Modrm, { Reg|Mem, 0, 0} },
|
||||
|
||||
{"shld", 3, 0x0fa4, _, Modrm, { Imm8, WordReg, WordReg|Mem} },
|
||||
{"shld", 3, 0x0fa5, _, Modrm, { ShiftCount, WordReg, WordReg|Mem} },
|
||||
{"shld", 2, 0x0fa5, _, Modrm, { WordReg, WordReg|Mem, 0} },
|
||||
|
||||
{"shr", 2, 0xd0, 5, W|Modrm, { Imm1, Reg|Mem, 0} },
|
||||
{"shr", 2, 0xc0, 5, W|Modrm, { Imm8, Reg|Mem, 0} },
|
||||
{"shr", 2, 0xd2, 5, W|Modrm, { ShiftCount, Reg|Mem, 0} },
|
||||
{"shr", 1, 0xd0, 5, W|Modrm, { Reg|Mem, 0, 0} },
|
||||
|
||||
{"shrd", 3, 0x0fac, _, Modrm, { Imm8, WordReg, WordReg|Mem} },
|
||||
{"shrd", 3, 0x0fad, _, Modrm, { ShiftCount, WordReg, WordReg|Mem} },
|
||||
{"shrd", 2, 0x0fad, _, Modrm, { WordReg, WordReg|Mem, 0} },
|
||||
|
||||
{"sar", 2, 0xd0, 7, W|Modrm, { Imm1, Reg|Mem, 0} },
|
||||
{"sar", 2, 0xc0, 7, W|Modrm, { Imm8, Reg|Mem, 0} },
|
||||
{"sar", 2, 0xd2, 7, W|Modrm, { ShiftCount, Reg|Mem, 0} },
|
||||
{"sar", 1, 0xd0, 7, W|Modrm, { Reg|Mem, 0, 0} },
|
||||
|
||||
/* control transfer instructions */
|
||||
#define CALL_PC_RELATIVE 0xe8
|
||||
{"call", 1, 0xe8, _, JumpDword, { Disp32, 0, 0} },
|
||||
{"call", 1, 0xff, 2, Modrm|Data32, { Reg|Mem|JumpAbsolute, 0, 0} },
|
||||
{"callw", 1, 0xff, 2, Modrm|Data16, { Reg|Mem|JumpAbsolute, 0, 0} },
|
||||
#define CALL_FAR_IMMEDIATE 0x9a
|
||||
{"lcall", 2, 0x9a, _, JumpInterSegment, { Imm16, Abs32|Imm32, 0} },
|
||||
{"lcall", 1, 0xff, 3, Modrm|Data32, { Mem, 0, 0} },
|
||||
{"lcallw", 1, 0xff, 3, Modrm|Data16, { Mem, 0, 0} },
|
||||
|
||||
#define JUMP_PC_RELATIVE 0xeb
|
||||
{"jmp", 1, 0xeb, _, Jump, { Disp, 0, 0} },
|
||||
{"jmp", 1, 0xff, 4, Modrm, { Reg32|Mem|JumpAbsolute, 0, 0} },
|
||||
#define JUMP_FAR_IMMEDIATE 0xea
|
||||
{"ljmp", 2, 0xea, _, JumpInterSegment, { Imm16, Imm32, 0} },
|
||||
{"ljmp", 1, 0xff, 5, Modrm|Data32, { Mem, 0, 0} },
|
||||
|
||||
{"ret", 0, 0xc3, _, NoModrm|Data32, { 0, 0, 0} },
|
||||
{"ret", 1, 0xc2, _, NoModrm|Data32, { Imm16, 0, 0} },
|
||||
{"retw", 0, 0xc3, _, NoModrm|Data16, { 0, 0, 0} },
|
||||
{"retw", 1, 0xc2, _, NoModrm|Data16, { Imm16, 0, 0} },
|
||||
{"lret", 0, 0xcb, _, NoModrm|Data32, { 0, 0, 0} },
|
||||
{"lret", 1, 0xca, _, NoModrm|Data32, { Imm16, 0, 0} },
|
||||
{"lretw", 0, 0xcb, _, NoModrm|Data16, { 0, 0, 0} },
|
||||
{"lretw", 1, 0xca, _, NoModrm|Data16, { Imm16, 0, 0} },
|
||||
{"enter", 2, 0xc8, _, NoModrm|Data32, { Imm16, Imm8, 0} },
|
||||
{"leave", 0, 0xc9, _, NoModrm|Data32, { 0, 0, 0} },
|
||||
{"enterw", 2, 0xc8, _, NoModrm|Data16, { Imm16, Imm8, 0} },
|
||||
{"leavew", 0, 0xc9, _, NoModrm|Data16, { 0, 0, 0} },
|
||||
|
||||
/* conditional jumps */
|
||||
{"jo", 1, 0x70, _, Jump, { Disp, 0, 0} },
|
||||
|
||||
{"jno", 1, 0x71, _, Jump, { Disp, 0, 0} },
|
||||
|
||||
{"jb", 1, 0x72, _, Jump, { Disp, 0, 0} },
|
||||
{"jc", 1, 0x72, _, Jump, { Disp, 0, 0} },
|
||||
{"jnae", 1, 0x72, _, Jump, { Disp, 0, 0} },
|
||||
|
||||
{"jnb", 1, 0x73, _, Jump, { Disp, 0, 0} },
|
||||
{"jnc", 1, 0x73, _, Jump, { Disp, 0, 0} },
|
||||
{"jae", 1, 0x73, _, Jump, { Disp, 0, 0} },
|
||||
|
||||
{"je", 1, 0x74, _, Jump, { Disp, 0, 0} },
|
||||
{"jz", 1, 0x74, _, Jump, { Disp, 0, 0} },
|
||||
|
||||
{"jne", 1, 0x75, _, Jump, { Disp, 0, 0} },
|
||||
{"jnz", 1, 0x75, _, Jump, { Disp, 0, 0} },
|
||||
|
||||
{"jbe", 1, 0x76, _, Jump, { Disp, 0, 0} },
|
||||
{"jna", 1, 0x76, _, Jump, { Disp, 0, 0} },
|
||||
|
||||
{"jnbe", 1, 0x77, _, Jump, { Disp, 0, 0} },
|
||||
{"ja", 1, 0x77, _, Jump, { Disp, 0, 0} },
|
||||
|
||||
{"js", 1, 0x78, _, Jump, { Disp, 0, 0} },
|
||||
|
||||
{"jns", 1, 0x79, _, Jump, { Disp, 0, 0} },
|
||||
|
||||
{"jp", 1, 0x7a, _, Jump, { Disp, 0, 0} },
|
||||
{"jpe", 1, 0x7a, _, Jump, { Disp, 0, 0} },
|
||||
|
||||
{"jnp", 1, 0x7b, _, Jump, { Disp, 0, 0} },
|
||||
{"jpo", 1, 0x7b, _, Jump, { Disp, 0, 0} },
|
||||
|
||||
{"jl", 1, 0x7c, _, Jump, { Disp, 0, 0} },
|
||||
{"jnge", 1, 0x7c, _, Jump, { Disp, 0, 0} },
|
||||
|
||||
{"jnl", 1, 0x7d, _, Jump, { Disp, 0, 0} },
|
||||
{"jge", 1, 0x7d, _, Jump, { Disp, 0, 0} },
|
||||
|
||||
{"jle", 1, 0x7e, _, Jump, { Disp, 0, 0} },
|
||||
{"jng", 1, 0x7e, _, Jump, { Disp, 0, 0} },
|
||||
|
||||
{"jnle", 1, 0x7f, _, Jump, { Disp, 0, 0} },
|
||||
{"jg", 1, 0x7f, _, Jump, { Disp, 0, 0} },
|
||||
|
||||
#if 0 /* XXX where are these macros used?
|
||||
To get them working again, they need to take
|
||||
an entire template as the parameter,
|
||||
and check for Data16/Data32 flags. */
|
||||
/* these turn into pseudo operations when disp is larger than 8 bits */
|
||||
#define IS_JUMP_ON_CX_ZERO(o) \
|
||||
(o == 0x66e3)
|
||||
#define IS_JUMP_ON_ECX_ZERO(o) \
|
||||
(o == 0xe3)
|
||||
#endif
|
||||
|
||||
{"jcxz", 1, 0xe3, _, JumpByte|Data16, { Disp, 0, 0} },
|
||||
{"jecxz", 1, 0xe3, _, JumpByte|Data32, { Disp, 0, 0} },
|
||||
|
||||
#define IS_LOOP_ECX_TIMES(o) \
|
||||
(o == 0xe2 || o == 0xe1 || o == 0xe0)
|
||||
|
||||
{"loop", 1, 0xe2, _, JumpByte, { Disp, 0, 0} },
|
||||
|
||||
{"loopz", 1, 0xe1, _, JumpByte, { Disp, 0, 0} },
|
||||
{"loope", 1, 0xe1, _, JumpByte, { Disp, 0, 0} },
|
||||
|
||||
{"loopnz", 1, 0xe0, _, JumpByte, { Disp, 0, 0} },
|
||||
{"loopne", 1, 0xe0, _, JumpByte, { Disp, 0, 0} },
|
||||
|
||||
/* set byte on flag instructions */
|
||||
{"seto", 1, 0x0f90, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
|
||||
{"setno", 1, 0x0f91, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
|
||||
{"setb", 1, 0x0f92, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
{"setc", 1, 0x0f92, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
{"setnae", 1, 0x0f92, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
|
||||
{"setnb", 1, 0x0f93, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
{"setnc", 1, 0x0f93, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
{"setae", 1, 0x0f93, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
|
||||
{"sete", 1, 0x0f94, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
{"setz", 1, 0x0f94, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
|
||||
{"setne", 1, 0x0f95, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
{"setnz", 1, 0x0f95, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
|
||||
{"setbe", 1, 0x0f96, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
{"setna", 1, 0x0f96, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
|
||||
{"setnbe", 1, 0x0f97, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
{"seta", 1, 0x0f97, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
|
||||
{"sets", 1, 0x0f98, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
|
||||
{"setns", 1, 0x0f99, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
|
||||
{"setp", 1, 0x0f9a, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
{"setpe", 1, 0x0f9a, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
|
||||
{"setnp", 1, 0x0f9b, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
{"setpo", 1, 0x0f9b, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
|
||||
{"setl", 1, 0x0f9c, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
{"setnge", 1, 0x0f9c, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
|
||||
{"setnl", 1, 0x0f9d, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
{"setge", 1, 0x0f9d, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
|
||||
{"setle", 1, 0x0f9e, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
{"setng", 1, 0x0f9e, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
|
||||
{"setnle", 1, 0x0f9f, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
{"setg", 1, 0x0f9f, 0, Modrm, { Reg8|Mem, 0, 0} },
|
||||
|
||||
#define IS_STRING_INSTRUCTION(o) \
|
||||
((o) == 0xa6 || (o) == 0x6c || (o) == 0x6e || (o) == 0x6e || \
|
||||
(o) == 0xac || (o) == 0xa4 || (o) == 0xae || (o) == 0xaa || \
|
||||
(o) == 0xd7)
|
||||
|
||||
/* string manipulation */
|
||||
{"cmps", 0, 0xa6, _, W|NoModrm, { 0, 0, 0} },
|
||||
{"scmp", 0, 0xa6, _, W|NoModrm, { 0, 0, 0} },
|
||||
{"ins", 0, 0x6c, _, W|NoModrm, { 0, 0, 0} },
|
||||
{"outs", 0, 0x6e, _, W|NoModrm, { 0, 0, 0} },
|
||||
{"lods", 0, 0xac, _, W|NoModrm, { 0, 0, 0} },
|
||||
{"slod", 0, 0xac, _, W|NoModrm, { 0, 0, 0} },
|
||||
{"movs", 0, 0xa4, _, W|NoModrm, { 0, 0, 0} },
|
||||
{"smov", 0, 0xa4, _, W|NoModrm, { 0, 0, 0} },
|
||||
{"scas", 0, 0xae, _, W|NoModrm, { 0, 0, 0} },
|
||||
{"ssca", 0, 0xae, _, W|NoModrm, { 0, 0, 0} },
|
||||
{"stos", 0, 0xaa, _, W|NoModrm, { 0, 0, 0} },
|
||||
{"ssto", 0, 0xaa, _, W|NoModrm, { 0, 0, 0} },
|
||||
{"xlat", 0, 0xd7, _, NoModrm, { 0, 0, 0} },
|
||||
|
||||
/* bit manipulation */
|
||||
{"bsf", 2, 0x0fbc, _, Modrm|ReverseRegRegmem, { Reg|Mem, Reg, 0} },
|
||||
{"bsr", 2, 0x0fbd, _, Modrm|ReverseRegRegmem, { Reg|Mem, Reg, 0} },
|
||||
{"bt", 2, 0x0fa3, _, Modrm, { Reg, Reg|Mem, 0} },
|
||||
{"bt", 2, 0x0fba, 4, Modrm, { Imm8, Reg|Mem, 0} },
|
||||
{"btc", 2, 0x0fbb, _, Modrm, { Reg, Reg|Mem, 0} },
|
||||
{"btc", 2, 0x0fba, 7, Modrm, { Imm8, Reg|Mem, 0} },
|
||||
{"btr", 2, 0x0fb3, _, Modrm, { Reg, Reg|Mem, 0} },
|
||||
{"btr", 2, 0x0fba, 6, Modrm, { Imm8, Reg|Mem, 0} },
|
||||
{"bts", 2, 0x0fab, _, Modrm, { Reg, Reg|Mem, 0} },
|
||||
{"bts", 2, 0x0fba, 5, Modrm, { Imm8, Reg|Mem, 0} },
|
||||
|
||||
/* interrupts & op. sys insns */
|
||||
/* See gas/config/tc-i386.c for conversion of 'int $3' into the special
|
||||
int 3 insn. */
|
||||
#define INT_OPCODE 0xcd
|
||||
#define INT3_OPCODE 0xcc
|
||||
{"int", 1, 0xcd, _, NoModrm, { Imm8, 0, 0} },
|
||||
{"int3", 0, 0xcc, _, NoModrm, { 0, 0, 0} },
|
||||
{"into", 0, 0xce, _, NoModrm, { 0, 0, 0} },
|
||||
{"iret", 0, 0xcf, _, NoModrm|Data32, { 0, 0, 0} },
|
||||
{"iretw", 0, 0xcf, _, NoModrm|Data16, { 0, 0, 0} },
|
||||
/* i386sl, i486sl, later 486, and Pentium */
|
||||
{"rsm", 0, 0x0faa, _, NoModrm,{ 0, 0, 0} },
|
||||
|
||||
{"boundl", 2, 0x62, _, Modrm|Data32, { Reg32, Mem, 0} },
|
||||
{"boundw", 2, 0x62, _, Modrm|Data16, { Reg16, Mem, 0} },
|
||||
|
||||
{"hlt", 0, 0xf4, _, NoModrm, { 0, 0, 0} },
|
||||
{"wait", 0, 0x9b, _, NoModrm, { 0, 0, 0} },
|
||||
/* nop is actually 'xchgl %eax, %eax' */
|
||||
{"nop", 0, 0x90, _, NoModrm, { 0, 0, 0} },
|
||||
|
||||
/* protection control */
|
||||
{"arpl", 2, 0x63, _, Modrm, { Reg16, Reg16|Mem, 0} },
|
||||
{"lar", 2, 0x0f02, _, Modrm|ReverseRegRegmem, { WordReg|Mem, WordReg, 0} },
|
||||
{"lgdt", 1, 0x0f01, 2, Modrm, { Mem, 0, 0} },
|
||||
{"lidt", 1, 0x0f01, 3, Modrm, { Mem, 0, 0} },
|
||||
{"lldt", 1, 0x0f00, 2, Modrm, { WordReg|Mem, 0, 0} },
|
||||
{"lmsw", 1, 0x0f01, 6, Modrm, { WordReg|Mem, 0, 0} },
|
||||
{"lsl", 2, 0x0f03, _, Modrm|ReverseRegRegmem, { WordReg|Mem, WordReg, 0} },
|
||||
{"ltr", 1, 0x0f00, 3, Modrm, { WordReg|Mem, 0, 0} },
|
||||
|
||||
{"sgdt", 1, 0x0f01, 0, Modrm, { Mem, 0, 0} },
|
||||
{"sidt", 1, 0x0f01, 1, Modrm, { Mem, 0, 0} },
|
||||
{"sldt", 1, 0x0f00, 0, Modrm, { WordReg|Mem, 0, 0} },
|
||||
{"smsw", 1, 0x0f01, 4, Modrm, { WordReg|Mem, 0, 0} },
|
||||
{"str", 1, 0x0f00, 1, Modrm, { Reg16|Mem, 0, 0} },
|
||||
|
||||
{"verr", 1, 0x0f00, 4, Modrm, { WordReg|Mem, 0, 0} },
|
||||
{"verw", 1, 0x0f00, 5, Modrm, { WordReg|Mem, 0, 0} },
|
||||
|
||||
/* floating point instructions */
|
||||
|
||||
/* load */
|
||||
{"fld", 1, 0xd9c0, _, ShortForm, { FloatReg, 0, 0} }, /* register */
|
||||
{"flds", 1, 0xd9, 0, Modrm, { Mem, 0, 0} }, /* %st0 <-- mem float */
|
||||
{"fldl", 1, 0xdd, 0, Modrm, { Mem, 0, 0} }, /* %st0 <-- mem double */
|
||||
{"fldl", 1, 0xd9c0, _, ShortForm, { FloatReg, 0, 0} }, /* register */
|
||||
{"fild", 1, 0xdf, 0, Modrm, { Mem, 0, 0} }, /* %st0 <-- mem word (16) */
|
||||
{"fildl", 1, 0xdb, 0, Modrm, { Mem, 0, 0} }, /* %st0 <-- mem dword (32) */
|
||||
{"fildq",1, 0xdf, 5, Modrm, { Mem, 0, 0} }, /* %st0 <-- mem qword (64) */
|
||||
{"fildll",1, 0xdf, 5, Modrm, { Mem, 0, 0} }, /* %st0 <-- mem qword (64) */
|
||||
{"fldt", 1, 0xdb, 5, Modrm, { Mem, 0, 0} }, /* %st0 <-- mem efloat */
|
||||
{"fbld", 1, 0xdf, 4, Modrm, { Mem, 0, 0} }, /* %st0 <-- mem bcd */
|
||||
|
||||
/* store (no pop) */
|
||||
{"fst", 1, 0xddd0, _, ShortForm, { FloatReg, 0, 0} }, /* register */
|
||||
{"fsts", 1, 0xd9, 2, Modrm, { Mem, 0, 0} }, /* %st0 --> mem float */
|
||||
{"fstl", 1, 0xdd, 2, Modrm, { Mem, 0, 0} }, /* %st0 --> mem double */
|
||||
{"fstl", 1, 0xddd0, _, ShortForm, { FloatReg, 0, 0} }, /* register */
|
||||
{"fist", 1, 0xdf, 2, Modrm, { Mem, 0, 0} }, /* %st0 --> mem word (16) */
|
||||
{"fistl", 1, 0xdb, 2, Modrm, { Mem, 0, 0} }, /* %st0 --> mem dword (32) */
|
||||
|
||||
/* store (with pop) */
|
||||
{"fstp", 1, 0xddd8, _, ShortForm, { FloatReg, 0, 0} }, /* register */
|
||||
{"fstps", 1, 0xd9, 3, Modrm, { Mem, 0, 0} }, /* %st0 --> mem float */
|
||||
{"fstpl", 1, 0xdd, 3, Modrm, { Mem, 0, 0} }, /* %st0 --> mem double */
|
||||
{"fstpl", 1, 0xddd8, _, ShortForm, { FloatReg, 0, 0} }, /* register */
|
||||
{"fistp", 1, 0xdf, 3, Modrm, { Mem, 0, 0} }, /* %st0 --> mem word (16) */
|
||||
{"fistpl",1, 0xdb, 3, Modrm, { Mem, 0, 0} }, /* %st0 --> mem dword (32) */
|
||||
{"fistpq",1, 0xdf, 7, Modrm, { Mem, 0, 0} }, /* %st0 --> mem qword (64) */
|
||||
{"fistpll",1,0xdf, 7, Modrm, { Mem, 0, 0} }, /* %st0 --> mem qword (64) */
|
||||
{"fstpt", 1, 0xdb, 7, Modrm, { Mem, 0, 0} }, /* %st0 --> mem efloat */
|
||||
{"fbstp", 1, 0xdf, 6, Modrm, { Mem, 0, 0} }, /* %st0 --> mem bcd */
|
||||
|
||||
/* exchange %st<n> with %st0 */
|
||||
{"fxch", 1, 0xd9c8, _, ShortForm, { FloatReg, 0, 0} },
|
||||
{"fxch", 0, 0xd9c9, _, NoModrm, { 0, 0, 0} }, /* alias for fxch %st, %st(1) */
|
||||
|
||||
/* comparison (without pop) */
|
||||
{"fcom", 1, 0xd8d0, _, ShortForm, { FloatReg, 0, 0} },
|
||||
{"fcoms", 1, 0xd8, 2, Modrm, { Mem, 0, 0} }, /* compare %st0, mem float */
|
||||
{"ficoml", 1, 0xda, 2, Modrm, { Mem, 0, 0} }, /* compare %st0, mem word */
|
||||
{"fcoml", 1, 0xdc, 2, Modrm, { Mem, 0, 0} }, /* compare %st0, mem double */
|
||||
{"fcoml", 1, 0xd8d0, _, ShortForm, { FloatReg, 0, 0} },
|
||||
{"ficoms", 1, 0xde, 2, Modrm, { Mem, 0, 0} }, /* compare %st0, mem dword */
|
||||
|
||||
/* comparison (with pop) */
|
||||
{"fcomp", 1, 0xd8d8, _, ShortForm, { FloatReg, 0, 0} },
|
||||
{"fcomps", 1, 0xd8, 3, Modrm, { Mem, 0, 0} }, /* compare %st0, mem float */
|
||||
{"ficompl", 1, 0xda, 3, Modrm, { Mem, 0, 0} }, /* compare %st0, mem word */
|
||||
{"fcompl", 1, 0xdc, 3, Modrm, { Mem, 0, 0} }, /* compare %st0, mem double */
|
||||
{"fcompl", 1, 0xd8d8, _, ShortForm, { FloatReg, 0, 0} },
|
||||
{"ficomps", 1, 0xde, 3, Modrm, { Mem, 0, 0} }, /* compare %st0, mem dword */
|
||||
{"fcompp", 0, 0xded9, _, NoModrm, { 0, 0, 0} }, /* compare %st0, %st1 & pop 2 */
|
||||
|
||||
/* unordered comparison (with pop) */
|
||||
{"fucom", 1, 0xdde0, _, ShortForm, { FloatReg, 0, 0} },
|
||||
{"fucomp", 1, 0xdde8, _, ShortForm, { FloatReg, 0, 0} },
|
||||
{"fucompp", 0, 0xdae9, _, NoModrm, { 0, 0, 0} }, /* ucompare %st0, %st1 & pop twice */
|
||||
|
||||
{"ftst", 0, 0xd9e4, _, NoModrm, { 0, 0, 0} }, /* test %st0 */
|
||||
{"fxam", 0, 0xd9e5, _, NoModrm, { 0, 0, 0} }, /* examine %st0 */
|
||||
|
||||
/* load constants into %st0 */
|
||||
{"fld1", 0, 0xd9e8, _, NoModrm, { 0, 0, 0} }, /* %st0 <-- 1.0 */
|
||||
{"fldl2t", 0, 0xd9e9, _, NoModrm, { 0, 0, 0} }, /* %st0 <-- log2(10) */
|
||||
{"fldl2e", 0, 0xd9ea, _, NoModrm, { 0, 0, 0} }, /* %st0 <-- log2(e) */
|
||||
{"fldpi", 0, 0xd9eb, _, NoModrm, { 0, 0, 0} }, /* %st0 <-- pi */
|
||||
{"fldlg2", 0, 0xd9ec, _, NoModrm, { 0, 0, 0} }, /* %st0 <-- log10(2) */
|
||||
{"fldln2", 0, 0xd9ed, _, NoModrm, { 0, 0, 0} }, /* %st0 <-- ln(2) */
|
||||
{"fldz", 0, 0xd9ee, _, NoModrm, { 0, 0, 0} }, /* %st0 <-- 0.0 */
|
||||
|
||||
/* arithmetic */
|
||||
|
||||
/* add */
|
||||
{"fadd", 1, 0xd8c0, _, ShortForm, { FloatReg, 0, 0} },
|
||||
{"fadd", 2, 0xd8c0, _, ShortForm|FloatD, { FloatReg, FloatAcc, 0} },
|
||||
{"fadd", 0, 0xdcc1, _, NoModrm, { 0, 0, 0} }, /* alias for fadd %st, %st(1) */
|
||||
{"faddp", 1, 0xdac0, _, ShortForm, { FloatReg, 0, 0} },
|
||||
{"faddp", 2, 0xdac0, _, ShortForm|FloatD, { FloatReg, FloatAcc, 0} },
|
||||
{"faddp", 0, 0xdec1, _, NoModrm, { 0, 0, 0} }, /* alias for faddp %st, %st(1) */
|
||||
{"fadds", 1, 0xd8, 0, Modrm, { Mem, 0, 0} },
|
||||
{"fiaddl", 1, 0xda, 0, Modrm, { Mem, 0, 0} },
|
||||
{"faddl", 1, 0xdc, 0, Modrm, { Mem, 0, 0} },
|
||||
{"fiadds", 1, 0xde, 0, Modrm, { Mem, 0, 0} },
|
||||
|
||||
/* sub */
|
||||
/* Note: intel has decided that certain of these operations are reversed
|
||||
in assembler syntax. */
|
||||
{"fsub", 1, 0xd8e0, _, ShortForm, { FloatReg, 0, 0} },
|
||||
{"fsub", 2, 0xd8e0, _, ShortForm, { FloatReg, FloatAcc, 0} },
|
||||
#ifdef NON_BROKEN_OPCODES
|
||||
{"fsub", 2, 0xdce8, _, ShortForm, { FloatAcc, FloatReg, 0} },
|
||||
#else
|
||||
{"fsub", 2, 0xdce0, _, ShortForm, { FloatAcc, FloatReg, 0} },
|
||||
#endif
|
||||
{"fsub", 0, 0xdce1, _, NoModrm, { 0, 0, 0} },
|
||||
{"fsubp", 1, 0xdae0, _, ShortForm, { FloatReg, 0, 0} },
|
||||
{"fsubp", 2, 0xdae0, _, ShortForm, { FloatReg, FloatAcc, 0} },
|
||||
#ifdef NON_BROKEN_OPCODES
|
||||
{"fsubp", 2, 0xdee8, _, ShortForm, { FloatAcc, FloatReg, 0} },
|
||||
#else
|
||||
{"fsubp", 2, 0xdee0, _, ShortForm, { FloatAcc, FloatReg, 0} },
|
||||
#endif
|
||||
{"fsubp", 0, 0xdee1, _, NoModrm, { 0, 0, 0} },
|
||||
{"fsubs", 1, 0xd8, 4, Modrm, { Mem, 0, 0} },
|
||||
{"fisubl", 1, 0xda, 4, Modrm, { Mem, 0, 0} },
|
||||
{"fsubl", 1, 0xdc, 4, Modrm, { Mem, 0, 0} },
|
||||
{"fisubs", 1, 0xde, 4, Modrm, { Mem, 0, 0} },
|
||||
|
||||
/* sub reverse */
|
||||
{"fsubr", 1, 0xd8e8, _, ShortForm, { FloatReg, 0, 0} },
|
||||
{"fsubr", 2, 0xd8e8, _, ShortForm, { FloatReg, FloatAcc, 0} },
|
||||
#ifdef NON_BROKEN_OPCODES
|
||||
{"fsubr", 2, 0xdce0, _, ShortForm, { FloatAcc, FloatReg, 0} },
|
||||
#else
|
||||
{"fsubr", 2, 0xdce8, _, ShortForm, { FloatAcc, FloatReg, 0} },
|
||||
#endif
|
||||
{"fsubr", 0, 0xdce9, _, NoModrm, { 0, 0, 0} },
|
||||
{"fsubrp", 1, 0xdae8, _, ShortForm, { FloatReg, 0, 0} },
|
||||
{"fsubrp", 2, 0xdae8, _, ShortForm, { FloatReg, FloatAcc, 0} },
|
||||
#ifdef NON_BROKEN_OPCODES
|
||||
{"fsubrp", 2, 0xdee0, _, ShortForm, { FloatAcc, FloatReg, 0} },
|
||||
#else
|
||||
{"fsubrp", 2, 0xdee8, _, ShortForm, { FloatAcc, FloatReg, 0} },
|
||||
#endif
|
||||
{"fsubrp", 0, 0xdee9, _, NoModrm, { 0, 0, 0} },
|
||||
{"fsubrs", 1, 0xd8, 5, Modrm, { Mem, 0, 0} },
|
||||
{"fisubrl", 1, 0xda, 5, Modrm, { Mem, 0, 0} },
|
||||
{"fsubrl", 1, 0xdc, 5, Modrm, { Mem, 0, 0} },
|
||||
{"fisubrs", 1, 0xde, 5, Modrm, { Mem, 0, 0} },
|
||||
|
||||
/* mul */
|
||||
{"fmul", 1, 0xd8c8, _, ShortForm, { FloatReg, 0, 0} },
|
||||
{"fmul", 2, 0xd8c8, _, ShortForm|FloatD, { FloatReg, FloatAcc, 0} },
|
||||
{"fmul", 0, 0xdcc9, _, NoModrm, { 0, 0, 0} },
|
||||
{"fmulp", 1, 0xdac8, _, ShortForm, { FloatReg, 0, 0} },
|
||||
{"fmulp", 2, 0xdac8, _, ShortForm|FloatD, { FloatReg, FloatAcc, 0} },
|
||||
{"fmulp", 0, 0xdec9, _, NoModrm, { 0, 0, 0} },
|
||||
{"fmuls", 1, 0xd8, 1, Modrm, { Mem, 0, 0} },
|
||||
{"fimull", 1, 0xda, 1, Modrm, { Mem, 0, 0} },
|
||||
{"fmull", 1, 0xdc, 1, Modrm, { Mem, 0, 0} },
|
||||
{"fimuls", 1, 0xde, 1, Modrm, { Mem, 0, 0} },
|
||||
|
||||
/* div */
|
||||
/* Note: intel has decided that certain of these operations are reversed
|
||||
in assembler syntax. */
|
||||
{"fdiv", 1, 0xd8f0, _, ShortForm, { FloatReg, 0, 0} },
|
||||
{"fdiv", 2, 0xd8f0, _, ShortForm, { FloatReg, FloatAcc, 0} },
|
||||
#ifdef NON_BROKEN_OPCODES
|
||||
{"fdiv", 2, 0xdcf8, _, ShortForm, { FloatAcc, FloatReg, 0} },
|
||||
#else
|
||||
{"fdiv", 2, 0xdcf0, _, ShortForm, { FloatAcc, FloatReg, 0} },
|
||||
#endif
|
||||
{"fdiv", 0, 0xdcf1, _, NoModrm, { 0, 0, 0} },
|
||||
{"fdivp", 1, 0xdaf0, _, ShortForm, { FloatReg, 0, 0} },
|
||||
{"fdivp", 2, 0xdaf0, _, ShortForm, { FloatReg, FloatAcc, 0} },
|
||||
#ifdef NON_BROKEN_OPCODES
|
||||
{"fdivp", 2, 0xdef8, _, ShortForm, { FloatAcc, FloatReg, 0} },
|
||||
#else
|
||||
{"fdivp", 2, 0xdef0, _, ShortForm, { FloatAcc, FloatReg, 0} },
|
||||
#endif
|
||||
{"fdivp", 0, 0xdef1, _, NoModrm, { 0, 0, 0} },
|
||||
{"fdivs", 1, 0xd8, 6, Modrm, { Mem, 0, 0} },
|
||||
{"fidivl", 1, 0xda, 6, Modrm, { Mem, 0, 0} },
|
||||
{"fdivl", 1, 0xdc, 6, Modrm, { Mem, 0, 0} },
|
||||
{"fidivs", 1, 0xde, 6, Modrm, { Mem, 0, 0} },
|
||||
|
||||
/* div reverse */
|
||||
{"fdivr", 1, 0xd8f8, _, ShortForm, { FloatReg, 0, 0} },
|
||||
{"fdivr", 2, 0xd8f8, _, ShortForm, { FloatReg, FloatAcc, 0} },
|
||||
#ifdef NON_BROKEN_OPCODES
|
||||
{"fdivr", 2, 0xdcf0, _, ShortForm, { FloatAcc, FloatReg, 0} },
|
||||
#else
|
||||
{"fdivr", 2, 0xdcf8, _, ShortForm, { FloatAcc, FloatReg, 0} },
|
||||
#endif
|
||||
{"fdivr", 0, 0xdcf9, _, NoModrm, { 0, 0, 0} },
|
||||
{"fdivrp", 1, 0xdaf8, _, ShortForm, { FloatReg, 0, 0} },
|
||||
{"fdivrp", 2, 0xdaf8, _, ShortForm, { FloatReg, FloatAcc, 0} },
|
||||
#ifdef NON_BROKEN_OPCODES
|
||||
{"fdivrp", 2, 0xdef0, _, ShortForm, { FloatAcc, FloatReg, 0} },
|
||||
#else
|
||||
{"fdivrp", 2, 0xdef8, _, ShortForm, { FloatAcc, FloatReg, 0} },
|
||||
#endif
|
||||
{"fdivrp", 0, 0xdef9, _, NoModrm, { 0, 0, 0} },
|
||||
{"fdivrs", 1, 0xd8, 7, Modrm, { Mem, 0, 0} },
|
||||
{"fidivrl", 1, 0xda, 7, Modrm, { Mem, 0, 0} },
|
||||
{"fdivrl", 1, 0xdc, 7, Modrm, { Mem, 0, 0} },
|
||||
{"fidivrs", 1, 0xde, 7, Modrm, { Mem, 0, 0} },
|
||||
|
||||
{"f2xm1", 0, 0xd9f0, _, NoModrm, { 0, 0, 0} },
|
||||
{"fyl2x", 0, 0xd9f1, _, NoModrm, { 0, 0, 0} },
|
||||
{"fptan", 0, 0xd9f2, _, NoModrm, { 0, 0, 0} },
|
||||
{"fpatan", 0, 0xd9f3, _, NoModrm, { 0, 0, 0} },
|
||||
{"fxtract", 0, 0xd9f4, _, NoModrm, { 0, 0, 0} },
|
||||
{"fprem1", 0, 0xd9f5, _, NoModrm, { 0, 0, 0} },
|
||||
{"fdecstp", 0, 0xd9f6, _, NoModrm, { 0, 0, 0} },
|
||||
{"fincstp", 0, 0xd9f7, _, NoModrm, { 0, 0, 0} },
|
||||
{"fprem", 0, 0xd9f8, _, NoModrm, { 0, 0, 0} },
|
||||
{"fyl2xp1", 0, 0xd9f9, _, NoModrm, { 0, 0, 0} },
|
||||
{"fsqrt", 0, 0xd9fa, _, NoModrm, { 0, 0, 0} },
|
||||
{"fsincos", 0, 0xd9fb, _, NoModrm, { 0, 0, 0} },
|
||||
{"frndint", 0, 0xd9fc, _, NoModrm, { 0, 0, 0} },
|
||||
{"fscale", 0, 0xd9fd, _, NoModrm, { 0, 0, 0} },
|
||||
{"fsin", 0, 0xd9fe, _, NoModrm, { 0, 0, 0} },
|
||||
{"fcos", 0, 0xd9ff, _, NoModrm, { 0, 0, 0} },
|
||||
|
||||
{"fchs", 0, 0xd9e0, _, NoModrm, { 0, 0, 0} },
|
||||
{"fabs", 0, 0xd9e1, _, NoModrm, { 0, 0, 0} },
|
||||
|
||||
/* processor control */
|
||||
{"fninit", 0, 0xdbe3, _, NoModrm, { 0, 0, 0} },
|
||||
{"finit", 0, 0x9bdbe3, _, NoModrm, { 0, 0, 0} },
|
||||
{"fldcw", 1, 0xd9, 5, Modrm, { Mem, 0, 0} },
|
||||
{"fnstcw", 1, 0xd9, 7, Modrm, { Mem, 0, 0} },
|
||||
{"fstcw", 1, 0x9bd9, 7, Modrm, { Mem, 0, 0} },
|
||||
{"fnstsw", 1, 0xdfe0, _, NoModrm, { Acc, 0, 0} },
|
||||
{"fnstsw", 1, 0xdd, 7, Modrm, { Mem, 0, 0} },
|
||||
{"fnstsw", 0, 0xdfe0, _, NoModrm, { 0, 0, 0} },
|
||||
{"fstsw", 1, 0x9bdfe0, _, NoModrm, { Acc, 0, 0} },
|
||||
{"fstsw", 1, 0x9bdd, 7, Modrm, { Mem, 0, 0} },
|
||||
{"fstsw", 0, 0x9bdfe0, _, NoModrm, { 0, 0, 0} },
|
||||
{"fnclex", 0, 0xdbe2, _, NoModrm, { 0, 0, 0} },
|
||||
{"fclex", 0, 0x9bdbe2, _, NoModrm, { 0, 0, 0} },
|
||||
/*
|
||||
We ignore the short format (287) versions of fstenv/fldenv & fsave/frstor
|
||||
instructions; i'm not sure how to add them or how they are different.
|
||||
My 386/387 book offers no details about this.
|
||||
*/
|
||||
{"fnstenv", 1, 0xd9, 6, Modrm, { Mem, 0, 0} },
|
||||
{"fstenv", 1, 0x9bd9, 6, Modrm, { Mem, 0, 0} },
|
||||
{"fldenv", 1, 0xd9, 4, Modrm, { Mem, 0, 0} },
|
||||
{"fnsave", 1, 0xdd, 6, Modrm, { Mem, 0, 0} },
|
||||
{"fsave", 1, 0x9bdd, 6, Modrm, { Mem, 0, 0} },
|
||||
{"frstor", 1, 0xdd, 4, Modrm, { Mem, 0, 0} },
|
||||
|
||||
{"ffree", 1, 0xddc0, _, ShortForm, { FloatReg, 0, 0} },
|
||||
{"fnop", 0, 0xd9d0, _, NoModrm, { 0, 0, 0} },
|
||||
{"fwait", 0, 0x9b, _, NoModrm, { 0, 0, 0} },
|
||||
|
||||
/*
|
||||
opcode prefixes; we allow them as seperate insns too
|
||||
(see prefix table below)
|
||||
*/
|
||||
{"aword", 0, 0x67, _, NoModrm, { 0, 0, 0} },
|
||||
{"addr16", 0, 0x67, _, NoModrm, { 0, 0, 0} },
|
||||
{"word", 0, 0x66, _, NoModrm, { 0, 0, 0} },
|
||||
{"data16", 0, 0x66, _, NoModrm, { 0, 0, 0} },
|
||||
{"lock", 0, 0xf0, _, NoModrm, { 0, 0, 0} },
|
||||
{"cs", 0, 0x2e, _, NoModrm, { 0, 0, 0} },
|
||||
{"ds", 0, 0x3e, _, NoModrm, { 0, 0, 0} },
|
||||
{"es", 0, 0x26, _, NoModrm, { 0, 0, 0} },
|
||||
{"fs", 0, 0x64, _, NoModrm, { 0, 0, 0} },
|
||||
{"gs", 0, 0x65, _, NoModrm, { 0, 0, 0} },
|
||||
{"ss", 0, 0x36, _, NoModrm, { 0, 0, 0} },
|
||||
{"rep", 0, 0xf3, _, NoModrm, { 0, 0, 0} },
|
||||
{"repe", 0, 0xf3, _, NoModrm, { 0, 0, 0} },
|
||||
{"repz", 0, 0xf3, _, NoModrm, { 0, 0, 0} },
|
||||
{"repne", 0, 0xf2, _, NoModrm, { 0, 0, 0} },
|
||||
{"repnz", 0, 0xf2, _, NoModrm, { 0, 0, 0} },
|
||||
|
||||
/* 486 extensions */
|
||||
|
||||
{"bswap", 1, 0x0fc8, _, ShortForm, { Reg32,0,0 } },
|
||||
{"xadd", 2, 0x0fc0, _, DW|Modrm, { Reg, Reg|Mem, 0 } },
|
||||
{"cmpxchg", 2, 0x0fb0, _, DW|Modrm, { Reg, Reg|Mem, 0 } },
|
||||
{"invd", 0, 0x0f08, _, NoModrm, { 0, 0, 0} },
|
||||
{"wbinvd", 0, 0x0f09, _, NoModrm, { 0, 0, 0} },
|
||||
{"invlpg", 1, 0x0f01, 7, Modrm, { Mem, 0, 0} },
|
||||
|
||||
/* 586 and late 486 extensions */
|
||||
{"cpuid", 0, 0x0fa2, _, NoModrm, { 0, 0, 0} },
|
||||
|
||||
/* Pentium extensions */
|
||||
{"wrmsr", 0, 0x0f30, _, NoModrm, { 0, 0, 0} },
|
||||
{"rdtsc", 0, 0x0f31, _, NoModrm, { 0, 0, 0} },
|
||||
{"rdmsr", 0, 0x0f32, _, NoModrm, { 0, 0, 0} },
|
||||
{"cmpxchg8b", 1, 0x0fc7, 1, Modrm, { Mem, 0, 0} },
|
||||
|
||||
/* Pentium Pro extensions */
|
||||
{"rdpmc", 0, 0x0f33, _, NoModrm, { 0, 0, 0} },
|
||||
|
||||
{"cmovo", 2, 0x0f40, _, W|Modrm|ReverseRegRegmem, { WordReg|WordMem, WordReg, 0} },
|
||||
{"cmovno", 2, 0x0f41, _, W|Modrm|ReverseRegRegmem, { WordReg|WordMem, WordReg, 0} },
|
||||
{"cmovb", 2, 0x0f42, _, W|Modrm|ReverseRegRegmem, { WordReg|WordMem, WordReg, 0} },
|
||||
{"cmovae", 2, 0x0f43, _, W|Modrm|ReverseRegRegmem, { WordReg|WordMem, WordReg, 0} },
|
||||
{"cmove", 2, 0x0f44, _, W|Modrm|ReverseRegRegmem, { WordReg|WordMem, WordReg, 0} },
|
||||
{"cmovne", 2, 0x0f45, _, W|Modrm|ReverseRegRegmem, { WordReg|WordMem, WordReg, 0} },
|
||||
{"cmovbe", 2, 0x0f46, _, W|Modrm|ReverseRegRegmem, { WordReg|WordMem, WordReg, 0} },
|
||||
{"cmova", 2, 0x0f47, _, W|Modrm|ReverseRegRegmem, { WordReg|WordMem, WordReg, 0} },
|
||||
{"cmovs", 2, 0x0f48, _, W|Modrm|ReverseRegRegmem, { WordReg|WordMem, WordReg, 0} },
|
||||
{"cmovns", 2, 0x0f49, _, W|Modrm|ReverseRegRegmem, { WordReg|WordMem, WordReg, 0} },
|
||||
{"cmovp", 2, 0x0f4a, _, W|Modrm|ReverseRegRegmem, { WordReg|WordMem, WordReg, 0} },
|
||||
{"cmovnp", 2, 0x0f4b, _, W|Modrm|ReverseRegRegmem, { WordReg|WordMem, WordReg, 0} },
|
||||
{"cmovl", 2, 0x0f4c, _, W|Modrm|ReverseRegRegmem, { WordReg|WordMem, WordReg, 0} },
|
||||
{"cmovge", 2, 0x0f4d, _, W|Modrm|ReverseRegRegmem, { WordReg|WordMem, WordReg, 0} },
|
||||
{"cmovle", 2, 0x0f4e, _, W|Modrm|ReverseRegRegmem, { WordReg|WordMem, WordReg, 0} },
|
||||
{"cmovg", 2, 0x0f4f, _, W|Modrm|ReverseRegRegmem, { WordReg|WordMem, WordReg, 0} },
|
||||
|
||||
{"fcmovb", 2, 0xdac0, _, ShortForm, { FloatReg, FloatAcc, 0} },
|
||||
{"fcmove", 2, 0xdac8, _, ShortForm, { FloatReg, FloatAcc, 0} },
|
||||
{"fcmovbe",2, 0xdad0, _, ShortForm, { FloatReg, FloatAcc, 0} },
|
||||
{"fcmovu", 2, 0xdad8, _, ShortForm, { FloatReg, FloatAcc, 0} },
|
||||
{"fcmovnb", 2, 0xdbc0, _, ShortForm, { FloatReg, FloatAcc, 0} },
|
||||
{"fcmovne", 2, 0xdbc8, _, ShortForm, { FloatReg, FloatAcc, 0} },
|
||||
{"fcmovnbe",2, 0xdbd0, _, ShortForm, { FloatReg, FloatAcc, 0} },
|
||||
{"fcmovnu", 2, 0xdbd8, _, ShortForm, { FloatReg, FloatAcc, 0} },
|
||||
|
||||
{"fcomi", 2, 0xdbf0, _, ShortForm, { FloatReg, FloatAcc, 0} },
|
||||
{"fucomi", 2, 0xdbe8, _, ShortForm, { FloatReg, FloatAcc, 0} },
|
||||
{"fcomip", 2, 0xdff0, _, ShortForm, { FloatReg, FloatAcc, 0} },
|
||||
{"fucomip",2, 0xdfe8, _, ShortForm, { FloatReg, FloatAcc, 0} },
|
||||
|
||||
{"", 0, 0, 0, 0, { 0, 0, 0} } /* sentinel */
|
||||
};
|
||||
#undef _
|
||||
|
||||
static const template *const i386_optab_end
|
||||
= i386_optab + sizeof (i386_optab)/sizeof(i386_optab[0]);
|
||||
|
||||
/* 386 register table */
|
||||
|
||||
static const reg_entry i386_regtab[] = {
|
||||
/* 8 bit regs */
|
||||
{"al", Reg8|Acc, 0}, {"cl", Reg8|ShiftCount, 1}, {"dl", Reg8, 2},
|
||||
{"bl", Reg8, 3},
|
||||
{"ah", Reg8, 4}, {"ch", Reg8, 5}, {"dh", Reg8, 6}, {"bh", Reg8, 7},
|
||||
/* 16 bit regs */
|
||||
{"ax", Reg16|Acc, 0}, {"cx", Reg16, 1}, {"dx", Reg16|InOutPortReg, 2}, {"bx", Reg16, 3},
|
||||
{"sp", Reg16, 4}, {"bp", Reg16, 5}, {"si", Reg16, 6}, {"di", Reg16, 7},
|
||||
/* 32 bit regs */
|
||||
{"eax", Reg32|Acc, 0}, {"ecx", Reg32, 1}, {"edx", Reg32, 2}, {"ebx", Reg32, 3},
|
||||
{"esp", Reg32, 4}, {"ebp", Reg32, 5}, {"esi", Reg32, 6}, {"edi", Reg32, 7},
|
||||
/* segment registers */
|
||||
{"es", SReg2, 0}, {"cs", SReg2, 1}, {"ss", SReg2, 2},
|
||||
{"ds", SReg2, 3}, {"fs", SReg3, 4}, {"gs", SReg3, 5},
|
||||
/* control registers */
|
||||
{"cr0", Control, 0}, {"cr2", Control, 2}, {"cr3", Control, 3},
|
||||
{"cr4", Control, 4},
|
||||
/* debug registers */
|
||||
{"db0", Debug, 0}, {"db1", Debug, 1}, {"db2", Debug, 2},
|
||||
{"db3", Debug, 3}, {"db6", Debug, 6}, {"db7", Debug, 7},
|
||||
{"dr0", Debug, 0}, {"dr1", Debug, 1}, {"dr2", Debug, 2},
|
||||
{"dr3", Debug, 3}, {"dr6", Debug, 6}, {"dr7", Debug, 7},
|
||||
/* test registers */
|
||||
{"tr3", Test, 3}, {"tr4", Test, 4}, {"tr5", Test, 5},
|
||||
{"tr6", Test, 6}, {"tr7", Test, 7},
|
||||
/* float registers */
|
||||
{"st(0)", FloatReg|FloatAcc, 0},
|
||||
{"st", FloatReg|FloatAcc, 0},
|
||||
{"st(1)", FloatReg, 1}, {"st(2)", FloatReg, 2},
|
||||
{"st(3)", FloatReg, 3}, {"st(4)", FloatReg, 4}, {"st(5)", FloatReg, 5},
|
||||
{"st(6)", FloatReg, 6}, {"st(7)", FloatReg, 7}
|
||||
};
|
||||
|
||||
#define MAX_REG_NAME_SIZE 8 /* for parsing register names from input */
|
||||
|
||||
static const reg_entry *const i386_regtab_end
|
||||
= i386_regtab + sizeof(i386_regtab)/sizeof(i386_regtab[0]);
|
||||
|
||||
/* segment stuff */
|
||||
static const seg_entry cs = { "cs", 0x2e };
|
||||
static const seg_entry ds = { "ds", 0x3e };
|
||||
static const seg_entry ss = { "ss", 0x36 };
|
||||
static const seg_entry es = { "es", 0x26 };
|
||||
static const seg_entry fs = { "fs", 0x64 };
|
||||
static const seg_entry gs = { "gs", 0x65 };
|
||||
static const seg_entry null = { "", 0x0 };
|
||||
|
||||
/*
|
||||
This table is used to store the default segment register implied by all
|
||||
possible memory addressing modes.
|
||||
It is indexed by the mode & modrm entries of the modrm byte as follows:
|
||||
index = (mode<<3) | modrm;
|
||||
*/
|
||||
static const seg_entry *const one_byte_segment_defaults[] = {
|
||||
/* mode 0 */
|
||||
&ds, &ds, &ds, &ds, &null, &ds, &ds, &ds,
|
||||
/* mode 1 */
|
||||
&ds, &ds, &ds, &ds, &null, &ss, &ds, &ds,
|
||||
/* mode 2 */
|
||||
&ds, &ds, &ds, &ds, &null, &ss, &ds, &ds,
|
||||
/* mode 3 --- not a memory reference; never referenced */
|
||||
};
|
||||
|
||||
static const seg_entry *const two_byte_segment_defaults[] = {
|
||||
/* mode 0 */
|
||||
&ds, &ds, &ds, &ds, &ss, &ds, &ds, &ds,
|
||||
/* mode 1 */
|
||||
&ds, &ds, &ds, &ds, &ss, &ds, &ds, &ds,
|
||||
/* mode 2 */
|
||||
&ds, &ds, &ds, &ds, &ss, &ds, &ds, &ds,
|
||||
/* mode 3 --- not a memory reference; never referenced */
|
||||
};
|
||||
|
||||
static const prefix_entry i386_prefixtab[] = {
|
||||
#define ADDR_PREFIX_OPCODE 0x67
|
||||
{ "addr16", 0x67 }, /* address size prefix ==> 16bit addressing
|
||||
* (How is this useful?) */
|
||||
#define WORD_PREFIX_OPCODE 0x66
|
||||
{ "data16", 0x66 }, /* operand size prefix */
|
||||
{ "lock", 0xf0 }, /* bus lock prefix */
|
||||
{ "wait", 0x9b }, /* wait for coprocessor */
|
||||
{ "cs", 0x2e }, { "ds", 0x3e }, /* segment overrides ... */
|
||||
{ "es", 0x26 }, { "fs", 0x64 },
|
||||
{ "gs", 0x65 }, { "ss", 0x36 },
|
||||
/* REPE & REPNE used to detect rep/repne with a non-string instruction */
|
||||
#define REPNE 0xf2
|
||||
#define REPE 0xf3
|
||||
{ "rep", 0xf3 }, /* repeat string instructions */
|
||||
{ "repe", 0xf3 }, { "repz", 0xf3 },
|
||||
{ "repne", 0xf2 }, { "repnz", 0xf2 }
|
||||
};
|
||||
|
||||
static const prefix_entry *const i386_prefixtab_end
|
||||
= i386_prefixtab + sizeof(i386_prefixtab)/sizeof(i386_prefixtab[0]);
|
||||
|
||||
/* end of i386-opcode.h */
|
238
opcode/ppc-dis.c
Normal file
238
opcode/ppc-dis.c
Normal file
|
@ -0,0 +1,238 @@
|
|||
/* ppc-dis.c -- Disassemble PowerPC instructions
|
||||
Copyright 1994 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor, Cygnus Support
|
||||
|
||||
This file is part of GDB, GAS, and the GNU binutils.
|
||||
|
||||
GDB, GAS, and the GNU binutils are free software; you can redistribute
|
||||
them and/or modify them 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.
|
||||
|
||||
GDB, GAS, and the GNU binutils are distributed in the hope that they
|
||||
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 file; see the file COPYING. If not, write to the Free
|
||||
Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "ansidecl.h"
|
||||
#include "sysdep.h"
|
||||
#include "dis-asm.h"
|
||||
#include "opcode/ppc.h"
|
||||
|
||||
/* This file provides several disassembler functions, all of which use
|
||||
the disassembler interface defined in dis-asm.h. Several functions
|
||||
are provided because this file handles disassembly for the PowerPC
|
||||
in both big and little endian mode and also for the POWER (RS/6000)
|
||||
chip. */
|
||||
|
||||
static int print_insn_powerpc PARAMS ((bfd_vma, struct disassemble_info *,
|
||||
int bigendian, int dialect));
|
||||
|
||||
/* Print a big endian PowerPC instruction. For convenience, also
|
||||
disassemble instructions supported by the Motorola PowerPC 601. */
|
||||
|
||||
int
|
||||
print_insn_big_powerpc (memaddr, info)
|
||||
bfd_vma memaddr;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
return print_insn_powerpc (memaddr, info, 1,
|
||||
PPC_OPCODE_PPC | PPC_OPCODE_601);
|
||||
}
|
||||
|
||||
/* Print a little endian PowerPC instruction. For convenience, also
|
||||
disassemble instructions supported by the Motorola PowerPC 601. */
|
||||
|
||||
int
|
||||
print_insn_little_powerpc (memaddr, info)
|
||||
bfd_vma memaddr;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
return print_insn_powerpc (memaddr, info, 0,
|
||||
PPC_OPCODE_PPC | PPC_OPCODE_601);
|
||||
}
|
||||
|
||||
/* Print a POWER (RS/6000) instruction. */
|
||||
|
||||
int
|
||||
print_insn_rs6000 (memaddr, info)
|
||||
bfd_vma memaddr;
|
||||
struct disassemble_info *info;
|
||||
{
|
||||
return print_insn_powerpc (memaddr, info, 1, PPC_OPCODE_POWER);
|
||||
}
|
||||
|
||||
/* Print a PowerPC or POWER instruction. */
|
||||
|
||||
static int
|
||||
print_insn_powerpc (memaddr, info, bigendian, dialect)
|
||||
bfd_vma memaddr;
|
||||
struct disassemble_info *info;
|
||||
int bigendian;
|
||||
int dialect;
|
||||
{
|
||||
bfd_byte buffer[4];
|
||||
int status;
|
||||
unsigned long insn;
|
||||
const struct powerpc_opcode *opcode;
|
||||
const struct powerpc_opcode *opcode_end;
|
||||
unsigned long op;
|
||||
|
||||
status = (*info->read_memory_func) (memaddr, buffer, 4, info);
|
||||
if (status != 0)
|
||||
{
|
||||
(*info->memory_error_func) (status, memaddr, info);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bigendian)
|
||||
insn = bfd_getb32 (buffer);
|
||||
else
|
||||
insn = bfd_getl32 (buffer);
|
||||
|
||||
/* Get the major opcode of the instruction. */
|
||||
op = PPC_OP (insn);
|
||||
|
||||
/* Find the first match in the opcode table. We could speed this up
|
||||
a bit by doing a binary search on the major opcode. */
|
||||
opcode_end = powerpc_opcodes + powerpc_num_opcodes;
|
||||
for (opcode = powerpc_opcodes; opcode < opcode_end; opcode++)
|
||||
{
|
||||
unsigned long table_op;
|
||||
const unsigned char *opindex;
|
||||
const struct powerpc_operand *operand;
|
||||
int invalid;
|
||||
int need_comma;
|
||||
int need_paren;
|
||||
|
||||
table_op = PPC_OP (opcode->opcode);
|
||||
if (op < table_op)
|
||||
break;
|
||||
if (op > table_op)
|
||||
continue;
|
||||
|
||||
if ((insn & opcode->mask) != opcode->opcode
|
||||
|| (opcode->flags & dialect) == 0)
|
||||
continue;
|
||||
|
||||
/* Make two passes over the operands. First see if any of them
|
||||
have extraction functions, and, if they do, make sure the
|
||||
instruction is valid. */
|
||||
invalid = 0;
|
||||
for (opindex = opcode->operands; *opindex != 0; opindex++)
|
||||
{
|
||||
operand = powerpc_operands + *opindex;
|
||||
if (operand->extract)
|
||||
(*operand->extract) (insn, &invalid);
|
||||
}
|
||||
if (invalid)
|
||||
continue;
|
||||
|
||||
/* The instruction is valid. */
|
||||
(*info->fprintf_func) (info->stream, "%s", opcode->name);
|
||||
if (opcode->operands[0] != 0)
|
||||
(*info->fprintf_func) (info->stream, "\t");
|
||||
|
||||
/* Now extract and print the operands. */
|
||||
need_comma = 0;
|
||||
need_paren = 0;
|
||||
for (opindex = opcode->operands; *opindex != 0; opindex++)
|
||||
{
|
||||
long value;
|
||||
|
||||
operand = powerpc_operands + *opindex;
|
||||
|
||||
/* Operands that are marked FAKE are simply ignored. We
|
||||
already made sure that the extract function considered
|
||||
the instruction to be valid. */
|
||||
if ((operand->flags & PPC_OPERAND_FAKE) != 0)
|
||||
continue;
|
||||
|
||||
/* Extract the value from the instruction. */
|
||||
if (operand->extract)
|
||||
value = (*operand->extract) (insn, (int *) NULL);
|
||||
else
|
||||
{
|
||||
value = (insn >> operand->shift) & ((1 << operand->bits) - 1);
|
||||
if ((operand->flags & PPC_OPERAND_SIGNED) != 0
|
||||
&& (value & (1 << (operand->bits - 1))) != 0)
|
||||
value -= 1 << operand->bits;
|
||||
}
|
||||
|
||||
/* If the operand is optional, and the value is zero, don't
|
||||
print anything. */
|
||||
if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0
|
||||
&& (operand->flags & PPC_OPERAND_NEXT) == 0
|
||||
&& value == 0)
|
||||
continue;
|
||||
|
||||
if (need_comma)
|
||||
{
|
||||
(*info->fprintf_func) (info->stream, ",");
|
||||
need_comma = 0;
|
||||
}
|
||||
|
||||
/* Print the operand as directed by the flags. */
|
||||
if ((operand->flags & PPC_OPERAND_GPR) != 0)
|
||||
(*info->fprintf_func) (info->stream, "r%ld", value);
|
||||
else if ((operand->flags & PPC_OPERAND_FPR) != 0)
|
||||
(*info->fprintf_func) (info->stream, "f%ld", value);
|
||||
else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0)
|
||||
(*info->print_address_func) (memaddr + value, info);
|
||||
else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0)
|
||||
(*info->print_address_func) ((bfd_vma) value & 0xffffffff, info);
|
||||
else if ((operand->flags & PPC_OPERAND_CR) == 0
|
||||
|| (dialect & PPC_OPCODE_PPC) == 0)
|
||||
(*info->fprintf_func) (info->stream, "%ld", value);
|
||||
else
|
||||
{
|
||||
if (operand->bits == 3)
|
||||
(*info->fprintf_func) (info->stream, "cr%d", value);
|
||||
else
|
||||
{
|
||||
static const char *cbnames[4] = { "lt", "gt", "eq", "so" };
|
||||
int cr;
|
||||
int cc;
|
||||
|
||||
cr = value >> 2;
|
||||
if (cr != 0)
|
||||
(*info->fprintf_func) (info->stream, "4*cr%d", cr);
|
||||
cc = value & 3;
|
||||
if (cc != 0)
|
||||
{
|
||||
if (cr != 0)
|
||||
(*info->fprintf_func) (info->stream, "+");
|
||||
(*info->fprintf_func) (info->stream, "%s", cbnames[cc]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (need_paren)
|
||||
{
|
||||
(*info->fprintf_func) (info->stream, ")");
|
||||
need_paren = 0;
|
||||
}
|
||||
|
||||
if ((operand->flags & PPC_OPERAND_PARENS) == 0)
|
||||
need_comma = 1;
|
||||
else
|
||||
{
|
||||
(*info->fprintf_func) (info->stream, "(");
|
||||
need_paren = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* We have found and printed an instruction; return. */
|
||||
return 4;
|
||||
}
|
||||
|
||||
/* We could not find a match. */
|
||||
(*info->fprintf_func) (info->stream, ".long 0x%lx", insn);
|
||||
|
||||
return 4;
|
||||
}
|
2830
opcode/ppc-opc.c
Normal file
2830
opcode/ppc-opc.c
Normal file
File diff suppressed because it is too large
Load diff
248
opcode/ppc.h
Normal file
248
opcode/ppc.h
Normal file
|
@ -0,0 +1,248 @@
|
|||
/* ppc.h -- Header file for PowerPC opcode table
|
||||
Copyright 1994, 1995 Free Software Foundation, Inc.
|
||||
Written by Ian Lance Taylor, Cygnus Support
|
||||
|
||||
This file is part of GDB, GAS, and the GNU binutils.
|
||||
|
||||
GDB, GAS, and the GNU binutils are free software; you can redistribute
|
||||
them and/or modify them under the terms of the GNU General Public
|
||||
License as published by the Free Software Foundation; either version
|
||||
1, or (at your option) any later version.
|
||||
|
||||
GDB, GAS, and the GNU binutils are distributed in the hope that they
|
||||
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 file; see the file COPYING. If not, write to the Free
|
||||
Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
#ifndef PPC_H
|
||||
#define PPC_H
|
||||
|
||||
/* The opcode table is an array of struct powerpc_opcode. */
|
||||
|
||||
struct powerpc_opcode
|
||||
{
|
||||
/* The opcode name. */
|
||||
const char *name;
|
||||
|
||||
/* The opcode itself. Those bits which will be filled in with
|
||||
operands are zeroes. */
|
||||
unsigned long opcode;
|
||||
|
||||
/* The opcode mask. This is used by the disassembler. This is a
|
||||
mask containing ones indicating those bits which must match the
|
||||
opcode field, and zeroes indicating those bits which need not
|
||||
match (and are presumably filled in by operands). */
|
||||
unsigned long mask;
|
||||
|
||||
/* One bit flags for the opcode. These are used to indicate which
|
||||
specific processors support the instructions. The defined values
|
||||
are listed below. */
|
||||
unsigned long flags;
|
||||
|
||||
/* An array of operand codes. Each code is an index into the
|
||||
operand table. They appear in the order which the operands must
|
||||
appear in assembly code, and are terminated by a zero. */
|
||||
unsigned char operands[8];
|
||||
};
|
||||
|
||||
/* The table itself is sorted by major opcode number, and is otherwise
|
||||
in the order in which the disassembler should consider
|
||||
instructions. */
|
||||
extern const struct powerpc_opcode powerpc_opcodes[];
|
||||
extern const int powerpc_num_opcodes;
|
||||
|
||||
/* Values defined for the flags field of a struct powerpc_opcode. */
|
||||
|
||||
/* Opcode is defined for the PowerPC architecture. */
|
||||
#define PPC_OPCODE_PPC (01)
|
||||
|
||||
/* Opcode is defined for the POWER (RS/6000) architecture. */
|
||||
#define PPC_OPCODE_POWER (02)
|
||||
|
||||
/* Opcode is defined for the POWER2 (Rios 2) architecture. */
|
||||
#define PPC_OPCODE_POWER2 (04)
|
||||
|
||||
/* Opcode is only defined on 32 bit architectures. */
|
||||
#define PPC_OPCODE_32 (010)
|
||||
|
||||
/* Opcode is only defined on 64 bit architectures. */
|
||||
#define PPC_OPCODE_64 (020)
|
||||
|
||||
/* Opcode is supported by the Motorola PowerPC 601 processor. The 601
|
||||
is assumed to support all PowerPC (PPC_OPCODE_PPC) instructions,
|
||||
but it also supports many additional POWER instructions. */
|
||||
#define PPC_OPCODE_601 (040)
|
||||
|
||||
/* Opcode is supported in both the Power and PowerPC architectures
|
||||
(ie, compiler's -mcpu=common or assembler's -mcom). */
|
||||
#define PPC_OPCODE_COMMON (0100)
|
||||
|
||||
/* Opcode is supported for any Power or PowerPC platform (this is
|
||||
for the assembler's -many option, and it eliminates duplicates). */
|
||||
#define PPC_OPCODE_ANY (0200)
|
||||
|
||||
/* A macro to extract the major opcode from an instruction. */
|
||||
#define PPC_OP(i) (((i) >> 26) & 0x3f)
|
||||
|
||||
/* The operands table is an array of struct powerpc_operand. */
|
||||
|
||||
struct powerpc_operand
|
||||
{
|
||||
/* The number of bits in the operand. */
|
||||
int bits;
|
||||
|
||||
/* How far the operand is left shifted in the instruction. */
|
||||
int shift;
|
||||
|
||||
/* Insertion function. This is used by the assembler. To insert an
|
||||
operand value into an instruction, check this field.
|
||||
|
||||
If it is NULL, execute
|
||||
i |= (op & ((1 << o->bits) - 1)) << o->shift;
|
||||
(i is the instruction which we are filling in, o is a pointer to
|
||||
this structure, and op is the opcode value; this assumes twos
|
||||
complement arithmetic).
|
||||
|
||||
If this field is not NULL, then simply call it with the
|
||||
instruction and the operand value. It will return the new value
|
||||
of the instruction. If the ERRMSG argument is not NULL, then if
|
||||
the operand value is illegal, *ERRMSG will be set to a warning
|
||||
string (the operand will be inserted in any case). If the
|
||||
operand value is legal, *ERRMSG will be unchanged (most operands
|
||||
can accept any value). */
|
||||
unsigned long (*insert) PARAMS ((unsigned long instruction, long op,
|
||||
const char **errmsg));
|
||||
|
||||
/* Extraction function. This is used by the disassembler. To
|
||||
extract this operand type from an instruction, check this field.
|
||||
|
||||
If it is NULL, compute
|
||||
op = ((i) >> o->shift) & ((1 << o->bits) - 1);
|
||||
if ((o->flags & PPC_OPERAND_SIGNED) != 0
|
||||
&& (op & (1 << (o->bits - 1))) != 0)
|
||||
op -= 1 << o->bits;
|
||||
(i is the instruction, o is a pointer to this structure, and op
|
||||
is the result; this assumes twos complement arithmetic).
|
||||
|
||||
If this field is not NULL, then simply call it with the
|
||||
instruction value. It will return the value of the operand. If
|
||||
the INVALID argument is not NULL, *INVALID will be set to
|
||||
non-zero if this operand type can not actually be extracted from
|
||||
this operand (i.e., the instruction does not match). If the
|
||||
operand is valid, *INVALID will not be changed. */
|
||||
long (*extract) PARAMS ((unsigned long instruction, int *invalid));
|
||||
|
||||
/* One bit syntax flags. */
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
/* Elements in the table are retrieved by indexing with values from
|
||||
the operands field of the powerpc_opcodes table. */
|
||||
|
||||
extern const struct powerpc_operand powerpc_operands[];
|
||||
|
||||
/* Values defined for the flags field of a struct powerpc_operand. */
|
||||
|
||||
/* This operand takes signed values. */
|
||||
#define PPC_OPERAND_SIGNED (01)
|
||||
|
||||
/* This operand takes signed values, but also accepts a full positive
|
||||
range of values when running in 32 bit mode. That is, if bits is
|
||||
16, it takes any value from -0x8000 to 0xffff. In 64 bit mode,
|
||||
this flag is ignored. */
|
||||
#define PPC_OPERAND_SIGNOPT (02)
|
||||
|
||||
/* This operand does not actually exist in the assembler input. This
|
||||
is used to support extended mnemonics such as mr, for which two
|
||||
operands fields are identical. The assembler should call the
|
||||
insert function with any op value. The disassembler should call
|
||||
the extract function, ignore the return value, and check the value
|
||||
placed in the valid argument. */
|
||||
#define PPC_OPERAND_FAKE (04)
|
||||
|
||||
/* The next operand should be wrapped in parentheses rather than
|
||||
separated from this one by a comma. This is used for the load and
|
||||
store instructions which want their operands to look like
|
||||
reg,displacement(reg)
|
||||
*/
|
||||
#define PPC_OPERAND_PARENS (010)
|
||||
|
||||
/* This operand may use the symbolic names for the CR fields, which
|
||||
are
|
||||
lt 0 gt 1 eq 2 so 3 un 3
|
||||
cr0 0 cr1 1 cr2 2 cr3 3
|
||||
cr4 4 cr5 5 cr6 6 cr7 7
|
||||
These may be combined arithmetically, as in cr2*4+gt. These are
|
||||
only supported on the PowerPC, not the POWER. */
|
||||
#define PPC_OPERAND_CR (020)
|
||||
|
||||
/* This operand names a register. The disassembler uses this to print
|
||||
register names with a leading 'r'. */
|
||||
#define PPC_OPERAND_GPR (040)
|
||||
|
||||
/* This operand names a floating point register. The disassembler
|
||||
prints these with a leading 'f'. */
|
||||
#define PPC_OPERAND_FPR (0100)
|
||||
|
||||
/* This operand is a relative branch displacement. The disassembler
|
||||
prints these symbolically if possible. */
|
||||
#define PPC_OPERAND_RELATIVE (0200)
|
||||
|
||||
/* This operand is an absolute branch address. The disassembler
|
||||
prints these symbolically if possible. */
|
||||
#define PPC_OPERAND_ABSOLUTE (0400)
|
||||
|
||||
/* This operand is optional, and is zero if omitted. This is used for
|
||||
the optional BF and L fields in the comparison instructions. The
|
||||
assembler must count the number of operands remaining on the line,
|
||||
and the number of operands remaining for the opcode, and decide
|
||||
whether this operand is present or not. The disassembler should
|
||||
print this operand out only if it is not zero. */
|
||||
#define PPC_OPERAND_OPTIONAL (01000)
|
||||
|
||||
/* This flag is only used with PPC_OPERAND_OPTIONAL. If this operand
|
||||
is omitted, then for the next operand use this operand value plus
|
||||
1, ignoring the next operand field for the opcode. This wretched
|
||||
hack is needed because the Power rotate instructions can take
|
||||
either 4 or 5 operands. The disassembler should print this operand
|
||||
out regardless of the PPC_OPERAND_OPTIONAL field. */
|
||||
#define PPC_OPERAND_NEXT (02000)
|
||||
|
||||
/* This operand should be regarded as a negative number for the
|
||||
purposes of overflow checking (i.e., the normal most negative
|
||||
number is disallowed and one more than the normal most positive
|
||||
number is allowed). This flag will only be set for a signed
|
||||
operand. */
|
||||
#define PPC_OPERAND_NEGATIVE (04000)
|
||||
|
||||
/* The POWER and PowerPC assemblers use a few macros. We keep them
|
||||
with the operands table for simplicity. The macro table is an
|
||||
array of struct powerpc_macro. */
|
||||
|
||||
struct powerpc_macro
|
||||
{
|
||||
/* The macro name. */
|
||||
const char *name;
|
||||
|
||||
/* The number of operands the macro takes. */
|
||||
unsigned int operands;
|
||||
|
||||
/* One bit flags for the opcode. These are used to indicate which
|
||||
specific processors support the instructions. The values are the
|
||||
same as those for the struct powerpc_opcode flags field. */
|
||||
unsigned long flags;
|
||||
|
||||
/* A format string to turn the macro into a normal instruction.
|
||||
Each %N in the string is replaced with operand number N (zero
|
||||
based). */
|
||||
const char *format;
|
||||
};
|
||||
|
||||
extern const struct powerpc_macro powerpc_macros[];
|
||||
extern const int powerpc_num_macros;
|
||||
|
||||
#endif /* PPC_H */
|
868
opcode/sparc-dis.c
Normal file
868
opcode/sparc-dis.c
Normal file
|
@ -0,0 +1,868 @@
|
|||
/* Print SPARC instructions.
|
||||
Copyright (C) 1989, 91-93, 1995, 1996 Free Software Foundation, Inc.
|
||||
|
||||
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 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
#include "ansidecl.h"
|
||||
#include "opcode/sparc.h"
|
||||
#include "dis-asm.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* Bitmask of v9 architectures. */
|
||||
#define MASK_V9 ((1 << SPARC_OPCODE_ARCH_V9) \
|
||||
| (1 << SPARC_OPCODE_ARCH_V9A))
|
||||
/* 1 if INSN is for v9 only. */
|
||||
#define V9_ONLY_P(insn) (! ((insn)->architecture & ~MASK_V9))
|
||||
/* 1 if INSN is for v9. */
|
||||
#define V9_P(insn) (((insn)->architecture & MASK_V9) != 0)
|
||||
|
||||
/* For faster lookup, after insns are sorted they are hashed. */
|
||||
/* ??? I think there is room for even more improvement. */
|
||||
|
||||
#define HASH_SIZE 256
|
||||
/* It is important that we only look at insn code bits as that is how the
|
||||
opcode table is hashed. OPCODE_BITS is a table of valid bits for each
|
||||
of the main types (0,1,2,3). */
|
||||
static int opcode_bits[4] = { 0x01c00000, 0x0, 0x01f80000, 0x01f80000 };
|
||||
#define HASH_INSN(INSN) \
|
||||
((((INSN) >> 24) & 0xc0) | (((INSN) & opcode_bits[((INSN) >> 30) & 3]) >> 19))
|
||||
struct opcode_hash {
|
||||
struct opcode_hash *next;
|
||||
struct sparc_opcode *opcode;
|
||||
};
|
||||
static struct opcode_hash *opcode_hash_table[HASH_SIZE];
|
||||
static void build_hash_table ();
|
||||
|
||||
/* Sign-extend a value which is N bits long. */
|
||||
#define SEX(value, bits) \
|
||||
((((int)(value)) << ((8 * sizeof (int)) - bits)) \
|
||||
>> ((8 * sizeof (int)) - bits) )
|
||||
|
||||
static char *reg_names[] =
|
||||
{ "g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7",
|
||||
"o0", "o1", "o2", "o3", "o4", "o5", "sp", "o7",
|
||||
"l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7",
|
||||
"i0", "i1", "i2", "i3", "i4", "i5", "fp", "i7",
|
||||
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
|
||||
"f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
|
||||
"f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
|
||||
"f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
|
||||
"f32", "f33", "f34", "f35", "f36", "f37", "f38", "f39",
|
||||
"f40", "f41", "f42", "f43", "f44", "f45", "f46", "f47",
|
||||
"f48", "f49", "f50", "f51", "f52", "f53", "f54", "f55",
|
||||
"f56", "f57", "f58", "f59", "f60", "f61", "f62", "f63",
|
||||
/* psr, wim, tbr, fpsr, cpsr are v8 only. */
|
||||
"y", "psr", "wim", "tbr", "pc", "npc", "fpsr", "cpsr"
|
||||
};
|
||||
|
||||
#define freg_names (®_names[4 * 8])
|
||||
|
||||
/* These are ordered according to there register number in
|
||||
rdpr and wrpr insns. */
|
||||
static char *v9_priv_reg_names[] =
|
||||
{
|
||||
"tpc", "tnpc", "tstate", "tt", "tick", "tba", "pstate", "tl",
|
||||
"pil", "cwp", "cansave", "canrestore", "cleanwin", "otherwin",
|
||||
"wstate", "fq"
|
||||
/* "ver" - special cased */
|
||||
};
|
||||
|
||||
/* Macros used to extract instruction fields. Not all fields have
|
||||
macros defined here, only those which are actually used. */
|
||||
|
||||
#define X_RD(i) (((i) >> 25) & 0x1f)
|
||||
#define X_RS1(i) (((i) >> 14) & 0x1f)
|
||||
#define X_LDST_I(i) (((i) >> 13) & 1)
|
||||
#define X_ASI(i) (((i) >> 5) & 0xff)
|
||||
#define X_RS2(i) (((i) >> 0) & 0x1f)
|
||||
#define X_IMM13(i) (((i) >> 0) & 0x1fff)
|
||||
#define X_DISP22(i) (((i) >> 0) & 0x3fffff)
|
||||
#define X_IMM22(i) X_DISP22 (i)
|
||||
#define X_DISP30(i) (((i) >> 0) & 0x3fffffff)
|
||||
|
||||
/* These are for v9. */
|
||||
#define X_DISP16(i) (((((i) >> 20) & 3) << 14) | (((i) >> 0) & 0x3fff))
|
||||
#define X_DISP19(i) (((i) >> 0) & 0x7ffff)
|
||||
#define X_MEMBAR(i) ((i) & 0x7f)
|
||||
|
||||
/* Here is the union which was used to extract instruction fields
|
||||
before the shift and mask macros were written.
|
||||
|
||||
union sparc_insn
|
||||
{
|
||||
unsigned long int code;
|
||||
struct
|
||||
{
|
||||
unsigned int anop:2;
|
||||
#define op ldst.anop
|
||||
unsigned int anrd:5;
|
||||
#define rd ldst.anrd
|
||||
unsigned int op3:6;
|
||||
unsigned int anrs1:5;
|
||||
#define rs1 ldst.anrs1
|
||||
unsigned int i:1;
|
||||
unsigned int anasi:8;
|
||||
#define asi ldst.anasi
|
||||
unsigned int anrs2:5;
|
||||
#define rs2 ldst.anrs2
|
||||
#define shcnt rs2
|
||||
} ldst;
|
||||
struct
|
||||
{
|
||||
unsigned int anop:2, anrd:5, op3:6, anrs1:5, i:1;
|
||||
unsigned int IMM13:13;
|
||||
#define imm13 IMM13.IMM13
|
||||
} IMM13;
|
||||
struct
|
||||
{
|
||||
unsigned int anop:2;
|
||||
unsigned int a:1;
|
||||
unsigned int cond:4;
|
||||
unsigned int op2:3;
|
||||
unsigned int DISP22:22;
|
||||
#define disp22 branch.DISP22
|
||||
#define imm22 disp22
|
||||
} branch;
|
||||
struct
|
||||
{
|
||||
unsigned int anop:2;
|
||||
unsigned int a:1;
|
||||
unsigned int z:1;
|
||||
unsigned int rcond:3;
|
||||
unsigned int op2:3;
|
||||
unsigned int DISP16HI:2;
|
||||
unsigned int p:1;
|
||||
unsigned int _rs1:5;
|
||||
unsigned int DISP16LO:14;
|
||||
} branch16;
|
||||
struct
|
||||
{
|
||||
unsigned int anop:2;
|
||||
unsigned int adisp30:30;
|
||||
#define disp30 call.adisp30
|
||||
} call;
|
||||
};
|
||||
|
||||
*/
|
||||
|
||||
/* Nonzero if INSN is the opcode for a delayed branch. */
|
||||
static int
|
||||
is_delayed_branch (insn)
|
||||
unsigned long insn;
|
||||
{
|
||||
struct opcode_hash *op;
|
||||
|
||||
for (op = opcode_hash_table[HASH_INSN (insn)]; op; op = op->next)
|
||||
{
|
||||
const struct sparc_opcode *opcode = op->opcode;
|
||||
if ((opcode->match & insn) == opcode->match
|
||||
&& (opcode->lose & insn) == 0)
|
||||
return (opcode->flags & F_DELAYED);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Nonzero of opcode table has been initialized. */
|
||||
static int opcodes_initialized = 0;
|
||||
|
||||
/* extern void qsort (); */
|
||||
static int compare_opcodes ();
|
||||
|
||||
/* Print one instruction from MEMADDR on INFO->STREAM.
|
||||
|
||||
We suffix the instruction with a comment that gives the absolute
|
||||
address involved, as well as its symbolic form, if the instruction
|
||||
is preceded by a findable `sethi' and it either adds an immediate
|
||||
displacement to that register, or it is an `add' or `or' instruction
|
||||
on that register. */
|
||||
|
||||
int
|
||||
print_insn_sparc (memaddr, info)
|
||||
bfd_vma memaddr;
|
||||
disassemble_info *info;
|
||||
{
|
||||
FILE *stream = info->stream;
|
||||
bfd_byte buffer[4];
|
||||
unsigned long insn;
|
||||
register unsigned int i;
|
||||
register struct opcode_hash *op;
|
||||
int sparc_v9_p = bfd_mach_sparc_v9_p (info->mach);
|
||||
|
||||
if (!opcodes_initialized)
|
||||
{
|
||||
qsort ((char *) sparc_opcodes, sparc_num_opcodes,
|
||||
sizeof (sparc_opcodes[0]), compare_opcodes);
|
||||
build_hash_table (sparc_opcodes, opcode_hash_table, sparc_num_opcodes);
|
||||
opcodes_initialized = 1;
|
||||
}
|
||||
|
||||
{
|
||||
int status =
|
||||
(*info->read_memory_func) (memaddr, buffer, sizeof (buffer), info);
|
||||
if (status != 0)
|
||||
{
|
||||
(*info->memory_error_func) (status, memaddr, info);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
insn = bfd_getb32 (buffer);
|
||||
|
||||
info->insn_info_valid = 1; /* We do return this info */
|
||||
info->insn_type = dis_nonbranch; /* Assume non branch insn */
|
||||
info->branch_delay_insns = 0; /* Assume no delay */
|
||||
info->target = 0; /* Assume no target known */
|
||||
|
||||
for (op = opcode_hash_table[HASH_INSN (insn)]; op; op = op->next)
|
||||
{
|
||||
const struct sparc_opcode *opcode = op->opcode;
|
||||
|
||||
/* ??? These architecture tests need to be more selective. */
|
||||
|
||||
/* If the current architecture isn't sparc64, skip sparc64 insns. */
|
||||
if (!sparc_v9_p
|
||||
&& V9_ONLY_P (opcode))
|
||||
continue;
|
||||
|
||||
/* If the current architecture is sparc64, skip sparc32 only insns. */
|
||||
if (sparc_v9_p
|
||||
&& ! V9_P (opcode))
|
||||
continue;
|
||||
|
||||
if ((opcode->match & insn) == opcode->match
|
||||
&& (opcode->lose & insn) == 0)
|
||||
{
|
||||
/* Nonzero means that we have found an instruction which has
|
||||
the effect of adding or or'ing the imm13 field to rs1. */
|
||||
int imm_added_to_rs1 = 0;
|
||||
|
||||
/* Nonzero means that we have found a plus sign in the args
|
||||
field of the opcode table. */
|
||||
int found_plus = 0;
|
||||
|
||||
/* Nonzero means we have an annulled branch. */
|
||||
int is_annulled = 0;
|
||||
|
||||
/* Do we have an `add' or `or' instruction where rs1 is the same
|
||||
as rsd, and which has the i bit set? */
|
||||
if ((opcode->match == 0x80102000 || opcode->match == 0x80002000)
|
||||
/* (or) (add) */
|
||||
&& X_RS1 (insn) == X_RD (insn))
|
||||
imm_added_to_rs1 = 1;
|
||||
|
||||
if (X_RS1 (insn) != X_RD (insn)
|
||||
&& strchr (opcode->args, 'r') != 0)
|
||||
/* Can't do simple format if source and dest are different. */
|
||||
continue;
|
||||
if (X_RS2 (insn) != X_RD (insn)
|
||||
&& strchr (opcode->args, 'O') != 0)
|
||||
/* Can't do simple format if source and dest are different. */
|
||||
continue;
|
||||
|
||||
(*info->fprintf_func) (stream, opcode->name);
|
||||
|
||||
{
|
||||
register const char *s;
|
||||
|
||||
if (opcode->args[0] != ',')
|
||||
(*info->fprintf_func) (stream, " ");
|
||||
for (s = opcode->args; *s != '\0'; ++s)
|
||||
{
|
||||
while (*s == ',')
|
||||
{
|
||||
(*info->fprintf_func) (stream, ",");
|
||||
++s;
|
||||
switch (*s) {
|
||||
case 'a':
|
||||
(*info->fprintf_func) (stream, "a");
|
||||
is_annulled = 1;
|
||||
++s;
|
||||
continue;
|
||||
case 'N':
|
||||
(*info->fprintf_func) (stream, "pn");
|
||||
++s;
|
||||
continue;
|
||||
|
||||
case 'T':
|
||||
(*info->fprintf_func) (stream, "pt");
|
||||
++s;
|
||||
continue;
|
||||
|
||||
default:
|
||||
break;
|
||||
} /* switch on arg */
|
||||
} /* while there are comma started args */
|
||||
|
||||
(*info->fprintf_func) (stream, " ");
|
||||
|
||||
switch (*s)
|
||||
{
|
||||
case '+':
|
||||
found_plus = 1;
|
||||
|
||||
/* note fall-through */
|
||||
default:
|
||||
(*info->fprintf_func) (stream, "%c", *s);
|
||||
break;
|
||||
|
||||
case '#':
|
||||
(*info->fprintf_func) (stream, "0");
|
||||
break;
|
||||
|
||||
#define reg(n) (*info->fprintf_func) (stream, "%%%s", reg_names[n])
|
||||
case '1':
|
||||
case 'r':
|
||||
reg (X_RS1 (insn));
|
||||
break;
|
||||
|
||||
case '2':
|
||||
case 'O':
|
||||
reg (X_RS2 (insn));
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
reg (X_RD (insn));
|
||||
break;
|
||||
#undef reg
|
||||
|
||||
#define freg(n) (*info->fprintf_func) (stream, "%%%s", freg_names[n])
|
||||
#define fregx(n) (*info->fprintf_func) (stream, "%%%s", freg_names[((n) & ~1) | (((n) & 1) << 5)])
|
||||
case 'e':
|
||||
freg (X_RS1 (insn));
|
||||
break;
|
||||
case 'v': /* double/even */
|
||||
case 'V': /* quad/multiple of 4 */
|
||||
fregx (X_RS1 (insn));
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
freg (X_RS2 (insn));
|
||||
break;
|
||||
case 'B': /* double/even */
|
||||
case 'R': /* quad/multiple of 4 */
|
||||
fregx (X_RS2 (insn));
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
freg (X_RD (insn));
|
||||
break;
|
||||
case 'H': /* double/even */
|
||||
case 'J': /* quad/multiple of 4 */
|
||||
fregx (X_RD (insn));
|
||||
break;
|
||||
#undef freg
|
||||
#undef fregx
|
||||
|
||||
#define creg(n) (*info->fprintf_func) (stream, "%%c%u", (unsigned int) (n))
|
||||
case 'b':
|
||||
creg (X_RS1 (insn));
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
creg (X_RS2 (insn));
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
creg (X_RD (insn));
|
||||
break;
|
||||
#undef creg
|
||||
|
||||
case 'h':
|
||||
(*info->fprintf_func) (stream, "%%hi(%#x)",
|
||||
(0xFFFFFFFF
|
||||
& ((int) X_IMM22 (insn) << 10)));
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
{
|
||||
int imm = SEX (X_IMM13 (insn), 13);
|
||||
|
||||
/* Check to see whether we have a 1+i, and take
|
||||
note of that fact.
|
||||
|
||||
Note: because of the way we sort the table,
|
||||
we will be matching 1+i rather than i+1,
|
||||
so it is OK to assume that i is after +,
|
||||
not before it. */
|
||||
if (found_plus)
|
||||
imm_added_to_rs1 = 1;
|
||||
|
||||
if (imm <= 9)
|
||||
(*info->fprintf_func) (stream, "%d", imm);
|
||||
else
|
||||
(*info->fprintf_func) (stream, "%#x", imm);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'I': /* 11 bit immediate. */
|
||||
case 'j': /* 10 bit immediate. */
|
||||
{
|
||||
int imm;
|
||||
|
||||
if (*s == 'I')
|
||||
imm = SEX (X_IMM13 (insn), 11);
|
||||
else
|
||||
imm = SEX (X_IMM13 (insn), 10);
|
||||
|
||||
/* Check to see whether we have a 1+i, and take
|
||||
note of that fact.
|
||||
|
||||
Note: because of the way we sort the table,
|
||||
we will be matching 1+i rather than i+1,
|
||||
so it is OK to assume that i is after +,
|
||||
not before it. */
|
||||
if (found_plus)
|
||||
imm_added_to_rs1 = 1;
|
||||
|
||||
if (imm <= 9)
|
||||
(info->fprintf_func) (stream, "%d", imm);
|
||||
else
|
||||
(info->fprintf_func) (stream, "%#x", (unsigned) imm);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'K':
|
||||
{
|
||||
int mask = X_MEMBAR (insn);
|
||||
int bit = 0x40, printed_one = 0;
|
||||
char *name;
|
||||
|
||||
if (mask == 0)
|
||||
(info->fprintf_func) (stream, "0");
|
||||
else
|
||||
while (bit)
|
||||
{
|
||||
if (mask & bit)
|
||||
{
|
||||
if (printed_one)
|
||||
(info->fprintf_func) (stream, "|");
|
||||
name = sparc_decode_membar (bit);
|
||||
(info->fprintf_func) (stream, "%s", name);
|
||||
printed_one = 1;
|
||||
}
|
||||
bit >>= 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'k':
|
||||
info->target = memaddr + SEX (X_DISP16 (insn), 16) * 4;
|
||||
(*info->print_address_func) (info->target, info);
|
||||
break;
|
||||
|
||||
case 'G':
|
||||
info->target = memaddr + SEX (X_DISP19 (insn), 19) * 4;
|
||||
(*info->print_address_func) (info->target, info);
|
||||
break;
|
||||
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
(*info->fprintf_func) (stream, "%%fcc%c", *s - '6' + '0');
|
||||
break;
|
||||
|
||||
case 'z':
|
||||
(*info->fprintf_func) (stream, "%%icc");
|
||||
break;
|
||||
|
||||
case 'Z':
|
||||
(*info->fprintf_func) (stream, "%%xcc");
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
(*info->fprintf_func) (stream, "%%ccr");
|
||||
break;
|
||||
|
||||
case 's':
|
||||
(*info->fprintf_func) (stream, "%%fprs");
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
(*info->fprintf_func) (stream, "%%asi");
|
||||
break;
|
||||
|
||||
case 'W':
|
||||
(*info->fprintf_func) (stream, "%%tick");
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
(*info->fprintf_func) (stream, "%%pc");
|
||||
break;
|
||||
|
||||
case '?':
|
||||
if (X_RS1 (insn) == 31)
|
||||
(*info->fprintf_func) (stream, "%%ver");
|
||||
else if ((unsigned) X_RS1 (insn) < 16)
|
||||
(*info->fprintf_func) (stream, "%%%s",
|
||||
v9_priv_reg_names[X_RS1 (insn)]);
|
||||
else
|
||||
(*info->fprintf_func) (stream, "%%reserved");
|
||||
break;
|
||||
|
||||
case '!':
|
||||
if ((unsigned) X_RD (insn) < 15)
|
||||
(*info->fprintf_func) (stream, "%%%s",
|
||||
v9_priv_reg_names[X_RD (insn)]);
|
||||
else
|
||||
(*info->fprintf_func) (stream, "%%reserved");
|
||||
break;
|
||||
|
||||
case '*':
|
||||
{
|
||||
char *name = sparc_decode_prefetch (X_RD (insn));
|
||||
|
||||
if (name)
|
||||
(*info->fprintf_func) (stream, "%s", name);
|
||||
else
|
||||
(*info->fprintf_func) (stream, "%d", X_RD (insn));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'M':
|
||||
(*info->fprintf_func) (stream, "%%asr%d", X_RS1 (insn));
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
(*info->fprintf_func) (stream, "%%asr%d", X_RD (insn));
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
info->target = memaddr + SEX (X_DISP30 (insn), 30) * 4;
|
||||
(*info->print_address_func) (info->target, info);
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
(*info->fprintf_func)
|
||||
(stream, "%#x", SEX (X_DISP22 (insn), 22));
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
info->target = memaddr + SEX (X_DISP22 (insn), 22) * 4;
|
||||
(*info->print_address_func) (info->target, info);
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
{
|
||||
char *name = sparc_decode_asi (X_ASI (insn));
|
||||
|
||||
if (name)
|
||||
(*info->fprintf_func) (stream, "%s", name);
|
||||
else
|
||||
(*info->fprintf_func) (stream, "(%d)", X_ASI (insn));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'C':
|
||||
(*info->fprintf_func) (stream, "%%csr");
|
||||
break;
|
||||
|
||||
case 'F':
|
||||
(*info->fprintf_func) (stream, "%%fsr");
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
(*info->fprintf_func) (stream, "%%psr");
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
(*info->fprintf_func) (stream, "%%fq");
|
||||
break;
|
||||
|
||||
case 'Q':
|
||||
(*info->fprintf_func) (stream, "%%cq");
|
||||
break;
|
||||
|
||||
case 't':
|
||||
(*info->fprintf_func) (stream, "%%tbr");
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
(*info->fprintf_func) (stream, "%%wim");
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
(*info->fprintf_func) (stream, "%d",
|
||||
((X_LDST_I (insn) << 8)
|
||||
+ X_ASI (insn)));
|
||||
break;
|
||||
|
||||
case 'y':
|
||||
(*info->fprintf_func) (stream, "%%y");
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
case 'U':
|
||||
{
|
||||
int val = *s == 'U' ? X_RS1 (insn) : X_RD (insn);
|
||||
char *name = sparc_decode_sparclet_cpreg (val);
|
||||
|
||||
if (name)
|
||||
(*info->fprintf_func) (stream, "%s", name);
|
||||
else
|
||||
(*info->fprintf_func) (stream, "%%cpreg(%d)", val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we are adding or or'ing something to rs1, then
|
||||
check to see whether the previous instruction was
|
||||
a sethi to the same register as in the sethi.
|
||||
If so, attempt to print the result of the add or
|
||||
or (in this context add and or do the same thing)
|
||||
and its symbolic value. */
|
||||
if (imm_added_to_rs1)
|
||||
{
|
||||
unsigned long prev_insn;
|
||||
int errcode;
|
||||
|
||||
errcode =
|
||||
(*info->read_memory_func)
|
||||
(memaddr - 4, buffer, sizeof (buffer), info);
|
||||
prev_insn = bfd_getb32 (buffer);
|
||||
|
||||
if (errcode == 0)
|
||||
{
|
||||
/* If it is a delayed branch, we need to look at the
|
||||
instruction before the delayed branch. This handles
|
||||
sequences such as
|
||||
|
||||
sethi %o1, %hi(_foo), %o1
|
||||
call _printf
|
||||
or %o1, %lo(_foo), %o1
|
||||
*/
|
||||
|
||||
if (is_delayed_branch (prev_insn))
|
||||
{
|
||||
errcode = (*info->read_memory_func)
|
||||
(memaddr - 8, buffer, sizeof (buffer), info);
|
||||
prev_insn = bfd_getb32 (buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/* If there was a problem reading memory, then assume
|
||||
the previous instruction was not sethi. */
|
||||
if (errcode == 0)
|
||||
{
|
||||
/* Is it sethi to the same register? */
|
||||
if ((prev_insn & 0xc1c00000) == 0x01000000
|
||||
&& X_RD (prev_insn) == X_RS1 (insn))
|
||||
{
|
||||
(*info->fprintf_func) (stream, "\t! ");
|
||||
info->target =
|
||||
(0xFFFFFFFF & (int) X_IMM22 (prev_insn) << 10)
|
||||
| SEX (X_IMM13 (insn), 13);
|
||||
(*info->print_address_func) (info->target, info);
|
||||
info->insn_type = dis_dref;
|
||||
info->data_size = 4; /* FIXME!!! */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (opcode->flags & (F_UNBR|F_CONDBR|F_JSR))
|
||||
{
|
||||
/* FIXME -- check is_annulled flag */
|
||||
if (opcode->flags & F_UNBR)
|
||||
info->insn_type = dis_branch;
|
||||
if (opcode->flags & F_CONDBR)
|
||||
info->insn_type = dis_condbranch;
|
||||
if (opcode->flags & F_JSR)
|
||||
info->insn_type = dis_jsr;
|
||||
if (opcode->flags & F_DELAYED)
|
||||
info->branch_delay_insns = 1;
|
||||
}
|
||||
|
||||
return sizeof (buffer);
|
||||
}
|
||||
}
|
||||
|
||||
info->insn_type = dis_noninsn; /* Mark as non-valid instruction */
|
||||
(*info->fprintf_func) (stream, "unknown");
|
||||
return sizeof (buffer);
|
||||
}
|
||||
|
||||
/* Compare opcodes A and B. */
|
||||
|
||||
static int
|
||||
compare_opcodes (a, b)
|
||||
char *a, *b;
|
||||
{
|
||||
struct sparc_opcode *op0 = (struct sparc_opcode *) a;
|
||||
struct sparc_opcode *op1 = (struct sparc_opcode *) b;
|
||||
unsigned long int match0 = op0->match, match1 = op1->match;
|
||||
unsigned long int lose0 = op0->lose, lose1 = op1->lose;
|
||||
register unsigned int i;
|
||||
|
||||
/* If a bit is set in both match and lose, there is something
|
||||
wrong with the opcode table. */
|
||||
if (match0 & lose0)
|
||||
{
|
||||
fprintf (stderr, "Internal error: bad sparc-opcode.h: \"%s\", %#.8lx, %#.8lx\n",
|
||||
op0->name, match0, lose0);
|
||||
op0->lose &= ~op0->match;
|
||||
lose0 = op0->lose;
|
||||
}
|
||||
|
||||
if (match1 & lose1)
|
||||
{
|
||||
fprintf (stderr, "Internal error: bad sparc-opcode.h: \"%s\", %#.8lx, %#.8lx\n",
|
||||
op1->name, match1, lose1);
|
||||
op1->lose &= ~op1->match;
|
||||
lose1 = op1->lose;
|
||||
}
|
||||
|
||||
/* Because the bits that are variable in one opcode are constant in
|
||||
another, it is important to order the opcodes in the right order. */
|
||||
for (i = 0; i < 32; ++i)
|
||||
{
|
||||
unsigned long int x = 1 << i;
|
||||
int x0 = (match0 & x) != 0;
|
||||
int x1 = (match1 & x) != 0;
|
||||
|
||||
if (x0 != x1)
|
||||
return x1 - x0;
|
||||
}
|
||||
|
||||
for (i = 0; i < 32; ++i)
|
||||
{
|
||||
unsigned long int x = 1 << i;
|
||||
int x0 = (lose0 & x) != 0;
|
||||
int x1 = (lose1 & x) != 0;
|
||||
|
||||
if (x0 != x1)
|
||||
return x1 - x0;
|
||||
}
|
||||
|
||||
/* Put non-sparc64 insns ahead of sparc64 ones. */
|
||||
if (V9_ONLY_P (op0) != V9_ONLY_P (op1))
|
||||
return V9_ONLY_P (op0) - V9_ONLY_P (op1);
|
||||
|
||||
/* They are functionally equal. So as long as the opcode table is
|
||||
valid, we can put whichever one first we want, on aesthetic grounds. */
|
||||
|
||||
/* Our first aesthetic ground is that aliases defer to real insns. */
|
||||
{
|
||||
int alias_diff = (op0->flags & F_ALIAS) - (op1->flags & F_ALIAS);
|
||||
if (alias_diff != 0)
|
||||
/* Put the one that isn't an alias first. */
|
||||
return alias_diff;
|
||||
}
|
||||
|
||||
/* Except for aliases, two "identical" instructions had
|
||||
better have the same opcode. This is a sanity check on the table. */
|
||||
i = strcmp (op0->name, op1->name);
|
||||
if (i)
|
||||
if (op0->flags & F_ALIAS) /* If they're both aliases, be arbitrary. */
|
||||
return i;
|
||||
else
|
||||
fprintf (stderr,
|
||||
"Internal error: bad sparc-opcode.h: \"%s\" == \"%s\"\n",
|
||||
op0->name, op1->name);
|
||||
|
||||
/* Fewer arguments are preferred. */
|
||||
{
|
||||
int length_diff = strlen (op0->args) - strlen (op1->args);
|
||||
if (length_diff != 0)
|
||||
/* Put the one with fewer arguments first. */
|
||||
return length_diff;
|
||||
}
|
||||
|
||||
/* Put 1+i before i+1. */
|
||||
{
|
||||
char *p0 = (char *) strchr(op0->args, '+');
|
||||
char *p1 = (char *) strchr(op1->args, '+');
|
||||
|
||||
if (p0 && p1)
|
||||
{
|
||||
/* There is a plus in both operands. Note that a plus
|
||||
sign cannot be the first character in args,
|
||||
so the following [-1]'s are valid. */
|
||||
if (p0[-1] == 'i' && p1[1] == 'i')
|
||||
/* op0 is i+1 and op1 is 1+i, so op1 goes first. */
|
||||
return 1;
|
||||
if (p0[1] == 'i' && p1[-1] == 'i')
|
||||
/* op0 is 1+i and op1 is i+1, so op0 goes first. */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Put 1,i before i,1. */
|
||||
{
|
||||
int i0 = strncmp (op0->args, "i,1", 3) == 0;
|
||||
int i1 = strncmp (op1->args, "i,1", 3) == 0;
|
||||
|
||||
if (i0 ^ i1)
|
||||
return i0 - i1;
|
||||
}
|
||||
|
||||
/* They are, as far as we can tell, identical.
|
||||
Since qsort may have rearranged the table partially, there is
|
||||
no way to tell which one was first in the opcode table as
|
||||
written, so just say there are equal. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Build a hash table from the opcode table. */
|
||||
|
||||
static void
|
||||
build_hash_table (table, hash_table, num_opcodes)
|
||||
struct sparc_opcode *table;
|
||||
struct opcode_hash **hash_table;
|
||||
int num_opcodes;
|
||||
{
|
||||
register int i;
|
||||
int hash_count[HASH_SIZE];
|
||||
static struct opcode_hash *hash_buf = NULL;
|
||||
|
||||
/* Start at the end of the table and work backwards so that each
|
||||
chain is sorted. */
|
||||
|
||||
memset (hash_table, 0, HASH_SIZE * sizeof (hash_table[0]));
|
||||
memset (hash_count, 0, HASH_SIZE * sizeof (hash_count[0]));
|
||||
if (hash_buf != NULL)
|
||||
free (hash_buf);
|
||||
hash_buf = (struct opcode_hash *) xmalloc (sizeof (struct opcode_hash) * num_opcodes);
|
||||
for (i = num_opcodes - 1; i >= 0; --i)
|
||||
{
|
||||
register int hash = HASH_INSN (sparc_opcodes[i].match);
|
||||
register struct opcode_hash *h = &hash_buf[i];
|
||||
h->next = hash_table[hash];
|
||||
h->opcode = &sparc_opcodes[i];
|
||||
hash_table[hash] = h;
|
||||
++hash_count[hash];
|
||||
}
|
||||
|
||||
#if 0 /* for debugging */
|
||||
{
|
||||
int min_count = num_opcodes, max_count = 0;
|
||||
int total;
|
||||
|
||||
for (i = 0; i < HASH_SIZE; ++i)
|
||||
{
|
||||
if (hash_count[i] < min_count)
|
||||
min_count = hash_count[i];
|
||||
if (hash_count[i] > max_count)
|
||||
max_count = hash_count[i];
|
||||
total += hash_count[i];
|
||||
}
|
||||
|
||||
printf ("Opcode hash table stats: min %d, max %d, ave %f\n",
|
||||
min_count, max_count, (double) total / HASH_SIZE);
|
||||
}
|
||||
#endif
|
||||
}
|
1757
opcode/sparc-opc.c
Normal file
1757
opcode/sparc-opc.c
Normal file
File diff suppressed because it is too large
Load diff
220
opcode/sparc.h
Normal file
220
opcode/sparc.h
Normal file
|
@ -0,0 +1,220 @@
|
|||
/* Definitions for opcode table for the sparc.
|
||||
Copyright (C) 1989, 1991, 1992, 1995, 1996 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GAS, the GNU Assembler, GDB, the GNU debugger, and
|
||||
the GNU Binutils.
|
||||
|
||||
GAS/GDB 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.
|
||||
|
||||
GAS/GDB 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 GAS or GDB; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* The SPARC opcode table (and other related data) is defined in
|
||||
the opcodes library in sparc-opc.c. If you change anything here, make
|
||||
sure you fix up that file, and vice versa. */
|
||||
|
||||
/* FIXME-someday: perhaps the ,a's and such should be embedded in the
|
||||
instruction's name rather than the args. This would make gas faster, pinsn
|
||||
slower, but would mess up some macros a bit. xoxorich. */
|
||||
|
||||
/* List of instruction sets variations.
|
||||
These values are such that each element is either a superset of a
|
||||
preceding each one or they conflict in which case SPARC_OPCODE_CONFLICT_P
|
||||
returns non-zero.
|
||||
The values are indices into `sparc_opcode_archs' defined in sparc-opc.c.
|
||||
Don't change this without updating sparc-opc.c. */
|
||||
|
||||
enum sparc_opcode_arch_val {
|
||||
SPARC_OPCODE_ARCH_V6 = 0,
|
||||
SPARC_OPCODE_ARCH_V7,
|
||||
SPARC_OPCODE_ARCH_V8,
|
||||
SPARC_OPCODE_ARCH_SPARCLET,
|
||||
SPARC_OPCODE_ARCH_SPARCLITE,
|
||||
/* v9 variants must appear last */
|
||||
SPARC_OPCODE_ARCH_V9,
|
||||
SPARC_OPCODE_ARCH_V9A, /* v9 with ultrasparc additions */
|
||||
SPARC_OPCODE_ARCH_BAD /* error return from sparc_opcode_lookup_arch */
|
||||
};
|
||||
|
||||
/* The highest architecture in the table. */
|
||||
#define SPARC_OPCODE_ARCH_MAX (SPARC_OPCODE_ARCH_BAD - 1)
|
||||
|
||||
/* Table of cpu variants. */
|
||||
|
||||
struct sparc_opcode_arch {
|
||||
const char *name;
|
||||
/* Mask of sparc_opcode_arch_val's supported.
|
||||
EG: For v7 this would be ((1 << v6) | (1 << v7)). */
|
||||
/* These are short's because sparc_opcode.architecture is. */
|
||||
short supported;
|
||||
};
|
||||
|
||||
extern const struct sparc_opcode_arch sparc_opcode_archs[];
|
||||
|
||||
/* Given architecture name, look up it's sparc_opcode_arch_val value. */
|
||||
extern enum sparc_opcode_arch_val sparc_opcode_lookup_arch ();
|
||||
|
||||
/* Return the bitmask of supported architectures for ARCH. */
|
||||
#define SPARC_OPCODE_SUPPORTED(ARCH) (sparc_opcode_archs[ARCH].supported)
|
||||
|
||||
/* Non-zero if ARCH1 conflicts with ARCH2.
|
||||
IE: ARCH1 as a supported bit set that ARCH2 doesn't, and vice versa. */
|
||||
#define SPARC_OPCODE_CONFLICT_P(ARCH1, ARCH2) \
|
||||
(((SPARC_OPCODE_SUPPORTED (ARCH1) & SPARC_OPCODE_SUPPORTED (ARCH2)) \
|
||||
!= SPARC_OPCODE_SUPPORTED (ARCH1)) \
|
||||
&& ((SPARC_OPCODE_SUPPORTED (ARCH1) & SPARC_OPCODE_SUPPORTED (ARCH2)) \
|
||||
!= SPARC_OPCODE_SUPPORTED (ARCH2)))
|
||||
|
||||
/* Structure of an opcode table entry. */
|
||||
|
||||
struct sparc_opcode {
|
||||
const char *name;
|
||||
unsigned long match; /* Bits that must be set. */
|
||||
unsigned long lose; /* Bits that must not be set. */
|
||||
const char *args;
|
||||
/* This was called "delayed" in versions before the flags. */
|
||||
char flags;
|
||||
short architecture; /* Bitmask of sparc_opcode_arch_val's. */
|
||||
};
|
||||
|
||||
#define F_DELAYED 1 /* Delayed branch */
|
||||
#define F_ALIAS 2 /* Alias for a "real" instruction */
|
||||
#define F_UNBR 4 /* Unconditional branch */
|
||||
#define F_CONDBR 8 /* Conditional branch */
|
||||
#define F_JSR 16 /* Subroutine call */
|
||||
/* FIXME: Add F_ANACHRONISTIC flag for v9. */
|
||||
|
||||
/*
|
||||
|
||||
All sparc opcodes are 32 bits, except for the `set' instruction (really a
|
||||
macro), which is 64 bits. It is handled as a special case.
|
||||
|
||||
The match component is a mask saying which bits must match a particular
|
||||
opcode in order for an instruction to be an instance of that opcode.
|
||||
|
||||
The args component is a string containing one character for each operand of the
|
||||
instruction.
|
||||
|
||||
Kinds of operands:
|
||||
# Number used by optimizer. It is ignored.
|
||||
1 rs1 register.
|
||||
2 rs2 register.
|
||||
d rd register.
|
||||
e frs1 floating point register.
|
||||
v frs1 floating point register (double/even).
|
||||
V frs1 floating point register (quad/multiple of 4).
|
||||
f frs2 floating point register.
|
||||
B frs2 floating point register (double/even).
|
||||
R frs2 floating point register (quad/multiple of 4).
|
||||
g frsd floating point register.
|
||||
H frsd floating point register (double/even).
|
||||
J frsd floating point register (quad/multiple of 4).
|
||||
b crs1 coprocessor register
|
||||
c crs2 coprocessor register
|
||||
D crsd coprocessor register
|
||||
m alternate space register (asr) in rd
|
||||
M alternate space register (asr) in rs1
|
||||
h 22 high bits.
|
||||
K MEMBAR mask (7 bits). (v9)
|
||||
j 10 bit Immediate. (v9)
|
||||
I 11 bit Immediate. (v9)
|
||||
i 13 bit Immediate.
|
||||
n 22 bit immediate.
|
||||
k 2+14 bit PC relative immediate. (v9)
|
||||
G 19 bit PC relative immediate. (v9)
|
||||
l 22 bit PC relative immediate.
|
||||
L 30 bit PC relative immediate.
|
||||
a Annul. The annul bit is set.
|
||||
A Alternate address space. Stored as 8 bits.
|
||||
C Coprocessor state register.
|
||||
F floating point state register.
|
||||
p Processor state register.
|
||||
N Branch predict clear ",pn" (v9)
|
||||
T Branch predict set ",pt" (v9)
|
||||
z %icc. (v9)
|
||||
Z %xcc. (v9)
|
||||
q Floating point queue.
|
||||
r Single register that is both rs1 and rd.
|
||||
O Single register that is both rs2 and rd.
|
||||
Q Coprocessor queue.
|
||||
S Special case.
|
||||
t Trap base register.
|
||||
w Window invalid mask register.
|
||||
y Y register.
|
||||
u sparclet coprocessor registers in rd position
|
||||
U sparclet coprocessor registers in rs1 position
|
||||
E %ccr. (v9)
|
||||
s %fprs. (v9)
|
||||
P %pc. (v9)
|
||||
W %tick. (v9)
|
||||
o %asi. (v9)
|
||||
6 %fcc0. (v9)
|
||||
7 %fcc1. (v9)
|
||||
8 %fcc2. (v9)
|
||||
9 %fcc3. (v9)
|
||||
! Privileged Register in rd (v9)
|
||||
? Privileged Register in rs1 (v9)
|
||||
* Prefetch function constant. (v9)
|
||||
x OPF field (v9 impdep).
|
||||
|
||||
The following chars are unused: (note: ,[] are used as punctuation)
|
||||
[XY3450]
|
||||
|
||||
*/
|
||||
|
||||
#define OP2(x) (((x)&0x7) << 22) /* op2 field of format2 insns */
|
||||
#define OP3(x) (((x)&0x3f) << 19) /* op3 field of format3 insns */
|
||||
#define OP(x) ((unsigned)((x)&0x3) << 30) /* op field of all insns */
|
||||
#define OPF(x) (((x)&0x1ff) << 5) /* opf field of float insns */
|
||||
#define OPF_LOW5(x) OPF((x)&0x1f) /* v9 */
|
||||
#define F3F(x, y, z) (OP(x) | OP3(y) | OPF(z)) /* format3 float insns */
|
||||
#define F3I(x) (((x)&0x1) << 13) /* immediate field of format 3 insns */
|
||||
#define F2(x, y) (OP(x) | OP2(y)) /* format 2 insns */
|
||||
#define F3(x, y, z) (OP(x) | OP3(y) | F3I(z)) /* format3 insns */
|
||||
#define F1(x) (OP(x))
|
||||
#define DISP30(x) ((x)&0x3fffffff)
|
||||
#define ASI(x) (((x)&0xff) << 5) /* asi field of format3 insns */
|
||||
#define RS2(x) ((x)&0x1f) /* rs2 field */
|
||||
#define SIMM13(x) ((x)&0x1fff) /* simm13 field */
|
||||
#define RD(x) (((x)&0x1f) << 25) /* destination register field */
|
||||
#define RS1(x) (((x)&0x1f) << 14) /* rs1 field */
|
||||
#define ASI_RS2(x) (SIMM13(x))
|
||||
#define MEMBAR(x) ((x)&0x7f)
|
||||
|
||||
#define ANNUL (1<<29)
|
||||
#define BPRED (1<<19) /* v9 */
|
||||
#define IMMED F3I(1)
|
||||
#define RD_G0 RD(~0)
|
||||
#define RS1_G0 RS1(~0)
|
||||
#define RS2_G0 RS2(~0)
|
||||
|
||||
extern struct sparc_opcode sparc_opcodes[];
|
||||
extern const int sparc_num_opcodes;
|
||||
|
||||
int sparc_encode_asi ();
|
||||
char *sparc_decode_asi ();
|
||||
int sparc_encode_membar ();
|
||||
char *sparc_decode_membar ();
|
||||
int sparc_encode_prefetch ();
|
||||
char *sparc_decode_prefetch ();
|
||||
int sparc_encode_sparclet_cpreg ();
|
||||
char *sparc_decode_sparclet_cpreg ();
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* fill-column: 131
|
||||
* comment-column: 0
|
||||
* End:
|
||||
*/
|
||||
|
||||
/* end of sparc.h */
|
10
opcode/sysdep.h
Normal file
10
opcode/sysdep.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#ifndef __SYSDEP_H_SEEN
|
||||
#define __SYSDEP_H_SEEN
|
||||
|
||||
#include "lightning.h"
|
||||
|
||||
#ifndef HAVE_MEMCPY
|
||||
#define memcpy(d, s, n) bcopy((s),(d),(n))
|
||||
#endif
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue