* This code is derived from software copyrighted by the Free Software
* Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
static char sccsid
[] = "@(#)m68k-pinsn.c 6.3 (Berkeley) 5/8/91";
/* Print m68k instructions for GDB, the GNU debugger.
Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
This file is part of GDB.
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 1, or (at your option)
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 GDB; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/* 68k instructions are never longer than this many bytes. */
/* Number of elements in the opcode table. */
#define NOPCODES (sizeof m68k_opcodes / sizeof m68k_opcodes[0])
extern char *reg_names
[];
char *fpcr_names
[] = { "", "fpiar", "fpsr", "fpiar/fpsr", "fpcr",
"fpiar/fpcr", "fpsr/fpcr", "fpiar-fpcr"};
static unsigned char *print_insn_arg ();
static unsigned char *print_indexed ();
static void print_base ();
#define NEXTBYTE(p) (p += 2, ((char *)p)[-1])
(p += 2, ((((char *)p)[-2]) << 8) + p[-1])
(p += 4, (((((p[-4] << 8) + p[-3]) << 8) + p[-2]) << 8) + p[-1])
(p += 4, *((float *)(p - 4)))
(p += 8, *((double *)(p - 8)))
(p += 12, 0.0) /* Need a function to convert from extended to double
(p += 12, 0.0) /* Need a function to convert from packed to double
precision. Actually, it's easier to print a
packed number than a double anyway, so maybe
there should be a special case to handle this... */
/* Print the m68k instruction at address MEMADDR in debugged memory,
on STREAM. Returns length of the instruction, in bytes. */
print_insn (memaddr
, stream
)
unsigned char buffer
[MAXLEN
];
register unsigned char *p
;
read_memory (memaddr
, buffer
, MAXLEN
);
for (i
= 0; i
< NOPCODES
; i
++)
register unsigned int opcode
= m68k_opcodes
[i
].opcode
;
register unsigned int match
= m68k_opcodes
[i
].match
;
if (((0xff & buffer
[0] & (match
>> 24)) == (0xff & (opcode
>> 24)))
&& ((0xff & buffer
[1] & (match
>> 16)) == (0xff & (opcode
>> 16)))
&& ((0xff & buffer
[2] & (match
>> 8)) == (0xff & (opcode
>> 8)))
&& ((0xff & buffer
[3] & match
) == (0xff & opcode
)))
/* Don't use for printout the variants of divul and divsl
that have the same register number in two places.
The more general variants will match instead. */
for (d
= m68k_opcodes
[i
].args
; *d
; d
+= 2)
/* Don't use for printout the variants of most floating
point coprocessor instructions which use the same
register number in two places, as above. */
for (d
= m68k_opcodes
[i
].args
; *d
; d
+= 2)
if (*d
== 0 && match
> bestmask
)
/* Handle undefined instructions. */
fprintf_filtered (stream
, "0%o", (buffer
[0] << 8) + buffer
[1]);
fprintf_filtered (stream
, "%s", m68k_opcodes
[best
].name
);
/* Point at first word of argument data,
and at descriptor for first argument. */
/* Why do this this way? -MelloN */
for (d
= m68k_opcodes
[best
].args
; *d
; d
+= 2)
if (d
[1] == 'l' && p
- buffer
< 6)
else if (p
- buffer
< 4 && d
[1] != 'C' && d
[1] != '8' )
if (d
[1] >= '1' && d
[1] <= '3' && p
- buffer
< 4)
if (d
[1] >= '4' && d
[1] <= '6' && p
- buffer
< 6)
if ((d
[0] == 'L' || d
[0] == 'l') && d
[1] == 'w' && p
- buffer
< 4)
d
= m68k_opcodes
[best
].args
;
fputs_filtered (" ", stream
);
p
= print_insn_arg (d
, buffer
, p
, memaddr
+ p
- buffer
, stream
);
if (*d
&& *(d
- 2) != 'I' && *d
!= 'k')
fputs_filtered (",", stream
);
print_insn_arg (d
, buffer
, p
, addr
, stream
)
register unsigned char *p
;
CORE_ADDR addr
; /* PC for this arg to be relative to */
register int place
= d
[1];
register unsigned char *p1
;
fprintf_filtered (stream
, "ccr");
fprintf_filtered (stream
, "sr");
fprintf_filtered (stream
, "usp");
static struct { char *name
; int value
; } names
[]
= {{"sfc", 0x000}, {"dfc", 0x001}, {"cacr", 0x002},
{"usp", 0x800}, {"vbr", 0x801}, {"caar", 0x802},
{"msp", 0x803}, {"isp", 0x804}};
val
= fetch_arg (buffer
, place
, 12);
for (regno
= sizeof names
/ sizeof names
[0] - 1; regno
>= 0; regno
--)
if (names
[regno
].value
== val
)
fprintf_filtered (stream
, names
[regno
].name
);
fprintf_filtered (stream
, "%d", val
);
val
= fetch_arg (buffer
, place
, 3);
fprintf_filtered (stream
, "#%d", val
);
val
= fetch_arg (buffer
, place
, 8);
fprintf_filtered (stream
, "#%d", val
);
val
= fetch_arg (buffer
, place
, 4);
fprintf_filtered (stream
, "#%d", val
);
fprintf_filtered (stream
, "%s", reg_names
[fetch_arg (buffer
, place
, 3)]);
fprintf_filtered (stream
, "%s",
reg_names
[fetch_arg (buffer
, place
, 3) + 010]);
fprintf_filtered (stream
, "%s", reg_names
[fetch_arg (buffer
, place
, 4)]);
fprintf_filtered (stream
, "fp%d", fetch_arg (buffer
, place
, 3));
val
= fetch_arg (buffer
, place
, 6);
fprintf_filtered (stream
, "%s", reg_names
[val
& 7]);
fprintf_filtered (stream
, "%d", val
);
fprintf_filtered (stream
, "%s@+",
reg_names
[fetch_arg (buffer
, place
, 3) + 8]);
fprintf_filtered (stream
, "%s@-",
reg_names
[fetch_arg (buffer
, place
, 3) + 8]);
fprintf_filtered (stream
, "{%s}", reg_names
[fetch_arg (buffer
, place
, 3)]);
val
= fetch_arg (buffer
, place
, 7);
if ( val
> 63 ) /* This is a signed constant. */
fprintf_filtered (stream
, "{#%d}", val
);
error ("Invalid arg format in opcode table: \"%c%c\".",
p1
= buffer
+ (*d
== '#' ? 2 : 4);
val
= fetch_arg (buffer
, place
, 4);
val
= fetch_arg (buffer
, place
, 7);
val
= fetch_arg (buffer
, place
, 3);
val
= fetch_arg (buffer
, place
, 8);
error ("Invalid arg format in opcode table: \"%c%c\".",
fprintf_filtered (stream
, "#%d", val
);
val
= ((char *)buffer
)[1];
if (buffer
[1] & 0x40) /* If bit six is one, long offset */
error ("Invalid arg format in opcode table: \"%c%c\".",
print_address (addr
+ val
, stream
);
fprintf_filtered (stream
, "%s@(%d)",
reg_names
[fetch_arg (buffer
, place
, 3)], val
);
fprintf_filtered (stream
, "%s",
fpcr_names
[fetch_arg (buffer
, place
, 3)]);
val
= fetch_arg (buffer
, 'd', 3); /* Get coprocessor ID... */
if (val
!= 1) /* Unusual coprocessor ID? */
fprintf_filtered (stream
, "(cpid=%d) ", val
);
p
+= 2; /* Skip coprocessor extended operands */
val
= fetch_arg (buffer
, 'x', 6);
val
= ((val
& 7) << 3) + ((val
>> 3) & 7);
val
= fetch_arg (buffer
, 's', 6);
/* Get register number assuming address register. */
regname
= reg_names
[regno
];
fprintf_filtered (stream
, "%s", reg_names
[val
]);
fprintf_filtered (stream
, "%s", regname
);
fprintf_filtered (stream
, "%s@", regname
);
fprintf_filtered (stream
, "%s@+", regname
);
fprintf_filtered (stream
, "%s@-", regname
);
fprintf_filtered (stream
, "%s@(%d)", regname
, val
);
p
= print_indexed (regno
, p
, addr
, stream
);
fprintf_filtered (stream
, "@#");
print_address (val
, stream
);
fprintf_filtered (stream
, "@#");
print_address (val
, stream
);
print_address (addr
+ val
, stream
);
p
= print_indexed (-1, p
, addr
, stream
);
flt_p
= 1; /* Assume it's a float... */
error ("Invalid arg format in opcode table: \"%c%c\".",
if ( flt_p
) /* Print a float? */
fprintf_filtered (stream
, "#%g", flval
);
fprintf_filtered (stream
, "#%d", val
);
fprintf_filtered (stream
, "<invalid address mode 0%o>", val
);
/* Move the pointer ahead if this point is farther ahead
fputs_filtered ("#0", stream
);
for (regno
= 0; regno
< 16; ++regno
)
if (val
& (0x8000 >> regno
))
for (regno
= 0; regno
< 16; ++regno
)
fputs_filtered ("/", stream
);
fprintf_filtered (stream
, "%s", reg_names
[regno
]);
while (val
& (1 << (regno
+ 1)))
fprintf_filtered (stream
, "-%s", reg_names
[regno
]);
val
= fetch_arg (buffer
, place
, 8);
fputs_filtered ("#0", stream
);
for (regno
= 0; regno
< 8; ++regno
)
if (val
& (0x80 >> regno
))
for (regno
= 0; regno
< 8; ++regno
)
fputs_filtered ("/", stream
);
fprintf_filtered (stream
, "fp%d", regno
);
while (val
& (1 << (regno
+ 1)))
fprintf_filtered (stream
, "-fp%d", regno
);
error ("Invalid arg format in opcode table: \"%c\".", *d
);
return (unsigned char *) p
;
/* Fetch BITS bits from a position in the instruction specified by CODE.
CODE is a "place to put an argument", or 'x' for a destination
that is a general address (mode and register).
BUFFER contains the instruction. */
fetch_arg (buffer
, code
, bits
)
case 'd': /* Destination, for register or quick. */
val
= (buffer
[0] << 8) + buffer
[1];
case 'x': /* Destination, for general arg */
val
= (buffer
[0] << 8) + buffer
[1];
val
= (buffer
[2] << 8) + buffer
[3];
val
= (buffer
[2] << 8) + buffer
[3];
val
= (buffer
[2] << 8) + buffer
[3];
val
= (buffer
[4] << 8) + buffer
[5];
val
= (buffer
[4] << 8) + buffer
[5];
val
= (buffer
[4] << 8) + buffer
[5];
val
= (buffer
[2] << 8) + buffer
[3];
val
= (buffer
[2] << 8) + buffer
[3];
/* Print an indexed argument. The base register is BASEREG (-1 for pc).
P points to extension word, in buffer.
ADDR is the nominal core address of that extension word. */
print_indexed (basereg
, p
, addr
, stream
)
static char *scales
[] = {"", "*2", "*4", "*8"};
/* Generate the text for the index register.
Where this will be output is not yet determined. */
sprintf (buf
, "[%s.%c%s]",
reg_names
[(word
>> 12) & 0xf],
(word
& 0x800) ? 'l' : 'w',
scales
[(word
>> 9) & 3]);
/* Handle the 68000 style of indexing. */
((word
& 0x80) ? word
| 0xff00 : word
& 0xff)
+ ((basereg
== -1) ? addr
: 0),
fputs_filtered (buf
, stream
);
/* Handle the generalized kind. */
/* First, compute the displacement to add to the base register. */
base_disp
= NEXTWORD (p
);
base_disp
= NEXTLONG (p
);
/* Handle single-level case (not indirect) */
print_base (basereg
, base_disp
, stream
);
fputs_filtered (buf
, stream
);
/* Two level. Compute displacement to add after indirection. */
outer_disp
= NEXTWORD (p
);
outer_disp
= NEXTLONG (p
);
fprintf_filtered (stream
, "%d(", outer_disp
);
print_base (basereg
, base_disp
, stream
);
/* If postindexed, print the closeparen before the index. */
fprintf_filtered (stream
, ")%s", buf
);
/* If preindexed, print the closeparen after the index. */
fprintf_filtered (stream
, "%s)", buf
);
/* Print a base register REGNO and displacement DISP, on STREAM.
REGNO = -1 for pc, -2 for none (suppressed). */
print_base (regno
, disp
, stream
)
fprintf_filtered (stream
, "%d", disp
);
fprintf_filtered (stream
, "0x%x", disp
);
fprintf_filtered (stream
, "%d(%s)", disp
, reg_names
[regno
]);
/* Nonzero if the host system has a 68881 (or compatible)
floating-point unit. This does *not* indicate whether the target
system has a co-processor, just whether the host system does.
There might be a difference in the case of remote debugging. */
/* This is not part of insn printing, but it is machine-specific,
so this is a convenient place to put it.
Convert a 68881 extended float to a double.
FROM is the address of the extended float.
Store the double in *TO. */
convert_from_68881 (from
, to
)
asm ("mov.l 8(%a6),%a0");
asm ("mov.l 12(%a6),%a1");
asm ("fmove.x (%a0),%fp0");
asm ("fmove.d %fp0,(%a1)");
/* Hand-assemble those insns since some assemblers lose
and some have different syntax. */
asm (".long 0xf2104800");
asm (".long 0xf2117400");
#endif /* not HPUX_ASM */
/* The converse: convert the double *FROM to an extended float
and store where TO points. */
convert_to_68881 (from
, to
)
asm ("mov.l 8(%a6),%a0");
asm ("mov.l 12(%a6),%a1");
asm ("fmove.d (%a0),%fp0");
asm ("fmove.x %fp0,(%a1)");
/* Hand-assemble those insns since some assemblers lose. */
asm (".long 0xf2105400");
asm (".long 0xf2116800");
#endif /* not HPUX_ASM */
static jmp_buf fpu_check
;
/* Want to figure out if we've got a coprocessor. The idea is to catch the
signal that gets delivered if no coprocessor is around (SIGEMT) then
execute a coprocessor instruction and see what happens. have_fpu is set
to zero if the EMT signal arrives. Else it is left at 1. */
/* If this turns out not to be portable to all 68k machines, we'll
have to move it to the dep files. */
emthandler
= (void (*) ()) signal (SIGEMT
, sigemt
);
asm (" long 0xf2000600"); /* fmovel fp0, d0 */
asm(".long 0xf2000600"); /* fmovel fp0, d0 */
signal(SIGEMT
, emthandler
);