#include <stdio.h>
#include <assert.h>


/* ------------------------------------------------- */

typedef unsigned char          UChar;
typedef unsigned short         UShort;
typedef unsigned int           UInt;
typedef unsigned long long int ULong;

typedef signed char     Char;
typedef short           Short;
typedef int             Int;
typedef long long int   Long;

typedef
   struct {
      UShort env[14];
      UChar  reg[80];
   }
   Fpu_State;

/* Offsets, in 16-bit ints, into the FPU environment (env) area. */
#define FP_ENV_CTRL   0
#define FP_ENV_STAT   2
#define FP_ENV_TAG    4
#define FP_ENV_IP     6 /* and 7 */
#define FP_ENV_CS     8
#define FP_ENV_OPOFF  10 /* and 11 */
#define FP_ENV_OPSEL  12
#define FP_REG(ii)    (10*(7-(ii)))

/* Bitfield offsets for exceptions in the FPU status and control words. */
#define FP_E_INVAL    0
#define FP_E_DENOR    1
#define FP_E_DIVZ     2
#define FP_E_OVERF    3
#define FP_E_UNDER    4
#define FP_E_LOS      5

/* More bitfield offsets, but for the status word only. */
#define FP_E_STACKF   6
#define FP_E_SUMMARY  7
#define FP_F_C0       8
#define FP_F_C1       9
#define FP_F_C2      10
#define FP_F_C3      14
/* top-of-stack ptr is bits 13,12,11 of the word */
#define FP_F_TOS_LO  11
#define FP_F_TOS_HI  13

/* Register tags. */
#define FP_TAG_VALID 0
#define FP_TAG_ZERO  1
#define FP_TAG_SPEC  2
#define FP_TAG_EMPTY 3

char* fp_tag_names[4]
   = { "Valid", "Zero", "Spec", "Empty" };

char* fp_exception_names[6]
   = { "INVAL", "DENOR", "DIVZ", "OVERF", "UNDERF", "LOS" };

Fpu_State m_fpu_state;



UInt fp_get_tos ( void )
{
   return (m_fpu_state.env[FP_ENV_STAT] >> FP_F_TOS_LO) & 7;
}

UInt fp_get_tag ( UInt regno )
{
   assert(!(regno < 0 || regno > 7));
   return (m_fpu_state.env[FP_ENV_TAG] >> (2*regno)) & 3;
}

UInt fp_get_statusword_flag ( UInt flagno )
{
   assert(!(flagno < 0 || flagno > 15));
   return (m_fpu_state.env[FP_ENV_STAT] >> flagno) & 0x1;
}

UInt fp_get_controlword_flag ( UInt flagno )
{
   assert(!(flagno < 0 || flagno > 15));
   return (m_fpu_state.env[FP_ENV_CTRL] >> flagno) & 0x1;
}

static 
void printFpuState ( void )
{
   Int i, j, k;
   assert(sizeof(Fpu_State)==108);
   for (i = 7; i >= 0; i--) {
      printf ( " %s fpreg%d: 0x", 
               (UInt)i == fp_get_tos() ? "**" : "  ", i );
      for (k = 0, j = FP_REG(i)+9; k < 10; k++,j--)
         printf ( "%02x", (UInt)m_fpu_state.reg[j]);
      printf ( "  %5s  ", fp_tag_names[fp_get_tag(i)] );
      printf("\n");
      //printf ( "%20.16e\n", fp_get_reg(i) );
   }
   printf("     fctrl:     0x%04x  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%04x  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%04x  ", (UInt)m_fpu_state.env[FP_ENV_TAG] );
   for (i = 7; i >= 0; i--)
      printf ( "%d:%s ", i, fp_tag_names[fp_get_tag(i)] );
   printf("\n");

   printf("       fip: 0x%08x\n", 
           (((UInt)m_fpu_state.env[FP_ENV_IP+1]) << 16) |
            ((UInt)m_fpu_state.env[FP_ENV_IP]) );
   printf("       fcs:     0x%04x\n", 
           ((UInt)m_fpu_state.env[FP_ENV_CS]) );
   printf("    fopoff: 0x%08x\n", 
           (((UInt)m_fpu_state.env[FP_ENV_OPOFF+1]) << 16) |
            ((UInt)m_fpu_state.env[FP_ENV_OPOFF]) );
   printf("    fopsel:     0x%04x\n", 
           ((UInt)m_fpu_state.env[FP_ENV_OPSEL]) );
}


/* ------------------------------------------------- */


/* Initialise the FPU, dump its state, and print it. */


void show ( unsigned char* st )
{
  int i;
  for (i = 0; i < 28; i++) {
    printf("%02x ", st[i]);
    if (i > 0 && ((i & 3) == 3)) printf("\n");
  }
  for (i = 0; i < 80; i++) {
    printf("%02x ", st[i+28]);
    if (i > 0 && ((i % 10) == 9)) printf("\n");
  }
  printf("\n");
}

int main ( void ) 
{
  Fpu_State* st = &m_fpu_state;
  assert(sizeof(m_fpu_state) == 108);
  asm volatile ("finit ; fnsave %0"
                : "=m" (m_fpu_state)
                : 
                : "memory" );
  printFpuState();
  printf("\n\n");

  asm volatile ("fildl 0(%%esp) ; pushl $17 ; fildl 0(%%esp) ; addl $4, %%esp ;  fnsave %0"
                : "=m" (m_fpu_state)
                : 
                : "memory" );
  printFpuState();
  printf("\n");
  show(st);
  return 0;
}