#define ASSERT(EXPRESSION) \
do { \
  if (!(EXPRESSION)) { \
    fprintf (stderr, "%s:%d: assertion failed - %s\n", \
             __FILE__, __LINE__, #EXPRESSION); \
    abort (); \
  } \
} while (0)

#define SIM_BITS_INLINE (INCLUDE_MODULE | INCLUDED_BY_MODULE)

#include "milieu.h"
#include "softfloat.h"
#include "systfloat.h"
#include "systmodes.h"

/* #define SIM_FPU_INLINE (INCLUDE_MODULE | INCLUDED_BY_MODULE) */


#include "sim-bits.h"
#include "sim-fpu.h"
#include "sim-fpu.c"



static int flags;

int8_t
syst_float_flags_clear ()
{
  int old_flags = 0;
  int i = 1;
  while (flags >= i)
    {
      switch ((sim_fpu_status) (flags & i))
	{
	case sim_fpu_status_denorm:
	  break;
	case sim_fpu_status_invalid_snan:
	case sim_fpu_status_invalid_qnan:
	case sim_fpu_status_invalid_isi:
	case sim_fpu_status_invalid_idi:
	case sim_fpu_status_invalid_zdz:
	case sim_fpu_status_invalid_imz:
	case sim_fpu_status_invalid_cvi:
	case sim_fpu_status_invalid_cmp:
	case sim_fpu_status_invalid_sqrt:
	  old_flags |= float_flag_invalid; /* v */
	  break;
	case sim_fpu_status_inexact:
	  old_flags |= float_flag_inexact; /* x */
	  break;
	case sim_fpu_status_overflow:
	  old_flags |= float_flag_overflow; /* o */
	  break;
	case sim_fpu_status_underflow:
	  old_flags |= float_flag_underflow; /* u */
	  break;
	case sim_fpu_status_invalid_div0:
	  old_flags |= float_flag_divbyzero; /* z */
	  break;
	case sim_fpu_status_rounded:
	  break;
	}
      i <<= 1;
    }
  flags = 0;
  return old_flags;
}


sim_fpu_round rounding_mode;

void
syst_float_set_rounding_mode(int8_t mode)
{
  switch (mode)
    {
    case float_round_nearest_even:
      rounding_mode = sim_fpu_round_near;
      break;
    case float_round_down:
      rounding_mode = sim_fpu_round_down;
      break;
    case float_round_up:
      rounding_mode = sim_fpu_round_up;
      break;
    case float_round_to_zero:
      rounding_mode = sim_fpu_round_zero;
      break;
    }
}


float32
syst_int32_to_float32(int32_t a)
{
  float32 z;
  sim_fpu s;
  flags |= sim_fpu_i32to (&s, a, rounding_mode);
  flags |= sim_fpu_round_32 (&s, rounding_mode, 0);
  sim_fpu_to32 (&z, &s);
  return z;
}

float64
syst_int32_to_float64( int32_t a )
{
  float64 z;
  sim_fpu s;
  flags |= sim_fpu_i32to (&s, a, rounding_mode);
  sim_fpu_to64 (&z, &s);
  return z;
}

int32_t
syst_float32_to_int32_round_to_zero( float32 a )
{
  int32_t z;
  sim_fpu s;
  sim_fpu_32to (&s, a);
  flags |= sim_fpu_to32i (&z, &s, sim_fpu_round_zero);
  return z;
}

float64
syst_float32_to_float64 (float32 a)
{
  float64 z;
  sim_fpu s;
  sim_fpu_32to (&s, a);
  flags |= sim_fpu_round_64 (&s, rounding_mode, 0);
  sim_fpu_to64 (&z, &s);
  return z;
}

float32 syst_float32_add( float32 a, float32 b )
{
  float32 z;
  sim_fpu A;
  sim_fpu B;
  sim_fpu ans;
  sim_fpu_32to (&A, a);
  sim_fpu_32to (&B, b);
#if 0
  fprintf (stdout, "\n  ");
  sim_fpu_print_fpu (&A, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, "\n+ ");
  sim_fpu_print_fpu (&B, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, "\n= ");
#endif
  flags |= sim_fpu_add (&ans, &A, &B);
  flags |= sim_fpu_round_32 (&ans, rounding_mode, 0);
#if 0
  sim_fpu_print_fpu (&ans, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, "\n");
#endif
  sim_fpu_to32 (&z, &ans);
  return z;
}

float32 syst_float32_sub( float32 a, float32 b )
{
  float32 z;
  sim_fpu A;
  sim_fpu B;
  sim_fpu ans;
  sim_fpu_32to (&A, a);
  sim_fpu_32to (&B, b);
#if 0
  sim_fpu_print_fpu (&A, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, " + ");
  sim_fpu_print_fpu (&B, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, " = ");
#endif
  flags |= sim_fpu_sub (&ans, &A, &B);
  flags |= sim_fpu_round_32 (&ans, rounding_mode, 0);
#if 0
  sim_fpu_print_fpu (&ans, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, "\n");
#endif
  sim_fpu_to32 (&z, &ans);
  return z;
}

float32 syst_float32_mul( float32 a, float32 b )
{
  float32 z;
  sim_fpu A;
  sim_fpu B;
  sim_fpu ans;
  sim_fpu_32to (&A, a);
  sim_fpu_32to (&B, b);
#if 0
  sim_fpu_print_fpu (&A, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, " + ");
  sim_fpu_print_fpu (&B, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, " = ");
#endif
  flags |= sim_fpu_mul (&ans, &A, &B);
#if 0
  sim_fpu_print_fpu (&ans, (sim_fpu_print_func*) fprintf, stdout);
#endif
  flags |= sim_fpu_round_32 (&ans, rounding_mode, 0);
#if 0
  sim_fpu_print_status (flags, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, "\n");
#endif
  sim_fpu_to32 (&z, &ans);
  return z;
}

float32 syst_float32_div( float32 a, float32 b )
{
  float32 z;
  sim_fpu A;
  sim_fpu B;
  sim_fpu ans;
  sim_fpu_32to (&A, a);
  sim_fpu_32to (&B, b);
  flags |= sim_fpu_div (&ans, &A, &B);
  flags |= sim_fpu_round_32 (&ans, rounding_mode, 0);
  sim_fpu_to32 (&z, &ans);
  return z;
}

float32 syst_float32_sqrt( float32 a )
{
  float32 z;
  sim_fpu A;
  sim_fpu ans;
  sim_fpu_32to (&A, a);
#if 0
  sim_fpu_print_fpu (&A, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, " sqrt> ");
#endif
  flags |= sim_fpu_sqrt (&ans, &A);
#if 0
  sim_fpu_print_fpu (&ans, (sim_fpu_print_func*) fprintf, stdout);
#endif
  flags |= sim_fpu_round_32 (&ans, rounding_mode, 0);
#if 0
  fprintf (stdout, " (%x)\n", flags);
#endif
  sim_fpu_to32 (&z, &ans);
  return z;
}

flag syst_float32_eq( float32 a, float32 b )
{
  sim_fpu A;
  sim_fpu B;
  int is;
  sim_fpu_32to (&A, a);
  sim_fpu_32to (&B, b);
  flags |= (sim_fpu_eq (&is, &A, &B) & ~sim_fpu_status_invalid_qnan);
  return is;
}

flag syst_float32_eq_signaling( float32 a, float32 b )
{
  sim_fpu A;
  sim_fpu B;
  int is;
  sim_fpu_32to (&A, a);
  sim_fpu_32to (&B, b);
  flags |= sim_fpu_eq (&is, &A, &B);
  return is;
}

flag syst_float32_le( float32 a, float32 b )
{
  sim_fpu A;
  sim_fpu B;
  int is;
  sim_fpu_32to (&A, a);
  sim_fpu_32to (&B, b);
  flags |= sim_fpu_le (&is, &A, &B);
  return is;
}

flag syst_float32_le_quiet( float32 a, float32 b )
{
  sim_fpu A;
  sim_fpu B;
  int is;
  sim_fpu_32to (&A, a);
  sim_fpu_32to (&B, b);
  flags |= (sim_fpu_le (&is, &A, &B) & ~sim_fpu_status_invalid_qnan);
  return is;
}

flag syst_float32_lt( float32 a, float32 b )
{
  sim_fpu A;
  sim_fpu B;
  int is;
  sim_fpu_32to (&A, a);
  sim_fpu_32to (&B, b);
  flags |= sim_fpu_lt (&is, &A, &B);
  return is;
}

flag syst_float32_lt_quiet( float32 a, float32 b )
{
  sim_fpu A;
  sim_fpu B;
  int is;
  sim_fpu_32to (&A, a);
  sim_fpu_32to (&B, b);
  flags |= (sim_fpu_lt (&is, &A, &B) & ~sim_fpu_status_invalid_qnan);
  return is;
}

int32_t syst_float64_to_int32_round_to_zero( float64 a )
{
  int32_t z;
  sim_fpu s;
  sim_fpu_64to (&s, a);
  flags |= sim_fpu_to32i (&z, &s, sim_fpu_round_zero);
  return z;
}

float32 syst_float64_to_float32( float64 a )
{
  float32 z;
  sim_fpu s;
  sim_fpu_64to (&s, a);
#if 0
  sim_fpu_print_fpu (&s, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, " -> ");
#endif
  flags |= sim_fpu_round_32 (&s, rounding_mode, 0);
#if 0
  sim_fpu_print_fpu (&s, (sim_fpu_print_func*) fprintf, stdout);
  sim_fpu_print_status (flags, (sim_fpu_print_func*) fprintf, stdout);
  printf ("\n");
#endif
  sim_fpu_to32 (&z, &s);
  return z;
}

float64 syst_float64_add( float64 a, float64 b )
{
  float64 z;
  sim_fpu A;
  sim_fpu B;
  sim_fpu ans;
  sim_fpu_64to (&A, a);
  sim_fpu_64to (&B, b);
#if 0
  sim_fpu_print_fpu (&A, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, " + ");
  sim_fpu_print_fpu (&B, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, " = ");
#endif
  flags |= sim_fpu_add (&ans, &A, &B);
#if 0
  sim_fpu_print_fpu (&ans, (sim_fpu_print_func*) fprintf, stdout);
#endif
  flags |= sim_fpu_round_64 (&ans, rounding_mode, 0);
#if 0
  fprintf (stdout, " (%x)\n", flags);
#endif
  sim_fpu_to64 (&z, &ans);
  return z;
}

float64 syst_float64_sub( float64 a, float64 b )
{
  float64 z;
  sim_fpu A;
  sim_fpu B;
  sim_fpu ans;
  sim_fpu_64to (&A, a);
  sim_fpu_64to (&B, b);
#if 0
  sim_fpu_print_fpu (&A, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, " + ");
  sim_fpu_print_fpu (&B, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, " = ");
#endif
  flags |= sim_fpu_sub (&ans, &A, &B);
#if 0
  sim_fpu_print_fpu (&ans, (sim_fpu_print_func*) fprintf, stdout);
#endif
  flags |= sim_fpu_round_64 (&ans, rounding_mode, 0);
#if 0
  fprintf (stdout, " (%x)\n", flags);
#endif
  sim_fpu_to64 (&z, &ans);
  return z;
}

float64 syst_float64_mul( float64 a, float64 b )
{
  float64 z;
  sim_fpu A;
  sim_fpu B;
  sim_fpu ans;
  sim_fpu_64to (&A, a);
  sim_fpu_64to (&B, b);
#if 0
  sim_fpu_print_fpu (&A, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, " * ");
  sim_fpu_print_fpu (&B, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, " = ");
#endif
  flags |= sim_fpu_mul (&ans, &A, &B);
#if 0
  sim_fpu_print_fpu (&ans, (sim_fpu_print_func*) fprintf, stdout);
#endif
  flags |= sim_fpu_round_64 (&ans, rounding_mode, 0);
#if 0
  sim_fpu_print_status (flags, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, "\n");
#endif
  sim_fpu_to64 (&z, &ans);
  return z;
}

float64 syst_float64_div( float64 a, float64 b )
{
  float64 z;
  sim_fpu A;
  sim_fpu B;
  sim_fpu ans;
  sim_fpu_64to (&A, a);
  sim_fpu_64to (&B, b);
#if 0
  sim_fpu_print_fpu (&A, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, " + ");
  sim_fpu_print_fpu (&B, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, " = ");
#endif
  flags |= sim_fpu_div (&ans, &A, &B);
#if 0
  sim_fpu_print_fpu (&ans, (sim_fpu_print_func*) fprintf, stdout);
#endif
  flags |= sim_fpu_round_64 (&ans, rounding_mode, 0);
#if 0
  sim_fpu_print_status (flags, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, "\n");
#endif
  sim_fpu_to64 (&z, &ans);
  return z;
}

float64 syst_float64_sqrt( float64 a )
{
  float64 z;
  sim_fpu A;
  sim_fpu ans;
  sim_fpu_64to (&A, a);
#if 0
  sim_fpu_print_fpu (&A, (sim_fpu_print_func*) fprintf, stdout);
  printf (" sqrt> ");
  printf ("\n");
#endif
  flags |= sim_fpu_sqrt (&ans, &A);
#if 0
  sim_fpu_print_fpu (&ans, (sim_fpu_print_func*) fprintf, stdout);
#endif
  flags |= sim_fpu_round_64 (&ans, rounding_mode, 0);
#if 0
  sim_fpu_print_status (flags, (sim_fpu_print_func*) fprintf, stdout);
  fprintf (stdout, "\n");
#endif
  sim_fpu_to64 (&z, &ans);
  return z;
}

flag syst_float64_eq( float64 a, float64 b )
{
  sim_fpu A;
  sim_fpu B;
  int is;
  sim_fpu_64to (&A, a);
  sim_fpu_64to (&B, b);
  flags |= (sim_fpu_eq (&is, &A, &B) & ~sim_fpu_status_invalid_qnan);
  return is;
}

flag syst_float64_eq_signaling( float64 a, float64 b )
{
  sim_fpu A;
  sim_fpu B;
  int is;
  sim_fpu_64to (&A, a);
  sim_fpu_64to (&B, b);
  flags |= sim_fpu_eq (&is, &A, &B);
  return is;
}

flag syst_float64_le( float64 a, float64 b )
{
  sim_fpu A;
  sim_fpu B;
  int is;
  sim_fpu_64to (&A, a);
  sim_fpu_64to (&B, b);
  flags |= sim_fpu_le (&is, &A, &B);
  return is;
}

flag syst_float64_le_quiet( float64 a, float64 b )
{
  sim_fpu A;
  sim_fpu B;
  int is;
  sim_fpu_64to (&A, a);
  sim_fpu_64to (&B, b);
  flags |= (sim_fpu_le (&is, &A, &B) & ~sim_fpu_status_invalid_qnan);
  return is;
}

flag syst_float64_lt( float64 a, float64 b )
{
  sim_fpu A;
  sim_fpu B;
  int is;
  sim_fpu_64to (&A, a);
  sim_fpu_64to (&B, b);
  flags |= sim_fpu_lt (&is, &A, &B);
  return is;
}

flag syst_float64_lt_quiet( float64 a, float64 b )
{
  sim_fpu A;
  sim_fpu B;
  int is;
  sim_fpu_64to (&A, a);
  sim_fpu_64to (&B, b);
  flags |= (sim_fpu_lt (&is, &A, &B) & ~sim_fpu_status_invalid_qnan);
  return is;
}

