📄 reg_ld_s.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 + -