patch-2.1.73 linux/arch/i386/math-emu/fpu_entry.c
Next file: linux/arch/i386/math-emu/fpu_etc.c
Previous file: linux/arch/i386/math-emu/fpu_emu.h
Back to the patch index
Back to the overall index
- Lines: 535
- Date:
Tue Dec 9 17:57:09 1997
- Orig file:
v2.1.72/linux/arch/i386/math-emu/fpu_entry.c
- Orig date:
Tue Dec 2 09:49:39 1997
diff -u --recursive --new-file v2.1.72/linux/arch/i386/math-emu/fpu_entry.c linux/arch/i386/math-emu/fpu_entry.c
@@ -3,9 +3,9 @@
| |
| The entry functions for wm-FPU-emu |
| |
- | Copyright (C) 1992,1993,1994,1996 |
+ | Copyright (C) 1992,1993,1994,1996,1997 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
- | E-mail billm@jacobi.maths.monash.edu.au |
+ | E-mail billm@suburbia.net |
| |
| See the files "README" and "COPYING" for further copyright and warranty |
| information. |
@@ -54,27 +54,27 @@
#define _df_d8_ fstp_i /* unofficial code (1f) */
static FUNC const st_instr_table[64] = {
- fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, _df_c0_,
- fmul__, fxch_i, __BAD__, __BAD__, fmul_i, _dd_c8_, fmulp_, _df_c8_,
- fcom_st, fp_nop, __BAD__, __BAD__, _dc_d0_, fst_i_, _de_d0_, _df_d0_,
- fcompst, _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i, fcompp, _df_d8_,
- fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
- fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
- fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
- fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
+ fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, _df_c0_,
+ fmul__, fxch_i, __BAD__, __BAD__, fmul_i, _dd_c8_, fmulp_, _df_c8_,
+ fcom_st, fp_nop, __BAD__, __BAD__, _dc_d0_, fst_i_, _de_d0_, _df_d0_,
+ fcompst, _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i, fcompp, _df_d8_,
+ fsub__, FPU_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
+ fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
+ fdiv__, FPU_triga, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
+ fdivr_, FPU_trigb, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
};
#else /* Support only documented FPU op-codes */
static FUNC const st_instr_table[64] = {
- fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, __BAD__,
- fmul__, fxch_i, __BAD__, __BAD__, fmul_i, __BAD__, fmulp_, __BAD__,
- fcom_st, fp_nop, __BAD__, __BAD__, __BAD__, fst_i_, __BAD__, __BAD__,
- fcompst, __BAD__, __BAD__, __BAD__, __BAD__, fstp_i, fcompp, __BAD__,
- fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
- fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
- fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
- fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
+ fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, __BAD__,
+ fmul__, fxch_i, __BAD__, __BAD__, fmul_i, __BAD__, fmulp_, __BAD__,
+ fcom_st, fp_nop, __BAD__, __BAD__, __BAD__, fst_i_, __BAD__, __BAD__,
+ fcompst, __BAD__, __BAD__, __BAD__, __BAD__, fstp_i, fcompp, __BAD__,
+ fsub__, FPU_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
+ fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
+ fdiv__, FPU_triga, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
+ fdivr_, FPU_trigb, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
};
#endif NO_UNDOC_CODE
@@ -95,7 +95,7 @@
/* Un-documented FPU op-codes supported by default. (see above) */
-static unsigned char const type_table[64] = {
+static u_char const type_table[64] = {
_REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_,
_REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_,
_REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
@@ -108,7 +108,7 @@
#else /* Support only documented FPU op-codes */
-static unsigned char const type_table[64] = {
+static u_char const type_table[64] = {
_REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _null_,
_REGI_, _REGIn, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
_REGIc, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_,
@@ -123,26 +123,26 @@
#ifdef RE_ENTRANT_CHECKING
-char emulating=0;
+u_char emulating=0;
#endif RE_ENTRANT_CHECKING
-static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip,
+static int valid_prefix(u_char *Byte, u_char **fpu_eip,
overrides *override);
asmlinkage void math_emulate(long arg)
{
- unsigned char FPU_modrm, byte1;
+ u_char FPU_modrm, byte1;
unsigned short code;
fpu_addr_modes addr_modes;
int unmasked;
FPU_REG loaded_data;
+ FPU_REG *st0_ptr;
+ u_char loaded_tag, st0_tag;
void *data_address;
struct address data_sel_off;
struct address entry_sel_off;
unsigned long code_base = 0;
unsigned long code_limit = 0; /* Initialized to stop compiler warnings */
- char st0_tag;
- FPU_REG *st0_ptr;
struct desc_struct code_descriptor;
#ifdef RE_ENTRANT_CHECKING
@@ -155,15 +155,6 @@
if (!current->used_math)
{
- int i;
- for ( i = 0; i < 8; i++ )
- {
- /* Make sure that the registers are compatible
- with the assumptions of the emulator. */
- if ( !((regs[i].exp == EXP_UNDER) && (regs[i].sigh == 0)
- && (regs[i].sigl == 0)) )
- regs[i].sigh |= 0x80000000;
- }
finit();
current->used_math = 1;
}
@@ -221,7 +212,7 @@
if (current->flags & PF_PTRACED)
FPU_lookahead = 0;
- if ( !valid_prefix(&byte1, (unsigned char **)&FPU_EIP,
+ if ( !valid_prefix(&byte1, (u_char **)&FPU_EIP,
&addr_modes.override) )
{
RE_ENTRANT_CHECK_OFF;
@@ -264,7 +255,7 @@
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1);
- get_user(FPU_modrm, (unsigned char *) FPU_EIP);
+ FPU_get_user(FPU_modrm, (u_char *) FPU_EIP);
RE_ENTRANT_CHECK_ON;
FPU_EIP++;
@@ -287,6 +278,7 @@
* interrupts here.
*/
do_the_FPU_interrupt:
+
FPU_EIP = FPU_ORIG_EIP; /* Point to current FPU instruction. */
RE_ENTRANT_CHECK_OFF;
@@ -309,11 +301,11 @@
if ( (addr_modes.default_mode & SIXTEEN)
^ (addr_modes.override.address_size == ADDR_SIZE_PREFIX) )
- data_address = get_address_16(FPU_modrm, &FPU_EIP, &data_sel_off,
- addr_modes);
+ data_address = FPU_get_address_16(FPU_modrm, &FPU_EIP, &data_sel_off,
+ addr_modes);
else
- data_address = get_address(FPU_modrm, &FPU_EIP, &data_sel_off,
- addr_modes);
+ data_address = FPU_get_address(FPU_modrm, &FPU_EIP, &data_sel_off,
+ addr_modes);
if ( addr_modes.default_mode )
{
@@ -326,7 +318,7 @@
unsigned short status1 = partial_status;
st0_ptr = &st(0);
- st0_tag = st0_ptr->tag;
+ st0_tag = FPU_gettag0();
/* Stack underflow has priority */
if ( NOT_EMPTY_ST0 )
@@ -342,29 +334,34 @@
switch ( (byte1 >> 1) & 3 )
{
case 0:
- unmasked = reg_load_single((float *)data_address,
+ unmasked = FPU_load_single((float *)data_address,
&loaded_data);
+ loaded_tag = unmasked & 0xff;
+ unmasked &= ~0xff;
break;
case 1:
- reg_load_int32((long *)data_address, &loaded_data);
+ loaded_tag = FPU_load_int32((long *)data_address, &loaded_data);
break;
case 2:
- unmasked = reg_load_double((double *)data_address,
+ unmasked = FPU_load_double((double *)data_address,
&loaded_data);
+ loaded_tag = unmasked & 0xff;
+ unmasked &= ~0xff;
break;
case 3:
- reg_load_int16((short *)data_address, &loaded_data);
+ default: /* Used here to suppress gcc warnings. */
+ loaded_tag = FPU_load_int16((short *)data_address, &loaded_data);
break;
}
-
+
/* No more access to user memory, it is safe
to use static data now */
/* NaN operands have the next priority. */
/* We have to delay looking at st(0) until after
loading the data, because that data might contain an SNaN */
- if ( (st0_tag == TW_NaN) ||
- (loaded_data.tag == TW_NaN) )
+ if ( ((st0_tag == TAG_Special) && isNaN(st0_ptr)) ||
+ ((loaded_tag == TAG_Special) && isNaN(&loaded_data)) )
{
/* Restore the status word; we might have loaded a
denormal. */
@@ -375,22 +372,22 @@
EXCEPTION(EX_Invalid);
setcc(SW_C3 | SW_C2 | SW_C0);
if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) )
- pop(); /* fcomp, masked, so we pop. */
+ FPU_pop(); /* fcomp, masked, so we pop. */
}
else
{
+ if ( loaded_tag == TAG_Special )
+ loaded_tag = FPU_Special(&loaded_data);
#ifdef PECULIAR_486
/* This is not really needed, but gives behaviour
identical to an 80486 */
if ( (FPU_modrm & 0x28) == 0x20 )
/* fdiv or fsub */
- real_2op_NaN(&loaded_data, st0_ptr,
- st0_ptr);
+ real_2op_NaN(&loaded_data, loaded_tag, 0, &loaded_data);
else
#endif PECULIAR_486
/* fadd, fdivr, fmul, or fsubr */
- real_2op_NaN(st0_ptr, &loaded_data,
- st0_ptr);
+ real_2op_NaN(&loaded_data, loaded_tag, 0, st0_ptr);
}
goto reg_mem_instr_done;
}
@@ -401,11 +398,13 @@
if ( (FPU_modrm & 0x38) == 0x38 )
{
/* fdivr */
- if ( (st0_tag == TW_Zero) &&
- (loaded_data.tag == TW_Valid) )
+ if ( (st0_tag == TAG_Zero) &&
+ ((loaded_tag == TAG_Valid)
+ || (loaded_tag == TAG_Special
+ && isdenormal(&loaded_data))) )
{
- if ( divide_by_zero(loaded_data.sign,
- st0_ptr) )
+ if ( FPU_divide_by_zero(0, getsign(&loaded_data))
+ < 0 )
{
/* We use the fact here that the unmasked
exception in the loaded data was for a
@@ -414,6 +413,8 @@
partial_status &= ~SW_Denorm_Op;
partial_status |= status1 & SW_Denorm_Op;
}
+ else
+ setsign(st0_ptr, getsign(&loaded_data));
}
}
goto reg_mem_instr_done;
@@ -423,43 +424,38 @@
{
case 0: /* fadd */
clear_C1();
- reg_add(st0_ptr, &loaded_data, st0_ptr,
- control_word);
+ FPU_add(&loaded_data, loaded_tag, 0, control_word);
break;
case 1: /* fmul */
clear_C1();
- reg_mul(st0_ptr, &loaded_data, st0_ptr,
- control_word);
+ FPU_mul(&loaded_data, loaded_tag, 0, control_word);
break;
case 2: /* fcom */
- compare_st_data(&loaded_data);
+ FPU_compare_st_data(&loaded_data, loaded_tag);
break;
case 3: /* fcomp */
- if ( !compare_st_data(&loaded_data) && !unmasked )
- pop();
+ if ( !FPU_compare_st_data(&loaded_data, loaded_tag)
+ && !unmasked )
+ FPU_pop();
break;
case 4: /* fsub */
clear_C1();
- reg_sub(st0_ptr, &loaded_data, st0_ptr,
- control_word);
+ FPU_sub(LOADED|loaded_tag, (int)&loaded_data, control_word);
break;
case 5: /* fsubr */
clear_C1();
- reg_sub(&loaded_data, st0_ptr, st0_ptr,
- control_word);
+ FPU_sub(REV|LOADED|loaded_tag, (int)&loaded_data, control_word);
break;
case 6: /* fdiv */
clear_C1();
- reg_div(st0_ptr, &loaded_data, st0_ptr,
- control_word);
+ FPU_div(LOADED|loaded_tag, (int)&loaded_data, control_word);
break;
case 7: /* fdivr */
clear_C1();
- if ( st0_tag == TW_Zero )
+ if ( st0_tag == TAG_Zero )
partial_status = status1; /* Undo any denorm tag,
- zero-divide has priority. */
- reg_div(&loaded_data, st0_ptr, st0_ptr,
- control_word);
+ zero-divide has priority. */
+ FPU_div(REV|LOADED|loaded_tag, (int)&loaded_data, control_word);
break;
}
}
@@ -471,10 +467,10 @@
EXCEPTION(EX_StackUnder);
setcc(SW_C3 | SW_C2 | SW_C0);
if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) )
- pop(); /* fcomp */
+ FPU_pop(); /* fcomp */
}
else
- stack_underflow();
+ FPU_stack_underflow();
}
reg_mem_instr_done:
operand_address = data_sel_off;
@@ -482,8 +478,8 @@
else
{
if ( !(no_ip_update =
- load_store_instr(((FPU_modrm & 0x38) | (byte1 & 6)) >> 1,
- addr_modes, data_address)) )
+ FPU_load_store(((FPU_modrm & 0x38) | (byte1 & 6)) >> 1,
+ addr_modes, data_address)) )
{
operand_address = data_sel_off;
}
@@ -493,7 +489,7 @@
else
{
/* None of these instructions access user memory */
- unsigned char instr_index = (FPU_modrm & 0x38) | (byte1 & 7);
+ u_char instr_index = (FPU_modrm & 0x38) | (byte1 & 7);
#ifdef PECULIAR_486
/* This is supposed to be undefined, but a real 80486 seems
@@ -503,7 +499,7 @@
#endif PECULIAR_486
st0_ptr = &st(0);
- st0_tag = st0_ptr->tag;
+ st0_tag = FPU_gettag0();
switch ( type_table[(int) instr_index] )
{
case _NONE_: /* also _REGIc: _REGIn */
@@ -511,28 +507,28 @@
case _REG0_:
if ( !NOT_EMPTY_ST0 )
{
- stack_underflow();
+ FPU_stack_underflow();
goto FPU_instruction_done;
}
break;
case _REGIi:
if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) )
{
- stack_underflow_i(FPU_rm);
+ FPU_stack_underflow_i(FPU_rm);
goto FPU_instruction_done;
}
break;
case _REGIp:
if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) )
{
- stack_underflow_pop(FPU_rm);
+ FPU_stack_underflow_pop(FPU_rm);
goto FPU_instruction_done;
}
break;
case _REGI_:
if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) )
{
- stack_underflow();
+ FPU_stack_underflow();
goto FPU_instruction_done;
}
break;
@@ -558,14 +554,14 @@
#ifdef DEBUG
RE_ENTRANT_CHECK_OFF;
- emu_printall();
+ FPU_printall();
RE_ENTRANT_CHECK_ON;
#endif DEBUG
if (FPU_lookahead && !need_resched)
{
FPU_ORIG_EIP = FPU_EIP - code_base;
- if ( valid_prefix(&byte1, (unsigned char **)&FPU_EIP,
+ if ( valid_prefix(&byte1, (u_char **)&FPU_EIP,
&addr_modes.override) )
goto do_another_FPU_instruction;
}
@@ -581,17 +577,17 @@
all prefix bytes, further changes are needed in the emulator code
which accesses user address space. Access to separate segments is
important for msdos emulation. */
-static int valid_prefix(unsigned char *Byte, unsigned char **fpu_eip,
+static int valid_prefix(u_char *Byte, u_char **fpu_eip,
overrides *override)
{
- unsigned char byte;
- unsigned char *ip = *fpu_eip;
+ u_char byte;
+ u_char *ip = *fpu_eip;
*override = (overrides) { 0, 0, PREFIX_DEFAULT }; /* defaults */
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1);
- get_user(byte, ip);
+ FPU_get_user(byte, ip);
RE_ENTRANT_CHECK_ON;
while ( 1 )
@@ -637,7 +633,7 @@
ip++;
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1);
- get_user(byte, ip);
+ FPU_get_user(byte, ip);
RE_ENTRANT_CHECK_ON;
break;
case FWAIT_OPCODE:
@@ -677,19 +673,79 @@
-void restore_i387_soft(struct _fpstate *buf)
+#define S387 ((struct i387_soft_struct *)s387)
+#define sstatus_word() \
+ ((S387->swd & ~SW_Top & 0xffff) | ((S387->ftop << SW_Top_Shift) & SW_Top))
+
+void restore_i387_soft(void *s387, struct _fpstate *buf)
{
- fpu_addr_modes addr_modes = {{ 0, 0, PREFIX_DEFAULT }, 0};
+ u_char *d = (u_char *)buf;
+ int offset, other, i, tags, regnr, tag, newtop;
+
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_READ, d, 7*4 + 8*10);
+ __copy_from_user(&S387->cwd, d, 7*4);
+ RE_ENTRANT_CHECK_ON;
+
+ d += 7*4;
+
+ S387->ftop = (S387->swd >> SW_Top_Shift) & 7;
+ offset = (S387->ftop & 7) * 10;
+ other = 80 - offset;
+
+ RE_ENTRANT_CHECK_OFF;
+ /* Copy all registers in stack order. */
+ __copy_from_user(((u_char *)&S387->st_space)+offset, d, other);
+ if ( offset )
+ __copy_from_user((u_char *)&S387->st_space, d+other, offset);
+ RE_ENTRANT_CHECK_ON;
+
+ /* The tags may need to be corrected now. */
+ tags = S387->twd;
+ newtop = S387->ftop;
+ for ( i = 0; i < 8; i++ )
+ {
+ regnr = (i+newtop) & 7;
+ if ( ((tags >> ((regnr & 7)*2)) & 3) != TAG_Empty )
+ {
+ /* The loaded data over-rides all other cases. */
+ tag = FPU_tagof((FPU_REG *)((u_char *)S387->st_space + 10*regnr));
+ tags &= ~(3 << (regnr*2));
+ tags |= (tag & 3) << (regnr*2);
+ }
+ }
+ S387->twd = tags;
- frstor(addr_modes, (char *)buf);
}
-struct _fpstate * save_i387_soft(struct _fpstate * buf)
+struct _fpstate * save_i387_soft(void *s387, struct _fpstate * buf)
{
- fpu_addr_modes addr_modes = {{ 0, 0, PREFIX_DEFAULT }, 0};
+ u_char *d = (u_char *)buf;
+ int offset = (S387->ftop & 7) * 10, other = 80 - offset;
- fsave(addr_modes, (char *)buf);
+ RE_ENTRANT_CHECK_OFF;
+ FPU_verify_area(VERIFY_WRITE, d, 7*4 + 8*10);
+#ifdef PECULIAR_486
+ S387->cwd &= ~0xe080;
+ /* An 80486 sets all the reserved bits to 1. */
+ S387->cwd |= 0xffff0000;
+ S387->swd = sstatus_word() | 0xffff0000;
+ S387->twd |= 0xffff0000;
+ S387->fcs |= 0xf8000000;
+ S387->fos |= 0xffff0000;
+#endif PECULIAR_486
+ __copy_to_user(d, &S387->cwd, 7*4);
+ RE_ENTRANT_CHECK_ON;
+
+ d += 7*4;
+
+ RE_ENTRANT_CHECK_OFF;
+ /* Copy all registers in stack order. */
+ __copy_to_user(d, ((u_char *)&S387->st_space)+offset, other);
+ if ( offset )
+ __copy_to_user(d+other, (u_char *)&S387->st_space, offset);
+ RE_ENTRANT_CHECK_ON;
return buf;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov