#if defined(__mips_hard_float)

#include <stdio.h>

/*
 * Bits 18 (NAN2008) and 19 (ABS2008) are preset by hardware and may differ
 * between platforms. Hence a macro to clear them before printing FCSR
 * values.
 */
#define FCSR_NAN2008 1 << 18
#define FCSR_ABS2008 1 << 19
#define FLAGS_RM_MASK 0xFFFFFFFF & ~(FCSR_ABS2008 | FCSR_NAN2008)
#define CLEAR_PRESETBITS_FCSR(fcsr) (fcsr & FLAGS_RM_MASK)

typedef enum {
   CEILWS=0, CEILWD,
   FLOORWS, FLOORWD,
   ROUNDWS, ROUNDWD,
   TRUNCWS, TRUNCWD
} flt_dir_op_t;

typedef enum {
   CVTDS, CVTDW,
   CVTSD, CVTSW,
   CVTWS, CVTWD
} flt_round_op_t;

typedef enum {
   TO_NEAREST=0, TO_ZERO, TO_PLUS_INFINITY, TO_MINUS_INFINITY } round_mode_t;
char *round_mode_name[] = { "near", "zero", "+inf", "-inf" };


const char *flt_dir_op_names[] = {
   "ceil.w.s", "ceil.w.d",
   "floor.w.s", "floor.w.d",
   "round.w.s", "round.w.d",
   "trunc.w.s", "trunc.w.d"
};

const char *flt_round_op_names[] = {
   "cvt.d.s", "cvt.d.w",
   "cvt.s.d", "cvt.s.w",
   "cvt.w.s", "cvt.w.d"
};

const double fs_d[] = {
   0, 456.25, 3, -1,
   1384.5, -7.25, 1000000000, -5786.25,
   1752, 0.015625, 0.03125, -248562.75,
   -45786.5, 456, 34.03125, 45786.75,
   1752065, 107, -45667.25, -7,
   -347856.5, 356047, -1.25, 23.0625
};

const float fs_f[] = {
   0, 456.25, 3, -1,
   1384.5, -7.25, 1000000000, -5786.25,
   1752, 0.015625, 0.03125, -248562.75,
   -45786.5, 456, 34.03125, 45786.75,
   1752065, 107, -45667.25, -7,
   -347856.5, 356047, -1.25, 23.0625
};

const int fs_w[] = {
   0, 456, 3, -1,
   0xffffffff, 356, 1000000000, -5786,
   1752, 24575, 10, -248562,
   -45786, 456, 34, 45786,
   1752065, 107, -45667, -7,
   -347856, 0x80000000, 0xFFFFFFF, 23
};

#define BINOP(op)                                   \
        __asm__ volatile(op"   %1, %2, %3"  "\n\t"  \
                         "cfc1 %0, $31"     "\n\t"  \
                         : "=r" (fcsr), "=f"(fd)    \
                         : "f"(f) , "f"(fB));

#define UNOPdd(op)                                  \
        fd_d = 0;                                   \
        __asm__ volatile(op"   %1, %2"      "\n\t"  \
                         "cfc1 %0, $31"     "\n\t"  \
                         : "=r" (fcsr), "=f"(fd_d)  \
                         : "f"(fs_d[i]));

#define UNOPff(op)                                  \
        fd_f = 0;                                   \
        __asm__ volatile(op" %1, %2"        "\n\t"  \
                         "cfc1 %0, $31"     "\n\t"  \
                         : "=r" (fcsr), "=f"(fd_f)  \
                         : "f"(fs_f[i]));

#define UNOPfd(op)                                  \
        fd_d = 0;                                   \
        __asm__ volatile(op"   %1, %2"   "\n\t"     \
                         "cfc1 %0, $31"  "\n\t"     \
                         : "=r" (fcsr), "=f"(fd_d)  \
                         : "f"(fs_f[i]));

#define UNOPdf(op)                                  \
        fd_f = 0;                                   \
        __asm__ volatile(op"   %1, %2"   "\n\t"     \
                         "cfc1 %0, $31"  "\n\t"     \
                         : "=r" (fcsr), "=f"(fd_f)  \
                         : "f"(fs_d[i]));

#define UNOPfw(op)                                  \
        fd_w = 0;                                   \
        __asm__ volatile(op"   $f0, %2"   "\n\t"    \
                         "mfc1 %1,  $f0"  "\n\t"    \
                         "cfc1 %0, $31"   "\n\t"    \
                         : "=r" (fcsr), "=r"(fd_w)  \
                         : "f"(fs_f[i])             \
                         : "$f0");

#define UNOPdw(op)                                  \
        fd_w = 0;                                   \
        __asm__ volatile(op" $f0, %2"    "\n\t"     \
                         "mfc1 %1, $f0"  "\n\t"     \
                         "cfc1 %0, $31"  "\n\t"     \
                         : "=r" (fcsr), "=r"(fd_w)  \
                         : "f"(fs_d[i])             \
                         : "$f0");

#define UNOPwd(op)                                  \
        fd_d = 0;                                   \
        __asm__ volatile("mtc1 %2, $f0"  "\n\t"     \
                         op"   %1, $f0"  "\n\t"     \
                         "cfc1 %0, $31"  "\n\t"     \
                         : "=r" (fcsr), "=f"(fd_d)  \
                         : "r"(fs_w[i])             \
                         : "$f0", "$f1");

#define UNOPwf(op)                                  \
        fd_f = 0;                                   \
        __asm__ volatile("mtc1 %2, $f0"  "\n\t"     \
                         op"   %1, $f0"  "\n\t"     \
                         "cfc1 %0, $31"  "\n\t"     \
                         : "=r" (fcsr), "=f"(fd_f)  \
                         : "r"(fs_w[i])             \
                         : "$f0");

void set_rounding_mode(round_mode_t mode)
{
   switch(mode) {
      case TO_NEAREST:
         __asm__ volatile("ctc1 $zero, $31"  "\n\t");
         break;
      case TO_ZERO:
         __asm__ volatile("li    $t0, 0x1"  "\n\t"
                          "ctc1  $t0, $31"  "\n\t");
         break;
      case TO_PLUS_INFINITY:
          __asm__ volatile("li    $t0, 0x2"  "\n\t"
                           "ctc1  $t0, $31"  "\n\t");
         break;
      case TO_MINUS_INFINITY:
          __asm__ volatile("li    $t0, 0x3"  "\n\t"
                           "ctc1  $t0, $31"  "\n\t");
         break;
   }
}

void clear_fcc(){
   __asm__ __volatile__(
      "cfc1 $t0, $31"            "\n\t"
      "and  $t0, $t0, 0x17FFFFF" "\n\t"
      "ctc1 $t0, $31"            "\n\t"
      :
      :
      : "t0"
   );
}

int directedRoundingMode(flt_dir_op_t op) {
   int fd_w = 0;
   int i;
   int fcsr = 0;
   round_mode_t rm = TO_NEAREST;
   for (i = 0; i < 24; i++) {
      clear_fcc();
      set_rounding_mode(rm);
      switch(op) {
         case CEILWS:
              UNOPfw("ceil.w.s");
              printf("%s %d %f\n", flt_dir_op_names[op], fd_w, fs_f[i]);
              printf("fcsr: 0x%x\n", CLEAR_PRESETBITS_FCSR(fcsr));
              break;
         case CEILWD:
              UNOPdw("ceil.w.d");
              printf("%s %d %lf\n", flt_dir_op_names[op], fd_w, fs_d[i]);
              printf("fcsr: 0x%x\n", CLEAR_PRESETBITS_FCSR(fcsr));
              break;
         case FLOORWS:
              UNOPfw("floor.w.s");
              printf("%s %d %f\n", flt_dir_op_names[op], fd_w, fs_f[i]);
              printf("fcsr: 0x%x\n", CLEAR_PRESETBITS_FCSR(fcsr));
              break;
         case FLOORWD:
              UNOPdw("floor.w.d");
              printf("%s %d %lf\n", flt_dir_op_names[op], fd_w, fs_d[i]);
              printf("fcsr: 0x%x\n", CLEAR_PRESETBITS_FCSR(fcsr));
              break;
         case ROUNDWS:
              UNOPfw("round.w.s");
              printf("%s %d %f\n", flt_dir_op_names[op], fd_w, fs_f[i]);
              printf("fcsr: 0x%x\n", CLEAR_PRESETBITS_FCSR(fcsr));
              break;
         case ROUNDWD:
              UNOPdw("round.w.d");
              printf("%s %d %lf\n", flt_dir_op_names[op], fd_w, fs_d[i]);
              printf("fcsr: 0x%x\n", CLEAR_PRESETBITS_FCSR(fcsr));
              break;
         case TRUNCWS:
              UNOPfw("trunc.w.s");
              printf("%s %d %f\n", flt_dir_op_names[op], fd_w, fs_f[i]);
              printf("fcsr: 0x%x\n", CLEAR_PRESETBITS_FCSR(fcsr));
              break;
         case TRUNCWD:
              UNOPdw("trunc.w.d");
              printf("%s %d %lf\n", flt_dir_op_names[op], fd_w, fs_d[i]);
              printf("fcsr: 0x%x\n", CLEAR_PRESETBITS_FCSR(fcsr));
              break;
        default:
            printf("error\n");
            break;
        }
    }
   return 0;
}

