📄 reg_ld_str.c
字号:
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, ®s[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(®s[(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 + -