⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 reg_ld_str.c

📁 LINUX 1.0 内核c源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/*---------------------------------------------------------------------------+
 |  reg_ld_str.c                                                             |
 |                                                                           |
 | All of the functions which transfer data between user memory and FPU_REGs.|
 |                                                                           |
 | Copyright (C) 1992,1993,1994                                              |
 |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
 |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
 |                                                                           |
 |                                                                           |
 +---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------+
 | Note:                                                                     |
 |    The file contains code which accesses user memory.                     |
 |    Emulator static data may change when user memory is accessed, due to   |
 |    other processes using the emulator while swapping is in progress.      |
 +---------------------------------------------------------------------------*/

#include <asm/segment.h>

#include "fpu_system.h"
#include "exception.h"
#include "reg_constant.h"
#include "fpu_emu.h"
#include "control_w.h"
#include "status_w.h"


#define EXTENDED_Ebias 0x3fff
#define EXTENDED_Emin (-0x3ffe)  /* smallest valid exponent */

#define DOUBLE_Emax 1023         /* largest valid exponent */
#define DOUBLE_Ebias 1023
#define DOUBLE_Emin (-1022)      /* smallest valid exponent */

#define SINGLE_Emax 127          /* largest valid exponent */
#define SINGLE_Ebias 127
#define SINGLE_Emin (-126)       /* smallest valid exponent */

static void write_to_extended(FPU_REG *rp, char *d);

FPU_REG FPU_loaded_data;


/* Get a long double from user memory */
int reg_load_extended(void)
{
  long double *s = (long double *)FPU_data_address;
  unsigned long sigl, sigh, exp;

  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_READ, s, 10);
  /* Use temporary variables here because FPU_loaded data is
     static and hence re-entrancy problems can arise */
  sigl = get_fs_long((unsigned long *) s);
  sigh = get_fs_long(1 + (unsigned long *) s);
  exp = get_fs_word(4 + (unsigned short *) s);
  RE_ENTRANT_CHECK_ON;

  FPU_loaded_data.tag = TW_Valid;   /* Default */
  FPU_loaded_data.sigl = sigl;
  FPU_loaded_data.sigh = sigh;
  if (exp & 0x8000)
    FPU_loaded_data.sign = SIGN_NEG;
  else
    FPU_loaded_data.sign = SIGN_POS;
  exp &= 0x7fff;
  FPU_loaded_data.exp = exp - EXTENDED_Ebias + EXP_BIAS;

  /* Assume that optimisation can keep sigl, sigh, and exp in
     registers, otherwise it would be more efficient to work
     with FPU_loaded_data (which is static) here. */
  if ( exp == 0 )
    {
      if ( !(sigh | sigl) )
	{
	  FPU_loaded_data.tag = TW_Zero;
	  return 0;
	}
      /* The number is a de-normal or pseudodenormal. */
      if (sigh & 0x80000000)
	{
	  /* Is a pseudodenormal. */
	  /* Convert it for internal use. */
	  /* This is non-80486 behaviour because the number
	     loses its 'denormal' identity. */
	  FPU_loaded_data.exp++;
	  return 1;
	}
      else
	{
	  /* Is a denormal. */
	  /* Convert it for internal use. */
	  FPU_loaded_data.exp++;
	  normalize_nuo(&FPU_loaded_data);
	  return 0;
	}
    }
  else if ( exp == 0x7fff )
    {
      if ( !((sigh ^ 0x80000000) | sigl) )
	{
	  /* Matches the bit pattern for Infinity. */
	  FPU_loaded_data.exp = EXP_Infinity;
	  FPU_loaded_data.tag = TW_Infinity;
	  return 0;
	}

      FPU_loaded_data.exp = EXP_NaN;
      FPU_loaded_data.tag = TW_NaN;
      if ( !(sigh & 0x80000000) )
	{
	  /* NaNs have the ms bit set to 1. */
	  /* This is therefore an Unsupported NaN data type. */
	  /* This is non 80486 behaviour */
	  /* This should generate an Invalid Operand exception
	     later, so we convert it to a SNaN */
	  FPU_loaded_data.sigh = 0x80000000;
	  FPU_loaded_data.sigl = 0x00000001;
	  FPU_loaded_data.sign = SIGN_NEG;
	  return 1;
	}
      return 0;
    }

  if ( !(sigh & 0x80000000) )
    {
      /* Unsupported data type. */
      /* Valid numbers have the ms bit set to 1. */
      /* Unnormal. */
      /* Convert it for internal use. */
      /* This is non-80486 behaviour */
      /* This should generate an Invalid Operand exception
	 later, so we convert it to a SNaN */
      FPU_loaded_data.sigh = 0x80000000;
      FPU_loaded_data.sigl = 0x00000001;
      FPU_loaded_data.sign = SIGN_NEG;
      FPU_loaded_data.exp = EXP_NaN;
      FPU_loaded_data.tag = TW_NaN;
      return 1;
    }
  return 0;
}


