OpenCores
URL https://opencores.org/ocsvn/or1k_old/or1k_old/trunk

Subversion Repositories or1k_old

[/] [or1k_old/] [trunk/] [rc203soc/] [sw/] [uClinux/] [arch/] [i386/] [math-emu/] [reg_ld_str.c] - Diff between revs 1765 and 1782

Only display areas with differences | Details | Blame | View Log

Rev 1765 Rev 1782
/*---------------------------------------------------------------------------+
/*---------------------------------------------------------------------------+
 |  reg_ld_str.c                                                             |
 |  reg_ld_str.c                                                             |
 |                                                                           |
 |                                                                           |
 | All of the functions which transfer data between user memory and FPU_REGs.|
 | All of the functions which transfer data between user memory and FPU_REGs.|
 |                                                                           |
 |                                                                           |
 | Copyright (C) 1992,1993,1994,1996                                         |
 | Copyright (C) 1992,1993,1994,1996                                         |
 |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
 |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
 |                  E-mail   billm@jacobi.maths.monash.edu.au                |
 |                  E-mail   billm@jacobi.maths.monash.edu.au                |
 |                                                                           |
 |                                                                           |
 |                                                                           |
 |                                                                           |
 +---------------------------------------------------------------------------*/
 +---------------------------------------------------------------------------*/
 
 
/*---------------------------------------------------------------------------+
/*---------------------------------------------------------------------------+
 | Note:                                                                     |
 | Note:                                                                     |
 |    The file contains code which accesses user memory.                     |
 |    The file contains code which accesses user memory.                     |
 |    Emulator static data may change when user memory is accessed, due to   |
 |    Emulator static data may change when user memory is accessed, due to   |
 |    other processes using the emulator while swapping is in progress.      |
 |    other processes using the emulator while swapping is in progress.      |
 +---------------------------------------------------------------------------*/
 +---------------------------------------------------------------------------*/
 
 
