#define __BAD__ FPU_illegal /* Illegal on an 80486, causes SIGILL */
/* fcmovCC and f(u)comi(p) are enabled if CPUID(1).EDX(15) "cmov" is set */
/* WARNING: "u" entries are not documented by Intel in their 80486 manual andmaynotworkonFPUclonesorlaterIntelFPUs.
Changes to support them provided by Linus Torvalds. */
#define _NONE_ 0/* Take no special action */ #define _REG0_ 1/* Need to check for not empty st(0) */ #define _REGI_ 2/* Need to check for not empty st(0) and st(rm) */ #define _REGi_ 0/* Uses st(rm) */ #define _PUSH_ 3/* Need to check for space to push onto stack */ #define _null_ 4/* Function illegal or not implemented */ #define _REGIi 5/* Uses st(0) and st(rm), result to st(rm) */ #define _REGIp 6/* Uses st(0) and st(rm), result to st(rm) then pop */ #define _REGIc 0/* Compare st(0) and st(rm) */ #define _REGIn 0/* Uses st(0) and st(rm), but handle checks later */
if ((FPU_CS & 4) != 4) { /* Must be in the LDT */ /* Can only handle segmented addressing via the LDT
for now, and it must be 16 bit */
printk("FPU emulator: Unsupported addressing mode\n");
math_abort(FPU_info, SIGILL);
}
code_descriptor = FPU_get_ldt_descriptor(FPU_CS); if (code_descriptor.d) { /* The above test may be wrong, the book is not clear */ /* Segmented 32 bit protected mode */
addr_modes.default_mode = SEG32;
} else { /* 16 bit protected mode */
addr_modes.default_mode = PM16;
}
FPU_EIP += code_base = seg_get_base(&code_descriptor);
code_limit = seg_get_limit(&code_descriptor) + 1;
code_limit *= seg_get_granularity(&code_descriptor);
code_limit += code_base - 1; if (code_limit < code_base)
code_limit = 0xffffffff;
}
FPU_EIP++; /* We have fetched the prefix and first code bytes. */
if (addr_modes.default_mode) { /* This checks for the minimum instruction bytes.
We also need to check any extra (address mode) code access. */ if (FPU_EIP > code_limit)
math_abort(FPU_info, SIGSEGV);
}
if (addr_modes.default_mode) { if (FPU_EIP - 1 > code_limit)
math_abort(FPU_info, SIGSEGV);
}
if (!(byte1 & 1)) { unsignedshort status1 = partial_status;
st0_ptr = &st(0);
st0_tag = FPU_gettag0();
/* Stack underflow has priority */ if (NOT_EMPTY_ST0) { if (addr_modes.default_mode & PROTECTED) { /* This table works for 16 and 32 bit protected mode */ if (access_limit <
data_sizes_16[(byte1 >> 1) & 3])
math_abort(FPU_info, SIGSEGV);
}
/* 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 == TAG_Special) && isNaN(st0_ptr))
|| ((loaded_tag == TAG_Special)
&& isNaN(&loaded_data))) { /* Restore the status word; we might have loaded a
denormal. */
partial_status = status1; if ((FPU_modrm & 0x30) == 0x10) { /* fcom or fcomp */
EXCEPTION(EX_Invalid);
setcc(SW_C3 | SW_C2 | SW_C0); if ((FPU_modrm & 0x08)
&& (control_word &
CW_Invalid))
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,
loaded_tag, 0,
&loaded_data); else #endif/* PECULIAR_486 */ /* fadd, fdivr, fmul, or fsubr */
real_2op_NaN
(&loaded_data,
loaded_tag, 0,
st0_ptr);
} goto reg_mem_instr_done;
}
if (unmasked && !((FPU_modrm & 0x30) == 0x10)) { /* Is not a comparison instruction. */ if ((FPU_modrm & 0x38) == 0x38) { /* fdivr */ if ((st0_tag == TAG_Zero) &&
((loaded_tag == TAG_Valid)
|| (loaded_tag ==
TAG_Special
&&
isdenormal
(&loaded_data)))) { if (FPU_divide_by_zero
(0,
getsign
(&loaded_data))
< 0) { /* We use the fact here that the unmasked exceptionintheloadeddatawasfora
denormal operand */ /* Restore the state of the denormal op bit */
partial_status
&=
~SW_Denorm_Op;
partial_status
|=
status1 &
SW_Denorm_Op;
} else
setsign(st0_ptr,
getsign
(&loaded_data));
}
} goto reg_mem_instr_done;
}
} else { /* None of these instructions access user memory */
u_char instr_index = (FPU_modrm & 0x38) | (byte1 & 7);
#ifdef PECULIAR_486 /* This is supposed to be undefined, but a real 80486 seems
to do this: */
operand_address.offset = 0;
operand_address.selector = FPU_DS; #endif/* PECULIAR_486 */
st0_ptr = &st(0);
st0_tag = FPU_gettag0(); switch (type_table[(int)instr_index]) { case _NONE_: /* also _REGIc: _REGIn */ break; case _REG0_: if (!NOT_EMPTY_ST0) {
FPU_stack_underflow(); goto FPU_instruction_done;
} break; case _REGIi: if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
FPU_stack_underflow_i(FPU_rm); goto FPU_instruction_done;
} break; case _REGIp: if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
FPU_stack_underflow_pop(FPU_rm); goto FPU_instruction_done;
} break; case _REGI_: if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
FPU_stack_underflow(); goto FPU_instruction_done;
} break; case _PUSH_: /* Only used by the fld st(i) instruction */ break; case _null_:
FPU_illegal(); goto FPU_instruction_done; default:
EXCEPTION(EX_INTERNAL | 0x111); goto FPU_instruction_done;
}
(*st_instr_table[(int)instr_index]) ();
FPU_instruction_done:
;
}
if (!no_ip_update)
instruction_address = entry_sel_off;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.