/* Get a double from user memory */
int reg_load_double(void)
{
  double *dfloat = (double *)FPU_data_address;
  int exp;
  unsigned m64, l64;

  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_READ, dfloat, 8);
  m64 = get_fs_long(1 + (unsigned long *) dfloat);
  l64 = get_fs_long((unsigned long *) dfloat);
  RE_ENTRANT_CHECK_ON;

  if (m64 & 0x80000000)
    FPU_loaded_data.sign = SIGN_NEG;
  else
    FPU_loaded_data.sign = SIGN_POS;
  exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias;
  m64 &= 0xfffff;
  if (exp > DOUBLE_Emax)
    {
      /* Infinity or NaN */
      if ((m64 == 0) && (l64 == 0))
	{
	  /* +- infinity */
	  FPU_loaded_data.sigh = 0x80000000;
	  FPU_loaded_data.sigl = 0x00000000;
	  FPU_loaded_data.exp = EXP_Infinity;
	  FPU_loaded_data.tag = TW_Infinity;
	  return 0;
	}
      else
	{
	  /* Must be a signaling or quiet NaN */
	  FPU_loaded_data.exp = EXP_NaN;
	  FPU_loaded_data.tag = TW_NaN;
	  FPU_loaded_data.sigh = (m64 << 11) | 0x80000000;
	  FPU_loaded_data.sigh |= l64 >> 21;
	  FPU_loaded_data.sigl = l64 << 11;
	  return 0; /* The calling function must look for NaNs */
	}
    }
  else if ( exp < DOUBLE_Emin )
    {
      /* Zero or de-normal */
      if ((m64 == 0) && (l64 == 0))
	{
	  /* Zero */
	  int c = FPU_loaded_data.sign;
	  reg_move(&CONST_Z, &FPU_loaded_data);
	  FPU_loaded_data.sign = c;
	  return 0;
	}
      else
	{
	  /* De-normal */
	  FPU_loaded_data.exp = DOUBLE_Emin + EXP_BIAS;
	  FPU_loaded_data.tag = TW_Valid;
	  FPU_loaded_data.sigh = m64 << 11;
	  FPU_loaded_data.sigh |= l64 >> 21;
	  FPU_loaded_data.sigl = l64 << 11;
	  normalize_nuo(&FPU_loaded_data);
	  return denormal_operand();
	}
    }
  else
    {
      FPU_loaded_data.exp = exp + EXP_BIAS;
      FPU_loaded_data.tag = TW_Valid;
      FPU_loaded_data.sigh = (m64 << 11) | 0x80000000;
      FPU_loaded_data.sigh |= l64 >> 21;
      FPU_loaded_data.sigl = l64 << 11;

      return 0;
    }
}


