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

📄 reg_ld_str.c

📁 linux 内核源代码
💻 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,1996,1997                                    | |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | |                  E-mail   billm@suburbia.net                              | |                                                                           | |                                                                           | +---------------------------------------------------------------------------*//*---------------------------------------------------------------------------+ | 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 "fpu_emu.h"#include <asm/uaccess.h>#include "fpu_system.h"#include "exception.h"#include "reg_constant.h"#include "control_w.h"#include "status_w.h"#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 u_char normalize_no_excep(FPU_REG *r, int exp, int sign){  u_char tag;  setexponent16(r, exp);  tag = FPU_normalize_nuo(r);  stdexp(r);  if ( sign )    setnegative(r);  return tag;}int FPU_tagof(FPU_REG *ptr){  int exp;  exp = exponent16(ptr) & 0x7fff;  if ( exp == 0 )    {      if ( !(ptr->sigh | ptr->sigl) )	{	  return TAG_Zero;	}      /* The number is a de-normal or pseudodenormal. */      return TAG_Special;    }  if ( exp == 0x7fff )    {      /* Is an Infinity, a NaN, or an unsupported data type. */      return TAG_Special;    }  if ( !(ptr->sigh & 0x80000000) )    {      /* Unsupported data type. */      /* Valid numbers have the ms bit set to 1. */      /* Unnormal. */      return TAG_Special;    }  return TAG_Valid;}/* Get a long double from user memory */int FPU_load_extended(long double __user *s, int stnr){  FPU_REG *sti_ptr = &st(stnr);  RE_ENTRANT_CHECK_OFF;  FPU_access_ok(VERIFY_READ, s, 10);  __copy_from_user(sti_ptr, s, 10);  RE_ENTRANT_CHECK_ON;  return FPU_tagof(sti_ptr);}/* Get a double from user memory */int FPU_load_double(double __user *dfloat, FPU_REG *loaded_data){  int exp, tag, negative;  unsigned m64, l64;  RE_ENTRANT_CHECK_OFF;  FPU_access_ok(VERIFY_READ, dfloat, 8);  FPU_get_user(m64, 1 + (unsigned long __user *) dfloat);  FPU_get_user(l64, (unsigned long __user *) dfloat);  RE_ENTRANT_CHECK_ON;  negative = (m64 & 0x80000000) ? SIGN_Negative : SIGN_Positive;  exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias + EXTENDED_Ebias;  m64 &= 0xfffff;  if ( exp > DOUBLE_Emax + EXTENDED_Ebias )    {      /* Infinity or NaN */      if ((m64 == 0) && (l64 == 0))	{	  /* +- infinity */	  loaded_data->sigh = 0x80000000;	  loaded_data->sigl = 0x00000000;	  exp = EXP_Infinity + EXTENDED_Ebias;	  tag = TAG_Special;	}      else	{	  /* Must be a signaling or quiet NaN */	  exp = EXP_NaN + EXTENDED_Ebias;	  loaded_data->sigh = (m64 << 11) | 0x80000000;	  loaded_data->sigh |= l64 >> 21;	  loaded_data->sigl = l64 << 11;	  tag = TAG_Special;    /* The calling function must look for NaNs */	}    }  else if ( exp < DOUBLE_Emin + EXTENDED_Ebias )    {      /* Zero or de-normal */      if ((m64 == 0) && (l64 == 0))	{	  /* Zero */	  reg_copy(&CONST_Z, loaded_data);	  exp = 0;	  tag = TAG_Zero;	}      else	{	  /* De-normal */	  loaded_data->sigh = m64 << 11;	  loaded_data->sigh |= l64 >> 21;	  loaded_data->sigl = l64 << 11;	  return normalize_no_excep(loaded_data, DOUBLE_Emin, negative)	    | (denormal_operand() < 0 ? FPU_Exception : 0);	}    }  else    {      loaded_data->sigh = (m64 << 11) | 0x80000000;      loaded_data->sigh |= l64 >> 21;      loaded_data->sigl = l64 << 11;      tag = TAG_Valid;    }  setexponent16(loaded_data, exp | negative);  return tag;}/* Get a float from user memory */int FPU_load_single(float __user *single, FPU_REG *loaded_data){  unsigned m32;  int exp, tag, negative;  RE_ENTRANT_CHECK_OFF;  FPU_access_ok(VERIFY_READ, single, 4);  FPU_get_user(m32, (unsigned long __user *) single);  RE_ENTRANT_CHECK_ON;  negative = (m32 & 0x80000000) ? SIGN_Negative : SIGN_Positive;  if (!(m32 & 0x7fffffff))    {      /* Zero */      reg_copy(&CONST_Z, loaded_data);      addexponent(loaded_data, negative);      return TAG_Zero;    }  exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias + EXTENDED_Ebias;  m32 = (m32 & 0x7fffff) << 8;  if ( exp < SINGLE_Emin + EXTENDED_Ebias )    {      /* De-normals */      loaded_data->sigh = m32;      loaded_data->sigl = 0;      return normalize_no_excep(loaded_data, SINGLE_Emin, negative)	| (denormal_operand() < 0 ? FPU_Exception : 0);    }  else if ( exp > SINGLE_Emax + EXTENDED_Ebias )    {    /* Infinity or NaN */      if ( m32 == 0 )	{	  /* +- infinity */	  loaded_data->sigh = 0x80000000;	  loaded_data->sigl = 0x00000000;	  exp = EXP_Infinity + EXTENDED_Ebias;	  tag = TAG_Special;	}      else	{	  /* Must be a signaling or quiet NaN */	  exp = EXP_NaN + EXTENDED_Ebias;	  loaded_data->sigh = m32 | 0x80000000;	  loaded_data->sigl = 0;	  tag = TAG_Special;  /* The calling function must look for NaNs */	}    }  else    {      loaded_data->sigh = m32 | 0x80000000;      loaded_data->sigl = 0;      tag = TAG_Valid;    }  setexponent16(loaded_data, exp | negative);  /* Set the sign. */  return tag;}/* Get a long long from user memory */int FPU_load_int64(long long __user *_s){  long long s;  int sign;  FPU_REG *st0_ptr = &st(0);  RE_ENTRANT_CHECK_OFF;  FPU_access_ok(VERIFY_READ, _s, 8);  if (copy_from_user(&s,_s,8))    FPU_abort;  RE_ENTRANT_CHECK_ON;  if (s == 0)    {      reg_copy(&CONST_Z, st0_ptr);      return TAG_Zero;    }  if (s > 0)    sign = SIGN_Positive;  else  {    s = -s;    sign = SIGN_Negative;  }  significand(st0_ptr) = s;  return normalize_no_excep(st0_ptr, 63, sign);}/* Get a long from user memory */int FPU_load_int32(long __user *_s, FPU_REG *loaded_data){  long s;  int negative;  RE_ENTRANT_CHECK_OFF;  FPU_access_ok(VERIFY_READ, _s, 4);  FPU_get_user(s, _s);  RE_ENTRANT_CHECK_ON;  if (s == 0)    { reg_copy(&CONST_Z, loaded_data); return TAG_Zero; }  if (s > 0)    negative = SIGN_Positive;  else    {      s = -s;      negative = SIGN_Negative;    }  loaded_data->sigh = s;  loaded_data->sigl = 0;  return normalize_no_excep(loaded_data, 31, negative);}/* Get a short from user memory */int FPU_load_int16(short __user *_s, FPU_REG *loaded_data){  int s, negative;  RE_ENTRANT_CHECK_OFF;  FPU_access_ok(VERIFY_READ, _s, 2);  /* Cast as short to get the sign extended. */  FPU_get_user(s, _s);  RE_ENTRANT_CHECK_ON;  if (s == 0)    { reg_copy(&CONST_Z, loaded_data); return TAG_Zero; }  if (s > 0)    negative = SIGN_Positive;  else    {      s = -s;      negative = SIGN_Negative;    }  loaded_data->sigh = s << 16;  loaded_data->sigl = 0;  return normalize_no_excep(loaded_data, 15, negative);}/* Get a packed bcd array from user memory */int FPU_load_bcd(u_char __user *s){  FPU_REG *st0_ptr = &st(0);  int pos;  u_char bcd;  long long l=0;  int sign;  RE_ENTRANT_CHECK_OFF;  FPU_access_ok(VERIFY_READ, s, 10);  RE_ENTRANT_CHECK_ON;  for ( pos = 8; pos >= 0; pos--)    {      l *= 10;      RE_ENTRANT_CHECK_OFF;      FPU_get_user(bcd, s+pos);      RE_ENTRANT_CHECK_ON;      l += bcd >> 4;      l *= 10;      l += bcd & 0x0f;    }   RE_ENTRANT_CHECK_OFF;  FPU_get_user(sign, s+9);  sign = sign & 0x80 ? SIGN_Negative : SIGN_Positive;  RE_ENTRANT_CHECK_ON;  if ( l == 0 )    {      reg_copy(&CONST_Z, st0_ptr);      addexponent(st0_ptr, sign);   /* Set the sign. */      return TAG_Zero;    }  else    {      significand(st0_ptr) = l;      return normalize_no_excep(st0_ptr, 63, sign);    }}/*===========================================================================*//* Put a long double into user memory */int FPU_store_extended(FPU_REG *st0_ptr, u_char st0_tag, long double __user *d){  /*    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.   */  if ( st0_tag != TAG_Empty )    {      RE_ENTRANT_CHECK_OFF;      FPU_access_ok(VERIFY_WRITE, d, 10);      FPU_put_user(st0_ptr->sigl, (unsigned long __user *) d);      FPU_put_user(st0_ptr->sigh, (unsigned long __user *) ((u_char __user *)d + 4));      FPU_put_user(exponent16(st0_ptr), (unsigned short __user *) ((u_char __user *)d + 8));      RE_ENTRANT_CHECK_ON;      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_access_ok(VERIFY_WRITE,d,10);      FPU_put_user(0, (unsigned long __user *) d);      FPU_put_user(0xc0000000, 1 + (unsigned long __user *) d);      FPU_put_user(0xffff, 4 + (short __user *) d);      RE_ENTRANT_CHECK_ON;      return 1;    }  else    return 0;}/* Put a double into user memory */int FPU_store_double(FPU_REG *st0_ptr, u_char st0_tag, double __user *dfloat){  unsigned long l[2];  unsigned long increment = 0;	/* avoid gcc warnings */  int precision_loss;  int exp;  FPU_REG tmp;  if ( st0_tag == TAG_Valid )    {      reg_copy(st0_ptr, &tmp);      exp = exponent(&tmp);      if ( exp < DOUBLE_Emin )     /* It may be a denormal */	{	  addexponent(&tmp, -DOUBLE_Emin + 52);  /* largest exp to be 51 */	denormal_arg:	  if ( (precision_loss = FPU_round_to_int(&tmp, st0_tag)) )	    {#ifdef PECULIAR_486	      /* Did it round to a non-denormal ? */	      /* This behaviour might be regarded as peculiar, it appears		 that the 80486 rounds to the dest precision, then		 converts to decide underflow. */	      if ( !((tmp.sigh == 0x00100000) && (tmp.sigl == 0) &&		  (st0_ptr->sigl & 0x000007ff)) )#endif /* PECULIAR_486 */		{		  EXCEPTION(EX_Underflow);		  /* This is a special case: see sec 16.2.5.1 of		     the 80486 book */		  if ( !(control_word & CW_Underflow) )		    return 0;		}	      EXCEPTION(precision_loss);	      if ( !(control_word & CW_Precision) )		return 0;	    }	  l[0] = tmp.sigl;	  l[1] = tmp.sigh;	}      else	{

⌨️ 快捷键说明

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