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

📄 reg_ld_str.c

📁 LINUX 1.0 内核c源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
  short *d = (short *)FPU_data_address;
  FPU_REG t;
  int precision_loss;

  if ( FPU_st0_tag == TW_Empty )
    {
      /* Empty register (stack underflow) */
      EXCEPTION(EX_StackUnder);
      goto invalid_operand;
    }
  else if ( (FPU_st0_tag == TW_Infinity) ||
	   (FPU_st0_tag == TW_NaN) )
    {
      EXCEPTION(EX_Invalid);
      goto invalid_operand;
    }

  reg_move(FPU_st0_ptr, &t);
  precision_loss = round_to_int(&t);
  if (t.sigh ||
      ((t.sigl & 0xffff8000) &&
       !((t.sigl == 0x8000) && (t.sign == SIGN_NEG))) )
    {
      EXCEPTION(EX_Invalid);
      /* This is a special case: see sec 16.2.5.1 of the 80486 book */
    invalid_operand:
      if ( control_word & EX_Invalid )
	{
	  /* Produce something like QNaN "indefinite" */
	  t.sigl = 0x8000;
	}
      else
	return 0;
    }
  else
    {
      if ( precision_loss )
	set_precision_flag(precision_loss);
      if ( t.sign )
	t.sigl = -t.sigl;
    }

  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_WRITE,d,2);
  put_fs_word((short)t.sigl,(short *) d);
  RE_ENTRANT_CHECK_ON;

  return 1;
}


/* Put a packed bcd array into user memory */
int reg_store_bcd(void)
{
  char *d = (char *)FPU_data_address;
  FPU_REG t;
  unsigned long long ll;
  unsigned char b;
  int i, precision_loss;
  unsigned char sign = (FPU_st0_ptr->sign == SIGN_NEG) ? 0x80 : 0;

  if ( FPU_st0_tag == TW_Empty )
    {
      /* Empty register (stack underflow) */
      EXCEPTION(EX_StackUnder);
      goto invalid_operand;
    }

  reg_move(FPU_st0_ptr, &t);
  precision_loss = round_to_int(&t);
  ll = significand(&t);

  /* Check for overflow, by comparing with 999999999999999999 decimal. */
  if ( (t.sigh > 0x0de0b6b3) ||
      ((t.sigh == 0x0de0b6b3) && (t.sigl > 0xa763ffff)) )
    {
      EXCEPTION(EX_Invalid);
      /* This is a special case: see sec 16.2.5.1 of the 80486 book */
    invalid_operand:
      if ( control_word & CW_Invalid )
	{
	  /* Produce the QNaN "indefinite" */
	  RE_ENTRANT_CHECK_OFF;
	  FPU_verify_area(VERIFY_WRITE,d,10);
	  for ( i = 0; i < 7; i++)
	    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(0xff, (unsigned char *) d+8);
	  put_fs_byte(0xff, (unsigned char *) d+9);
	  RE_ENTRANT_CHECK_ON;
	  return 1;
	}
      else
	return 0;
    }
  else if ( precision_loss )
    {
      /* Precision loss doesn't stop the data transfer */
      set_precision_flag(precision_loss);
    }

  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_WRITE,d,10);
  RE_ENTRANT_CHECK_ON;
  for ( i = 0; i < 9; i++)
    {
      b = div_small(&ll, 10);
      b |= (div_small(&ll, 10)) << 4;
      RE_ENTRANT_CHECK_OFF;
      put_fs_byte(b,(unsigned char *) d+i);
      RE_ENTRANT_CHECK_ON;
    }
  RE_ENTRANT_CHECK_OFF;
  put_fs_byte(sign,(unsigned char *) d+9);
  RE_ENTRANT_CHECK_ON;

  return 1;
}

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

/* r gets mangled such that sig is int, sign: 
   it is NOT normalized */
/* The return value (in eax) is zero if the result is exact,
   if bits are changed due to rounding, truncation, etc, then
   a non-zero value is returned */