/* Get a float from user memory */
int reg_load_single(void)
{
  float *single = (float *)FPU_data_address;
  unsigned m32;
  int exp;

  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_READ, single, 4);
  m32 = get_fs_long((unsigned long *) single);
  RE_ENTRANT_CHECK_ON;

  if (m32 & 0x80000000)
    FPU_loaded_data.sign = SIGN_NEG;
  else
    FPU_loaded_data.sign = SIGN_POS;
  if (!(m32 & 0x7fffffff))
    {
      /* Zero */
      int c = FPU_loaded_data.sign;
      reg_move(&CONST_Z, &FPU_loaded_data);
      FPU_loaded_data.sign = c;
      return 0;
    }
  exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias;
  m32 = (m32 & 0x7fffff) << 8;
  if ( exp < SINGLE_Emin )
    {
      /* De-normals */
      FPU_loaded_data.exp = SINGLE_Emin + EXP_BIAS;
      FPU_loaded_data.tag = TW_Valid;
      FPU_loaded_data.sigh = m32;
      FPU_loaded_data.sigl = 0;
      normalize_nuo(&FPU_loaded_data);
      return denormal_operand();
    }
  else if ( exp > SINGLE_Emax )
    {
    /* Infinity or NaN */
      if ( m32 == 0 )
	{
	  /* +- infinity */
	  FPU_loaded_data.sigh = 0x80000000;
	  FPU_loaded_data.sigl = 0x00000000;
	  FPU_loaded_data.exp = EXP_Infinity;
	  FPU_loaded_data.tag = TW_Infinity;
	  return 0;
	}
      else
	{
	  /* Must be a signaling or quiet NaN */
	  FPU_loaded_data.exp = EXP_NaN;
	  FPU_loaded_data.tag = TW_NaN;
	  FPU_loaded_data.sigh = m32 | 0x80000000;
	  FPU_loaded_data.sigl = 0;
	  return 0; /* The calling function must look for NaNs */
	}
    }
  else
    {
      FPU_loaded_data.exp = exp + EXP_BIAS;
      FPU_loaded_data.sigh = m32 | 0x80000000;
      FPU_loaded_data.sigl = 0;
      FPU_loaded_data.tag = TW_Valid;
      return 0;
    }
}


/* Get a long long from user memory */
void reg_load_int64(void)
{
  long long *_s = (long long *)FPU_data_address;
  int e;
  long long s;

  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_READ, _s, 8);
  ((unsigned long *)&s)[0] = get_fs_long((unsigned long *) _s);
  ((unsigned long *)&s)[1] = get_fs_long(1 + (unsigned long *) _s);
  RE_ENTRANT_CHECK_ON;

  if (s == 0)
    { reg_move(&CONST_Z, &FPU_loaded_data); return; }

  if (s > 0)
    FPU_loaded_data.sign = SIGN_POS;
  else
  {
    s = -s;
    FPU_loaded_data.sign = SIGN_NEG;
  }

  e = EXP_BIAS + 63;
  significand(&FPU_loaded_data) = s;
  FPU_loaded_data.exp = e;
  FPU_loaded_data.tag = TW_Valid;
  normalize_nuo(&FPU_loaded_data);
}


/* Get a long from user memory */
void reg_load_int32(void)
{
  long *_s = (long *)FPU_data_address;
  long s;
  int e;

  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_READ, _s, 4);
  s = (long)get_fs_long((unsigned long *) _s);
  RE_ENTRANT_CHECK_ON;

  if (s == 0)
    { reg_move(&CONST_Z, &FPU_loaded_data); return; }

  if (s > 0)
    FPU_loaded_data.sign = SIGN_POS;
  else
  {
    s = -s;
    FPU_loaded_data.sign = SIGN_NEG;
  }

  e = EXP_BIAS + 31;
  FPU_loaded_data.sigh = s;
  FPU_loaded_data.sigl = 0;
  FPU_loaded_data.exp = e;
  FPU_loaded_data.tag = TW_Valid;
  normalize_nuo(&FPU_loaded_data);
}


