]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Add the old Heimdall x87 FPU simulator, as reference.
authorJulian Seward <jseward@acm.org>
Thu, 2 Sep 2004 12:24:06 +0000 (12:24 +0000)
committerJulian Seward <jseward@acm.org>
Thu, 2 Sep 2004 12:24:06 +0000 (12:24 +0000)
Add a small program to print out the state of the x87 FPU.

git-svn-id: svn://svn.valgrind.org/vex/trunk@227

VEX/useful/hd_fpu.c [new file with mode: 0644]
VEX/useful/show_fp_state.c [moved from VEX/test/fpgames.c with 100% similarity]

diff --git a/VEX/useful/hd_fpu.c b/VEX/useful/hd_fpu.c
new file mode 100644 (file)
index 0000000..3007029
--- /dev/null
@@ -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 <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 ---*/
+/*--------------------------------------------------------------------*/