198944Sobrien/* i387-specific utility functions, for the remote server for GDB.
298944Sobrien   Copyright 2000, 2001, 2002
398944Sobrien   Free Software Foundation, Inc.
498944Sobrien
598944Sobrien   This file is part of GDB.
698944Sobrien
798944Sobrien   This program is free software; you can redistribute it and/or modify
898944Sobrien   it under the terms of the GNU General Public License as published by
998944Sobrien   the Free Software Foundation; either version 2 of the License, or
1098944Sobrien   (at your option) any later version.
1198944Sobrien
1298944Sobrien   This program is distributed in the hope that it will be useful,
1398944Sobrien   but WITHOUT ANY WARRANTY; without even the implied warranty of
1498944Sobrien   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1598944Sobrien   GNU General Public License for more details.
1698944Sobrien
1798944Sobrien   You should have received a copy of the GNU General Public License
1898944Sobrien   along with this program; if not, write to the Free Software
1998944Sobrien   Foundation, Inc., 59 Temple Place - Suite 330,
2098944Sobrien   Boston, MA 02111-1307, USA.  */
2198944Sobrien
2298944Sobrien#include "server.h"
23130803Smarcel#include "i387-fp.h"
2498944Sobrien
2598944Sobrienint num_xmm_registers = 8;
2698944Sobrien
2798944Sobrien/* Note: These functions preserve the reserved bits in control registers.
2898944Sobrien   However, gdbserver promptly throws away that information.  */
2998944Sobrien
3098944Sobrien/* These structs should have the proper sizes and alignment on both
3198944Sobrien   i386 and x86-64 machines.  */
3298944Sobrien
3398944Sobrienstruct i387_fsave {
3498944Sobrien  /* All these are only sixteen bits, plus padding, except for fop (which
3598944Sobrien     is only eleven bits), and fooff / fioff (which are 32 bits each).  */
3698944Sobrien  unsigned int fctrl;
3798944Sobrien  unsigned int fstat;
3898944Sobrien  unsigned int ftag;
3998944Sobrien  unsigned int fioff;
4098944Sobrien  unsigned short fiseg;
4198944Sobrien  unsigned short fop;
4298944Sobrien  unsigned int fooff;
4398944Sobrien  unsigned int foseg;
4498944Sobrien
4598944Sobrien  /* Space for eight 80-bit FP values.  */
4698944Sobrien  char st_space[80];
4798944Sobrien};
4898944Sobrien
4998944Sobrienstruct i387_fxsave {
5098944Sobrien  /* All these are only sixteen bits, plus padding, except for fop (which
5198944Sobrien     is only eleven bits), and fooff / fioff (which are 32 bits each).  */
5298944Sobrien  unsigned short fctrl;
5398944Sobrien  unsigned short fstat;
5498944Sobrien  unsigned short ftag;
5598944Sobrien  unsigned short fop;
5698944Sobrien  unsigned int fioff;
5798944Sobrien  unsigned int fiseg;
5898944Sobrien  unsigned int fooff;
5998944Sobrien  unsigned int foseg;
6098944Sobrien
6198944Sobrien  unsigned int mxcsr;
6298944Sobrien
6398944Sobrien  unsigned int _pad1;
6498944Sobrien
6598944Sobrien  /* Space for eight 80-bit FP values in 128-bit spaces.  */
6698944Sobrien  char st_space[128];
6798944Sobrien
6898944Sobrien  /* Space for eight 128-bit XMM values, or 16 on x86-64.  */
6998944Sobrien  char xmm_space[256];
7098944Sobrien};
7198944Sobrien
7298944Sobrienvoid
7398944Sobrieni387_cache_to_fsave (void *buf)
7498944Sobrien{
7598944Sobrien  struct i387_fsave *fp = (struct i387_fsave *) buf;
7698944Sobrien  int i;
7798944Sobrien  int st0_regnum = find_regno ("st0");
7898944Sobrien  unsigned long val, val2;
7998944Sobrien
8098944Sobrien  for (i = 0; i < 8; i++)
8198944Sobrien    collect_register (i + st0_regnum, ((char *) &fp->st_space[0]) + i * 10);
8298944Sobrien
8398944Sobrien  collect_register_by_name ("fioff", &fp->fioff);
8498944Sobrien  collect_register_by_name ("fooff", &fp->fooff);
8598944Sobrien
8698944Sobrien  /* This one's 11 bits... */
8798944Sobrien  collect_register_by_name ("fop", &val2);
8898944Sobrien  fp->fop = (val2 & 0x7FF) | (fp->fop & 0xF800);
8998944Sobrien
9098944Sobrien  /* Some registers are 16-bit.  */
9198944Sobrien  collect_register_by_name ("fctrl", &val);
9298944Sobrien  *(unsigned short *) &fp->fctrl = val;
9398944Sobrien
9498944Sobrien  collect_register_by_name ("fstat", &val);
9598944Sobrien  val &= 0xFFFF;
9698944Sobrien  *(unsigned short *) &fp->fstat = val;
9798944Sobrien
9898944Sobrien  collect_register_by_name ("ftag", &val);
9998944Sobrien  val &= 0xFFFF;
10098944Sobrien  *(unsigned short *) &fp->ftag = val;
10198944Sobrien
10298944Sobrien  collect_register_by_name ("fiseg", &val);
10398944Sobrien  val &= 0xFFFF;
10498944Sobrien  *(unsigned short *) &fp->fiseg = val;
10598944Sobrien
10698944Sobrien  collect_register_by_name ("foseg", &val);
10798944Sobrien  val &= 0xFFFF;
10898944Sobrien  *(unsigned short *) &fp->foseg = val;
10998944Sobrien}
11098944Sobrien
11198944Sobrienvoid
112130803Smarceli387_fsave_to_cache (const void *buf)
11398944Sobrien{
11498944Sobrien  struct i387_fsave *fp = (struct i387_fsave *) buf;
11598944Sobrien  int i;
11698944Sobrien  int st0_regnum = find_regno ("st0");
11798944Sobrien  unsigned long val;
11898944Sobrien
11998944Sobrien  for (i = 0; i < 8; i++)
12098944Sobrien    supply_register (i + st0_regnum, ((char *) &fp->st_space[0]) + i * 10);
12198944Sobrien
12298944Sobrien  supply_register_by_name ("fioff", &fp->fioff);
12398944Sobrien  supply_register_by_name ("fooff", &fp->fooff);
12498944Sobrien
12598944Sobrien  /* Some registers are 16-bit.  */
12698944Sobrien  val = fp->fctrl & 0xFFFF;
12798944Sobrien  supply_register_by_name ("fctrl", &val);
12898944Sobrien
12998944Sobrien  val = fp->fstat & 0xFFFF;
13098944Sobrien  supply_register_by_name ("fstat", &val);
13198944Sobrien
13298944Sobrien  val = fp->ftag & 0xFFFF;
13398944Sobrien  supply_register_by_name ("ftag", &val);
13498944Sobrien
13598944Sobrien  val = fp->fiseg & 0xFFFF;
13698944Sobrien  supply_register_by_name ("fiseg", &val);
13798944Sobrien
13898944Sobrien  val = fp->foseg & 0xFFFF;
13998944Sobrien  supply_register_by_name ("foseg", &val);
14098944Sobrien
14198944Sobrien  val = (fp->fop) & 0x7FF;
14298944Sobrien  supply_register_by_name ("fop", &val);
14398944Sobrien}
14498944Sobrien
14598944Sobrienvoid
14698944Sobrieni387_cache_to_fxsave (void *buf)
14798944Sobrien{
14898944Sobrien  struct i387_fxsave *fp = (struct i387_fxsave *) buf;
14998944Sobrien  int i;
15098944Sobrien  int st0_regnum = find_regno ("st0");
15198944Sobrien  int xmm0_regnum = find_regno ("xmm0");
15298944Sobrien  unsigned long val, val2;
15398944Sobrien
15498944Sobrien  for (i = 0; i < 8; i++)
15598944Sobrien    collect_register (i + st0_regnum, ((char *) &fp->st_space[0]) + i * 16);
15698944Sobrien  for (i = 0; i < num_xmm_registers; i++)
15798944Sobrien    collect_register (i + xmm0_regnum, ((char *) &fp->xmm_space[0]) + i * 16);
15898944Sobrien
15998944Sobrien  collect_register_by_name ("fioff", &fp->fioff);
16098944Sobrien  collect_register_by_name ("fooff", &fp->fooff);
16198944Sobrien  collect_register_by_name ("mxcsr", &fp->mxcsr);
16298944Sobrien
16398944Sobrien  /* This one's 11 bits... */
16498944Sobrien  collect_register_by_name ("fop", &val2);
16598944Sobrien  fp->fop = (val2 & 0x7FF) | (fp->fop & 0xF800);
16698944Sobrien
16798944Sobrien  /* Some registers are 16-bit.  */
16898944Sobrien  collect_register_by_name ("fctrl", &val);
16998944Sobrien  *(unsigned short *) &fp->fctrl = val;
17098944Sobrien
17198944Sobrien  collect_register_by_name ("fstat", &val);
17298944Sobrien  val &= 0xFFFF;
17398944Sobrien  *(unsigned short *) &fp->fstat = val;
17498944Sobrien
17598944Sobrien  /* Convert to the simplifed tag form stored in fxsave data.  */
17698944Sobrien  collect_register_by_name ("ftag", &val);
17798944Sobrien  val &= 0xFFFF;
17898944Sobrien  for (i = 7; i >= 0; i--)
17998944Sobrien    {
18098944Sobrien      int tag = (val >> (i * 2)) & 3;
18198944Sobrien
18298944Sobrien      if (tag != 3)
18398944Sobrien	val2 |= (1 << i);
18498944Sobrien    }
18598944Sobrien  *(unsigned short *) &fp->ftag = val2;
18698944Sobrien
18798944Sobrien  collect_register_by_name ("fiseg", &val);
18898944Sobrien  val &= 0xFFFF;
18998944Sobrien  *(unsigned short *) &fp->fiseg = val;
19098944Sobrien
19198944Sobrien  collect_register_by_name ("foseg", &val);
19298944Sobrien  val &= 0xFFFF;
19398944Sobrien  *(unsigned short *) &fp->foseg = val;
19498944Sobrien}
19598944Sobrien
19698944Sobrienstatic int
19798944Sobrieni387_ftag (struct i387_fxsave *fp, int regno)
19898944Sobrien{
19998944Sobrien  unsigned char *raw = &fp->st_space[regno * 16];
20098944Sobrien  unsigned int exponent;
20198944Sobrien  unsigned long fraction[2];
20298944Sobrien  int integer;
20398944Sobrien
20498944Sobrien  integer = raw[7] & 0x80;
20598944Sobrien  exponent = (((raw[9] & 0x7f) << 8) | raw[8]);
20698944Sobrien  fraction[0] = ((raw[3] << 24) | (raw[2] << 16) | (raw[1] << 8) | raw[0]);
20798944Sobrien  fraction[1] = (((raw[7] & 0x7f) << 24) | (raw[6] << 16)
20898944Sobrien                 | (raw[5] << 8) | raw[4]);
20998944Sobrien
21098944Sobrien  if (exponent == 0x7fff)
21198944Sobrien    {
21298944Sobrien      /* Special.  */
21398944Sobrien      return (2);
21498944Sobrien    }
21598944Sobrien  else if (exponent == 0x0000)
21698944Sobrien    {
21798944Sobrien      if (fraction[0] == 0x0000 && fraction[1] == 0x0000 && !integer)
21898944Sobrien        {
21998944Sobrien          /* Zero.  */
22098944Sobrien          return (1);
22198944Sobrien        }
22298944Sobrien      else
22398944Sobrien        {
22498944Sobrien          /* Special.  */
22598944Sobrien          return (2);
22698944Sobrien        }
22798944Sobrien    }
22898944Sobrien  else
22998944Sobrien    {
23098944Sobrien      if (integer)
23198944Sobrien        {
23298944Sobrien          /* Valid.  */
23398944Sobrien          return (0);
23498944Sobrien        }
23598944Sobrien      else
23698944Sobrien        {
23798944Sobrien          /* Special.  */
23898944Sobrien          return (2);
23998944Sobrien        }
24098944Sobrien    }
24198944Sobrien}
24298944Sobrien
24398944Sobrienvoid
244130803Smarceli387_fxsave_to_cache (const void *buf)
24598944Sobrien{
24698944Sobrien  struct i387_fxsave *fp = (struct i387_fxsave *) buf;
24798944Sobrien  int i, top;
24898944Sobrien  int st0_regnum = find_regno ("st0");
24998944Sobrien  int xmm0_regnum = find_regno ("xmm0");
25098944Sobrien  unsigned long val;
25198944Sobrien
25298944Sobrien  for (i = 0; i < 8; i++)
25398944Sobrien    supply_register (i + st0_regnum, ((char *) &fp->st_space[0]) + i * 16);
25498944Sobrien  for (i = 0; i < num_xmm_registers; i++)
25598944Sobrien    supply_register (i + xmm0_regnum, ((char *) &fp->xmm_space[0]) + i * 16);
25698944Sobrien
25798944Sobrien  supply_register_by_name ("fioff", &fp->fioff);
25898944Sobrien  supply_register_by_name ("fooff", &fp->fooff);
25998944Sobrien  supply_register_by_name ("mxcsr", &fp->mxcsr);
26098944Sobrien
26198944Sobrien  /* Some registers are 16-bit.  */
26298944Sobrien  val = fp->fctrl & 0xFFFF;
26398944Sobrien  supply_register_by_name ("fctrl", &val);
26498944Sobrien
26598944Sobrien  val = fp->fstat & 0xFFFF;
26698944Sobrien  supply_register_by_name ("fstat", &val);
26798944Sobrien
26898944Sobrien  /* Generate the form of ftag data that GDB expects.  */
26998944Sobrien  top = (fp->fstat >> 11) & 0x7;
27098944Sobrien  val = 0;
27198944Sobrien  for (i = 7; i >= 0; i--)
27298944Sobrien    {
27398944Sobrien      int tag;
27498944Sobrien      if (val & (1 << i))
27598944Sobrien	tag = i387_ftag (fp, (i + 8 - top) % 8);
27698944Sobrien      else
27798944Sobrien	tag = 3;
27898944Sobrien      val |= tag << (2 * i);
27998944Sobrien    }
28098944Sobrien  supply_register_by_name ("ftag", &val);
28198944Sobrien
28298944Sobrien  val = fp->fiseg & 0xFFFF;
28398944Sobrien  supply_register_by_name ("fiseg", &val);
28498944Sobrien
28598944Sobrien  val = fp->foseg & 0xFFFF;
28698944Sobrien  supply_register_by_name ("foseg", &val);
28798944Sobrien
28898944Sobrien  val = (fp->fop) & 0x7FF;
28998944Sobrien  supply_register_by_name ("fop", &val);
29098944Sobrien}
291