/* Get a short from user memory */
void reg_load_int16(void)
{
  short *_s = (short *)FPU_data_address;
  int s, e;

  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_READ, _s, 2);
  /* Cast as short to get the sign extended. */
  s = (short)get_fs_word((unsigned short *) _s);
  RE_ENTRANT_CHECK_ON;

  if (s == 0)
    { reg_move(&CONST_Z, &FPU_loaded_data); return; }

  if (s > 0)
    FPU_loaded_data.sign = SIGN_POS;
  else
  {
    s = -s;
    FPU_loaded_data.sign = SIGN_NEG;
  }

  e = EXP_BIAS + 15;
  FPU_loaded_data.sigh = s << 16;

  FPU_loaded_data.sigl = 0;
  FPU_loaded_data.exp = e;
  FPU_loaded_data.tag = TW_Valid;
  normalize_nuo(&FPU_loaded_data);
}


/* Get a packed bcd array from user memory */
void reg_load_bcd(void)
{
  char *s = (char *)FPU_data_address;
  int pos;
  unsigned char bcd;
  long long l=0;

  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_READ, s, 10);
  RE_ENTRANT_CHECK_ON;
  for ( pos = 8; pos >= 0; pos--)
    {
      l *= 10;
      RE_ENTRANT_CHECK_OFF;
      bcd = (unsigned char)get_fs_byte((unsigned char *) s+pos);
      RE_ENTRANT_CHECK_ON;
      l += bcd >> 4;
      l *= 10;
      l += bcd & 0x0f;
    }
  
  /* Finish all access to user memory before putting stuff into
     the static FPU_loaded_data */
  RE_ENTRANT_CHECK_OFF;
  FPU_loaded_data.sign =
    ((unsigned char)get_fs_byte((unsigned char *) s+9)) & 0x80 ?
      SIGN_NEG : SIGN_POS;
  RE_ENTRANT_CHECK_ON;

  if (l == 0)
    {
      char sign = FPU_loaded_data.sign;
      reg_move(&CONST_Z, &FPU_loaded_data);
      FPU_loaded_data.sign = sign;
    }
  else
    {
      significand(&FPU_loaded_data) = l;
      FPU_loaded_data.exp = EXP_BIAS + 63;
      FPU_loaded_data.tag = TW_Valid;
      normalize_nuo(&FPU_loaded_data);
    }
}

/*===========================================================================*/

/* Put a long double into user memory */
int reg_store_extended(void)
{
  /*
    The only exception raised by an attempt to store to an
    extended format is the Invalid Stack exception, i.e.
    attempting to store from an empty register.
   */
  long double *d = (long double *)FPU_data_address;

  if ( FPU_st0_tag != TW_Empty )
    {
      RE_ENTRANT_CHECK_OFF;
      FPU_verify_area(VERIFY_WRITE, d, 10);
      RE_ENTRANT_CHECK_ON;
      write_to_extended(FPU_st0_ptr, (char *) FPU_data_address);
      return 1;
    }

  /* Empty register (stack underflow) */
  EXCEPTION(EX_StackUnder);
  if ( control_word & CW_Invalid )
    {
      /* The masked response */
      /* Put out the QNaN indefinite */
      RE_ENTRANT_CHECK_OFF;
      FPU_verify_area(VERIFY_WRITE,d,10);
      put_fs_long(0, (unsigned long *) d);
      put_fs_long(0xc0000000, 1 + (unsigned long *) d);
      put_fs_word(0xffff, 4 + (short *) d);
      RE_ENTRANT_CHECK_ON;
      return 1;
    }
  else
    return 0;

}


/* Put a double into user memory */
int reg_store_double(void)
{
  double *dfloat = (double *)FPU_data_address;
  unsigned long l[2];
  unsigned long increment = 0;	/* avoid gcc warnings */

  if (FPU_st0_tag == TW_Valid)
    {
      int exp;
      FPU_REG tmp;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -