/*--------------------------------------------------------------------*/ /*--- 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 <nan.h> */ ); } 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 ---*/ /*--------------------------------------------------------------------*/