📄 reg_ld_str.c
字号:
/*---------------------------------------------------------------------------+ | 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 + -