#include <asm/segment.h>
#include <asm/segment.h>
 
 
#include "fpu_system.h"
#include "fpu_system.h"
#include "exception.h"
#include "exception.h"
#include "reg_constant.h"
#include "reg_constant.h"
#include "fpu_emu.h"
#include "fpu_emu.h"
#include "control_w.h"
#include "control_w.h"
#include "status_w.h"
#include "status_w.h"
 
 
 
 
#define EXTENDED_Ebias 0x3fff
#define EXTENDED_Ebias 0x3fff
#define EXTENDED_Emin (-0x3ffe)  /* smallest valid exponent */
#define EXTENDED_Emin (-0x3ffe)  /* smallest valid exponent */
 
 
#define DOUBLE_Emax 1023         /* largest valid exponent */
#define DOUBLE_Emax 1023         /* largest valid exponent */
#define DOUBLE_Ebias 1023
#define DOUBLE_Ebias 1023
#define DOUBLE_Emin (-1022)      /* smallest valid exponent */
#define DOUBLE_Emin (-1022)      /* smallest valid exponent */
 
 
#define SINGLE_Emax 127          /* largest valid exponent */
#define SINGLE_Emax 127          /* largest valid exponent */
#define SINGLE_Ebias 127
#define SINGLE_Ebias 127
#define SINGLE_Emin (-126)       /* smallest valid exponent */
#define SINGLE_Emin (-126)       /* smallest valid exponent */
 
 
static void write_to_extended(FPU_REG *rp, char *d);
static void write_to_extended(FPU_REG *rp, char *d);
 
 
 
 
/* Get a long double from user memory */
/* Get a long double from user memory */
int reg_load_extended(long double *s, FPU_REG *loaded_data)
int reg_load_extended(long double *s, FPU_REG *loaded_data)
{
{
  unsigned long sigl, sigh, exp;
  unsigned long sigl, sigh, exp;
 
 
  RE_ENTRANT_CHECK_OFF;
  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_READ, s, 10);
  FPU_verify_area(VERIFY_READ, s, 10);
  sigl = get_fs_long((unsigned long *) s);
  sigl = get_fs_long((unsigned long *) s);
  sigh = get_fs_long(1 + (unsigned long *) s);
  sigh = get_fs_long(1 + (unsigned long *) s);
  exp = get_fs_word(4 + (unsigned short *) s);
  exp = get_fs_word(4 + (unsigned short *) s);
  RE_ENTRANT_CHECK_ON;
  RE_ENTRANT_CHECK_ON;
 
 
  loaded_data->tag = TW_Valid;   /* Default */
  loaded_data->tag = TW_Valid;   /* Default */
  loaded_data->sigl = sigl;
  loaded_data->sigl = sigl;
  loaded_data->sigh = sigh;
  loaded_data->sigh = sigh;
  if (exp & 0x8000)
  if (exp & 0x8000)
    loaded_data->sign = SIGN_NEG;
    loaded_data->sign = SIGN_NEG;
  else
  else
    loaded_data->sign = SIGN_POS;
    loaded_data->sign = SIGN_POS;
  exp &= 0x7fff;
  exp &= 0x7fff;
  loaded_data->exp = exp - EXTENDED_Ebias + EXP_BIAS;
  loaded_data->exp = exp - EXTENDED_Ebias + EXP_BIAS;
 
 
  if ( exp == 0 )
  if ( exp == 0 )
    {
    {
      if ( !(sigh | sigl) )
      if ( !(sigh | sigl) )
        {
        {
          loaded_data->tag = TW_Zero;
          loaded_data->tag = TW_Zero;
          return 0;
          return 0;
        }
        }
      /* The number is a de-normal or pseudodenormal. */
      /* The number is a de-normal or pseudodenormal. */
      if (sigh & 0x80000000)
      if (sigh & 0x80000000)
        {
        {
          /* Is a pseudodenormal. */
          /* Is a pseudodenormal. */
          /* Convert it for internal use. */
          /* Convert it for internal use. */
          /* This is non-80486 behaviour because the number
          /* This is non-80486 behaviour because the number
             loses its 'denormal' identity. */
             loses its 'denormal' identity. */
          loaded_data->exp++;
          loaded_data->exp++;
          return 1;
          return 1;
        }
        }
      else
      else
        {
        {
          /* Is a denormal. */
          /* Is a denormal. */
          /* Convert it for internal use. */
          /* Convert it for internal use. */
          loaded_data->exp++;
          loaded_data->exp++;
          normalize_nuo(loaded_data);
          normalize_nuo(loaded_data);
          return 0;
          return 0;
        }
        }
    }
    }
  else if ( exp == 0x7fff )
  else if ( exp == 0x7fff )
    {
    {
      if ( !((sigh ^ 0x80000000) | sigl) )
      if ( !((sigh ^ 0x80000000) | sigl) )
        {
        {
          /* Matches the bit pattern for Infinity. */
          /* Matches the bit pattern for Infinity. */
          loaded_data->exp = EXP_Infinity;
          loaded_data->exp = EXP_Infinity;
          loaded_data->tag = TW_Infinity;
          loaded_data->tag = TW_Infinity;
          return 0;
          return 0;
        }
        }
 
 
      loaded_data->exp = EXP_NaN;
      loaded_data->exp = EXP_NaN;
      loaded_data->tag = TW_NaN;
      loaded_data->tag = TW_NaN;
      if ( !(sigh & 0x80000000) )
      if ( !(sigh & 0x80000000) )
        {
        {
          /* NaNs have the ms bit set to 1. */
          /* NaNs have the ms bit set to 1. */
          /* This is therefore an Unsupported NaN data type. */
          /* This is therefore an Unsupported NaN data type. */
          /* This is non 80486 behaviour */
          /* This is non 80486 behaviour */
          /* This should generate an Invalid Operand exception
          /* This should generate an Invalid Operand exception
             later, so we convert it to a SNaN */
             later, so we convert it to a SNaN */
          loaded_data->sigh = 0x80000000;
          loaded_data->sigh = 0x80000000;
          loaded_data->sigl = 0x00000001;
          loaded_data->sigl = 0x00000001;
          loaded_data->sign = SIGN_NEG;
          loaded_data->sign = SIGN_NEG;
          return 1;
          return 1;
        }
        }
      return 0;
      return 0;
    }
    }
 
 
  if ( !(sigh & 0x80000000) )
  if ( !(sigh & 0x80000000) )
    {
    {
      /* Unsupported data type. */
      /* Unsupported data type. */
      /* Valid numbers have the ms bit set to 1. */
      /* Valid numbers have the ms bit set to 1. */
      /* Unnormal. */
      /* Unnormal. */
      /* Convert it for internal use. */
      /* Convert it for internal use. */
      /* This is non-80486 behaviour */
      /* This is non-80486 behaviour */
      /* This should generate an Invalid Operand exception
      /* This should generate an Invalid Operand exception
         later, so we convert it to a SNaN */
         later, so we convert it to a SNaN */
      loaded_data->sigh = 0x80000000;
      loaded_data->sigh = 0x80000000;
      loaded_data->sigl = 0x00000001;
      loaded_data->sigl = 0x00000001;
      loaded_data->sign = SIGN_NEG;
      loaded_data->sign = SIGN_NEG;
      loaded_data->exp = EXP_NaN;
      loaded_data->exp = EXP_NaN;
      loaded_data->tag = TW_NaN;
      loaded_data->tag = TW_NaN;
      return 1;
      return 1;
    }
    }
  return 0;
  return 0;
}
}
 
 
 
 
/* Get a double from user memory */
/* Get a double from user memory */
int reg_load_double(double *dfloat, FPU_REG *loaded_data)
int reg_load_double(double *dfloat, FPU_REG *loaded_data)
{
{
  int exp;
  int exp;
  unsigned m64, l64;
  unsigned m64, l64;
 
 
  RE_ENTRANT_CHECK_OFF;
  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_READ, dfloat, 8);
  FPU_verify_area(VERIFY_READ, dfloat, 8);
  m64 = get_fs_long(1 + (unsigned long *) dfloat);
  m64 = get_fs_long(1 + (unsigned long *) dfloat);
  l64 = get_fs_long((unsigned long *) dfloat);
  l64 = get_fs_long((unsigned long *) dfloat);
  RE_ENTRANT_CHECK_ON;
  RE_ENTRANT_CHECK_ON;
 
 
  if (m64 & 0x80000000)
  if (m64 & 0x80000000)
    loaded_data->sign = SIGN_NEG;
    loaded_data->sign = SIGN_NEG;
  else
  else
    loaded_data->sign = SIGN_POS;
    loaded_data->sign = SIGN_POS;
  exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias;
  exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias;
  m64 &= 0xfffff;
  m64 &= 0xfffff;
  if (exp > DOUBLE_Emax)
  if (exp > DOUBLE_Emax)
    {
    {
      /* Infinity or NaN */
      /* Infinity or NaN */
      if ((m64 == 0) && (l64 == 0))
      if ((m64 == 0) && (l64 == 0))
        {
        {
          /* +- infinity */
          /* +- infinity */
          loaded_data->sigh = 0x80000000;
          loaded_data->sigh = 0x80000000;
          loaded_data->sigl = 0x00000000;
          loaded_data->sigl = 0x00000000;
          loaded_data->exp = EXP_Infinity;
          loaded_data->exp = EXP_Infinity;
          loaded_data->tag = TW_Infinity;
          loaded_data->tag = TW_Infinity;
          return 0;
          return 0;
        }
        }
      else
      else
        {
        {
          /* Must be a signaling or quiet NaN */
          /* Must be a signaling or quiet NaN */
          loaded_data->exp = EXP_NaN;
          loaded_data->exp = EXP_NaN;
          loaded_data->tag = TW_NaN;
          loaded_data->tag = TW_NaN;
          loaded_data->sigh = (m64 << 11) | 0x80000000;
          loaded_data->sigh = (m64 << 11) | 0x80000000;
          loaded_data->sigh |= l64 >> 21;
          loaded_data->sigh |= l64 >> 21;
          loaded_data->sigl = l64 << 11;
          loaded_data->sigl = l64 << 11;
          return 0; /* The calling function must look for NaNs */
          return 0; /* The calling function must look for NaNs */
        }
        }
    }
    }
  else if ( exp < DOUBLE_Emin )
  else if ( exp < DOUBLE_Emin )
    {
    {
      /* Zero or de-normal */
      /* Zero or de-normal */
      if ((m64 == 0) && (l64 == 0))
      if ((m64 == 0) && (l64 == 0))
        {
        {
          /* Zero */
          /* Zero */
          int c = loaded_data->sign;
          int c = loaded_data->sign;
          reg_move(&CONST_Z, loaded_data);
          reg_move(&CONST_Z, loaded_data);
          loaded_data->sign = c;
          loaded_data->sign = c;
          return 0;
          return 0;
        }
        }
      else
      else
        {
        {
          /* De-normal */
          /* De-normal */
          loaded_data->exp = DOUBLE_Emin + EXP_BIAS;
          loaded_data->exp = DOUBLE_Emin + EXP_BIAS;
          loaded_data->tag = TW_Valid;
          loaded_data->tag = TW_Valid;
          loaded_data->sigh = m64 << 11;
          loaded_data->sigh = m64 << 11;
          loaded_data->sigh |= l64 >> 21;
          loaded_data->sigh |= l64 >> 21;
          loaded_data->sigl = l64 << 11;
          loaded_data->sigl = l64 << 11;
          normalize_nuo(loaded_data);
          normalize_nuo(loaded_data);
          return denormal_operand();
          return denormal_operand();
        }
        }
    }
    }
  else
  else
    {
    {
      loaded_data->exp = exp + EXP_BIAS;
      loaded_data->exp = exp + EXP_BIAS;
      loaded_data->tag = TW_Valid;
      loaded_data->tag = TW_Valid;
      loaded_data->sigh = (m64 << 11) | 0x80000000;
      loaded_data->sigh = (m64 << 11) | 0x80000000;
      loaded_data->sigh |= l64 >> 21;
      loaded_data->sigh |= l64 >> 21;
      loaded_data->sigl = l64 << 11;
      loaded_data->sigl = l64 << 11;
 
 
      return 0;
      return 0;
    }
    }
}
}
 
 
 
 
/* Get a float from user memory */
/* Get a float from user memory */
int reg_load_single(float *single, FPU_REG *loaded_data)
int reg_load_single(float *single, FPU_REG *loaded_data)
{
{
  unsigned m32;
  unsigned m32;
  int exp;
  int exp;
 
 
  RE_ENTRANT_CHECK_OFF;
  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_READ, single, 4);
  FPU_verify_area(VERIFY_READ, single, 4);
  m32 = get_fs_long((unsigned long *) single);
  m32 = get_fs_long((unsigned long *) single);
  RE_ENTRANT_CHECK_ON;
  RE_ENTRANT_CHECK_ON;
 
 
  if (m32 & 0x80000000)
  if (m32 & 0x80000000)
    loaded_data->sign = SIGN_NEG;
    loaded_data->sign = SIGN_NEG;
  else
  else
    loaded_data->sign = SIGN_POS;
    loaded_data->sign = SIGN_POS;
  if (!(m32 & 0x7fffffff))
  if (!(m32 & 0x7fffffff))
    {
    {
      /* Zero */
      /* Zero */
      int c = loaded_data->sign;
      int c = loaded_data->sign;
      reg_move(&CONST_Z, loaded_data);
      reg_move(&CONST_Z, loaded_data);
      loaded_data->sign = c;
      loaded_data->sign = c;
      return 0;
      return 0;
    }
    }
  exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias;
  exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias;
  m32 = (m32 & 0x7fffff) << 8;
  m32 = (m32 & 0x7fffff) << 8;
  if ( exp < SINGLE_Emin )
  if ( exp < SINGLE_Emin )
    {
    {
      /* De-normals */
      /* De-normals */
      loaded_data->exp = SINGLE_Emin + EXP_BIAS;
      loaded_data->exp = SINGLE_Emin + EXP_BIAS;
      loaded_data->tag = TW_Valid;
      loaded_data->tag = TW_Valid;
      loaded_data->sigh = m32;
      loaded_data->sigh = m32;
      loaded_data->sigl = 0;
      loaded_data->sigl = 0;
      normalize_nuo(loaded_data);
      normalize_nuo(loaded_data);
      return denormal_operand();
      return denormal_operand();
    }
    }
  else if ( exp > SINGLE_Emax )
  else if ( exp > SINGLE_Emax )
    {
    {
    /* Infinity or NaN */
    /* Infinity or NaN */
      if ( m32 == 0 )
      if ( m32 == 0 )
        {
        {
          /* +- infinity */
          /* +- infinity */
          loaded_data->sigh = 0x80000000;
          loaded_data->sigh = 0x80000000;
          loaded_data->sigl = 0x00000000;
          loaded_data->sigl = 0x00000000;
          loaded_data->exp = EXP_Infinity;
          loaded_data->exp = EXP_Infinity;
          loaded_data->tag = TW_Infinity;
          loaded_data->tag = TW_Infinity;
          return 0;
          return 0;
        }
        }
      else
      else
        {
        {
          /* Must be a signaling or quiet NaN */
          /* Must be a signaling or quiet NaN */
          loaded_data->exp = EXP_NaN;
          loaded_data->exp = EXP_NaN;
          loaded_data->tag = TW_NaN;
          loaded_data->tag = TW_NaN;
          loaded_data->sigh = m32 | 0x80000000;
          loaded_data->sigh = m32 | 0x80000000;
          loaded_data->sigl = 0;
          loaded_data->sigl = 0;
          return 0; /* The calling function must look for NaNs */
          return 0; /* The calling function must look for NaNs */
        }
        }
    }
    }
  else
  else
    {
    {
      loaded_data->exp = exp + EXP_BIAS;
      loaded_data->exp = exp + EXP_BIAS;
      loaded_data->sigh = m32 | 0x80000000;
      loaded_data->sigh = m32 | 0x80000000;
      loaded_data->sigl = 0;
      loaded_data->sigl = 0;
      loaded_data->tag = TW_Valid;
      loaded_data->tag = TW_Valid;
      return 0;
      return 0;
    }
    }
}
}
 
 
 
 
/* Get a long long from user memory */
/* Get a long long from user memory */
void reg_load_int64(long long *_s, FPU_REG *loaded_data)
void reg_load_int64(long long *_s, FPU_REG *loaded_data)
{
{
  int e;
  int e;
  long long s;
  long long s;
 
 
  RE_ENTRANT_CHECK_OFF;
  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_READ, _s, 8);
  FPU_verify_area(VERIFY_READ, _s, 8);
  ((unsigned long *)&s)[0] = get_fs_long((unsigned long *) _s);
  ((unsigned long *)&s)[0] = get_fs_long((unsigned long *) _s);
  ((unsigned long *)&s)[1] = get_fs_long(1 + (unsigned long *) _s);
  ((unsigned long *)&s)[1] = get_fs_long(1 + (unsigned long *) _s);
  RE_ENTRANT_CHECK_ON;
  RE_ENTRANT_CHECK_ON;
 
 
  if (s == 0)
  if (s == 0)
    { reg_move(&CONST_Z, loaded_data); return; }
    { reg_move(&CONST_Z, loaded_data); return; }
 
 
  if (s > 0)
  if (s > 0)
    loaded_data->sign = SIGN_POS;
    loaded_data->sign = SIGN_POS;
  else
  else
  {
  {
    s = -s;
    s = -s;
    loaded_data->sign = SIGN_NEG;
    loaded_data->sign = SIGN_NEG;
  }
  }
 
 
  e = EXP_BIAS + 63;
  e = EXP_BIAS + 63;
  significand(loaded_data) = s;
  significand(loaded_data) = s;
  loaded_data->exp = e;
  loaded_data->exp = e;
  loaded_data->tag = TW_Valid;
  loaded_data->tag = TW_Valid;
  normalize_nuo(loaded_data);
  normalize_nuo(loaded_data);
}
}
 
 
 
 
/* Get a long from user memory */
/* Get a long from user memory */
void reg_load_int32(long *_s, FPU_REG *loaded_data)
void reg_load_int32(long *_s, FPU_REG *loaded_data)
{
{
  long s;
  long s;
  int e;
  int e;
 
 
  RE_ENTRANT_CHECK_OFF;
  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_READ, _s, 4);
  FPU_verify_area(VERIFY_READ, _s, 4);
  s = (long)get_fs_long((unsigned long *) _s);
  s = (long)get_fs_long((unsigned long *) _s);
  RE_ENTRANT_CHECK_ON;
  RE_ENTRANT_CHECK_ON;
 
 
  if (s == 0)
  if (s == 0)
    { reg_move(&CONST_Z, loaded_data); return; }
    { reg_move(&CONST_Z, loaded_data); return; }
 
 
  if (s > 0)
  if (s > 0)
    loaded_data->sign = SIGN_POS;
    loaded_data->sign = SIGN_POS;
  else
  else
  {
  {
    s = -s;
    s = -s;
    loaded_data->sign = SIGN_NEG;
    loaded_data->sign = SIGN_NEG;
  }
  }
 
 
  e = EXP_BIAS + 31;
  e = EXP_BIAS + 31;
  loaded_data->sigh = s;
  loaded_data->sigh = s;
  loaded_data->sigl = 0;
  loaded_data->sigl = 0;
  loaded_data->exp = e;
  loaded_data->exp = e;
  loaded_data->tag = TW_Valid;
  loaded_data->tag = TW_Valid;
  normalize_nuo(loaded_data);
  normalize_nuo(loaded_data);
}
}
 
 
 
 
/* Get a short from user memory */
/* Get a short from user memory */
void reg_load_int16(short *_s, FPU_REG *loaded_data)
void reg_load_int16(short *_s, FPU_REG *loaded_data)
{
{
  int s, e;
  int s, e;
 
 
  RE_ENTRANT_CHECK_OFF;
  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_READ, _s, 2);
  FPU_verify_area(VERIFY_READ, _s, 2);
  /* Cast as short to get the sign extended. */
  /* Cast as short to get the sign extended. */
  s = (short)get_fs_word((unsigned short *) _s);
  s = (short)get_fs_word((unsigned short *) _s);
  RE_ENTRANT_CHECK_ON;
  RE_ENTRANT_CHECK_ON;
 
 
  if (s == 0)
  if (s == 0)
    { reg_move(&CONST_Z, loaded_data); return; }
    { reg_move(&CONST_Z, loaded_data); return; }
 
 
  if (s > 0)
  if (s > 0)
    loaded_data->sign = SIGN_POS;
    loaded_data->sign = SIGN_POS;
  else
  else
  {
  {
    s = -s;
    s = -s;
    loaded_data->sign = SIGN_NEG;
    loaded_data->sign = SIGN_NEG;
  }
  }
 
 
  e = EXP_BIAS + 15;
  e = EXP_BIAS + 15;
  loaded_data->sigh = s << 16;
  loaded_data->sigh = s << 16;
 
 
  loaded_data->sigl = 0;
  loaded_data->sigl = 0;
  loaded_data->exp = e;
  loaded_data->exp = e;
  loaded_data->tag = TW_Valid;
  loaded_data->tag = TW_Valid;
  normalize_nuo(loaded_data);
  normalize_nuo(loaded_data);
}
}
 
 
 
 
/* Get a packed bcd array from user memory */
/* Get a packed bcd array from user memory */
void reg_load_bcd(char *s, FPU_REG *loaded_data)
void reg_load_bcd(char *s, FPU_REG *loaded_data)
{
{
  int pos;
  int pos;
  unsigned char bcd;
  unsigned char bcd;
  long long l=0;
  long long l=0;
 
 
  RE_ENTRANT_CHECK_OFF;
  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_READ, s, 10);
  FPU_verify_area(VERIFY_READ, s, 10);
  RE_ENTRANT_CHECK_ON;
  RE_ENTRANT_CHECK_ON;
  for ( pos = 8; pos >= 0; pos--)
  for ( pos = 8; pos >= 0; pos--)
    {
    {
      l *= 10;
      l *= 10;
      RE_ENTRANT_CHECK_OFF;
      RE_ENTRANT_CHECK_OFF;
      bcd = (unsigned char)get_fs_byte((unsigned char *) s+pos);
      bcd = (unsigned char)get_fs_byte((unsigned char *) s+pos);
      RE_ENTRANT_CHECK_ON;
      RE_ENTRANT_CHECK_ON;
      l += bcd >> 4;
      l += bcd >> 4;
      l *= 10;
      l *= 10;
      l += bcd & 0x0f;
      l += bcd & 0x0f;
    }
    }
 
 
  RE_ENTRANT_CHECK_OFF;
  RE_ENTRANT_CHECK_OFF;
  loaded_data->sign =
  loaded_data->sign =
    ((unsigned char)get_fs_byte((unsigned char *) s+9)) & 0x80 ?
    ((unsigned char)get_fs_byte((unsigned char *) s+9)) & 0x80 ?
      SIGN_NEG : SIGN_POS;
      SIGN_NEG : SIGN_POS;
  RE_ENTRANT_CHECK_ON;
  RE_ENTRANT_CHECK_ON;
 
 
  if (l == 0)
  if (l == 0)
    {
    {
      char sign = loaded_data->sign;
      char sign = loaded_data->sign;
      reg_move(&CONST_Z, loaded_data);
      reg_move(&CONST_Z, loaded_data);
      loaded_data->sign = sign;
      loaded_data->sign = sign;
    }
    }
  else
  else
    {
    {
      significand(loaded_data) = l;
      significand(loaded_data) = l;
      loaded_data->exp = EXP_BIAS + 63;
      loaded_data->exp = EXP_BIAS + 63;
      loaded_data->tag = TW_Valid;
      loaded_data->tag = TW_Valid;
      normalize_nuo(loaded_data);
      normalize_nuo(loaded_data);
    }
    }
}
}
 
 
/*===========================================================================*/
/*===========================================================================*/
 
 
/* Put a long double into user memory */
/* Put a long double into user memory */
int reg_store_extended(long double *d, FPU_REG *st0_ptr)
int reg_store_extended(long double *d, FPU_REG *st0_ptr)
{
{
  /*
  /*
    The only exception raised by an attempt to store to an
    The only exception raised by an attempt to store to an
    extended format is the Invalid Stack exception, i.e.
    extended format is the Invalid Stack exception, i.e.
    attempting to store from an empty register.
    attempting to store from an empty register.
   */
   */
 
 
  if ( st0_ptr->tag != TW_Empty )
  if ( st0_ptr->tag != TW_Empty )
    {
    {
      RE_ENTRANT_CHECK_OFF;
      RE_ENTRANT_CHECK_OFF;
      FPU_verify_area(VERIFY_WRITE, d, 10);
      FPU_verify_area(VERIFY_WRITE, d, 10);
      RE_ENTRANT_CHECK_ON;
      RE_ENTRANT_CHECK_ON;
      write_to_extended(st0_ptr, (char *) d);
      write_to_extended(st0_ptr, (char *) d);
      return 1;
      return 1;
    }
    }
 
 
  /* Empty register (stack underflow) */
  /* Empty register (stack underflow) */
  EXCEPTION(EX_StackUnder);
  EXCEPTION(EX_StackUnder);
  if ( control_word & CW_Invalid )
  if ( control_word & CW_Invalid )
    {
    {
      /* The masked response */
      /* The masked response */
      /* Put out the QNaN indefinite */
      /* Put out the QNaN indefinite */
      RE_ENTRANT_CHECK_OFF;
      RE_ENTRANT_CHECK_OFF;
      FPU_verify_area(VERIFY_WRITE,d,10);
      FPU_verify_area(VERIFY_WRITE,d,10);
      put_fs_long(0, (unsigned long *) d);
      put_fs_long(0, (unsigned long *) d);
      put_fs_long(0xc0000000, 1 + (unsigned long *) d);
      put_fs_long(0xc0000000, 1 + (unsigned long *) d);
      put_fs_word(0xffff, 4 + (short *) d);
      put_fs_word(0xffff, 4 + (short *) d);
      RE_ENTRANT_CHECK_ON;
      RE_ENTRANT_CHECK_ON;
      return 1;
      return 1;
    }
    }
  else
  else
    return 0;
    return 0;
 
 
}
}
 
 
 
 
/* Put a double into user memory */
/* Put a double into user memory */
int reg_store_double(double *dfloat, FPU_REG *st0_ptr)
int reg_store_double(double *dfloat, FPU_REG *st0_ptr)
{
{
  unsigned long l[2];
  unsigned long l[2];
  unsigned long increment = 0;   /* avoid gcc warnings */
  unsigned long increment = 0;   /* avoid gcc warnings */
  char st0_tag = st0_ptr->tag;
  char st0_tag = st0_ptr->tag;
 
 
  if (st0_tag == TW_Valid)
  if (st0_tag == TW_Valid)
    {
    {
      int precision_loss;
      int precision_loss;
      int exp;
      int exp;
      FPU_REG tmp;
      FPU_REG tmp;
 
 
      reg_move(st0_ptr, &tmp);
      reg_move(st0_ptr, &tmp);
      exp = tmp.exp - EXP_BIAS;
      exp = tmp.exp - EXP_BIAS;
 
 
      if ( exp < DOUBLE_Emin )     /* It may be a denormal */
      if ( exp < DOUBLE_Emin )     /* It may be a denormal */
        {
        {
          /* A denormal will always underflow. */
          /* A denormal will always underflow. */
#ifndef PECULIAR_486
#ifndef PECULIAR_486
          /* An 80486 is supposed to be able to generate
          /* An 80486 is supposed to be able to generate
             a denormal exception here, but... */
             a denormal exception here, but... */
          if ( st0_ptr->exp <= EXP_UNDER )
          if ( st0_ptr->exp <= EXP_UNDER )
            {
            {
              /* Underflow has priority. */
              /* Underflow has priority. */
              if ( control_word & CW_Underflow )
              if ( control_word & CW_Underflow )
                denormal_operand();
                denormal_operand();
            }
            }
#endif PECULIAR_486
#endif PECULIAR_486
 
 
          tmp.exp += -DOUBLE_Emin + 52;  /* largest exp to be 51 */
          tmp.exp += -DOUBLE_Emin + 52;  /* largest exp to be 51 */
 
 
          if ( (precision_loss = round_to_int(&tmp)) )
          if ( (precision_loss = round_to_int(&tmp)) )
            {
            {
#ifdef PECULIAR_486
#ifdef PECULIAR_486
              /* Did it round to a non-denormal ? */
              /* Did it round to a non-denormal ? */
              /* This behaviour might be regarded as peculiar, it appears
              /* This behaviour might be regarded as peculiar, it appears
                 that the 80486 rounds to the dest precision, then
                 that the 80486 rounds to the dest precision, then
                 converts to decide underflow. */
                 converts to decide underflow. */
              if ( !((tmp.sigh == 0x00100000) && (tmp.sigl == 0) &&
              if ( !((tmp.sigh == 0x00100000) && (tmp.sigl == 0) &&
                  (st0_ptr->sigl & 0x000007ff)) )
                  (st0_ptr->sigl & 0x000007ff)) )
#endif PECULIAR_486
#endif PECULIAR_486
                {
                {
                  EXCEPTION(EX_Underflow);
                  EXCEPTION(EX_Underflow);
                  /* This is a special case: see sec 16.2.5.1 of
                  /* This is a special case: see sec 16.2.5.1 of
                     the 80486 book */
                     the 80486 book */
                  if ( !(control_word & CW_Underflow) )
                  if ( !(control_word & CW_Underflow) )
                    return 0;
                    return 0;
                }
                }
              EXCEPTION(precision_loss);
              EXCEPTION(precision_loss);
              if ( !(control_word & CW_Precision) )
              if ( !(control_word & CW_Precision) )
                return 0;
                return 0;
            }
            }
          l[0] = tmp.sigl;
          l[0] = tmp.sigl;
          l[1] = tmp.sigh;
          l[1] = tmp.sigh;
        }
        }
      else
      else
        {
        {
          if ( tmp.sigl & 0x000007ff )
          if ( tmp.sigl & 0x000007ff )
            {
            {
              precision_loss = 1;
              precision_loss = 1;
              switch (control_word & CW_RC)
              switch (control_word & CW_RC)
                {
                {
                case RC_RND:
                case RC_RND:
                  /* Rounding can get a little messy.. */
                  /* Rounding can get a little messy.. */
                  increment = ((tmp.sigl & 0x7ff) > 0x400) |  /* nearest */
                  increment = ((tmp.sigl & 0x7ff) > 0x400) |  /* nearest */
                    ((tmp.sigl & 0xc00) == 0xc00);            /* odd -> even */
                    ((tmp.sigl & 0xc00) == 0xc00);            /* odd -> even */
                  break;
                  break;
                case RC_DOWN:   /* towards -infinity */
                case RC_DOWN:   /* towards -infinity */
                  increment = (tmp.sign == SIGN_POS) ? 0 : tmp.sigl & 0x7ff;
                  increment = (tmp.sign == SIGN_POS) ? 0 : tmp.sigl & 0x7ff;
                  break;
                  break;
                case RC_UP:     /* towards +infinity */
                case RC_UP:     /* towards +infinity */
                  increment = (tmp.sign == SIGN_POS) ? tmp.sigl & 0x7ff : 0;
                  increment = (tmp.sign == SIGN_POS) ? tmp.sigl & 0x7ff : 0;
                  break;
                  break;
                case RC_CHOP:
                case RC_CHOP:
                  increment = 0;
                  increment = 0;
                  break;
                  break;
                }
                }
 
 
              /* Truncate the mantissa */
              /* Truncate the mantissa */
              tmp.sigl &= 0xfffff800;
              tmp.sigl &= 0xfffff800;
 
 
              if ( increment )
              if ( increment )
                {
                {
                  if ( tmp.sigl >= 0xfffff800 )
                  if ( tmp.sigl >= 0xfffff800 )
                    {
                    {
                      /* the sigl part overflows */
                      /* the sigl part overflows */
                      if ( tmp.sigh == 0xffffffff )
                      if ( tmp.sigh == 0xffffffff )
                        {
                        {
                          /* The sigh part overflows */
                          /* The sigh part overflows */
                          tmp.sigh = 0x80000000;
                          tmp.sigh = 0x80000000;
                          exp++;
                          exp++;
                          if (exp >= EXP_OVER)
                          if (exp >= EXP_OVER)
                            goto overflow;
                            goto overflow;
                        }
                        }
                      else
                      else
                        {
                        {
                          tmp.sigh ++;
                          tmp.sigh ++;
                        }
                        }
                      tmp.sigl = 0x00000000;
                      tmp.sigl = 0x00000000;
                    }
                    }
                  else
                  else
                    {
                    {
                      /* We only need to increment sigl */
                      /* We only need to increment sigl */
                      tmp.sigl += 0x00000800;
                      tmp.sigl += 0x00000800;
                    }
                    }
                }
                }
            }
            }
          else
          else
            precision_loss = 0;
            precision_loss = 0;
 
 
          l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21);
          l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21);
          l[1] = ((tmp.sigh >> 11) & 0xfffff);
          l[1] = ((tmp.sigh >> 11) & 0xfffff);
 
 
          if ( exp > DOUBLE_Emax )
          if ( exp > DOUBLE_Emax )
            {
            {
            overflow:
            overflow:
              EXCEPTION(EX_Overflow);
              EXCEPTION(EX_Overflow);
              if ( !(control_word & CW_Overflow) )
              if ( !(control_word & CW_Overflow) )
                return 0;
                return 0;
              set_precision_flag_up();
              set_precision_flag_up();
              if ( !(control_word & CW_Precision) )
              if ( !(control_word & CW_Precision) )
                return 0;
                return 0;
 
 
              /* This is a special case: see sec 16.2.5.1 of the 80486 book */
              /* This is a special case: see sec 16.2.5.1 of the 80486 book */
              /* Overflow to infinity */
              /* Overflow to infinity */
              l[0] = 0x00000000; /* Set to */
              l[0] = 0x00000000; /* Set to */
              l[1] = 0x7ff00000;        /* + INF */
              l[1] = 0x7ff00000;        /* + INF */
            }
            }
          else
          else
            {
            {
              if ( precision_loss )
              if ( precision_loss )
                {
                {
                  if ( increment )
                  if ( increment )
                    set_precision_flag_up();
                    set_precision_flag_up();
                  else
                  else
                    set_precision_flag_down();
                    set_precision_flag_down();
                }
                }
              /* Add the exponent */
              /* Add the exponent */
              l[1] |= (((exp+DOUBLE_Ebias) & 0x7ff) << 20);
              l[1] |= (((exp+DOUBLE_Ebias) & 0x7ff) << 20);
            }
            }
        }
        }
    }
    }
  else if (st0_tag == TW_Zero)
  else if (st0_tag == TW_Zero)
    {
    {
      /* Number is zero */
      /* Number is zero */
      l[0] = 0;
      l[0] = 0;
      l[1] = 0;
      l[1] = 0;
    }
    }
  else if (st0_tag == TW_Infinity)
  else if (st0_tag == TW_Infinity)
    {
    {
      l[0] = 0;
      l[0] = 0;
      l[1] = 0x7ff00000;
      l[1] = 0x7ff00000;
    }
    }
  else if (st0_tag == TW_NaN)
  else if (st0_tag == TW_NaN)
    {
    {
      /* See if we can get a valid NaN from the FPU_REG */
      /* See if we can get a valid NaN from the FPU_REG */
      l[0] = (st0_ptr->sigl >> 11) | (st0_ptr->sigh << 21);
      l[0] = (st0_ptr->sigl >> 11) | (st0_ptr->sigh << 21);
      l[1] = ((st0_ptr->sigh >> 11) & 0xfffff);
      l[1] = ((st0_ptr->sigh >> 11) & 0xfffff);
      if ( !(st0_ptr->sigh & 0x40000000) )
      if ( !(st0_ptr->sigh & 0x40000000) )
        {
        {
          /* It is a signalling NaN */
          /* It is a signalling NaN */
          EXCEPTION(EX_Invalid);
          EXCEPTION(EX_Invalid);
          if ( !(control_word & CW_Invalid) )
          if ( !(control_word & CW_Invalid) )
            return 0;
            return 0;
          l[1] |= (0x40000000 >> 11);
          l[1] |= (0x40000000 >> 11);
        }
        }
      l[1] |= 0x7ff00000;
      l[1] |= 0x7ff00000;
    }
    }
  else if ( st0_tag == TW_Empty )
  else if ( st0_tag == TW_Empty )
    {
    {
      /* Empty register (stack underflow) */
      /* Empty register (stack underflow) */
      EXCEPTION(EX_StackUnder);
      EXCEPTION(EX_StackUnder);
      if ( control_word & CW_Invalid )
      if ( control_word & CW_Invalid )
        {
        {
          /* The masked response */
          /* The masked response */
          /* Put out the QNaN indefinite */
          /* Put out the QNaN indefinite */
          RE_ENTRANT_CHECK_OFF;
          RE_ENTRANT_CHECK_OFF;
          FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8);
          FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8);
          put_fs_long(0, (unsigned long *) dfloat);
          put_fs_long(0, (unsigned long *) dfloat);
          put_fs_long(0xfff80000, 1 + (unsigned long *) dfloat);
          put_fs_long(0xfff80000, 1 + (unsigned long *) dfloat);
          RE_ENTRANT_CHECK_ON;
          RE_ENTRANT_CHECK_ON;
          return 1;
          return 1;
        }
        }
      else
      else
        return 0;
        return 0;
    }
    }
  if ( st0_ptr->sign )
  if ( st0_ptr->sign )
    l[1] |= 0x80000000;
    l[1] |= 0x80000000;
 
 
  RE_ENTRANT_CHECK_OFF;
  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8);
  FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8);
  put_fs_long(l[0], (unsigned long *)dfloat);
  put_fs_long(l[0], (unsigned long *)dfloat);
  put_fs_long(l[1], 1 + (unsigned long *)dfloat);
  put_fs_long(l[1], 1 + (unsigned long *)dfloat);
  RE_ENTRANT_CHECK_ON;
  RE_ENTRANT_CHECK_ON;
 
 
  return 1;
  return 1;
}
}
 
 
 
 
/* Put a float into user memory */
/* Put a float into user memory */
int reg_store_single(float *single, FPU_REG *st0_ptr)
int reg_store_single(float *single, FPU_REG *st0_ptr)
{
{
  long templ;
  long templ;
  unsigned long increment = 0;           /* avoid gcc warnings */
  unsigned long increment = 0;           /* avoid gcc warnings */
  char st0_tag = st0_ptr->tag;
  char st0_tag = st0_ptr->tag;
 
 
  if (st0_tag == TW_Valid)
  if (st0_tag == TW_Valid)
    {
    {
      int precision_loss;
      int precision_loss;
      int exp;
      int exp;
      FPU_REG tmp;
      FPU_REG tmp;
 
 
      reg_move(st0_ptr, &tmp);
      reg_move(st0_ptr, &tmp);
      exp = tmp.exp - EXP_BIAS;
      exp = tmp.exp - EXP_BIAS;
 
 
      if ( exp < SINGLE_Emin )
      if ( exp < SINGLE_Emin )
        {
        {
          /* A denormal will always underflow. */
          /* A denormal will always underflow. */
#ifndef PECULIAR_486
#ifndef PECULIAR_486
          /* An 80486 is supposed to be able to generate
          /* An 80486 is supposed to be able to generate
             a denormal exception here, but... */
             a denormal exception here, but... */
          if ( st0_ptr->exp <= EXP_UNDER )
          if ( st0_ptr->exp <= EXP_UNDER )
            {
            {
              /* Underflow has priority. */
              /* Underflow has priority. */
              if ( control_word & CW_Underflow )
              if ( control_word & CW_Underflow )
                denormal_operand();
                denormal_operand();
            }
            }
#endif PECULIAR_486
#endif PECULIAR_486
 
 
          tmp.exp += -SINGLE_Emin + 23;  /* largest exp to be 22 */
          tmp.exp += -SINGLE_Emin + 23;  /* largest exp to be 22 */
 
 
          if ( (precision_loss = round_to_int(&tmp)) )
          if ( (precision_loss = round_to_int(&tmp)) )
            {
            {
#ifdef PECULIAR_486
#ifdef PECULIAR_486
              /* Did it round to a non-denormal ? */
              /* Did it round to a non-denormal ? */
              /* This behaviour might be regarded as peculiar, it appears
              /* This behaviour might be regarded as peculiar, it appears
                 that the 80486 rounds to the dest precision, then
                 that the 80486 rounds to the dest precision, then
                 converts to decide underflow. */
                 converts to decide underflow. */
              if ( !((tmp.sigl == 0x00800000) &&
              if ( !((tmp.sigl == 0x00800000) &&
                  ((st0_ptr->sigh & 0x000000ff) || st0_ptr->sigl)) )
                  ((st0_ptr->sigh & 0x000000ff) || st0_ptr->sigl)) )
#endif PECULIAR_486
#endif PECULIAR_486
                {
                {
                  EXCEPTION(EX_Underflow);
                  EXCEPTION(EX_Underflow);
                  /* This is a special case: see sec 16.2.5.1 of
                  /* This is a special case: see sec 16.2.5.1 of
                     the 80486 book */
                     the 80486 book */
                  if ( !(control_word & EX_Underflow) )
                  if ( !(control_word & EX_Underflow) )
                    return 0;
                    return 0;
                }
                }
              EXCEPTION(precision_loss);
              EXCEPTION(precision_loss);
              if ( !(control_word & EX_Precision) )
              if ( !(control_word & EX_Precision) )
                return 0;
                return 0;
            }
            }
          templ = tmp.sigl;
          templ = tmp.sigl;
        }
        }
      else
      else
        {
        {
          if ( tmp.sigl | (tmp.sigh & 0x000000ff) )
          if ( tmp.sigl | (tmp.sigh & 0x000000ff) )
            {
            {
              unsigned long sigh = tmp.sigh;
              unsigned long sigh = tmp.sigh;
              unsigned long sigl = tmp.sigl;
              unsigned long sigl = tmp.sigl;
 
 
              precision_loss = 1;
              precision_loss = 1;
              switch (control_word & CW_RC)
              switch (control_word & CW_RC)
                {
                {
                case RC_RND:
                case RC_RND:
                  increment = ((sigh & 0xff) > 0x80)       /* more than half */
                  increment = ((sigh & 0xff) > 0x80)       /* more than half */
                    || (((sigh & 0xff) == 0x80) && sigl)   /* more than half */
                    || (((sigh & 0xff) == 0x80) && sigl)   /* more than half */
                      || ((sigh & 0x180) == 0x180);        /* round to even */
                      || ((sigh & 0x180) == 0x180);        /* round to even */
                  break;
                  break;
                case RC_DOWN:   /* towards -infinity */
                case RC_DOWN:   /* towards -infinity */
                  increment = (tmp.sign == SIGN_POS)
                  increment = (tmp.sign == SIGN_POS)
                              ? 0 : (sigl | (sigh & 0xff));
                              ? 0 : (sigl | (sigh & 0xff));
                  break;
                  break;
                case RC_UP:     /* towards +infinity */
                case RC_UP:     /* towards +infinity */
                  increment = (tmp.sign == SIGN_POS)
                  increment = (tmp.sign == SIGN_POS)
                              ? (sigl | (sigh & 0xff)) : 0;
                              ? (sigl | (sigh & 0xff)) : 0;
                  break;
                  break;
                case RC_CHOP:
                case RC_CHOP:
                  increment = 0;
                  increment = 0;
                  break;
                  break;
                }
                }
 
 
              /* Truncate part of the mantissa */
              /* Truncate part of the mantissa */
              tmp.sigl = 0;
              tmp.sigl = 0;
 
 
              if (increment)
              if (increment)
                {
                {
                  if ( sigh >= 0xffffff00 )
                  if ( sigh >= 0xffffff00 )
                    {
                    {
                      /* The sigh part overflows */
                      /* The sigh part overflows */
                      tmp.sigh = 0x80000000;
                      tmp.sigh = 0x80000000;
                      exp++;
                      exp++;
                      if ( exp >= EXP_OVER )
                      if ( exp >= EXP_OVER )
                        goto overflow;
                        goto overflow;
                    }
                    }
                  else
                  else
                    {
                    {
                      tmp.sigh &= 0xffffff00;
                      tmp.sigh &= 0xffffff00;
                      tmp.sigh += 0x100;
                      tmp.sigh += 0x100;
                    }
                    }
                }
                }
              else
              else
                {
                {
                  tmp.sigh &= 0xffffff00;  /* Finish the truncation */
                  tmp.sigh &= 0xffffff00;  /* Finish the truncation */
                }
                }
            }
            }
          else
          else
            precision_loss = 0;
            precision_loss = 0;
 
 
          templ = (tmp.sigh >> 8) & 0x007fffff;
          templ = (tmp.sigh >> 8) & 0x007fffff;
 
 
          if ( exp > SINGLE_Emax )
          if ( exp > SINGLE_Emax )
            {
            {
            overflow:
            overflow:
              EXCEPTION(EX_Overflow);
              EXCEPTION(EX_Overflow);
              if ( !(control_word & CW_Overflow) )
              if ( !(control_word & CW_Overflow) )
                return 0;
                return 0;
              set_precision_flag_up();
              set_precision_flag_up();
              if ( !(control_word & CW_Precision) )
              if ( !(control_word & CW_Precision) )
                return 0;
                return 0;
 
 
              /* This is a special case: see sec 16.2.5.1 of the 80486 book. */
              /* This is a special case: see sec 16.2.5.1 of the 80486 book. */
              /* Masked response is overflow to infinity. */
              /* Masked response is overflow to infinity. */
              templ = 0x7f800000;
              templ = 0x7f800000;
            }
            }
          else
          else
            {
            {
              if ( precision_loss )
              if ( precision_loss )
                {
                {
                  if ( increment )
                  if ( increment )
                    set_precision_flag_up();
                    set_precision_flag_up();
                  else
                  else
                    set_precision_flag_down();
                    set_precision_flag_down();
                }
                }
              /* Add the exponent */
              /* Add the exponent */
              templ |= ((exp+SINGLE_Ebias) & 0xff) << 23;
              templ |= ((exp+SINGLE_Ebias) & 0xff) << 23;
            }
            }
        }
        }
    }
    }
  else if (st0_tag == TW_Zero)
  else if (st0_tag == TW_Zero)
    {
    {
      templ = 0;
      templ = 0;
    }
    }
  else if (st0_tag == TW_Infinity)
  else if (st0_tag == TW_Infinity)
    {
    {
      templ = 0x7f800000;
      templ = 0x7f800000;
    }
    }
  else if (st0_tag == TW_NaN)
  else if (st0_tag == TW_NaN)
    {
    {
      /* See if we can get a valid NaN from the FPU_REG */
      /* See if we can get a valid NaN from the FPU_REG */
      templ = st0_ptr->sigh >> 8;
      templ = st0_ptr->sigh >> 8;
      if ( !(st0_ptr->sigh & 0x40000000) )
      if ( !(st0_ptr->sigh & 0x40000000) )
        {
        {
          /* It is a signalling NaN */
          /* It is a signalling NaN */
          EXCEPTION(EX_Invalid);
          EXCEPTION(EX_Invalid);
          if ( !(control_word & CW_Invalid) )
          if ( !(control_word & CW_Invalid) )
            return 0;
            return 0;
          templ |= (0x40000000 >> 8);
          templ |= (0x40000000 >> 8);
        }
        }
      templ |= 0x7f800000;
      templ |= 0x7f800000;
    }
    }
  else if ( st0_tag == TW_Empty )
  else if ( st0_tag == TW_Empty )
    {
    {
      /* Empty register (stack underflow) */
      /* Empty register (stack underflow) */
      EXCEPTION(EX_StackUnder);
      EXCEPTION(EX_StackUnder);
      if ( control_word & EX_Invalid )
      if ( control_word & EX_Invalid )
        {
        {
          /* The masked response */
          /* The masked response */
          /* Put out the QNaN indefinite */
          /* Put out the QNaN indefinite */
          RE_ENTRANT_CHECK_OFF;
          RE_ENTRANT_CHECK_OFF;
          FPU_verify_area(VERIFY_WRITE,(void *)single,4);
          FPU_verify_area(VERIFY_WRITE,(void *)single,4);
          put_fs_long(0xffc00000, (unsigned long *) single);
          put_fs_long(0xffc00000, (unsigned long *) single);
          RE_ENTRANT_CHECK_ON;
          RE_ENTRANT_CHECK_ON;
          return 1;
          return 1;
        }
        }
      else
      else
        return 0;
        return 0;
    }
    }
#ifdef PARANOID
#ifdef PARANOID
  else
  else
    {
    {
      EXCEPTION(EX_INTERNAL|0x163);
      EXCEPTION(EX_INTERNAL|0x163);
      return 0;
      return 0;
    }
    }
#endif
#endif
  if (st0_ptr->sign)
  if (st0_ptr->sign)
    templ |= 0x80000000;
    templ |= 0x80000000;
 
 
  RE_ENTRANT_CHECK_OFF;
  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_WRITE,(void *)single,4);
  FPU_verify_area(VERIFY_WRITE,(void *)single,4);
  put_fs_long(templ,(unsigned long *) single);
  put_fs_long(templ,(unsigned long *) single);
  RE_ENTRANT_CHECK_ON;
  RE_ENTRANT_CHECK_ON;
 
 
  return 1;
  return 1;
}
}
 
 
 
 
/* Put a long long into user memory */
/* Put a long long into user memory */
int reg_store_int64(long long *d, FPU_REG *st0_ptr)
int reg_store_int64(long long *d, FPU_REG *st0_ptr)
{
{
  FPU_REG t;
  FPU_REG t;
  long long tll;
  long long tll;
  int precision_loss;
  int precision_loss;
  char st0_tag = st0_ptr->tag;
  char st0_tag = st0_ptr->tag;
 
 
  if ( st0_tag == TW_Empty )
  if ( st0_tag == TW_Empty )
    {
    {
      /* Empty register (stack underflow) */
      /* Empty register (stack underflow) */
      EXCEPTION(EX_StackUnder);
      EXCEPTION(EX_StackUnder);
      goto invalid_operand;
      goto invalid_operand;
    }
    }
  else if ( (st0_tag == TW_Infinity) ||
  else if ( (st0_tag == TW_Infinity) ||
           (st0_tag == TW_NaN) )
           (st0_tag == TW_NaN) )
    {
    {
      EXCEPTION(EX_Invalid);
      EXCEPTION(EX_Invalid);
      goto invalid_operand;
      goto invalid_operand;
    }
    }
 
 
  reg_move(st0_ptr, &t);
  reg_move(st0_ptr, &t);
  precision_loss = round_to_int(&t);
  precision_loss = round_to_int(&t);
  ((long *)&tll)[0] = t.sigl;
  ((long *)&tll)[0] = t.sigl;
  ((long *)&tll)[1] = t.sigh;
  ((long *)&tll)[1] = t.sigh;
  if ( (precision_loss == 1) ||
  if ( (precision_loss == 1) ||
      ((t.sigh & 0x80000000) &&
      ((t.sigh & 0x80000000) &&
       !((t.sigh == 0x80000000) && (t.sigl == 0) &&
       !((t.sigh == 0x80000000) && (t.sigl == 0) &&
         (t.sign == SIGN_NEG))) )
         (t.sign == SIGN_NEG))) )
    {
    {
      EXCEPTION(EX_Invalid);
      EXCEPTION(EX_Invalid);
      /* This is a special case: see sec 16.2.5.1 of the 80486 book */
      /* This is a special case: see sec 16.2.5.1 of the 80486 book */
    invalid_operand:
    invalid_operand:
      if ( control_word & EX_Invalid )
      if ( control_word & EX_Invalid )
        {
        {
          /* Produce something like QNaN "indefinite" */
          /* Produce something like QNaN "indefinite" */
          tll = 0x8000000000000000LL;
          tll = 0x8000000000000000LL;
        }
        }
      else
      else
        return 0;
        return 0;
    }
    }
  else
  else
    {
    {
      if ( precision_loss )
      if ( precision_loss )
        set_precision_flag(precision_loss);
        set_precision_flag(precision_loss);
      if ( t.sign )
      if ( t.sign )
        tll = - tll;
        tll = - tll;
    }
    }
 
 
  RE_ENTRANT_CHECK_OFF;
  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_WRITE,(void *)d,8);
  FPU_verify_area(VERIFY_WRITE,(void *)d,8);
  put_fs_long(((long *)&tll)[0],(unsigned long *) d);
  put_fs_long(((long *)&tll)[0],(unsigned long *) d);
  put_fs_long(((long *)&tll)[1],1 + (unsigned long *) d);
  put_fs_long(((long *)&tll)[1],1 + (unsigned long *) d);
  RE_ENTRANT_CHECK_ON;
  RE_ENTRANT_CHECK_ON;
 
 
  return 1;
  return 1;
}
}
 
 
 
 
/* Put a long into user memory */
/* Put a long into user memory */
int reg_store_int32(long *d, FPU_REG *st0_ptr)
int reg_store_int32(long *d, FPU_REG *st0_ptr)
{
{
  FPU_REG t;
  FPU_REG t;
  int precision_loss;
  int precision_loss;
  char st0_tag = st0_ptr->tag;
  char st0_tag = st0_ptr->tag;
 
 
  if ( st0_tag == TW_Empty )
  if ( st0_tag == TW_Empty )
    {
    {
      /* Empty register (stack underflow) */
      /* Empty register (stack underflow) */
      EXCEPTION(EX_StackUnder);
      EXCEPTION(EX_StackUnder);
      goto invalid_operand;
      goto invalid_operand;
    }
    }
  else if ( (st0_tag == TW_Infinity) ||
  else if ( (st0_tag == TW_Infinity) ||
           (st0_tag == TW_NaN) )
           (st0_tag == TW_NaN) )
    {
    {
      EXCEPTION(EX_Invalid);
      EXCEPTION(EX_Invalid);
      goto invalid_operand;
      goto invalid_operand;
    }
    }
 
 
  reg_move(st0_ptr, &t);
  reg_move(st0_ptr, &t);
  precision_loss = round_to_int(&t);
  precision_loss = round_to_int(&t);
  if (t.sigh ||
  if (t.sigh ||
      ((t.sigl & 0x80000000) &&
      ((t.sigl & 0x80000000) &&
       !((t.sigl == 0x80000000) && (t.sign == SIGN_NEG))) )
       !((t.sigl == 0x80000000) && (t.sign == SIGN_NEG))) )
    {
    {
      EXCEPTION(EX_Invalid);
      EXCEPTION(EX_Invalid);
      /* This is a special case: see sec 16.2.5.1 of the 80486 book */
      /* This is a special case: see sec 16.2.5.1 of the 80486 book */
    invalid_operand:
    invalid_operand:
      if ( control_word & EX_Invalid )
      if ( control_word & EX_Invalid )
        {
        {
          /* Produce something like QNaN "indefinite" */
          /* Produce something like QNaN "indefinite" */
          t.sigl = 0x80000000;
          t.sigl = 0x80000000;
        }
        }
      else
      else
        return 0;
        return 0;
    }
    }
  else
  else
    {
    {
      if ( precision_loss )
      if ( precision_loss )
        set_precision_flag(precision_loss);
        set_precision_flag(precision_loss);
      if ( t.sign )
      if ( t.sign )
        t.sigl = -(long)t.sigl;
        t.sigl = -(long)t.sigl;
    }
    }
 
 
  RE_ENTRANT_CHECK_OFF;
  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_WRITE,d,4);
  FPU_verify_area(VERIFY_WRITE,d,4);
  put_fs_long(t.sigl, (unsigned long *) d);
  put_fs_long(t.sigl, (unsigned long *) d);
  RE_ENTRANT_CHECK_ON;
  RE_ENTRANT_CHECK_ON;
 
 
  return 1;
  return 1;
}
}
 
 
 
 
/* Put a short into user memory */
/* Put a short into user memory */
int reg_store_int16(short *d, FPU_REG *st0_ptr)
int reg_store_int16(short *d, FPU_REG *st0_ptr)
{
{
  FPU_REG t;
  FPU_REG t;
  int precision_loss;
  int precision_loss;
  char st0_tag = st0_ptr->tag;
  char st0_tag = st0_ptr->tag;
 
 
  if ( st0_tag == TW_Empty )
  if ( st0_tag == TW_Empty )
    {
    {
      /* Empty register (stack underflow) */
      /* Empty register (stack underflow) */
      EXCEPTION(EX_StackUnder);
      EXCEPTION(EX_StackUnder);
      goto invalid_operand;
      goto invalid_operand;
    }
    }
  else if ( (st0_tag == TW_Infinity) ||
  else if ( (st0_tag == TW_Infinity) ||
           (st0_tag == TW_NaN) )
           (st0_tag == TW_NaN) )
    {
    {
      EXCEPTION(EX_Invalid);
      EXCEPTION(EX_Invalid);
      goto invalid_operand;
      goto invalid_operand;
    }
    }
 
 
  reg_move(st0_ptr, &t);
  reg_move(st0_ptr, &t);
  precision_loss = round_to_int(&t);
  precision_loss = round_to_int(&t);
  if (t.sigh ||
  if (t.sigh ||
      ((t.sigl & 0xffff8000) &&
      ((t.sigl & 0xffff8000) &&
       !((t.sigl == 0x8000) && (t.sign == SIGN_NEG))) )
       !((t.sigl == 0x8000) && (t.sign == SIGN_NEG))) )
    {
    {
      EXCEPTION(EX_Invalid);
      EXCEPTION(EX_Invalid);
      /* This is a special case: see sec 16.2.5.1 of the 80486 book */
      /* This is a special case: see sec 16.2.5.1 of the 80486 book */
    invalid_operand:
    invalid_operand:
      if ( control_word & EX_Invalid )
      if ( control_word & EX_Invalid )
        {
        {
          /* Produce something like QNaN "indefinite" */
          /* Produce something like QNaN "indefinite" */
          t.sigl = 0x8000;
          t.sigl = 0x8000;
        }
        }
      else
      else
        return 0;
        return 0;
    }
    }
  else
  else
    {
    {
      if ( precision_loss )
      if ( precision_loss )
        set_precision_flag(precision_loss);
        set_precision_flag(precision_loss);
      if ( t.sign )
      if ( t.sign )
        t.sigl = -t.sigl;
        t.sigl = -t.sigl;
    }
    }
 
 
  RE_ENTRANT_CHECK_OFF;
  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_WRITE,d,2);
  FPU_verify_area(VERIFY_WRITE,d,2);
  put_fs_word((short)t.sigl,(short *) d);
  put_fs_word((short)t.sigl,(short *) d);
  RE_ENTRANT_CHECK_ON;
  RE_ENTRANT_CHECK_ON;
 
 
  return 1;
  return 1;
}
}
 
 
 
 
/* Put a packed bcd array into user memory */
/* Put a packed bcd array into user memory */
int reg_store_bcd(char *d, FPU_REG *st0_ptr)
int reg_store_bcd(char *d, FPU_REG *st0_ptr)
{
{
  FPU_REG t;
  FPU_REG t;
  unsigned long long ll;
  unsigned long long ll;
  unsigned char b;
  unsigned char b;
  int i, precision_loss;
  int i, precision_loss;
  unsigned char sign = (st0_ptr->sign == SIGN_NEG) ? 0x80 : 0;
  unsigned char sign = (st0_ptr->sign == SIGN_NEG) ? 0x80 : 0;
  char st0_tag = st0_ptr->tag;
  char st0_tag = st0_ptr->tag;
 
 
  if ( st0_tag == TW_Empty )
  if ( st0_tag == TW_Empty )
    {
    {
      /* Empty register (stack underflow) */
      /* Empty register (stack underflow) */
      EXCEPTION(EX_StackUnder);
      EXCEPTION(EX_StackUnder);
      goto invalid_operand;
      goto invalid_operand;
    }
    }
 
 
  reg_move(st0_ptr, &t);
  reg_move(st0_ptr, &t);
  precision_loss = round_to_int(&t);
  precision_loss = round_to_int(&t);
  ll = significand(&t);
  ll = significand(&t);
 
 
  /* Check for overflow, by comparing with 999999999999999999 decimal. */
  /* Check for overflow, by comparing with 999999999999999999 decimal. */
  if ( (t.sigh > 0x0de0b6b3) ||
  if ( (t.sigh > 0x0de0b6b3) ||
      ((t.sigh == 0x0de0b6b3) && (t.sigl > 0xa763ffff)) )
      ((t.sigh == 0x0de0b6b3) && (t.sigl > 0xa763ffff)) )
    {
    {
      EXCEPTION(EX_Invalid);
      EXCEPTION(EX_Invalid);
      /* This is a special case: see sec 16.2.5.1 of the 80486 book */
      /* This is a special case: see sec 16.2.5.1 of the 80486 book */
    invalid_operand:
    invalid_operand:
      if ( control_word & CW_Invalid )
      if ( control_word & CW_Invalid )
        {
        {
          /* Produce the QNaN "indefinite" */
          /* Produce the QNaN "indefinite" */
          RE_ENTRANT_CHECK_OFF;
          RE_ENTRANT_CHECK_OFF;
          FPU_verify_area(VERIFY_WRITE,d,10);
          FPU_verify_area(VERIFY_WRITE,d,10);
          for ( i = 0; i < 7; i++)
          for ( i = 0; i < 7; i++)
            put_fs_byte(0, (unsigned char *) d+i); /* These bytes "undefined" */
            put_fs_byte(0, (unsigned char *) d+i); /* These bytes "undefined" */
          put_fs_byte(0xc0, (unsigned char *) d+7); /* This byte "undefined" */
          put_fs_byte(0xc0, (unsigned char *) d+7); /* This byte "undefined" */
          put_fs_byte(0xff, (unsigned char *) d+8);
          put_fs_byte(0xff, (unsigned char *) d+8);
          put_fs_byte(0xff, (unsigned char *) d+9);
          put_fs_byte(0xff, (unsigned char *) d+9);
          RE_ENTRANT_CHECK_ON;
          RE_ENTRANT_CHECK_ON;
          return 1;
          return 1;
        }
        }
      else
      else
        return 0;
        return 0;
    }
    }
  else if ( precision_loss )
  else if ( precision_loss )
    {
    {
      /* Precision loss doesn't stop the data transfer */
      /* Precision loss doesn't stop the data transfer */
      set_precision_flag(precision_loss);
      set_precision_flag(precision_loss);
    }
    }
 
 
  RE_ENTRANT_CHECK_OFF;
  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_WRITE,d,10);
  FPU_verify_area(VERIFY_WRITE,d,10);
  RE_ENTRANT_CHECK_ON;
  RE_ENTRANT_CHECK_ON;
  for ( i = 0; i < 9; i++)
  for ( i = 0; i < 9; i++)
    {
    {
      b = div_small(&ll, 10);
      b = div_small(&ll, 10);
      b |= (div_small(&ll, 10)) << 4;
      b |= (div_small(&ll, 10)) << 4;
      RE_ENTRANT_CHECK_OFF;
      RE_ENTRANT_CHECK_OFF;
      put_fs_byte(b,(unsigned char *) d+i);
      put_fs_byte(b,(unsigned char *) d+i);
      RE_ENTRANT_CHECK_ON;
      RE_ENTRANT_CHECK_ON;
    }
    }
  RE_ENTRANT_CHECK_OFF;
  RE_ENTRANT_CHECK_OFF;
  put_fs_byte(sign,(unsigned char *) d+9);
  put_fs_byte(sign,(unsigned char *) d+9);
  RE_ENTRANT_CHECK_ON;
  RE_ENTRANT_CHECK_ON;
 
 
  return 1;
  return 1;
}
}
 
 
/*===========================================================================*/
/*===========================================================================*/
 
 
/* r gets mangled such that sig is int, sign:
/* r gets mangled such that sig is int, sign:
   it is NOT normalized */
   it is NOT normalized */
