From: Julian Seward Date: Thu, 2 Sep 2004 12:24:06 +0000 (+0000) Subject: Add the old Heimdall x87 FPU simulator, as reference. X-Git-Tag: svn/VALGRIND_3_0_1^2~1107 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6e69311d4b5161b203b648eba9fd94940684d3aa;p=thirdparty%2Fvalgrind.git Add the old Heimdall x87 FPU simulator, as reference. Add a small program to print out the state of the x87 FPU. git-svn-id: svn://svn.valgrind.org/vex/trunk@227 --- diff --git a/VEX/useful/hd_fpu.c b/VEX/useful/hd_fpu.c new file mode 100644 index 0000000000..300702916e --- /dev/null +++ b/VEX/useful/hd_fpu.c @@ -0,0 +1,1707 @@ + +/*--------------------------------------------------------------------*/ +/*--- Implementation of the floating point instruction set. ---*/ +/*--- hd_fpu.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Heimdall, an x86 protected-mode emulator + designed for debugging and profiling binaries on x86-Unixes. + + Copyright (C) 2000 Julian Seward + jseward@acm.org + Julian_Seward@muraroa.demon.co.uk + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file LICENSE. +*/ + +#include "hd_include.h" + + +/* --------------------------------------------------------------------- + Packing and unpacking the FPU data registers. + ------------------------------------------------------------------ */ + +INLINE +UInt fp_get_tos ( void ) +{ + return (m_fpu_state.env[FP_ENV_STAT] >> FP_F_TOS_LO) & 7; +} + +static +UInt read_bit_array ( UChar* arr, UInt n ) +{ + UChar c = arr[n >> 3]; + c >>= (n&7); + return c & 1; +} + +static +void write_bit_array ( UChar* arr, UInt n, UInt b ) +{ + UChar c = arr[n >> 3]; + c &= ~(1 << (n&7)); + b &= 1; + c |= (b << (n&7)); + arr[n >> 3] = c; +} + +/* Read an IEEE double from the memory image of an Intel 80-bit + extended floating-point number. +*/ +static +double fp_double_from_extended ( UChar* e_lsb ) +{ + int i; + double d; + UChar* d_lsb = (UChar*)(&d); + + UInt sign = e_lsb[9] >> 7; + Int bexp = ((UInt)e_lsb[9] << 8) | (UInt)e_lsb[8]; + bexp &= 0x7fff; + + if (bexp == 0) + bexp = 0; /* preserve zeroes */ + else + if (bexp == 0x7FFF) + bexp = 0x7FF; /* preserve Infs/Nans */ + else { + bexp -= (16383 - 1023); + if (bexp < 0) bexp = 0; + if (bexp > 0x7FF) bexp = 0x7FF; + } + + d_lsb[6] = (bexp & 0xF) << 4; + d_lsb[7] = ((bexp >> 4) & 0x7F) | ((sign & 0x1) << 7); + + for (i = 0; i < 52; i++) + write_bit_array ( d_lsb, + i, + read_bit_array ( e_lsb, i+11 ) ); + return d; +} + +/* Given an IEEE double, create the memory image of an Intel 80-bit + extended floating-point number. +*/ +static +void fp_extended_from_double ( UChar* e_lsb, double d ) +{ + int i; + UChar* d_lsb = (UChar*)(&d); + + UInt sign = d_lsb[7] >> 7; + Int bexp = ((UInt)d_lsb[7] << 4) | + ((((UInt)d_lsb[6]) >> 4) & 0xF); + bexp &= 0x7ff; + + if (bexp == 0) + bexp = 0; /* preserve zeroes */ + else + if (bexp == 0x7FF) + bexp = 0x7FFF; /* preserve Infs/Nans */ + else + bexp += (16383 - 1023); + + e_lsb[9] = ((bexp >> 8) & 0x7F) | ((sign & 0x1) << 7); + e_lsb[8] = bexp & 0xFF; + + for (i = 0; i < 52; i++) + write_bit_array ( e_lsb, + i+11, + read_bit_array ( d_lsb, i ) ); + for (i = 0; i < 11; i++) + write_bit_array ( e_lsb, i, 0 ); + + /* this isn't really right, but I can't get fpclassify to work. */ + i = 0; + if (isnan(d) || isinf(d) || d != 0.0) i = 1; + write_bit_array ( e_lsb, 63, i ); +} + +/* For the transition Real CPU -> Simulated CPU, copy the + .reg values in m_fpu_state, which are in stack order, to + the m_fpu_data_regs array, in register (non-stack) order. +*/ +void fp_unpack_data_regs ( void ) +{ + Int reg, st; + reg = fp_get_tos(); + for (st = 0; st < 8; st++) { + m_fpu_data_regs[reg] + = fp_double_from_extended ( &m_fpu_state.reg[FP_REG(st)] ); + if (reg == 7) reg = 0; else reg++; + } +} + +void fp_repack_data_regs ( void ) +{ + Int reg, st; + st = fp_get_tos(); + for (reg = 0; reg < 8; reg++) { + fp_extended_from_double ( &m_fpu_state.reg[FP_REG(reg)], + m_fpu_data_regs[st] ); + if (st == 7) st = 0; else st++; + } +} + +/* --------------------------------------------------------------------- + Helper functions for the floating point unit. + ------------------------------------------------------------------ */ + +static +INLINE +void setFMem ( UInt addr, double f ) +{ + * ((float*)addr) = (float)f; +} + +static +INLINE +double getFMem ( UInt addr ) +{ + return (double) (* ((float*)addr)); +} + +static +INLINE +void setDMem ( UInt addr, double f ) +{ + * ((double*)addr) = f; +} + +static +INLINE +double getDMem ( UInt addr ) +{ + return (* ((double*)addr)); +} + +static +INLINE +void setTMem ( UInt addr, double f ) +{ + fp_extended_from_double ( (Addr)addr, f ); +} + +static +INLINE +double getTMem ( UInt addr ) +{ + return fp_double_from_extended ( (Addr)addr ); +} + +#define fp_extended_from_double ERROR__fp_extended_from_double_used +#define fp_double_from_extended ERROR__fp_double_from_extended_used + +static +INLINE +UInt fp_get_statusword_flag ( UInt flagno ) +{ + if (flagno < 0 || flagno > 15) panic("fp_get_statusword_flag"); + return (m_fpu_state.env[FP_ENV_STAT] >> flagno) & 0x1; +} + +#if DEBUG +static +UInt fp_get_controlword_flag ( UInt flagno ) +{ + if (flagno < 0 || flagno > 15) panic("fp_get_controlword_flag"); + return (m_fpu_state.env[FP_ENV_CTRL] >> flagno) & 0x1; +} +#endif + +static +INLINE +void fp_set_statusword_flag_to ( UInt flagno, UInt bit ) +{ + if (flagno < 0 || flagno > 15) panic("fp_set_statusword_flag_to"); + if (bit) + m_fpu_state.env[FP_ENV_STAT] |= (1 << flagno); + else + m_fpu_state.env[FP_ENV_STAT] &= ~(1 << flagno); +} + +static +void fp_set_stack_overflow ( void ) +{ + fprintf(stderr, "--- FP STACK OVERFLOW!\n" ); + fp_set_statusword_flag_to(FP_E_INVAL,1); + fp_set_statusword_flag_to(FP_E_STACKF,1); + fp_set_statusword_flag_to(FP_F_C1,1); +} + +static +void fp_set_stack_underflow ( void ) +{ + fprintf(stderr, "--- FP STACK UNDERFLOW!\n" ); + fp_set_statusword_flag_to(FP_E_INVAL,1); + fp_set_statusword_flag_to(FP_E_STACKF,1); + fp_set_statusword_flag_to(FP_F_C1,0); +} + +static +INLINE +void fp_set_tos ( UInt tos ) +{ + if (tos < 0 || tos > 7) panic("fp_set_tos"); + fp_set_statusword_flag_to(FP_F_TOS_LO,0); + fp_set_statusword_flag_to(FP_F_TOS_LO+1,0); + fp_set_statusword_flag_to(FP_F_TOS_HI,0); + m_fpu_state.env[FP_ENV_STAT] |= (tos << FP_F_TOS_LO); +} + +static +INLINE +UInt fp_STno_to_regno ( UInt stregno ) +{ + UInt regno = fp_get_tos(); + assert(regno >= 0 && regno < 8); + regno += stregno; + if (regno >= 8) regno -= 8; + assert(regno >= 0 && regno < 8); + return regno; +} + +static +INLINE +void fp_dec_tos ( void ) +{ + fp_set_tos ( fp_STno_to_regno ( 7 )); +} + +static +INLINE +void fp_inc_tos ( void ) +{ + fp_set_tos ( fp_STno_to_regno ( 1 )); +} + +static +INLINE +Bool fp_is_empty_tag ( UInt tag ) +{ + return tag == FP_TAG_EMPTY; +} + +static +INLINE +UInt fp_get_tag ( UInt regno ) +{ + if (regno < 0 || regno > 7) panic("fp_get_tag"); + return (m_fpu_state.env[FP_ENV_TAG] >> (2*regno)) & 3; +} + +static +INLINE +UInt fp_get_tag_ST ( UInt stregno ) +{ + if (stregno < 0 || stregno > 7) panic("fp_get_tag_ST"); + return fp_get_tag ( fp_STno_to_regno(stregno) ); +} + +static +INLINE +void fp_set_tag ( UInt regno, UInt val ) +{ + if (regno < 0 || regno > 7 || + val < 0 || val > 3) panic("fp_get_tag"); + m_fpu_state.env[FP_ENV_TAG] &= ~(3 << (2*regno)); + m_fpu_state.env[FP_ENV_TAG] |= (val << (2*regno)); +} + +static +INLINE +void fp_set_tag_ST ( UInt stregno, UInt val ) +{ + if (stregno < 0 || stregno > 7) panic("fp_set_tag_ST"); + fp_set_tag ( fp_STno_to_regno(stregno), val ); +} + + +static +INLINE +void fp_set_reg ( UInt r, double d ) +{ + if (r < 0 || r > 7) panic("fp_set_reg"); + m_fpu_data_regs[r] = d; + fp_set_tag ( r, d==0.0 ? FP_TAG_ZERO + : (finite(d) ? FP_TAG_VALID : FP_TAG_SPEC) ); +} + +static +INLINE +void fp_set_reg_ST ( UInt str, double d ) +{ + UInt r; + if (str < 0 || str > 7) panic("fp_set_reg_ST"); + r = fp_STno_to_regno(str); + fp_set_reg ( r, d ); +} + +static +INLINE +double fp_get_reg ( UInt r ) +{ + double d; + if (r < 0 || r > 7) panic("fp_get_reg"); + d = m_fpu_data_regs[r]; + return d; +} + +static +INLINE +double fp_get_reg_ST ( UInt str ) +{ + UInt r; + if (str < 0 || str > 7) panic("fp_get_reg_ST"); + r = fp_STno_to_regno(str); + return fp_get_reg(r); +} + +static +INLINE +void fp_set_tos_reg ( double d ) +{ + fp_set_reg ( fp_get_tos(), d ); +} + +static +INLINE +double fp_get_tos_reg ( void ) +{ + return fp_get_reg ( fp_get_tos() ); +} + +static +INLINE +void fp_set_tos_reg_QNaN ( void ) +{ + fp_set_reg ( fp_get_tos(), NAN /* see */ ); +} + +static +INLINE +double fp_pop ( void ) +{ + double d = fp_get_tos_reg(); + fp_set_tag ( fp_get_tos(), FP_TAG_EMPTY ); + fp_inc_tos(); + return d; +} + +/* Push d and update flags. */ +static +INLINE +void fp_push ( double d ) +{ + if (fp_is_empty_tag(fp_get_tag_ST(7))) { + fp_dec_tos(); + fp_set_tos_reg(d); + fp_set_statusword_flag_to(FP_F_C1, d == 0.0); + } else { + fp_dec_tos(); + fp_set_tos_reg_QNaN(); + fp_set_stack_overflow(); + } +} + +static +void fp_set_statusword_flags_COM ( double vd_dst, double vd_src ) +{ + UInt vis_dst; + if (isnan(vd_src) || isnan(vd_dst)) vis_dst = 7; + else if (vd_dst > vd_src) vis_dst = 0; + else if (vd_dst < vd_src) vis_dst = 1; + else if (vd_dst == vd_src) vis_dst = 4; + else vis_dst = 7; + fp_set_statusword_flag_to(FP_F_C3, (vis_dst >> 2) & 1); + fp_set_statusword_flag_to(FP_F_C2, (vis_dst >> 1) & 1); + fp_set_statusword_flag_to(FP_F_C0, vis_dst & 1); +} + +static +void fp_set_statusword_flags_COM_STACKF ( void ) +{ + UInt vis_dst = 7; + fp_set_statusword_flag_to(FP_F_C3, (vis_dst >> 2) & 1); + fp_set_statusword_flag_to(FP_F_C2, (vis_dst >> 1) & 1); + fp_set_statusword_flag_to(FP_F_C0, vis_dst & 1); +} + +static +double fp_calc_yl2xp1 ( double st_0, double st_1 ) +{ + st_0 += 1.0; + st_0 = log(st_0) / log(2.0); + st_0 *= st_1; + return st_0; +} + +static +double fp_calc_yl2x ( double st_0, double st_1 ) +{ + st_0 = log(st_0) / log(2.0); + st_0 *= st_1; + return st_0; +} + +static +double fp_calc_2xm1 ( double st_0 ) +{ + st_0 = st_0 * 0.69314718055994530942; + st_0 = exp(st_0); + st_0 = st_0 - 1.0; + return st_0; +} + +static +double fp_calc_scale ( double st_0, double st_1 ) +{ + Int n = 0; + if (st_1 > 0.0) { + if (st_1 > 2.0*308.0) st_1 = 2.0*308.0; + n = (Int)(floor(st_1)); + if (n < 0) n = 0; /* impossible, but ... */ + if (n > 2*308) n = 2*308; /* limit exponent change */ + while (n > 0) { n--; st_0 *= 2.0; }; + } + else + if (st_1 < 0.0) { + if (st_1 < -2.0*308.0) st_1 = -2.0*308.0; + n = ((Int)(floor(-st_1))); + if (n < 0) n = 0; + if (n > 2*308) n = 2*308; + while (n > 0) { n--; st_0 *= 0.5; }; + } + return st_0; +} + +static +void fp_calc_fprem ( Int* qq, double* result, double st_0, double st_1 ) +{ + double tmp = st_0 / st_1; + if (tmp < 0) + *qq = - (Int)floor(-tmp); + else + *qq = (Int)floor(tmp); + *result = st_0 - (st_1 * (double)(*qq)); +} + +#if DEBUG +static +void printFpuState ( void ) +{ + Int i; + assert(sizeof(Fpu_State)==108); + for (i = 7; i >= 0; i--) { + printf ( " %s fpreg%d: 0x", + (UInt)i == fp_get_tos() ? "**" : " ", i ); + //for (j = FP_REG(i+1)-1; j >= FP_REG(i); j--) + // printf ( "%2x", (UInt)m_fpu_state.reg[j]); + printf ( " %5s ", fp_tag_names[fp_get_tag(i)] ); + printf ( "%20.16e\n", fp_get_reg(i) ); + } + printf(" fctrl: 0x%4x masked: ", + (UInt)m_fpu_state.env[FP_ENV_CTRL] ); + for (i = FP_E_INVAL; i <= FP_E_LOS; i++) + if (fp_get_controlword_flag(i)) + printf ( "%s ", fp_exception_names[i] ); + printf ( "\n" ); + + printf(" fstat: 0x%4x except:", + (UInt)m_fpu_state.env[FP_ENV_STAT] ); + for (i = FP_E_INVAL; i <= FP_E_LOS; i++) + if (fp_get_statusword_flag(i)) + printf ( "%s ", fp_exception_names[i] ); + printf ( " top: %d ", fp_get_tos() ); + printf ( "c3210: %d%d%d%d", + fp_get_statusword_flag(FP_F_C3), + fp_get_statusword_flag(FP_F_C2), + fp_get_statusword_flag(FP_F_C1), + fp_get_statusword_flag(FP_F_C0) ); + printf ( " STACKF: %d\n", fp_get_statusword_flag(FP_E_STACKF) ); + + printf(" ftag: 0x%4x ", (UInt)m_fpu_state.env[FP_ENV_TAG] ); + for (i = 7; i >= 0; i--) + printf ( "%s ", fp_tag_names[fp_get_tag(i)] ); + printf("\n"); + + printf(" fip: 0x%8x\n", + (((UInt)m_fpu_state.env[FP_ENV_IP+1]) << 16) | + ((UInt)m_fpu_state.env[FP_ENV_IP]) ); + printf(" fcs: 0x%4x\n", + ((UInt)m_fpu_state.env[FP_ENV_CS]) ); + printf(" fopoff: 0x%8x\n", + (((UInt)m_fpu_state.env[FP_ENV_OPOFF+1]) << 16) | + ((UInt)m_fpu_state.env[FP_ENV_OPOFF]) ); + printf(" fopsel: 0x%4x\n", + ((UInt)m_fpu_state.env[FP_ENV_OPSEL]) ); +} +#endif + +/* --------------------------------------------------------------------- + Implementation of the floating point instruction set. + ------------------------------------------------------------------ */ + +/* A pretty nasty kludge. Arithmetic is done using standard IEEE + doubles, which means that programs which rely on the extra accuracy + supplied by Intel's internal 80-bit format will get different + results. + + To make exception handling tractable, we assume that the FPU is + running with all exceptions masked, so we do the "default fixup" + action for all exceptions. Fortunately that's fairly simple. + + Support for non-normal numbers (infinities, nans, denorms, etc) is + minimal and probably wrong. +*/ + +typedef + enum { Fp_Add, Fp_Sub, Fp_Mul, Fp_Div, Fp_SubR, Fp_DivR } + Fp_Op; + +#if DEBUG +char* fp_Op_name ( Fp_Op op ) +{ + switch (op) { + case Fp_Add: return "add"; case Fp_Sub: return "sub"; + case Fp_Mul: return "mul"; case Fp_Div: return "div"; + case Fp_SubR: return "subr"; case Fp_DivR: return "divr"; + default: panic("fp_Op_name"); + } + return NULL; /*notreached*/ +} +#endif + +static +void fp_do_op_ST_ST ( UInt a_src, UInt a_dst, Fp_Op op, Bool pop ) +{ + double vd_src, vd_dst; + IFDB( if (dis) printf("\tf%s%s\t%%st(%d),%%st(%d)\n", + fp_Op_name(op), pop?"p":"", + a_src, a_dst ); ) + if (!fp_is_empty_tag(fp_get_tag_ST(a_src)) && + !fp_is_empty_tag(fp_get_tag_ST(a_dst))) { + vd_dst = fp_get_reg_ST(a_dst); + vd_src = fp_get_reg_ST(a_src); + switch (op) { + case Fp_Add: vd_dst = vd_dst + vd_src; break; + case Fp_Sub: vd_dst = vd_dst - vd_src; break; + case Fp_Mul: vd_dst = vd_dst * vd_src; break; + case Fp_Div: vd_dst = vd_dst / vd_src; break; + case Fp_SubR: vd_dst = vd_src - vd_dst; break; + case Fp_DivR: vd_dst = vd_src / vd_dst; break; + default: panic("fp_do_op_ST_ST"); + } + } else { + vd_dst = NAN; + fp_set_stack_underflow(); + } + fp_set_reg_ST(a_dst,vd_dst); + if (pop) (void)fp_pop(); +} + +static +void fp_do_COM_ST_ST ( UInt a_src, UInt a_dst, UInt nPops ) +{ + double vd_src, vd_dst; + IFDB( if (dis) printf("\tfcom%s\t%%st(%d),%%st(%d)\n", + nPops==0 ? "" : (nPops==1 ? "p" : "pp"), + a_src, a_dst ); ) + if (!fp_is_empty_tag(fp_get_tag_ST(a_src)) && + !fp_is_empty_tag(fp_get_tag_ST(a_dst))) { + vd_dst = fp_get_reg_ST(a_dst); + vd_src = fp_get_reg_ST(a_src); + fp_set_statusword_flags_COM(vd_dst,vd_src); + } else { + fp_set_statusword_flags_COM_STACKF(); + fp_set_stack_underflow(); + } + while (nPops > 0) { + (void)fp_pop(); + nPops--; + } +} + +static +void fp_do_op_mem_ST_0 ( UInt a_src, + IFDB(Text t_src CC) + Fp_Op op, Bool dbl ) +{ + double vd_src, vd_dst; + IFDB( if (dis) printf("\tf%s%c\t%s,%%st(0)\n", + fp_Op_name(op), dbl?'D':'F', t_src ); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_dst = fp_get_reg_ST(0); + vd_src = dbl ? getDMem(a_src) : getFMem(a_src); + switch (op) { + case Fp_Add: vd_dst = vd_dst + vd_src; break; + case Fp_Sub: vd_dst = vd_dst - vd_src; break; + case Fp_Mul: vd_dst = vd_dst * vd_src; break; + case Fp_Div: vd_dst = vd_dst / vd_src; break; + case Fp_SubR: vd_dst = vd_src - vd_dst; break; + case Fp_DivR: vd_dst = vd_src / vd_dst; break; + default: panic("fp_do_op_mem_ST_0"); + } + } else { + vd_dst = NAN; + fp_set_stack_underflow(); + } + fp_set_reg_ST(0,vd_dst); +} + +static +void fp_do_COM_mem_ST_0 ( UInt a_src, + IFDB( Text t_src CC) + Bool dbl, Bool pop ) +{ + double vd_src, vd_dst; + IFDB( if (dis) printf("\tfcom%s%c\t%s,%%st(0)\n", + pop?"p":"", dbl?'D':'F', t_src ); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_dst = fp_get_reg_ST(0); + vd_src = dbl ? getDMem(a_src) : getFMem(a_src); + fp_set_statusword_flags_COM(vd_dst,vd_src); + } else { + fp_set_statusword_flags_COM_STACKF(); + fp_set_stack_underflow(); + } + if (pop) (void)fp_pop(); +} + + +Addr do_one_insn_fp ( Addr r_eip, UChar first_opcode ) +{ + UChar modrm; + UInt a_addr, a_src, a_dst; + UInt opc_aux; + Bool isreg; + Int vis_addr; + Int vis_dst; + double vd_addr, vd_src, vd_dst; + +# if DEBUG + Text t_opc_aux; + Text t_addr, t_dst; + Bool ppFpuState = False; + + if (ppFpuState) { + printf("\n\nBEFORE\n"); + printFpuState(); + printf("\n"); + } +# endif + + /* assert that we are running with all exceptions masked */ + assert( (m_fpu_state.env[FP_ENV_CTRL] & 0x3F) == 0x3F ); + /* and the implication is that there are no unmasked exceptions + reported by the exception status flag. */ + assert( fp_get_statusword_flag(FP_E_SUMMARY) == 0 ); + + modrm = *r_eip; + + /* -+-+-+-+-+-+-+-+-+-+-+-+ 0xD8 opcodes +-+-+-+-+-+-+-+ */ + + if (first_opcode == 0xD8) { + if (modrm < 0xC0) { + /* bits 5,4,3 are an opcode extension, and the modRM also + specifies an address. */ + opc_aux = regno_from_modRM ( r_eip, 4 IFDB(CC &t_opc_aux) ); + r_eip = amode_from_modRM ( r_eip, 4, &a_addr + IFDB(CC &t_addr), &isreg ); + assert(!isreg); + switch (opc_aux) { + + case 0: /* FADD single-real */ + fp_do_op_mem_ST_0 ( a_addr, IFDB(t_addr CC) + Fp_Add, False ); + break; + + case 1: /* FMUL single-real */ + fp_do_op_mem_ST_0 ( a_addr, IFDB(t_addr CC) + Fp_Mul, False ); + break; + + case 2: /* FCOM single-real */ + fp_do_COM_mem_ST_0 ( a_addr, IFDB(t_addr CC) + False, False ); + break; + + case 3: /* FCOMP single-real */ + fp_do_COM_mem_ST_0 ( a_addr, IFDB(t_addr CC) + False, True ); + break; + + case 4: /* FSUB single-real */ + fp_do_op_mem_ST_0 ( a_addr, IFDB(t_addr CC) + Fp_Sub, False ); + break; + + case 5: /* FSUBR single-real */ + fp_do_op_mem_ST_0 ( a_addr, IFDB(t_addr CC) + Fp_SubR, False ); + break; + + case 6: /* FDIV single-real */ + fp_do_op_mem_ST_0 ( a_addr, IFDB(t_addr CC) + Fp_Div, False ); + break; + + case 7: /* FDIVR single-real */ + fp_do_op_mem_ST_0 ( a_addr, IFDB(t_addr CC) + Fp_DivR, False ); + break; + + default: + printf("unhandled opc_aux = 0x%2x\n", opc_aux); + panic("do_one_insn_fp: first_opcode == 0xD8"); + break; + } + } else { + /* The entire modRM byte is an opcode extension. */ + r_eip++; + switch (modrm) { + + case 0xC0 ... 0xC7: /* FADD %st(?),%st(0) */ + fp_do_op_ST_ST ( modrm - 0xC0, 0, Fp_Add, False ); + break; + + case 0xC8 ... 0xCF: /* FMUL %st(?),%st(0) */ + fp_do_op_ST_ST ( modrm - 0xC8, 0, Fp_Mul, False ); + break; + + case 0xD0 ... 0xD7: /* FCOM %st(?),%st(0) */ + fp_do_COM_ST_ST ( modrm - 0xD0, 0, 0 ); + break; + + case 0xD8 ... 0xDF: /* FCOMP %st(?),%st(0) */ + fp_do_COM_ST_ST ( modrm - 0xD8, 0, 1 ); + break; + + case 0xE0 ... 0xE7: /* FSUB %st(?),%st(0) */ + fp_do_op_ST_ST ( modrm - 0xE0, 0, Fp_Sub, False ); + break; + + case 0xE8 ... 0xEF: /* FSUBR %st(?),%st(0) */ + fp_do_op_ST_ST ( modrm - 0xE8, 0, Fp_SubR, False ); + break; + + case 0xF0 ... 0xF7: /* FDIV %st(?),%st(0) */ + fp_do_op_ST_ST ( modrm - 0xF0, 0, Fp_Div, False ); + break; + + case 0xF8 ... 0xFF: /* FDIVR %st(?),%st(0) */ + fp_do_op_ST_ST ( modrm - 0xF8, 0, Fp_DivR, False ); + break; + + default: + goto unhandled; + } + } + } + + /* -+-+-+-+-+-+-+-+-+-+-+-+ 0xD9 opcodes +-+-+-+-+-+-+-+ */ + else + if (first_opcode == 0xD9) { + if (modrm < 0xC0) { + /* bits 5,4,3 are an opcode extension, and the modRM also + specifies an address. */ + opc_aux = regno_from_modRM ( r_eip, 4 IFDB(CC &t_opc_aux) ); + r_eip = amode_from_modRM ( r_eip, 4, &a_addr + IFDB(CC &t_addr), &isreg ); + assert(!isreg); + switch (opc_aux) { + + case 0: /* FLD single-real */ + IFDB( if (dis) printf("\tfldF\t%s\n",t_addr); ) + vd_addr = getFMem(a_addr); + fp_push(vd_addr); + break; + + case 2: /* FST single-real */ + IFDB( if (dis) printf("\tfstF\t%s\n",t_addr); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_addr = fp_get_reg_ST(0); + } else { + vd_addr = NAN; + fp_set_stack_underflow(); + } + setFMem(a_addr,vd_addr); + break; + + case 3: /* FSTP single-real */ + IFDB( if (dis) printf("\tfstpF\t%s\n",t_addr); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_addr = fp_pop(); + } else { + vd_addr = fp_pop(); /* then throw away result */ + vd_addr = NAN; + fp_set_stack_underflow(); + } + setFMem(a_addr,vd_addr); + break; + + case 5: /* FLDCW */ + IFDB( if (dis) printf("\tfldcw\t%s\n",t_addr); ) + m_fpu_state.env[FP_ENV_CTRL] = (UShort)getIMem2(a_addr); + break; + + case 7: /* FNSTCW */ + IFDB( if (dis) printf("\tfnstcw\t%s\n",t_addr); ) + setIMem2(a_addr,(UInt)m_fpu_state.env[FP_ENV_CTRL]); + break; + + default: + printf("unhandled opc_aux = 0x%2x\n", opc_aux); + panic("do_one_insn_fp: first_opcode == 0xD9"); + break; + } + } else { + /* The entire modRM byte is an opcode extension. */ + r_eip++; + switch (modrm) { + + case 0xC0 ... 0xC7: /* FLD %st(?) */ + a_dst = (UInt)modrm - 0xC0; + IFDB( if (dis) printf("\tfld\t%%st(%d)\n",a_dst); ) + if (!fp_is_empty_tag(fp_get_tag_ST(a_dst)) && + fp_is_empty_tag(fp_get_tag_ST(7))) { + vd_dst = fp_get_reg_ST(a_dst); + } else { + vd_dst = NAN; + fp_set_stack_underflow(); + } + fp_push(vd_dst); + break; + + case 0xC8 ... 0xCF: /* FXCH %st(?) */ + a_dst = (UInt)modrm - 0xC8; + IFDB( if (dis) printf("\tfxch\t%%st(%d)\n",a_dst); ) + if (!fp_is_empty_tag(fp_get_tag_ST(a_dst)) && + !fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_dst = fp_get_reg_ST(a_dst); + vd_src = fp_get_reg_ST(0); + } else { + vd_dst = NAN; + vd_src = NAN; + fp_set_stack_underflow(); + } + fp_set_reg_ST(a_dst,vd_src); + fp_set_reg_ST(0,vd_dst); + break; + + case 0xE0: /* FCHS */ + IFDB( if (dis) printf("\tfchs\n"); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_dst = - fp_get_reg_ST(0); + } else { + vd_dst = NAN; + fp_set_stack_underflow(); + } + fp_set_reg_ST(0,vd_dst); + break; + + case 0xE1: /* FABS */ + IFDB( if (dis) printf("\tfabs\n"); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_dst = fabs(fp_get_reg_ST(0)); + } else { + vd_dst = NAN; + fp_set_stack_underflow(); + } + fp_set_reg_ST(0,vd_dst); + break; + + case 0xE5: + /* An approximation to the correct behaviour */ + IFDB( if (dis) printf("\tfxam\n"); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_dst = fabs(fp_get_reg_ST(0)); + if (isnan(vd_dst)) + vis_dst = 1; /* C320 = 001 */ + else if (isinf(vd_dst)) + vis_dst = 3; /* C320 = 011 */ + else if (vd_dst == 0.0 || vd_dst == -0.0) + vis_dst = 4; /* C320 = 100 */ + else + vis_dst = 2; /* C320 = 010 */ + fp_set_statusword_flag_to(FP_F_C1, + vd_dst < 0.0 ? 1 : 0); + } else { + vis_dst = 5; /* C320 = 101 */ + /* no idea if this is right */ + fp_set_statusword_flag_to(FP_F_C1, 0); + } + fp_set_statusword_flag_to(FP_F_C3, (vis_dst >> 2) & 1); + fp_set_statusword_flag_to(FP_F_C2, (vis_dst >> 1) & 1); + fp_set_statusword_flag_to(FP_F_C0, vis_dst & 1); + break; + + case 0xE8: /* FLD1 */ + IFDB( t_dst = "1"; ) + vd_dst = 1.0; + goto do_fld_CONST; + case 0xEC: /* FLDLG2 */ + IFDB( t_dst = "lg2"; ) + vd_dst = 0.301029995663981143; + goto do_fld_CONST; + case 0xED: /* FLDLN2 */ + IFDB( t_dst = "ln2"; ) + vd_dst = 0.69314718055994530942; + goto do_fld_CONST; + case 0xEE: /* FLDZ */ + IFDB( t_dst = "z"; ) + vd_dst = 0.0; + goto do_fld_CONST; + do_fld_CONST: + IFDB( if (dis) printf("\tfld%s\n",t_dst); ) + fp_push(vd_dst); + break; + + case 0xF0: /* F2XM1 */ + IFDB( if (dis) printf("\tf2xm1\n"); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_dst = fp_calc_2xm1(fp_get_reg_ST(0)); + } else { + vd_dst = NAN; + fp_set_stack_underflow(); + } + fp_set_reg_ST(0,vd_dst); + break; + + case 0xF1: /* FYL2X */ + IFDB( if (dis) printf("\tfyl2x\n"); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0)) && + !fp_is_empty_tag(fp_get_tag_ST(1))) { + vd_dst = fp_calc_yl2x( + fp_get_reg_ST(0), fp_get_reg_ST(1)); + } else { + vd_dst = NAN; + fp_set_stack_underflow(); + } + fp_set_reg_ST(1,vd_dst); + (void)fp_pop(); + break; + + case 0xF3: /* FPATAN */ + IFDB( if (dis) printf("\tfpatan\n"); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0)) && + !fp_is_empty_tag(fp_get_tag_ST(1))) { + vd_dst = atan2( + fp_get_reg_ST(1), fp_get_reg_ST(0)); + } else { + vd_dst = NAN; + fp_set_stack_underflow(); + } + fp_set_reg_ST(1,vd_dst); + (void)fp_pop(); + break; + + case 0xF8: { /* FPREM */ + /* Very incomplete implementation. */ + Int qq; + IFDB( if (dis) printf("\tfprem\n"); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0)) && + !fp_is_empty_tag(fp_get_tag_ST(1))) { + fp_calc_fprem( &qq, &vd_dst, + fp_get_reg_ST(0), fp_get_reg_ST(1) ); + fp_set_statusword_flag_to(FP_F_C0, (qq & 4) ? 1 : 0); + fp_set_statusword_flag_to(FP_F_C1, (qq & 1) ? 1 : 0); + fp_set_statusword_flag_to(FP_F_C2, 0); /* reduction complete */ + fp_set_statusword_flag_to(FP_F_C3, (qq & 2) ? 1 : 0); + } else { + vd_dst = NAN; + fp_set_stack_underflow(); + fp_set_statusword_flag_to(FP_F_C1, 0); /* stack underflow */ + } + fp_set_reg_ST(0,vd_dst); + break; + } + case 0xF9: /* FYL2XP1 */ + IFDB( if (dis) printf("\tfyl2xp1\n"); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0)) && + !fp_is_empty_tag(fp_get_tag_ST(1))) { + vd_dst = fp_calc_yl2xp1( + fp_get_reg_ST(0), fp_get_reg_ST(1)); + } else { + vd_dst = NAN; + fp_set_stack_underflow(); + } + fp_set_reg_ST(1,vd_dst); + (void)fp_pop(); + break; + + case 0xFA: /* FSQRT */ + IFDB( if (dis) printf("\tfsqrt\n"); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_dst = sqrt(fp_get_reg_ST(0)); + } else { + vd_dst = NAN; + fp_set_stack_underflow(); + } + fp_set_reg_ST(0,vd_dst); + break; + + case 0xFC: /* FRNDINT */ + IFDB( if (dis) printf("\tfrndint\n"); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_dst = rint(fp_get_reg_ST(0)); + } else { + vd_dst = NAN; + fp_set_stack_underflow(); + } + fp_set_reg_ST(0,vd_dst); + break; + + case 0xFD: /* FSCALE */ + IFDB( if (dis) printf("\tfscale\n"); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0)) && + !fp_is_empty_tag(fp_get_tag_ST(1))) { + vd_dst = fp_calc_scale( + fp_get_reg_ST(0), fp_get_reg_ST(1)); + } else { + vd_dst = NAN; + fp_set_stack_underflow(); + } + fp_set_reg_ST(0,vd_dst); + break; + + case 0xFE: /* FSIN */ + IFDB( if (dis) printf("\tfsin\n"); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_dst = sin(fp_get_reg_ST(0)); + } else { + vd_dst = NAN; + fp_set_stack_underflow(); + } + fp_set_reg_ST(0,vd_dst); + break; + + case 0xFF: /* FCOS */ + IFDB( if (dis) printf("\tfcos\n"); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_dst = cos(fp_get_reg_ST(0)); + } else { + vd_dst = NAN; + fp_set_stack_underflow(); + } + fp_set_reg_ST(0,vd_dst); + break; + + default: + goto unhandled; + } + } + } + + /* -+-+-+-+-+-+-+-+-+-+-+-+ 0xDA opcodes +-+-+-+-+-+-+-+ */ + else + if (first_opcode == 0xDA) { + if (modrm < 0xC0) { + /* bits 5,4,3 are an opcode extension, and the modRM also + specifies an address. */ + opc_aux = regno_from_modRM ( r_eip, 4 IFDB(CC &t_opc_aux) ); + r_eip = amode_from_modRM ( r_eip, 4, &a_addr + IFDB(CC &t_addr), &isreg ); + assert(!isreg); + switch (opc_aux) { + + case 0: /* FIADD m32int */ + IFDB( if (dis) printf("\tfiaddl\t%s\n",t_addr); ) + vis_addr = getIMem4(a_addr); + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_addr = fp_get_reg_ST(0) + (double)vis_addr; + fp_set_reg_ST(0, vd_addr); + /* we should set C1 here */ + } else { + fp_set_reg_ST(0, NAN); + fp_set_stack_underflow(); + } + break; + + case 1: /* FIMUL m32int */ + IFDB( if (dis) printf("\tfimull\t%s\n",t_addr); ) + vis_addr = getIMem4(a_addr); + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_addr = fp_get_reg_ST(0) * (double)vis_addr; + fp_set_reg_ST(0, vd_addr); + /* we should set C1 here */ + } else { + fp_set_reg_ST(0, NAN); + fp_set_stack_underflow(); + } + break; + + case 2: /* FICOM m32int */ + IFDB( if (dis) printf("\tficoml\t%s\n",t_addr); ) + vis_addr = getIMem4(a_addr); + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_dst = fp_get_reg_ST(0); + vd_src = (double)vis_addr; + fp_set_statusword_flags_COM(vd_dst,vd_src); + /* we should set C1 here */ + } else { + fp_set_statusword_flags_COM_STACKF(); + fp_set_stack_underflow(); + } + break; + + case 3: /* FICOMP m32int */ + IFDB( if (dis) printf("\tficompl\t%s\n",t_addr); ) + vis_addr = getIMem4(a_addr); + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_dst = fp_get_reg_ST(0); + vd_src = (double)vis_addr; + fp_set_statusword_flags_COM(vd_dst,vd_src); + /* we should set C1 here */ + } else { + fp_set_statusword_flags_COM_STACKF(); + fp_set_stack_underflow(); + } + (void)fp_pop(); + break; + + case 4: /* FISUB m32int */ + IFDB( if (dis) printf("\tfisubl\t%s\n",t_addr); ) + vis_addr = getIMem4(a_addr); + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_addr = fp_get_reg_ST(0) - (double)vis_addr; + fp_set_reg_ST(0, vd_addr); + /* we should set C1 here */ + } else { + fp_set_reg_ST(0, NAN); + fp_set_stack_underflow(); + } + break; + + case 5: /* FISUBR m32int */ + IFDB( if (dis) printf("\tfisubrl\t%s\n",t_addr); ) + vis_addr = getIMem4(a_addr); + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_addr = (double)vis_addr - fp_get_reg_ST(0); + fp_set_reg_ST(0, vd_addr); + /* we should set C1 here */ + } else { + fp_set_reg_ST(0, NAN); + fp_set_stack_underflow(); + } + break; + + case 6: /* FIDIV m32int */ + IFDB( if (dis) printf("\tfidivl\t%s\n",t_addr); ) + vis_addr = getIMem4(a_addr); + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_addr = fp_get_reg_ST(0) / (double)vis_addr; + fp_set_reg_ST(0, vd_addr); + /* we should set C1 here */ + } else { + fp_set_reg_ST(0, NAN); + fp_set_stack_underflow(); + } + break; + + case 7: /* FIDIVR m32int */ + IFDB( if (dis) printf("\tfidivl\t%s\n",t_addr); ) + vis_addr = getIMem4(a_addr); + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_addr = (double)vis_addr / fp_get_reg_ST(0); + fp_set_reg_ST(0, vd_addr); + /* we should set C1 here */ + } else { + fp_set_reg_ST(0, NAN); + fp_set_stack_underflow(); + } + break; + + default: + printf("unhandled opc_aux = 0x%2x\n", opc_aux); + panic("do_one_insn_fp: first_opcode == 0xDA"); + break; + } + } else { + /* The entire modRM byte is an opcode extension. */ + r_eip++; + switch (modrm) { + + case 0xE9: /* FUCOMPP %st(0),%st(1) */ + /* seems the wrong way round. */ + fp_do_COM_ST_ST ( 1, 0, 2 ); + break; + + default: + goto unhandled; + } + } + } + + /* -+-+-+-+-+-+-+-+-+-+-+-+ 0xDB opcodes +-+-+-+-+-+-+-+ */ + else + if (first_opcode == 0xDB) { + if (modrm < 0xC0) { + /* bits 5,4,3 are an opcode extension, and the modRM also + specifies an address. */ + opc_aux = regno_from_modRM ( r_eip, 4 IFDB(CC &t_opc_aux) ); + r_eip = amode_from_modRM ( r_eip, 4, &a_addr + IFDB(CC &t_addr), &isreg ); + assert(!isreg); + switch (opc_aux) { + + case 0: /* FILD m32int */ + IFDB( if (dis) printf("\tfildl\t%s\n",t_addr); ) + vis_addr = getIMem4(a_addr); + fp_push ( (double)vis_addr ); + break; + + case 2: /* FIST m32 */ + IFDB( if (dis) printf("\tfistl\t%s\n",t_addr); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_addr = fp_get_reg_ST(0); + if (vd_addr <= -2147483648.5 || + vd_addr >= 2147483647.5) + vis_addr = 0x80000000; /* 32-bit int indefinite */ + else + vis_addr = (Int)vd_addr; + } else { + vis_addr = 0x80000000; /* 32-bit indefinite */ + fp_set_stack_underflow(); + } + setIMem4(a_addr,vis_addr); + break; + + case 3: /* FISTP m32 */ + IFDB( if (dis) printf("\tfistpl\t%s\n",t_addr); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_addr = fp_pop(); + if (vd_addr <= -2147483648.5 || + vd_addr >= 2147483647.5) + vis_addr = 0x80000000; /* 32-bit int indefinite */ + else + vis_addr = (Int)vd_addr; + } else { + vd_addr = fp_pop(); /* then throw away result */ + vis_addr = 0x80000000; /* 32-bit indefinite */ + fp_set_stack_underflow(); + } + setIMem4(a_addr,vis_addr); + break; + + case 5: /* FLD extended-real */ + IFDB( if (dis) printf("\tfldT\t%s\n",t_addr); ) + vd_addr = getTMem(a_addr); + fp_push(vd_addr); + break; + + case 7: /* FSTP extended-real */ + IFDB( if (dis) printf("\tfstpT\t%s\n",t_addr); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_addr = fp_pop(); + } else { + vd_addr = fp_pop(); /* then throw away result */ + vd_addr = NAN; + fp_set_stack_underflow(); + } + setTMem(a_addr,vd_addr); + break; + + default: + printf("unhandled opc_aux = 0x%2x\n", opc_aux); + panic("do_one_insn_fp: first_opcode == 0xDB"); + break; + } + } else { + /* The entire modRM byte is an opcode extension. */ + r_eip++; + switch (modrm) { + default: + goto unhandled; + } + } + } + + /* -+-+-+-+-+-+-+-+-+-+-+-+ 0xDC opcodes +-+-+-+-+-+-+-+ */ + else + if (first_opcode == 0xDC) { + if (modrm < 0xC0) { + /* bits 5,4,3 are an opcode extension, and the modRM also + specifies an address. */ + opc_aux = regno_from_modRM ( r_eip, 4 IFDB(CC &t_opc_aux) ); + r_eip = amode_from_modRM ( r_eip, 4, &a_addr + IFDB(CC &t_addr), &isreg ); + assert(!isreg); + switch (opc_aux) { + + case 0: /* FADD double-real */ + fp_do_op_mem_ST_0 ( a_addr, IFDB(t_addr CC) Fp_Add, True ); + break; + + case 1: /* FMUL double-real */ + fp_do_op_mem_ST_0 ( a_addr, IFDB(t_addr CC) Fp_Mul, True ); + break; + + case 2: /* FCOM double-real */ + fp_do_COM_mem_ST_0 ( a_addr, IFDB(t_addr CC) True, False ); + break; + + case 3: /* FCOMP double-real */ + fp_do_COM_mem_ST_0 ( a_addr, IFDB(t_addr CC) True, True ); + break; + + case 4: /* FSUB double-real */ + fp_do_op_mem_ST_0 ( a_addr, IFDB(t_addr CC) Fp_Sub, True ); + break; + + case 5: /* FSUBR double-real */ + fp_do_op_mem_ST_0 ( a_addr, IFDB(t_addr CC) Fp_SubR, True ); + break; + + case 6: /* FDIV double-real */ + fp_do_op_mem_ST_0 ( a_addr, IFDB(t_addr CC) Fp_Div, True ); + break; + + case 7: /* FDIVR double-real */ + fp_do_op_mem_ST_0 ( a_addr, IFDB(t_addr CC) Fp_DivR, True ); + break; + + default: + printf("unhandled opc_aux = 0x%2x\n", opc_aux); + panic("do_one_insn_fp: first_opcode == 0xDC"); + break; + } + } else { + /* The entire modRM byte is an opcode extension. */ + r_eip++; + switch (modrm) { + + case 0xC0 ... 0xC7: /* FADD %st(0),%st(?) */ + fp_do_op_ST_ST ( 0, modrm - 0xC0, Fp_Add, False ); + break; + + case 0xC8 ... 0xCF: /* FMUL %st(0),%st(?) */ + fp_do_op_ST_ST ( 0, modrm - 0xC8, Fp_Mul, False ); + break; + + case 0xE0 ... 0xE7: /* FSUBR %st(0),%st(?) */ + fp_do_op_ST_ST ( 0, modrm - 0xE0, Fp_SubR, False ); + break; + + case 0xE8 ... 0xEF: /* FSUB %st(0),%st(?) */ + fp_do_op_ST_ST ( 0, modrm - 0xE8, Fp_Sub, False ); + break; + + case 0xF8 ... 0xFF: /* FDIV %st(0),%st(?) */ + fp_do_op_ST_ST ( 0, modrm - 0xF8, Fp_Div, False ); + break; + + default: + goto unhandled; + } + } + } + + /* -+-+-+-+-+-+-+-+-+-+-+-+ 0xDD opcodes +-+-+-+-+-+-+-+ */ + else + if (first_opcode == 0xDD) { + if (modrm < 0xC0) { + /* bits 5,4,3 are an opcode extension, and the modRM also + specifies an address. */ + opc_aux = regno_from_modRM ( r_eip, 4 IFDB(CC &t_opc_aux) ); + r_eip = amode_from_modRM ( r_eip, 4, &a_addr + IFDB(CC &t_addr), &isreg ); + assert(!isreg); + switch (opc_aux) { + + case 0: /* FLD double-real */ + IFDB( if (dis) printf("\tfldD\t%s\n",t_addr); ) + vd_addr = getDMem(a_addr); + fp_push(vd_addr); + break; + + case 2: /* FST double-real */ + IFDB( if (dis) printf("\tfstD\t%s\n",t_addr); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_addr = fp_get_reg_ST(0); + } else { + vd_addr = NAN; + fp_set_stack_underflow(); + } + setDMem(a_addr,vd_addr); + break; + + case 3: /* FSTP double-real */ + IFDB( if (dis) printf("\tfstpD\t%s\n",t_addr); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_addr = fp_pop(); + } else { + vd_addr = fp_pop(); /* then throw away result */ + vd_addr = NAN; + fp_set_stack_underflow(); + } + setDMem(a_addr,vd_addr); + break; + default: + printf("unhandled opc_aux = 0x%2x\n", opc_aux); + panic("do_one_insn_fp: first_opcode == 0xDD"); + break; + } + } else { + /* The entire modRM byte is an opcode extension. */ + r_eip++; + switch (modrm) { + + case 0xC0 ... 0xC7: /* FFREE %st(?) */ + a_dst = (UInt)modrm - 0xC0; + IFDB( if (dis) printf("\tffree\t%%st(%d)\n", a_dst); ) + fp_set_tag_ST( a_dst, FP_TAG_EMPTY ); + break; + + case 0xD0 ... 0xD7: /* FST %st(0),%st(?) */ + a_dst = (UInt)modrm - 0xD0; + IFDB( if (dis) printf("\tfst\t%%st(0),%%st(%d)\n", + a_dst); ) + if ( /* don't check the destination tag */ + !fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_dst = fp_get_reg_ST(0); + } else { + vd_dst = NAN; + fp_set_stack_underflow(); + } + fp_set_reg_ST(a_dst,vd_dst); + break; + + case 0xD8 ... 0xDF: /* FSTP %st(0),%st(?) */ + a_dst = (UInt)modrm - 0xD8; + IFDB( if (dis) printf("\tfstp\t%%st(0),%%st(%d)\n", + a_dst); ) + if ( /* don't check the destination tag */ + !fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_dst = fp_get_reg_ST(0); + } else { + vd_dst = NAN; + fp_set_stack_underflow(); + } + fp_set_reg_ST(a_dst,vd_dst); + (void)fp_pop(); + break; + + case 0xE0 ... 0xE7: /* FUCOM %st(0),%st(?) */ + a_src = (UInt)modrm - 0xE0; + IFDB( if (dis) printf("\tfucom\t%%st(0),%%st(%d)\n", + a_src); ) + if (!fp_is_empty_tag(fp_get_tag_ST(a_src)) && + !fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_src = fp_get_reg_ST(a_src); + vd_dst = fp_get_reg_ST(0); + fp_set_statusword_flags_COM(vd_dst,vd_src); + } else { + fp_set_statusword_flags_COM_STACKF(); + fp_set_stack_underflow(); + } + break; + + case 0xE8 ... 0xEF: /* FUCOMP %st(0),%st(?) */ + a_src = (UInt)modrm - 0xE8; + IFDB( if (dis) printf("\tfucomp\t%%st(0),%%st(%d)\n", + a_src); ) + if (!fp_is_empty_tag(fp_get_tag_ST(a_src)) && + !fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_src = fp_get_reg_ST(a_src); + vd_dst = fp_get_reg_ST(0); + fp_set_statusword_flags_COM(vd_dst,vd_src); + } else { + fp_set_statusword_flags_COM_STACKF(); + fp_set_stack_underflow(); + } + (void)fp_pop(); + break; + + default: + goto unhandled; + } + } + } + + /* -+-+-+-+-+-+-+-+-+-+-+-+ 0xDE opcodes +-+-+-+-+-+-+-+ */ + else + if (first_opcode == 0xDE) { + if (modrm < 0xC0) { + /* bits 5,4,3 are an opcode extension, and the modRM also + specifies an address. */ + opc_aux = regno_from_modRM ( r_eip, 4 IFDB(CC &t_opc_aux) ); + r_eip = amode_from_modRM ( r_eip, 4, &a_addr + IFDB(CC &t_addr), &isreg ); + assert(!isreg); + switch (opc_aux) { + default: + printf("unhandled opc_aux = 0x%2x\n", opc_aux); + panic("do_one_insn_fp: first_opcode == 0xDE"); + break; + } + } else { + /* The entire modRM byte is an opcode extension. */ + r_eip++; + switch (modrm) { + + case 0xC0 ... 0xC7: /* FADDP %st(0),%st(?) */ + fp_do_op_ST_ST ( 0, modrm - 0xC0, Fp_Add, True ); + break; + + case 0xC8 ... 0xCF: /* FMULP %st(0),%st(?) */ + fp_do_op_ST_ST ( 0, modrm - 0xC8, Fp_Mul, True ); + break; + + case 0xD9: /* FCOMPP %st(0),%st(1) */ + /* seems the wrong way round. */ + fp_do_COM_ST_ST ( 1, 0, 2 ); + break; + + case 0xE0 ... 0xE7: /* FSUBRP %st(0),%st(?) */ + fp_do_op_ST_ST ( 0, modrm - 0xE0, Fp_SubR, True ); + break; + + case 0xE8 ... 0xEF: /* FSUBP %st(0),%st(?) */ + fp_do_op_ST_ST ( 0, modrm - 0xE8, Fp_Sub, True ); + break; + + case 0xF0 ... 0xF7: /* FDIVRP %st(0),%st(?) */ + fp_do_op_ST_ST ( 0, modrm - 0xF0, Fp_DivR, True ); + break; + + case 0xF8 ... 0xFF: /* FDIVP %st(0),%st(?) */ + fp_do_op_ST_ST ( 0, modrm - 0xF8, Fp_Div, True ); + break; + + default: + goto unhandled; + } + } + } + + /* -+-+-+-+-+-+-+-+-+-+-+-+ 0xDF opcodes +-+-+-+-+-+-+-+ */ + else + if (first_opcode == 0xDF) { + if (modrm < 0xC0) { + /* bits 5,4,3 are an opcode extension, and the modRM also + specifies an address. */ + opc_aux = regno_from_modRM ( r_eip, 4 IFDB(CC &t_opc_aux) ); + r_eip = amode_from_modRM ( r_eip, 4, &a_addr + IFDB(CC &t_addr), &isreg ); + assert(!isreg); + switch (opc_aux) { + + case 0: /* FILD m16int */ + IFDB( if (dis) printf("\tfildw\t%s\n",t_addr); ) + vis_addr = extend_s_16to32(getIMem2(a_addr)); + fp_push ( (double) vis_addr ); + break; + + case 3: /* FISTP m16 */ + IFDB( if (dis) printf("\tfistpw\t%s\n",t_addr); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_addr = fp_pop(); + if (vd_addr <= -32768.50 || + vd_addr >= 32767.50) + vis_addr = 0x00008000; /* 16-bit int indefinite */ + else + vis_addr = (Short)vd_addr; + } else { + vd_addr = fp_pop(); /* then throw away result */ + vis_addr = 0x00008000; /* 32-bit indefinite */ + fp_set_stack_underflow(); + } + setIMem2(a_addr,vis_addr); + break; + + case 5: { /* FILD m64int */ + ULong vis_addr64; + IFDB( if (dis) printf("\tfildq\t%s\n",t_addr); ) + vis_addr = getIMem4(a_addr+4); + vis_addr64 = ((ULong)vis_addr) << 32; + vis_addr = getIMem4(a_addr); + vis_addr64 += (ULong)vis_addr; + fp_push ( (double) ((Long)vis_addr64) ); + break; + } + + case 7: { /* FISTP m64int */ + ULong vis_addr64; + IFDB( if (dis) printf("\tfistpq\t%s\n",t_addr); ) + if (!fp_is_empty_tag(fp_get_tag_ST(0))) { + vd_addr = fp_pop(); + if (vd_addr <= -9223372036854775808.5 || + vd_addr >= 9223372036854775807.5) + vis_addr64 = 0x8000000000000000LL; + /* 64-bit int indefinite */ + else + vis_addr64 = (Long)vd_addr; + } else { + vd_addr = fp_pop(); /* then throw away result */ + vis_addr64 = 0x8000000000000000LL; /* 64-bit indefinite */ + fp_set_stack_underflow(); + } + setIMem4(a_addr,vis_addr64 & 0xFFFFFFFFLL); + setIMem4(a_addr+4, (((Long)vis_addr64) >> 32) + & 0xFFFFFFFFLL); + break; + } + + default: + printf("unhandled opc_aux = 0x%2x\n", opc_aux); + panic("do_one_insn_fp: first_opcode == 0xDF"); + break; + } + } else { + /* The entire modRM byte is an opcode extension. */ + r_eip++; + switch (modrm) { + + case 0xE0: /* FNSTSW %ax */ + IFDB( if (dis) printf("\tfnstsw\t%%ax\n"); ) + setIReg2(R_EAX, (UInt)m_fpu_state.env[FP_ENV_STAT]); + break; + + default: + goto unhandled; + } + } + } + + /* -+-+-+-+-+-+-+-+-+-+-+-+ Unhandled ESC opcode +-+-+-+ */ + else goto unhandled; + +# if DEBUG + if (ppFpuState) { + printf("\nAFTER\n"); + printFpuState(); + printf("\n"); + } +# endif + + return r_eip; + + unhandled: + hd_message(Hd_DebugMsg, + "first opcode = 0x%x, modRM = 0x%x", + (UInt)first_opcode, (UInt)modrm ); + panic("do_one_insn_fp: unhandled first_opcode/modrm combination"); + assert(0); +} + +/*--------------------------------------------------------------------*/ +/*--- end hd_fpu.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/test/fpgames.c b/VEX/useful/show_fp_state.c similarity index 100% rename from VEX/test/fpgames.c rename to VEX/useful/show_fp_state.c