/* Overflow is signalled by a non-zero return value (in eax).
   In the case of overflow, the returned significand always has the
   the largest possible value */
int round_to_int(FPU_REG *r)
{
  char     very_big;
  unsigned eax;

  if (r->tag == TW_Zero)
    {
      /* Make sure that zero is returned */
      significand(r) = 0;
      return 0;        /* o.k. */
    }
  
  if (r->exp > EXP_BIAS + 63)
    {
      r->sigl = r->sigh = ~0;      /* The largest representable number */
      return 1;        /* overflow */
    }

  eax = shrxs(&r->sigl, EXP_BIAS + 63 - r->exp);
  very_big = !(~(r->sigh) | ~(r->sigl));  /* test for 0xfff...fff */
#define	half_or_more	(eax & 0x80000000)
#define	frac_part	(eax)
#define more_than_half  ((eax & 0x80000001) == 0x80000001)
  switch (control_word & CW_RC)
    {
    case RC_RND:
      if ( more_than_half               	/* nearest */
	  || (half_or_more && (r->sigl & 1)) )	/* odd -> even */
	{
	  if ( very_big ) return 1;        /* overflow */
	  significand(r) ++;
	  return PRECISION_LOST_UP;
	}
      break;
    case RC_DOWN:
      if (frac_part && r->sign)
	{
	  if ( very_big ) return 1;        /* overflow */
	  significand(r) ++;
	  return PRECISION_LOST_UP;
	}
      break;
    case RC_UP:
      if (frac_part && !r->sign)
	{
	  if ( very_big ) return 1;        /* overflow */
	  significand(r) ++;
	  return PRECISION_LOST_UP;
	}
      break;
    case RC_CHOP:
      break;
    }

  return eax ? PRECISION_LOST_DOWN : 0;

}

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

char *fldenv(fpu_addr_modes addr_modes)
{
  char *s = (char *)FPU_data_address;
  unsigned short tag_word = 0;
  unsigned char tag;
  int i;

  if ( addr_modes.vm86
      || (addr_modes.override.operand_size == OP_SIZE_PREFIX) )
    {
      RE_ENTRANT_CHECK_OFF;
      FPU_verify_area(VERIFY_READ, s, 0x0e);
      control_word = get_fs_word((unsigned short *) s);
      partial_status = get_fs_word((unsigned short *) (s+2));
      tag_word = get_fs_word((unsigned short *) (s+4));
      ip_offset = get_fs_word((unsigned short *) (s+6));
      cs_selector = get_fs_word((unsigned short *) (s+8));
      data_operand_offset = get_fs_word((unsigned short *) (s+0x0a));
      operand_selector = get_fs_word((unsigned short *) (s+0x0c));
      RE_ENTRANT_CHECK_ON;
      s += 0x0e;
      if ( addr_modes.vm86 )
	{
	  ip_offset += (cs_selector & 0xf000) << 4;
	  data_operand_offset += (operand_selector & 0xf000) << 4;
	}
    }
  else
    {
      RE_ENTRANT_CHECK_OFF;
      FPU_verify_area(VERIFY_READ, s, 0x1c);
      control_word = get_fs_word((unsigned short *) s);
      partial_status = get_fs_word((unsigned short *) (s+4));
      tag_word = get_fs_word((unsigned short *) (s+8));
      ip_offset = get_fs_long((unsigned long *) (s+0x0c));
      cs_selector = get_fs_long((unsigned long *) (s+0x10));
      data_operand_offset = get_fs_long((unsigned long *) (s+0x14));
      operand_selector = get_fs_long((unsigned long *) (s+0x18));
      RE_ENTRANT_CHECK_ON;
      s += 0x1c;
    }

#ifdef PECULIAR_486
  control_word &= ~0xe080;
#endif PECULIAR_486

  top = (partial_status >> SW_Top_Shift) & 7;

  if ( partial_status & ~control_word & CW_Exceptions )
    partial_status |= (SW_Summary | SW_Backward);
  else
    partial_status &= ~(SW_Summary | SW_Backward);

  for ( i = 0; i < 8; i++ )
    {
      tag = tag_word & 3;
      tag_word >>= 2;

      if ( tag == 3 )
	/* New tag is empty.  Accept it */
	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
	     by old reg contents */
	  if ( regs[i].exp == EXP_BIAS - EXTENDED_Ebias )
	    {
	      if ( !(regs[i].sigl | regs[i].sigh) )
		regs[i].tag = TW_Zero;
	      else
		regs[i].tag = TW_Valid;
	    }
	  else if ( regs[i].exp == 0x7fff + EXP_BIAS - EXTENDED_Ebias )
	    {
	      if ( !((regs[i].sigh & ~0x80000000) | regs[i].sigl) )
		regs[i].tag = TW_Infinity;
	      else
		regs[i].tag = TW_NaN;
	    }
	  else
	    regs[i].tag = TW_Valid;
  	}
      /* Else old tag is not empty and new tag is not empty.  Old tag
	 remains correct */
    }

  /* Ensure that the values just loaded are not changed by
     fix-up operations. */
  NO_NET_DATA_EFFECT;
  NO_NET_INSTR_EFFECT;

  return s;
}