int FCSRRoundingMode(flt_round_op_t op1)
{
   double fd_d = 0;
   float fd_f = 0;
   int fd_w = 0;
   int i;
   int fcsr = 0;
   round_mode_t rm;
   for (rm = TO_NEAREST; rm <= TO_MINUS_INFINITY; rm ++) {
      set_rounding_mode(rm);
      printf("roundig mode: %s\n", round_mode_name[rm]);
      for (i = 0; i < 24; i++) {
         clear_fcc();
         set_rounding_mode(rm);
         switch(op1) {
            case CVTDS:
                 UNOPfd("cvt.d.s");
                 printf("%s %lf %lf\n", flt_round_op_names[op1], fd_d, fs_f[i]);
                 printf("fcsr: 0x%x\n", CLEAR_PRESETBITS_FCSR(fcsr));
                 break;
            case CVTDW:
                 UNOPwd("cvt.d.w");
                 printf("%s %lf %d\n", flt_round_op_names[op1], fd_d, fs_w[i]);
                 printf("fcsr: 0x%x\n", CLEAR_PRESETBITS_FCSR(fcsr));
                 break;
            case CVTSD:
                 UNOPdf("cvt.s.d");
                 printf("%s %f %lf\n", flt_round_op_names[op1], fd_f, fs_d[i]);
                 printf("fcsr: 0x%x\n", CLEAR_PRESETBITS_FCSR(fcsr));
                 break;
            case CVTSW:
                 UNOPwf("cvt.s.w");
                 printf("%s %f %d\n", flt_round_op_names[op1], fd_f, fs_w[i]);
                 printf("fcsr: 0x%x\n", CLEAR_PRESETBITS_FCSR(fcsr));
                 break;
            case CVTWS:
                 UNOPfw("cvt.w.s");
                 printf("%s %d %f\n", flt_round_op_names[op1], fd_w, fs_f[i]);
                 printf("fcsr: 0x%x\n", CLEAR_PRESETBITS_FCSR(fcsr));
                 break;
            case CVTWD:
                 UNOPdw("cvt.w.d");
                 printf("%s %d %lf\n", flt_round_op_names[op1], fd_w, fs_d[i]);
                 printf("fcsr: 0x%x\n", CLEAR_PRESETBITS_FCSR(fcsr));
                 break;
            default:
                 printf("error\n");
                 break;
         }
      }
   }
   return 0;
}

int main()
{
   flt_dir_op_t op;
   flt_round_op_t op1;

   printf("-------------------------- %s --------------------------\n",
        "test FPU Conversion Operations Using a Directed Rounding Mode");
   for (op = CEILWS; op <= TRUNCWD; op++) {
      directedRoundingMode(op);
   }

   printf("-------------------------- %s --------------------------\n",
        "test FPU Conversion Operations Using the FCSR Rounding Mode");
   for (op1 = CVTDS; op1 <= CVTWD; op1++) {
      FCSRRoundingMode(op1);
   }
   return 0;
}
#else
int main() {
   return 0;
}
#endif