/* The return value (in eax) is zero if the result is exact,
/* The return value (in eax) is zero if the result is exact,
   if bits are changed due to rounding, truncation, etc, then
   if bits are changed due to rounding, truncation, etc, then
   a non-zero value is returned */
   a non-zero value is returned */
/* Overflow is signalled by a non-zero return value (in eax).
/* Overflow is signalled by a non-zero return value (in eax).
   In the case of overflow, the returned significand always has the
   In the case of overflow, the returned significand always has the
   largest possible value */
   largest possible value */
int round_to_int(FPU_REG *r)
int round_to_int(FPU_REG *r)
{
{
  char     very_big;
  char     very_big;
  unsigned eax;
  unsigned eax;
 
 
  if (r->tag == TW_Zero)
  if (r->tag == TW_Zero)
    {
    {
      /* Make sure that zero is returned */
      /* Make sure that zero is returned */
      significand(r) = 0;
      significand(r) = 0;
      return 0;        /* o.k. */
      return 0;        /* o.k. */
    }
    }
 
 
  if (r->exp > EXP_BIAS + 63)
  if (r->exp > EXP_BIAS + 63)
    {
    {
      r->sigl = r->sigh = ~0;      /* The largest representable number */
      r->sigl = r->sigh = ~0;      /* The largest representable number */
      return 1;        /* overflow */
      return 1;        /* overflow */
    }
    }
 
 
  eax = shrxs(&r->sigl, EXP_BIAS + 63 - r->exp);
  eax = shrxs(&r->sigl, EXP_BIAS + 63 - r->exp);
  very_big = !(~(r->sigh) | ~(r->sigl));  /* test for 0xfff...fff */
  very_big = !(~(r->sigh) | ~(r->sigl));  /* test for 0xfff...fff */
#define half_or_more    (eax & 0x80000000)
#define half_or_more    (eax & 0x80000000)
#define frac_part       (eax)
#define frac_part       (eax)
#define more_than_half  ((eax & 0x80000001) == 0x80000001)
#define more_than_half  ((eax & 0x80000001) == 0x80000001)
  switch (control_word & CW_RC)
  switch (control_word & CW_RC)
    {
    {
    case RC_RND:
    case RC_RND:
      if ( more_than_half                       /* nearest */
      if ( more_than_half                       /* nearest */
          || (half_or_more && (r->sigl & 1)) )  /* odd -> even */
          || (half_or_more && (r->sigl & 1)) )  /* odd -> even */
        {
        {
          if ( very_big ) return 1;        /* overflow */
          if ( very_big ) return 1;        /* overflow */
          significand(r) ++;
          significand(r) ++;
          return PRECISION_LOST_UP;
          return PRECISION_LOST_UP;
        }
        }
      break;
      break;
    case RC_DOWN:
    case RC_DOWN:
      if (frac_part && r->sign)
      if (frac_part && r->sign)
        {
        {
          if ( very_big ) return 1;        /* overflow */
          if ( very_big ) return 1;        /* overflow */
          significand(r) ++;
          significand(r) ++;
          return PRECISION_LOST_UP;
          return PRECISION_LOST_UP;
        }
        }
      break;
      break;
    case RC_UP:
    case RC_UP:
      if (frac_part && !r->sign)
      if (frac_part && !r->sign)
        {
        {
          if ( very_big ) return 1;        /* overflow */
          if ( very_big ) return 1;        /* overflow */
          significand(r) ++;
          significand(r) ++;
          return PRECISION_LOST_UP;
          return PRECISION_LOST_UP;
        }
        }
      break;
      break;
    case RC_CHOP:
    case RC_CHOP:
      break;
      break;
    }
    }
 
 
  return eax ? PRECISION_LOST_DOWN : 0;
  return eax ? PRECISION_LOST_DOWN : 0;
 
 
}
}
 
 
/*===========================================================================*/
/*===========================================================================*/
 
 
char *fldenv(fpu_addr_modes addr_modes, char *s)
char *fldenv(fpu_addr_modes addr_modes, char *s)
{
{
  unsigned short tag_word = 0;
  unsigned short tag_word = 0;
  unsigned char tag;
  unsigned char tag;
  int i;
  int i;
 
 
  if ( (addr_modes.default_mode == VM86) ||
  if ( (addr_modes.default_mode == VM86) ||
      ((addr_modes.default_mode == PM16)
      ((addr_modes.default_mode == PM16)
      ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) )
      ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) )
    {
    {
      RE_ENTRANT_CHECK_OFF;
      RE_ENTRANT_CHECK_OFF;
      FPU_verify_area(VERIFY_READ, s, 0x0e);
      FPU_verify_area(VERIFY_READ, s, 0x0e);
      control_word = get_fs_word((unsigned short *) s);
      control_word = get_fs_word((unsigned short *) s);
      partial_status = get_fs_word((unsigned short *) (s+2));
      partial_status = get_fs_word((unsigned short *) (s+2));
      tag_word = get_fs_word((unsigned short *) (s+4));
      tag_word = get_fs_word((unsigned short *) (s+4));
      instruction_address.offset = get_fs_word((unsigned short *) (s+6));
      instruction_address.offset = get_fs_word((unsigned short *) (s+6));
      instruction_address.selector = get_fs_word((unsigned short *) (s+8));
      instruction_address.selector = get_fs_word((unsigned short *) (s+8));
      operand_address.offset = get_fs_word((unsigned short *) (s+0x0a));
      operand_address.offset = get_fs_word((unsigned short *) (s+0x0a));
      operand_address.selector = get_fs_word((unsigned short *) (s+0x0c));
      operand_address.selector = get_fs_word((unsigned short *) (s+0x0c));
      RE_ENTRANT_CHECK_ON;
      RE_ENTRANT_CHECK_ON;
      s += 0x0e;
      s += 0x0e;
      if ( addr_modes.default_mode == VM86 )
      if ( addr_modes.default_mode == VM86 )
        {
        {
          instruction_address.offset
          instruction_address.offset
            += (instruction_address.selector & 0xf000) << 4;
            += (instruction_address.selector & 0xf000) << 4;
          operand_address.offset += (operand_address.selector & 0xf000) << 4;
          operand_address.offset += (operand_address.selector & 0xf000) << 4;
        }
        }
    }
    }
  else
  else
    {
    {
      RE_ENTRANT_CHECK_OFF;
      RE_ENTRANT_CHECK_OFF;
      FPU_verify_area(VERIFY_READ, s, 0x1c);
      FPU_verify_area(VERIFY_READ, s, 0x1c);
      control_word = get_fs_word((unsigned short *) s);
      control_word = get_fs_word((unsigned short *) s);
      partial_status = get_fs_word((unsigned short *) (s+4));
      partial_status = get_fs_word((unsigned short *) (s+4));
      tag_word = get_fs_word((unsigned short *) (s+8));
      tag_word = get_fs_word((unsigned short *) (s+8));
      instruction_address.offset = get_fs_long((unsigned long *) (s+0x0c));
      instruction_address.offset = get_fs_long((unsigned long *) (s+0x0c));
      instruction_address.selector = get_fs_word((unsigned short *) (s+0x10));
      instruction_address.selector = get_fs_word((unsigned short *) (s+0x10));
      instruction_address.opcode = get_fs_word((unsigned short *) (s+0x12));
      instruction_address.opcode = get_fs_word((unsigned short *) (s+0x12));
      operand_address.offset = get_fs_long((unsigned long *) (s+0x14));
      operand_address.offset = get_fs_long((unsigned long *) (s+0x14));
      operand_address.selector = get_fs_long((unsigned long *) (s+0x18));
      operand_address.selector = get_fs_long((unsigned long *) (s+0x18));
      RE_ENTRANT_CHECK_ON;
      RE_ENTRANT_CHECK_ON;
      s += 0x1c;
      s += 0x1c;
    }
    }
 
 
#ifdef PECULIAR_486
#ifdef PECULIAR_486
  control_word &= ~0xe080;
  control_word &= ~0xe080;
#endif PECULIAR_486
#endif PECULIAR_486
 
 
  top = (partial_status >> SW_Top_Shift) & 7;
  top = (partial_status >> SW_Top_Shift) & 7;
 
 
  if ( partial_status & ~control_word & CW_Exceptions )
  if ( partial_status & ~control_word & CW_Exceptions )
    partial_status |= (SW_Summary | SW_Backward);
    partial_status |= (SW_Summary | SW_Backward);
  else
  else
    partial_status &= ~(SW_Summary | SW_Backward);
    partial_status &= ~(SW_Summary | SW_Backward);
 
 
  for ( i = 0; i < 8; i++ )
  for ( i = 0; i < 8; i++ )
    {
    {
      tag = tag_word & 3;
      tag = tag_word & 3;
      tag_word >>= 2;
      tag_word >>= 2;
 
 
      if ( tag == 3 )
      if ( tag == 3 )
        /* New tag is empty.  Accept it */
        /* New tag is empty.  Accept it */
        regs[i].tag = TW_Empty;
        regs[i].tag = TW_Empty;
      else if ( regs[i].tag == TW_Empty )
      else if ( regs[i].tag == TW_Empty )
        {
        {
          /* Old tag is empty and new tag is not empty.  New tag is determined
          /* Old tag is empty and new tag is not empty.  New tag is determined
             by old reg contents */
             by old reg contents */
          if ( regs[i].exp == EXP_BIAS - EXTENDED_Ebias )
          if ( regs[i].exp == EXP_BIAS - EXTENDED_Ebias )
            {
            {
              if ( !(regs[i].sigl | regs[i].sigh) )
              if ( !(regs[i].sigl | regs[i].sigh) )
                regs[i].tag = TW_Zero;
                regs[i].tag = TW_Zero;
              else
              else
                regs[i].tag = TW_Valid;
                regs[i].tag = TW_Valid;
            }
            }
          else if ( regs[i].exp == 0x7fff + EXP_BIAS - EXTENDED_Ebias )
          else if ( regs[i].exp == 0x7fff + EXP_BIAS - EXTENDED_Ebias )
            {
            {
              if ( !((regs[i].sigh & ~0x80000000) | regs[i].sigl) )
              if ( !((regs[i].sigh & ~0x80000000) | regs[i].sigl) )
                regs[i].tag = TW_Infinity;
                regs[i].tag = TW_Infinity;
              else
              else
                regs[i].tag = TW_NaN;
                regs[i].tag = TW_NaN;
            }
            }
          else
          else
            regs[i].tag = TW_Valid;
            regs[i].tag = TW_Valid;
        }
        }
      /* Else old tag is not empty and new tag is not empty.  Old tag
      /* Else old tag is not empty and new tag is not empty.  Old tag
         remains correct */
         remains correct */
    }
    }
 
 
  return s;
  return s;
}
}
 
 
 
 
void frstor(fpu_addr_modes addr_modes, char *data_address)
void frstor(fpu_addr_modes addr_modes, char *data_address)
{
{
  int i, stnr;
  int i, stnr;
  unsigned char tag;
  unsigned char tag;
  char *s = fldenv(addr_modes, data_address);
  char *s = fldenv(addr_modes, data_address);
 
 
  for ( i = 0; i < 8; i++ )
  for ( i = 0; i < 8; i++ )
    {
    {
      /* Load each register. */
      /* Load each register. */
      stnr = (i+top) & 7;
      stnr = (i+top) & 7;
      tag = regs[stnr].tag;   /* Derived from the fldenv() loaded tag word. */
      tag = regs[stnr].tag;   /* Derived from the fldenv() loaded tag word. */
      reg_load_extended((long double *)(s+i*10), &regs[stnr]);
      reg_load_extended((long double *)(s+i*10), &regs[stnr]);
      if ( tag == TW_Empty )  /* The loaded data over-rides all other cases. */
      if ( tag == TW_Empty )  /* The loaded data over-rides all other cases. */
        regs[stnr].tag = tag;
        regs[stnr].tag = tag;
    }
    }
 
 
}
}
 
 
 
 
unsigned short tag_word(void)
unsigned short tag_word(void)
{
{
  unsigned short word = 0;
  unsigned short word = 0;
  unsigned char tag;
  unsigned char tag;
  int i;
  int i;
 
 
  for ( i = 7; i >= 0; i-- )
  for ( i = 7; i >= 0; i-- )
    {
    {
      switch ( tag = regs[i].tag )
      switch ( tag = regs[i].tag )
        {
        {
        case TW_Valid:
        case TW_Valid:
          if ( regs[i].exp <= (EXP_BIAS - EXTENDED_Ebias) )
          if ( regs[i].exp <= (EXP_BIAS - EXTENDED_Ebias) )
            tag = 2;
            tag = 2;
          break;
          break;
        case TW_Infinity:
        case TW_Infinity:
        case TW_NaN:
        case TW_NaN:
          tag = 2;
          tag = 2;
          break;
          break;
        case TW_Empty:
        case TW_Empty:
          tag = 3;
          tag = 3;
          break;
          break;
          /* TW_Zero already has the correct value */
          /* TW_Zero already has the correct value */
        }
        }
      word <<= 2;
      word <<= 2;
      word |= tag;
      word |= tag;
    }
    }
  return word;
  return word;
}
}
 
 
 
 
char *fstenv(fpu_addr_modes addr_modes, char *d)
char *fstenv(fpu_addr_modes addr_modes, char *d)
{
{
  if ( (addr_modes.default_mode == VM86) ||
  if ( (addr_modes.default_mode == VM86) ||
      ((addr_modes.default_mode == PM16)
      ((addr_modes.default_mode == PM16)
      ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) )
      ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) )
    {
    {
      RE_ENTRANT_CHECK_OFF;
      RE_ENTRANT_CHECK_OFF;
      FPU_verify_area(VERIFY_WRITE,d,14);
      FPU_verify_area(VERIFY_WRITE,d,14);
#ifdef PECULIAR_486
#ifdef PECULIAR_486
      put_fs_long(control_word & ~0xe080, (unsigned short *) d);
      put_fs_long(control_word & ~0xe080, (unsigned short *) d);
#else
#else
      put_fs_word(control_word, (unsigned short *) d);
      put_fs_word(control_word, (unsigned short *) d);
#endif PECULIAR_486
#endif PECULIAR_486
      put_fs_word(status_word(), (unsigned short *) (d+2));
      put_fs_word(status_word(), (unsigned short *) (d+2));
      put_fs_word(tag_word(), (unsigned short *) (d+4));
      put_fs_word(tag_word(), (unsigned short *) (d+4));
      put_fs_word(instruction_address.offset, (unsigned short *) (d+6));
      put_fs_word(instruction_address.offset, (unsigned short *) (d+6));
      put_fs_word(operand_address.offset, (unsigned short *) (d+0x0a));
      put_fs_word(operand_address.offset, (unsigned short *) (d+0x0a));
      if ( addr_modes.default_mode == VM86 )
      if ( addr_modes.default_mode == VM86 )
        {
        {
          put_fs_word((instruction_address.offset & 0xf0000) >> 4,
          put_fs_word((instruction_address.offset & 0xf0000) >> 4,
                      (unsigned short *) (d+8));
                      (unsigned short *) (d+8));
          put_fs_word((operand_address.offset & 0xf0000) >> 4,
          put_fs_word((operand_address.offset & 0xf0000) >> 4,
                      (unsigned short *) (d+0x0c));
                      (unsigned short *) (d+0x0c));
        }
        }
      else
      else
        {
        {
          put_fs_word(instruction_address.selector, (unsigned short *) (d+8));
          put_fs_word(instruction_address.selector, (unsigned short *) (d+8));
          put_fs_word(operand_address.selector, (unsigned short *) (d+0x0c));
          put_fs_word(operand_address.selector, (unsigned short *) (d+0x0c));
        }
        }
      RE_ENTRANT_CHECK_ON;
      RE_ENTRANT_CHECK_ON;
      d += 0x0e;
      d += 0x0e;
    }
    }
  else
  else
    {
    {
      RE_ENTRANT_CHECK_OFF;
      RE_ENTRANT_CHECK_OFF;
      FPU_verify_area(VERIFY_WRITE,d,28);
      FPU_verify_area(VERIFY_WRITE,d,28);
#ifdef PECULIAR_486
#ifdef PECULIAR_486
      /* An 80486 sets all the reserved bits to 1. */
      /* An 80486 sets all the reserved bits to 1. */
      put_fs_long(0xffff0040 | (control_word & ~0xe080), (unsigned long *) d);
      put_fs_long(0xffff0040 | (control_word & ~0xe080), (unsigned long *) d);
      put_fs_long(0xffff0000 | status_word(), (unsigned long *) (d+4));
      put_fs_long(0xffff0000 | status_word(), (unsigned long *) (d+4));
      put_fs_long(0xffff0000 | tag_word(), (unsigned long *) (d+8));
      put_fs_long(0xffff0000 | tag_word(), (unsigned long *) (d+8));
#else
#else
      put_fs_word(control_word, (unsigned short *) d);
      put_fs_word(control_word, (unsigned short *) d);
      put_fs_word(status_word(), (unsigned short *) (d+4));
      put_fs_word(status_word(), (unsigned short *) (d+4));
      put_fs_word(tag_word(), (unsigned short *) (d+8));
      put_fs_word(tag_word(), (unsigned short *) (d+8));
#endif PECULIAR_486
#endif PECULIAR_486
      put_fs_long(instruction_address.offset, (unsigned long *) (d+0x0c));
      put_fs_long(instruction_address.offset, (unsigned long *) (d+0x0c));
      put_fs_word(instruction_address.selector, (unsigned short *) (d+0x10));
      put_fs_word(instruction_address.selector, (unsigned short *) (d+0x10));
      put_fs_word(instruction_address.opcode, (unsigned short *) (d+0x12));
      put_fs_word(instruction_address.opcode, (unsigned short *) (d+0x12));
      put_fs_long(operand_address.offset, (unsigned long *) (d+0x14));
      put_fs_long(operand_address.offset, (unsigned long *) (d+0x14));
#ifdef PECULIAR_486
#ifdef PECULIAR_486
      /* An 80486 sets all the reserved bits to 1. */
      /* An 80486 sets all the reserved bits to 1. */
      put_fs_word(operand_address.selector, (unsigned short *) (d+0x18));
      put_fs_word(operand_address.selector, (unsigned short *) (d+0x18));
      put_fs_word(0xffff, (unsigned short *) (d+0x1a));
      put_fs_word(0xffff, (unsigned short *) (d+0x1a));
#else
#else
      put_fs_long(operand_address.selector, (unsigned long *) (d+0x18));
      put_fs_long(operand_address.selector, (unsigned long *) (d+0x18));
#endif PECULIAR_486
#endif PECULIAR_486
      RE_ENTRANT_CHECK_ON;
      RE_ENTRANT_CHECK_ON;
      d += 0x1c;
      d += 0x1c;
    }
    }
 
 
  control_word |= CW_Exceptions;
  control_word |= CW_Exceptions;
  partial_status &= ~(SW_Summary | SW_Backward);
  partial_status &= ~(SW_Summary | SW_Backward);
 
 
  return d;
  return d;
}
}
 
 
 
 
void fsave(fpu_addr_modes addr_modes, char *data_address)
void fsave(fpu_addr_modes addr_modes, char *data_address)
{
{
  char *d;
  char *d;
  int i;
  int i;
 
 
  d = fstenv(addr_modes, data_address);
  d = fstenv(addr_modes, data_address);
  RE_ENTRANT_CHECK_OFF;
  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_WRITE,d,80);
  FPU_verify_area(VERIFY_WRITE,d,80);
  RE_ENTRANT_CHECK_ON;
  RE_ENTRANT_CHECK_ON;
  for ( i = 0; i < 8; i++ )
  for ( i = 0; i < 8; i++ )
    write_to_extended(&regs[(top + i) & 7], d + 10 * i);
    write_to_extended(&regs[(top + i) & 7], d + 10 * i);
 
 
  finit();
  finit();
 
 
}
}
 
 
/*===========================================================================*/
/*===========================================================================*/
 
 
/*
/*
  A call to this function must be preceded by a call to
  A call to this function must be preceded by a call to
  FPU_verify_area() to verify access to the 10 bytes at d
  FPU_verify_area() to verify access to the 10 bytes at d
  */
  */