void frstor(fpu_addr_modes addr_modes)
{
  int i, stnr;
  unsigned char tag;
  char *s = fldenv(addr_modes);

  for ( i = 0; i < 8; i++ )
    {
      /* Load each register. */
      FPU_data_address = (void *)(s+i*10);
      reg_load_extended();
      stnr = (i+top) & 7;
      tag = regs[stnr].tag;   /* Derived from the loaded tag word. */
      reg_move(&FPU_loaded_data, &regs[stnr]);
      if ( tag == TW_Empty )  /* The loaded data over-rides all other cases. */
	regs[stnr].tag = tag;
    }

  /* Reverse the effect which loading the registers had on the
     data pointer */
  NO_NET_DATA_EFFECT;

}


unsigned short tag_word(void)
{
  unsigned short word = 0;
  unsigned char tag;
  int i;

  for ( i = 7; i >= 0; i-- )
    {
      switch ( tag = regs[i].tag )
	{
	case TW_Valid:
	  if ( regs[i].exp <= (EXP_BIAS - EXTENDED_Ebias) )
	    tag = 2;
	  break;
	case TW_Infinity:
	case TW_NaN:
	  tag = 2;
	  break;
	case TW_Empty:
	  tag = 3;
	  break;
	  /* TW_Zero already has the correct value */
	}
      word <<= 2;
      word |= tag;
    }
  return word;
}


char *fstenv(fpu_addr_modes addr_modes)
{
  char *d = (char *)FPU_data_address;

  if ( addr_modes.vm86
      || (addr_modes.override.operand_size == OP_SIZE_PREFIX) )
    {
      RE_ENTRANT_CHECK_OFF;
      FPU_verify_area(VERIFY_WRITE,d,14);
#ifdef PECULIAR_486
      put_fs_long(control_word & ~0xe080, (unsigned short *) d);
#else
      put_fs_word(control_word, (unsigned short *) d);
#endif PECULIAR_486
      put_fs_word(status_word(), (unsigned short *) (d+2));
      put_fs_word(tag_word(), (unsigned short *) (d+4));
      put_fs_word(ip_offset, (unsigned short *) (d+6));
      put_fs_word(data_operand_offset, (unsigned short *) (d+0x0a));
      if ( addr_modes.vm86 )
	{
	  put_fs_word((ip_offset & 0xf0000) >> 4,
		      (unsigned short *) (d+8));
	  put_fs_word((data_operand_offset & 0xf0000) >> 4,
		      (unsigned short *) (d+0x0c));
	}
      else
	{
	  put_fs_word(cs_selector, (unsigned short *) (d+8));
	  put_fs_word(operand_selector, (unsigned short *) (d+0x0c));
	}
      RE_ENTRANT_CHECK_ON;
      d += 0x0e;
    }
  else
    {
      RE_ENTRANT_CHECK_OFF;
      FPU_verify_area(VERIFY_WRITE,d,28);
#ifdef PECULIAR_486
      /* An 80486 sets all the reserved bits to 1. */
      put_fs_long(0xffff0040 | (control_word & ~0xe080), (unsigned long *) d);
      put_fs_long(0xffff0000 | status_word(), (unsigned long *) (d+4));
      put_fs_long(0xffff0000 | tag_word(), (unsigned long *) (d+8));
#else
      put_fs_word(control_word, (unsigned short *) d);
      put_fs_word(status_word(), (unsigned short *) (d+4));
      put_fs_word(tag_word(), (unsigned short *) (d+8));
#endif PECULIAR_486
      put_fs_long(ip_offset, (unsigned long *) (d+0x0c));
      put_fs_long(cs_selector & ~0xf8000000, (unsigned long *) (d+0x10));
      put_fs_long(data_operand_offset, (unsigned long *) (d+0x14));
#ifdef PECULIAR_486
      /* An 80486 sets all the reserved bits to 1. */
      put_fs_long(0xffff0000 | operand_selector, (unsigned long *) (d+0x18));
#else
      put_fs_long(operand_selector, (unsigned long *) (d+0x18));
#endif PECULIAR_486
      RE_ENTRANT_CHECK_ON;
      d += 0x1c;
    }
  
  control_word |= CW_Exceptions;
  partial_status &= ~(SW_Summary | SW_Backward);

  return d;
}


