diff options
Diffstat (limited to 'gdb/i387-tdep.c')
-rw-r--r-- | gdb/i387-tdep.c | 318 |
1 files changed, 316 insertions, 2 deletions
diff --git a/gdb/i387-tdep.c b/gdb/i387-tdep.c index 2a6f77c..59b60d7 100644 --- a/gdb/i387-tdep.c +++ b/gdb/i387-tdep.c @@ -1,6 +1,6 @@ /* Intel 387 floating point stuff. - Copyright 1988, 1989, 1991, 1992, 1993, 1994, 1998, 1999, 2000, 2001 - Free Software Foundation, Inc. + Copyright 1988, 1989, 1991, 1992, 1993, 1994, 1998, 1999, 2000, + 2001, 2002 Free Software Foundation, Inc. This file is part of GDB. @@ -388,3 +388,317 @@ i387_float_info (void) printf_filtered ("Opcode: %s\n", local_hex_string_custom (fop ? (fop | 0xd800) : 0, "04")); } + +/* FIXME: kettenis/2000-05-21: Right now more than a few i386 targets + define their own routines to manage the floating-point registers in + GDB's register array. Most (if not all) of these targets use the + format used by the "fsave" instruction in their communication with + the OS. They should all be converted to use the routines below. */ + +/* At fsave_offset[REGNUM] you'll find the offset to the location in + the data structure used by the "fsave" instruction where GDB + register REGNUM is stored. */ + +static int fsave_offset[] = +{ + 28 + 0 * FPU_REG_RAW_SIZE, /* FP0_REGNUM through ... */ + 28 + 1 * FPU_REG_RAW_SIZE, + 28 + 2 * FPU_REG_RAW_SIZE, + 28 + 3 * FPU_REG_RAW_SIZE, + 28 + 4 * FPU_REG_RAW_SIZE, + 28 + 5 * FPU_REG_RAW_SIZE, + 28 + 6 * FPU_REG_RAW_SIZE, + 28 + 7 * FPU_REG_RAW_SIZE, /* ... FP7_REGNUM. */ + 0, /* FCTRL_REGNUM (16 bits). */ + 4, /* FSTAT_REGNUM (16 bits). */ + 8, /* FTAG_REGNUM (16 bits). */ + 16, /* FISEG_REGNUM (16 bits). */ + 12, /* FIOFF_REGNUM. */ + 24, /* FOSEG_REGNUM. */ + 20, /* FOOFF_REGNUM. */ + 18 /* FOP_REGNUM (bottom 11 bits). */ +}; + +#define FSAVE_ADDR(fsave, regnum) (fsave + fsave_offset[regnum - FP0_REGNUM]) + + +/* Fill register REGNUM in GDB's register array with the appropriate + value from *FSAVE. This function masks off any of the reserved + bits in *FSAVE. */ + +void +i387_supply_register (int regnum, char *fsave) +{ + /* Most of the FPU control registers occupy only 16 bits in + the fsave area. Give those a special treatment. */ + if (regnum >= FPC_REGNUM + && regnum != FIOFF_REGNUM && regnum != FOOFF_REGNUM) + { + unsigned char val[4]; + + memcpy (val, FSAVE_ADDR (fsave, regnum), 2); + val[2] = val[3] = 0; + if (regnum == FOP_REGNUM) + val[1] &= ((1 << 3) - 1); + supply_register (regnum, val); + } + else + supply_register (regnum, FSAVE_ADDR (fsave, regnum)); +} + +/* Fill GDB's register array with the floating-point register values + in *FSAVE. This function masks off any of the reserved + bits in *FSAVE. */ + +void +i387_supply_fsave (char *fsave) +{ + int i; + + for (i = FP0_REGNUM; i < XMM0_REGNUM; i++) + i387_supply_register (i, fsave); +} + +/* Fill register REGNUM (if it is a floating-point register) in *FSAVE + with the value in GDB's register array. If REGNUM is -1, do this + for all registers. This function doesn't touch any of the reserved + bits in *FSAVE. */ + +void +i387_fill_fsave (char *fsave, int regnum) +{ + int i; + + for (i = FP0_REGNUM; i < XMM0_REGNUM; i++) + if (regnum == -1 || regnum == i) + { + /* Most of the FPU control registers occupy only 16 bits in + the fsave area. Give those a special treatment. */ + if (i >= FPC_REGNUM + && i != FIOFF_REGNUM && i != FOOFF_REGNUM) + { + unsigned char buf[4]; + + regcache_collect (i, buf); + + if (i == FOP_REGNUM) + { + /* The opcode occupies only 11 bits. Make sure we + don't touch the other bits. */ + buf[1] &= ((1 << 3) - 1); + buf[1] |= ((FSAVE_ADDR (fsave, i))[1] & ~((1 << 3) - 1)); + } + memcpy (FSAVE_ADDR (fsave, i), buf, 2); + } + else + regcache_collect (i, FSAVE_ADDR (fsave, i)); + } +} + + +/* At fxsave_offset[REGNUM] you'll find the offset to the location in + the data structure used by the "fxsave" instruction where GDB + register REGNUM is stored. */ + +static int fxsave_offset[] = +{ + 32, /* FP0_REGNUM through ... */ + 48, + 64, + 80, + 96, + 112, + 128, + 144, /* ... FP7_REGNUM (80 bits each). */ + 0, /* FCTRL_REGNUM (16 bits). */ + 2, /* FSTAT_REGNUM (16 bits). */ + 4, /* FTAG_REGNUM (16 bits). */ + 12, /* FISEG_REGNUM (16 bits). */ + 8, /* FIOFF_REGNUM. */ + 20, /* FOSEG_REGNUM (16 bits). */ + 16, /* FOOFF_REGNUM. */ + 6, /* FOP_REGNUM (bottom 11 bits). */ + 160, /* XMM0_REGNUM through ... */ + 176, + 192, + 208, + 224, + 240, + 256, + 272, /* ... XMM7_REGNUM (128 bits each). */ + 24, /* MXCSR_REGNUM. */ +}; + +#define FXSAVE_ADDR(fxsave, regnum) \ + (fxsave + fxsave_offset[regnum - FP0_REGNUM]) + +static int i387_tag (unsigned char *raw); + + +/* Fill GDB's register array with the floating-point and SSE register + values in *FXSAVE. This function masks off any of the reserved + bits in *FXSAVE. */ + +void +i387_supply_fxsave (char *fxsave) +{ + int i, last_regnum = MXCSR_REGNUM; + + if (gdbarch_tdep (current_gdbarch)->num_xmm_regs == 0) + last_regnum = FOP_REGNUM; + + for (i = FP0_REGNUM; i <= last_regnum; i++) + { + /* Most of the FPU control registers occupy only 16 bits in + the fxsave area. Give those a special treatment. */ + if (i >= FPC_REGNUM && i < XMM0_REGNUM + && i != FIOFF_REGNUM && i != FOOFF_REGNUM) + { + unsigned char val[4]; + + memcpy (val, FXSAVE_ADDR (fxsave, i), 2); + val[2] = val[3] = 0; + if (i == FOP_REGNUM) + val[1] &= ((1 << 3) - 1); + else if (i== FTAG_REGNUM) + { + /* The fxsave area contains a simplified version of the + tag word. We have to look at the actual 80-bit FP + data to recreate the traditional i387 tag word. */ + + unsigned long ftag = 0; + int fpreg; + int top; + + top = (((FXSAVE_ADDR (fxsave, FSTAT_REGNUM))[1] >> 3) & 0x7); + + for (fpreg = 7; fpreg >= 0; fpreg--) + { + int tag; + + if (val[0] & (1 << fpreg)) + { + int regnum = (fpreg + 8 - top) % 8 + FP0_REGNUM; + tag = i387_tag (FXSAVE_ADDR (fxsave, regnum)); + } + else + tag = 3; /* Empty */ + + ftag |= tag << (2 * fpreg); + } + val[0] = ftag & 0xff; + val[1] = (ftag >> 8) & 0xff; + } + supply_register (i, val); + } + else + supply_register (i, FXSAVE_ADDR (fxsave, i)); + } +} + +/* Fill register REGNUM (if it is a floating-point or SSE register) in + *FXSAVE with the value in GDB's register array. If REGNUM is -1, do + this for all registers. This function doesn't touch any of the + reserved bits in *FXSAVE. */ + +void +i387_fill_fxsave (char *fxsave, int regnum) +{ + int i, last_regnum = MXCSR_REGNUM; + + if (gdbarch_tdep (current_gdbarch)->num_xmm_regs == 0) + last_regnum = FOP_REGNUM; + + for (i = FP0_REGNUM; i <= last_regnum; i++) + if (regnum == -1 || regnum == i) + { + /* Most of the FPU control registers occupy only 16 bits in + the fxsave area. Give those a special treatment. */ + if (i >= FPC_REGNUM && i < XMM0_REGNUM + && i != FIOFF_REGNUM && i != FDOFF_REGNUM) + { + unsigned char buf[4]; + + regcache_collect (i, buf); + + if (i == FOP_REGNUM) + { + /* The opcode occupies only 11 bits. Make sure we + don't touch the other bits. */ + buf[1] &= ((1 << 3) - 1); + buf[1] |= ((FXSAVE_ADDR (fxsave, i))[1] & ~((1 << 3) - 1)); + } + else if (i == FTAG_REGNUM) + { + /* Converting back is much easier. */ + + 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 (fxsave, i), buf, 2); + } + else + regcache_collect (i, FXSAVE_ADDR (fxsave, i)); + } +} + +/* Recreate the FTW (tag word) valid bits from the 80-bit FP data in + *RAW. */ + +static int +i387_tag (unsigned char *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) + { + /* Special. */ + return (2); + } + else if (exponent == 0x0000) + { + if (fraction[0] == 0x0000 && fraction[1] == 0x0000 && !integer) + { + /* Zero. */ + return (1); + } + else + { + /* Special. */ + return (2); + } + } + else + { + if (integer) + { + /* Valid. */ + return (0); + } + else + { + /* Special. */ + return (2); + } + } +} |