static void write_to_extended(FPU_REG *rp, char *d)
static void write_to_extended(FPU_REG *rp, char *d)
{
{
  long e;
  long e;
  FPU_REG tmp;
  FPU_REG tmp;
 
 
  e = rp->exp - EXP_BIAS + EXTENDED_Ebias;
  e = rp->exp - EXP_BIAS + EXTENDED_Ebias;
 
 
#ifdef PARANOID
#ifdef PARANOID
  switch ( rp->tag )
  switch ( rp->tag )
    {
    {
    case TW_Zero:
    case TW_Zero:
      if ( rp->sigh | rp->sigl | e )
      if ( rp->sigh | rp->sigl | e )
        EXCEPTION(EX_INTERNAL | 0x160);
        EXCEPTION(EX_INTERNAL | 0x160);
      break;
      break;
    case TW_Infinity:
    case TW_Infinity:
    case TW_NaN:
    case TW_NaN:
      if ( (e ^ 0x7fff) | !(rp->sigh & 0x80000000) )
      if ( (e ^ 0x7fff) | !(rp->sigh & 0x80000000) )
        EXCEPTION(EX_INTERNAL | 0x161);
        EXCEPTION(EX_INTERNAL | 0x161);
      break;
      break;
    default:
    default:
      if (e > 0x7fff || e < -63)
      if (e > 0x7fff || e < -63)
        EXCEPTION(EX_INTERNAL | 0x162);
        EXCEPTION(EX_INTERNAL | 0x162);
    }
    }
#endif PARANOID
#endif PARANOID
 
 
  /*
  /*
    All numbers except denormals are stored internally in a
    All numbers except denormals are stored internally in a
    format which is compatible with the extended real number
    format which is compatible with the extended real number
    format.
    format.
   */
   */
  if ( e > 0 )
  if ( e > 0 )
    {
    {
      /* just copy the reg */
      /* just copy the reg */
      RE_ENTRANT_CHECK_OFF;
      RE_ENTRANT_CHECK_OFF;
      put_fs_long(rp->sigl, (unsigned long *) d);
      put_fs_long(rp->sigl, (unsigned long *) d);
      put_fs_long(rp->sigh, (unsigned long *) (d + 4));
      put_fs_long(rp->sigh, (unsigned long *) (d + 4));
      RE_ENTRANT_CHECK_ON;
      RE_ENTRANT_CHECK_ON;
    }
    }
  else
  else
    {
    {
      /*
      /*
        The number is a de-normal stored as a normal using our
        The number is a de-normal stored as a normal using our
        extra exponent range, or is Zero.
        extra exponent range, or is Zero.
        Convert it back to a de-normal, or leave it as Zero.
        Convert it back to a de-normal, or leave it as Zero.
       */
       */
      reg_move(rp, &tmp);
      reg_move(rp, &tmp);
      tmp.exp += -EXTENDED_Emin + 63;  /* largest exp to be 63 */
      tmp.exp += -EXTENDED_Emin + 63;  /* largest exp to be 63 */
      round_to_int(&tmp);
      round_to_int(&tmp);
      e = 0;
      e = 0;
      RE_ENTRANT_CHECK_OFF;
      RE_ENTRANT_CHECK_OFF;
      put_fs_long(tmp.sigl, (unsigned long *) d);
      put_fs_long(tmp.sigl, (unsigned long *) d);
      put_fs_long(tmp.sigh, (unsigned long *) (d + 4));
      put_fs_long(tmp.sigh, (unsigned long *) (d + 4));
      RE_ENTRANT_CHECK_ON;
      RE_ENTRANT_CHECK_ON;
    }
    }
  e |= rp->sign == SIGN_POS ? 0 : 0x8000;
  e |= rp->sign == SIGN_POS ? 0 : 0x8000;
  RE_ENTRANT_CHECK_OFF;
  RE_ENTRANT_CHECK_OFF;
  put_fs_word(e, (unsigned short *) (d + 8));
  put_fs_word(e, (unsigned short *) (d + 8));
  RE_ENTRANT_CHECK_ON;
  RE_ENTRANT_CHECK_ON;
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.