#include "defs.h"
#include "doublest.h"
#include "floatformat.h"
#include "frame.h"
#include "gdbcore.h"
#include "inferior.h"
#include "language.h"
#include "regcache.h"
#include "value.h"
#include "gdb_assert.h"
#include "gdb_string.h"
#include "i386-tdep.h"
#include "i387-tdep.h"
static void
print_i387_value (const gdb_byte *raw, struct ui_file *file)
{
DOUBLEST value;
value = extract_typed_floating (raw, builtin_type_i387_ext);
#ifdef PRINTF_HAS_LONG_DOUBLE
fprintf_filtered (file, " %-+27.19Lg", (long double) value);
#else
fprintf_filtered (file, " %-+27.19g", (double) value);
#endif
}
static void
print_i387_ext (const gdb_byte *raw, struct ui_file *file)
{
int sign;
int integer;
unsigned int exponent;
unsigned long fraction[2];
sign = raw[9] & 0x80;
integer = raw[7] & 0x80;
exponent = (((raw[9] & 0x7f) << 8) | raw[8]);
fraction[0] = ((raw[3] << 24) | (raw[2] << 16) | (raw[1] << 8) | raw[0]);
fraction[1] = (((raw[7] & 0x7f) << 24) | (raw[6] << 16)
| (raw[5] << 8) | raw[4]);
if (exponent == 0x7fff && integer)
{
if (fraction[0] == 0x00000000 && fraction[1] == 0x00000000)
fprintf_filtered (file, " %cInf", (sign ? '-' : '+'));
else if (sign && fraction[0] == 0x00000000 && fraction[1] == 0x40000000)
fputs_unfiltered (" Real Indefinite (QNaN)", file);
else if (fraction[1] & 0x40000000)
fputs_filtered (" QNaN", file);
else
fputs_filtered (" SNaN", file);
}
else if (exponent < 0x7fff && exponent > 0x0000 && integer)
print_i387_value (raw, file);
else if (exponent == 0x0000)
{
print_i387_value (raw, file);
if (integer)
fputs_filtered (" Pseudo-denormal", file);
else if (fraction[0] || fraction[1])
fputs_filtered (" Denormal", file);
}
else
fputs_filtered (" Unsupported", file);
}
static void
print_i387_status_word (unsigned int status, struct ui_file *file)
{
fprintf_filtered (file, "Status Word: %s",
hex_string_custom (status, 4));
fputs_filtered (" ", file);
fprintf_filtered (file, " %s", (status & 0x0001) ? "IE" : " ");
fprintf_filtered (file, " %s", (status & 0x0002) ? "DE" : " ");
fprintf_filtered (file, " %s", (status & 0x0004) ? "ZE" : " ");
fprintf_filtered (file, " %s", (status & 0x0008) ? "OE" : " ");
fprintf_filtered (file, " %s", (status & 0x0010) ? "UE" : " ");
fprintf_filtered (file, " %s", (status & 0x0020) ? "PE" : " ");
fputs_filtered (" ", file);
fprintf_filtered (file, " %s", (status & 0x0080) ? "ES" : " ");
fputs_filtered (" ", file);
fprintf_filtered (file, " %s", (status & 0x0040) ? "SF" : " ");
fputs_filtered (" ", file);
fprintf_filtered (file, " %s", (status & 0x0100) ? "C0" : " ");
fprintf_filtered (file, " %s", (status & 0x0200) ? "C1" : " ");
fprintf_filtered (file, " %s", (status & 0x0400) ? "C2" : " ");
fprintf_filtered (file, " %s", (status & 0x4000) ? "C3" : " ");
fputs_filtered ("\n", file);
fprintf_filtered (file,
" TOP: %d\n", ((status >> 11) & 7));
}
static void
print_i387_control_word (unsigned int control, struct ui_file *file)
{
fprintf_filtered (file, "Control Word: %s",
hex_string_custom (control, 4));
fputs_filtered (" ", file);
fprintf_filtered (file, " %s", (control & 0x0001) ? "IM" : " ");
fprintf_filtered (file, " %s", (control & 0x0002) ? "DM" : " ");
fprintf_filtered (file, " %s", (control & 0x0004) ? "ZM" : " ");
fprintf_filtered (file, " %s", (control & 0x0008) ? "OM" : " ");
fprintf_filtered (file, " %s", (control & 0x0010) ? "UM" : " ");
fprintf_filtered (file, " %s", (control & 0x0020) ? "PM" : " ");
fputs_filtered ("\n", file);
fputs_filtered (" PC: ", file);
switch ((control >> 8) & 3)
{
case 0:
fputs_filtered ("Single Precision (24-bits)\n", file);
break;
case 1:
fputs_filtered ("Reserved\n", file);
break;
case 2:
fputs_filtered ("Double Precision (53-bits)\n", file);
break;
case 3:
fputs_filtered ("Extended Precision (64-bits)\n", file);
break;
}
fputs_filtered (" RC: ", file);
switch ((control >> 10) & 3)
{
case 0:
fputs_filtered ("Round to nearest\n", file);
break;
case 1:
fputs_filtered ("Round down\n", file);
break;
case 2:
fputs_filtered ("Round up\n", file);
break;
case 3:
fputs_filtered ("Round toward zero\n", file);
break;
}
}
void
i387_print_float_info (struct gdbarch *gdbarch, struct ui_file *file,
struct frame_info *frame, const char *args)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (get_frame_arch (frame));
ULONGEST fctrl;
ULONGEST fstat;
ULONGEST ftag;
ULONGEST fiseg;
ULONGEST fioff;
ULONGEST foseg;
ULONGEST fooff;
ULONGEST fop;
int fpreg;
int top;
gdb_assert (gdbarch == get_frame_arch (frame));
#define I387_ST0_REGNUM tdep->st0_regnum
fctrl = get_frame_register_unsigned (frame, I387_FCTRL_REGNUM);
fstat = get_frame_register_unsigned (frame, I387_FSTAT_REGNUM);
ftag = get_frame_register_unsigned (frame, I387_FTAG_REGNUM);
fiseg = get_frame_register_unsigned (frame, I387_FISEG_REGNUM);
fioff = get_frame_register_unsigned (frame, I387_FIOFF_REGNUM);
foseg = get_frame_register_unsigned (frame, I387_FOSEG_REGNUM);
fooff = get_frame_register_unsigned (frame, I387_FOOFF_REGNUM);
fop = get_frame_register_unsigned (frame, I387_FOP_REGNUM);
top = ((fstat >> 11) & 7);
for (fpreg = 7; fpreg >= 0; fpreg--)
{
gdb_byte raw[I386_MAX_REGISTER_SIZE];
int tag = (ftag >> (fpreg * 2)) & 3;
int i;
fprintf_filtered (file, "%sR%d: ", fpreg == top ? "=>" : " ", fpreg);
switch (tag)
{
case 0:
fputs_filtered ("Valid ", file);
break;
case 1:
fputs_filtered ("Zero ", file);
break;
case 2:
fputs_filtered ("Special ", file);
break;
case 3:
fputs_filtered ("Empty ", file);
break;
}
get_frame_register (frame, (fpreg + 8 - top) % 8 + I387_ST0_REGNUM, raw);
fputs_filtered ("0x", file);
for (i = 9; i >= 0; i--)
fprintf_filtered (file, "%02x", raw[i]);
if (tag != 3)
print_i387_ext (raw, file);
fputs_filtered ("\n", file);
}
fputs_filtered ("\n", file);
print_i387_status_word (fstat, file);
print_i387_control_word (fctrl, file);
fprintf_filtered (file, "Tag Word: %s\n",
hex_string_custom (ftag, 4));
fprintf_filtered (file, "Instruction Pointer: %s:",
hex_string_custom (fiseg, 2));
fprintf_filtered (file, "%s\n", hex_string_custom (fioff, 8));
fprintf_filtered (file, "Operand Pointer: %s:",
hex_string_custom (foseg, 2));
fprintf_filtered (file, "%s\n", hex_string_custom (fooff, 8));
fprintf_filtered (file, "Opcode: %s\n",
hex_string_custom (fop ? (fop | 0xd800) : 0, 4));
#undef I387_ST0_REGNUM
}
void
i387_register_to_value (struct frame_info *frame, int regnum,
struct type *type, gdb_byte *to)
{
gdb_byte from[I386_MAX_REGISTER_SIZE];
gdb_assert (i386_fp_regnum_p (regnum));
if (TYPE_CODE (type) != TYPE_CODE_FLT)
{
warning (_("Cannot convert floating-point register value "
"to non-floating-point type."));
return;
}
get_frame_register (frame, regnum, from);
convert_typed_floating (from, builtin_type_i387_ext, to, type);
}
void
i387_value_to_register (struct frame_info *frame, int regnum,
struct type *type, const gdb_byte *from)
{
gdb_byte to[I386_MAX_REGISTER_SIZE];
gdb_assert (i386_fp_regnum_p (regnum));
if (TYPE_CODE (type) != TYPE_CODE_FLT)
{
warning (_("Cannot convert non-floating-point type "
"to floating-point register value."));
return;
}
convert_typed_floating (from, type, to, builtin_type_i387_ext);
put_frame_register (frame, regnum, to);
}
static int fsave_offset[] =
{
28 + 0 * 10,
28 + 1 * 10,
28 + 2 * 10,
28 + 3 * 10,
28 + 4 * 10,
28 + 5 * 10,
28 + 6 * 10,
28 + 7 * 10,
0,
4,
8,
16,
12,
24,
20,
18
};
#define FSAVE_ADDR(fsave, regnum) \
(fsave + fsave_offset[regnum - I387_ST0_REGNUM])
void
i387_supply_fsave (struct regcache *regcache, int regnum, const void *fsave)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (get_regcache_arch (regcache));
const gdb_byte *regs = fsave;
int i;
gdb_assert (tdep->st0_regnum >= I386_ST0_REGNUM);
#define I387_ST0_REGNUM tdep->st0_regnum
#define I387_NUM_XMM_REGS tdep->num_xmm_regs
for (i = I387_ST0_REGNUM; i < I387_XMM0_REGNUM; i++)
if (regnum == -1 || regnum == i)
{
if (fsave == NULL)
{
regcache_raw_supply (regcache, i, NULL);
continue;
}
if (i >= I387_FCTRL_REGNUM
&& i != I387_FIOFF_REGNUM && i != I387_FOOFF_REGNUM)
{
gdb_byte val[4];
memcpy (val, FSAVE_ADDR (regs, i), 2);
val[2] = val[3] = 0;
if (i == I387_FOP_REGNUM)
val[1] &= ((1 << 3) - 1);
regcache_raw_supply (regcache, i, val);
}
else
regcache_raw_supply (regcache, i, FSAVE_ADDR (regs, i));
}
for (i = I387_XMM0_REGNUM; i < I387_MXCSR_REGNUM; i++)
if (regnum == -1 || regnum == i)
regcache_raw_supply (regcache, i, NULL);
if (regnum == -1 || regnum == I387_MXCSR_REGNUM)
{
gdb_byte buf[4];
store_unsigned_integer (buf, 4, 0x1f80);
regcache_raw_supply (regcache, I387_MXCSR_REGNUM, buf);
}
#undef I387_ST0_REGNUM
#undef I387_NUM_XMM_REGS
}
void
i387_collect_fsave (const struct regcache *regcache, int regnum, void *fsave)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
gdb_byte *regs = fsave;
int i;
gdb_assert (tdep->st0_regnum >= I386_ST0_REGNUM);
#define I387_ST0_REGNUM tdep->st0_regnum
for (i = I387_ST0_REGNUM; i < I387_XMM0_REGNUM; i++)
if (regnum == -1 || regnum == i)
{
if (i >= I387_FCTRL_REGNUM
&& i != I387_FIOFF_REGNUM && i != I387_FOOFF_REGNUM)
{
gdb_byte buf[4];
regcache_raw_collect (regcache, i, buf);
if (i == I387_FOP_REGNUM)
{
buf[1] &= ((1 << 3) - 1);
buf[1] |= ((FSAVE_ADDR (regs, i))[1] & ~((1 << 3) - 1));
}
memcpy (FSAVE_ADDR (regs, i), buf, 2);
}
else
regcache_raw_collect (regcache, i, FSAVE_ADDR (regs, i));
}
#undef I387_ST0_REGNUM
}
void
i387_fill_fsave (void *fsave, int regnum)
{
i387_collect_fsave (current_regcache, regnum, fsave);
}
static int fxsave_offset[] =
{
32,
48,
64,
80,
96,
112,
128,
144,
0,
2,
4,
12,
8,
20,
16,
6,
160 + 0 * 16,
160 + 1 * 16,
160 + 2 * 16,
160 + 3 * 16,
160 + 4 * 16,
160 + 5 * 16,
160 + 6 * 16,
160 + 7 * 16,
160 + 8 * 16,
160 + 9 * 16,
160 + 10 * 16,
160 + 11 * 16,
160 + 12 * 16,
160 + 13 * 16,
160 + 14 * 16,
160 + 15 * 16,
};
#define FXSAVE_ADDR(fxsave, regnum) \
(fxsave + fxsave_offset[regnum - I387_ST0_REGNUM])
#define FXSAVE_MXCSR_ADDR(fxsave) (fxsave + 24)
static int i387_tag (const gdb_byte *raw);
void
i387_swap_fxsave (struct regcache *regcache, const uint8_t *fxsave)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (get_regcache_arch (regcache));
int i, j;
#define I387_ST0_REGNUM tdep->st0_regnum
#define I387_NUM_XMM_REGS tdep->num_xmm_regs
for (i = 0; i < tdep->num_xmm_regs; i++)
{
unsigned char *buf = (unsigned char *) FXSAVE_ADDR (fxsave, I387_XMM0_REGNUM + i);
for (j = 0; j < 8; j++) {
unsigned char c = buf[15 - j];
buf[15 - j] = buf[j];
buf[j] = c;
}
}
#undef I387_ST0_REGNUM
#undef I387_NUM_XMM_REGS
}
void
i387_supply_fxsave (struct regcache *regcache, int regnum, const void *fxsave)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (get_regcache_arch (regcache));
const gdb_byte *regs = fxsave;
int i;
gdb_assert (tdep->st0_regnum >= I386_ST0_REGNUM);
gdb_assert (tdep->num_xmm_regs > 0);
#define I387_ST0_REGNUM tdep->st0_regnum
#define I387_NUM_XMM_REGS tdep->num_xmm_regs
for (i = I387_ST0_REGNUM; i < I387_MXCSR_REGNUM; i++)
if (regnum == -1 || regnum == i)
{
if (regs == NULL)
{
regcache_raw_supply (regcache, i, NULL);
continue;
}
if (i >= I387_FCTRL_REGNUM && i < I387_XMM0_REGNUM
&& i != I387_FIOFF_REGNUM && i != I387_FOOFF_REGNUM)
{
gdb_byte val[4];
memcpy (val, FXSAVE_ADDR (regs, i), 2);
val[2] = val[3] = 0;
if (i == I387_FOP_REGNUM)
val[1] &= ((1 << 3) - 1);
else if (i== I387_FTAG_REGNUM)
{
unsigned long ftag = 0;
int fpreg;
int top;
top = ((FXSAVE_ADDR (regs, I387_FSTAT_REGNUM))[1] >> 3);
top &= 0x7;
for (fpreg = 7; fpreg >= 0; fpreg--)
{
int tag;
if (val[0] & (1 << fpreg))
{
int regnum = (fpreg + 8 - top) % 8 + I387_ST0_REGNUM;
tag = i387_tag (FXSAVE_ADDR (regs, regnum));
}
else
tag = 3;
ftag |= tag << (2 * fpreg);
}
val[0] = ftag & 0xff;
val[1] = (ftag >> 8) & 0xff;
}
regcache_raw_supply (regcache, i, val);
}
else
regcache_raw_supply (regcache, i, FXSAVE_ADDR (regs, i));
}
if (regnum == I387_MXCSR_REGNUM || regnum == -1)
{
if (regs == NULL)
regcache_raw_supply (regcache, I387_MXCSR_REGNUM, NULL);
else
regcache_raw_supply (regcache, I387_MXCSR_REGNUM,
FXSAVE_MXCSR_ADDR (regs));
}
#undef I387_ST0_REGNUM
#undef I387_NUM_XMM_REGS
}
void
i387_collect_fxsave (const struct regcache *regcache, int regnum, void *fxsave)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
gdb_byte *regs = fxsave;
int i;
gdb_assert (tdep->st0_regnum >= I386_ST0_REGNUM);
gdb_assert (tdep->num_xmm_regs > 0);
#define I387_ST0_REGNUM tdep->st0_regnum
#define I387_NUM_XMM_REGS tdep->num_xmm_regs
for (i = I387_ST0_REGNUM; i < I387_MXCSR_REGNUM; i++)
if (regnum == -1 || regnum == i)
{
if (i >= I387_FCTRL_REGNUM && i < I387_XMM0_REGNUM
&& i != I387_FIOFF_REGNUM && i != I387_FOOFF_REGNUM)
{
gdb_byte buf[4];
regcache_raw_collect (regcache, i, buf);
if (i == I387_FOP_REGNUM)
{
buf[1] &= ((1 << 3) - 1);
buf[1] |= ((FXSAVE_ADDR (regs, i))[1] & ~((1 << 3) - 1));
}
else if (i == I387_FTAG_REGNUM)
{
unsigned short ftag;
int fpreg;
ftag = (buf[1] << 8) | buf[0];
buf[0] = 0;
buf[1] = 0;
for (fpreg = 7; fpreg >= 0; fpreg--)
{
int tag = (ftag >> (fpreg * 2)) & 3;
if (tag != 3)
buf[0] |= (1 << fpreg);
}
}
memcpy (FXSAVE_ADDR (regs, i), buf, 2);
}
else
regcache_raw_collect (regcache, i, FXSAVE_ADDR (regs, i));
}
if (regnum == I387_MXCSR_REGNUM || regnum == -1)
regcache_raw_collect (regcache, I387_MXCSR_REGNUM,
FXSAVE_MXCSR_ADDR (regs));
#undef I387_ST0_REGNUM
#undef I387_NUM_XMM_REGS
}
void
i387_fill_fxsave (void *fxsave, int regnum)
{
i387_collect_fxsave (current_regcache, regnum, fxsave);
}
static int
i387_tag (const gdb_byte *raw)
{
int integer;
unsigned int exponent;
unsigned long fraction[2];
integer = raw[7] & 0x80;
exponent = (((raw[9] & 0x7f) << 8) | raw[8]);
fraction[0] = ((raw[3] << 24) | (raw[2] << 16) | (raw[1] << 8) | raw[0]);
fraction[1] = (((raw[7] & 0x7f) << 24) | (raw[6] << 16)
| (raw[5] << 8) | raw[4]);
if (exponent == 0x7fff)
{
return (2);
}
else if (exponent == 0x0000)
{
if (fraction[0] == 0x0000 && fraction[1] == 0x0000 && !integer)
{
return (1);
}
else
{
return (2);
}
}
else
{
if (integer)
{
return (0);
}
else
{
return (2);
}
}
}
void
i387_return_value (struct gdbarch *gdbarch, struct regcache *regcache)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
ULONGEST fstat;
#define I387_ST0_REGNUM tdep->st0_regnum
regcache_raw_read_unsigned (regcache, I387_FSTAT_REGNUM, &fstat);
fstat |= (7 << 11);
regcache_raw_write_unsigned (regcache, I387_FSTAT_REGNUM, fstat);
regcache_raw_write_unsigned (regcache, I387_FTAG_REGNUM, 0x3fff);
#undef I387_ST0_REGNUM
}