void fsave(fpu_addr_modes addr_modes)
{
  char *d;
  int i;

  d = fstenv(addr_modes);
  RE_ENTRANT_CHECK_OFF;
  FPU_verify_area(VERIFY_WRITE,d,80);
  RE_ENTRANT_CHECK_ON;
  for ( i = 0; i < 8; i++ )
    write_to_extended(&regs[(top + i) & 7], d + 10 * i);

  finit();

}

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

/*
  A call to this function must be preceeded by a call to
  FPU_verify_area() to verify access to the 10 bytes at d
  */
static void write_to_extended(FPU_REG *rp, char *d)
{
  long e;
  FPU_REG tmp;
  
  e = rp->exp - EXP_BIAS + EXTENDED_Ebias;

#ifdef PARANOID
  switch ( rp->tag )
    {
    case TW_Zero:
      if ( rp->sigh | rp->sigl | e )
	EXCEPTION(EX_INTERNAL | 0x114);
      break;
    case TW_Infinity:
    case TW_NaN:
      if ( (e ^ 0x7fff) | !(rp->sigh & 0x80000000) )
	EXCEPTION(EX_INTERNAL | 0x114);
      break;
    default:
      if (e > 0x7fff || e < -63)
	EXCEPTION(EX_INTERNAL | 0x114);
    }
#endif PARANOID

  /*
    All numbers except denormals are stored internally in a
    format which is compatible with the extended real number
    format.
   */
  if ( e > 0 )
    {
      /* just copy the reg */
      RE_ENTRANT_CHECK_OFF;
      put_fs_long(rp->sigl, (unsigned long *) d);
      put_fs_long(rp->sigh, (unsigned long *) (d + 4));
      RE_ENTRANT_CHECK_ON;
    }
  else
    {
      /*
	The number is a de-normal stored as a normal using our
	extra exponent range, or is Zero.
	Convert it back to a de-normal, or leave it as Zero.
       */
      reg_move(rp, &tmp);
      tmp.exp += -EXTENDED_Emin + 63;  /* largest exp to be 63 */
      round_to_int(&tmp);
      e = 0;
      RE_ENTRANT_CHECK_OFF;
      put_fs_long(tmp.sigl, (unsigned long *) d);
      put_fs_long(tmp.sigh, (unsigned long *) (d + 4));
      RE_ENTRANT_CHECK_ON;
    }
  e |= rp->sign == SIGN_POS ? 0 : 0x8000;
  RE_ENTRANT_CHECK_OFF;
  put_fs_word(e, (unsigned short *) (d + 8));
  RE_ENTRANT_CHECK_ON;
}

⌨️ 快捷键说明

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