#include "const.h"

typedef enum {
   ABSS=0, ABSD,   ADDS,   ADDD,
   DIVS,   DIVD,   MULS,   MULD,
   NEGS,   NEGD,   SQRTS,  SQRTD,
   SUBS,   SUBD,   RECIPS, RECIPD,
   RSQRTS, RSQRTD, MSUBS,  MSUBD,
   MADDS,  MADDD,  NMADDS, NMADDD,
   NMSUBS, NMSUBD
} flt_art_op_t;

typedef enum {
   CEILWS=0, CEILWD,  FLOORWS,  FLOORWD,
   ROUNDWS,  ROUNDWD, TRUNCWS,  TRUNCWD,
   CEILLS,   CEILLD,  FLOORLS,  FLOORLD,
   ROUNDLS,  ROUNDLD, TRUNCLS,  TRUNCLD
} flt_dir_op_t;

typedef enum {
   CVTDS, CVTDW, CVTSD, CVTSW,
   CVTWS, CVTWD, CVTDL, CVTLS,
   CVTLD, CVTSL,
} flt_round_op_t;

const char *flt_art_op_names[] = {
   "abs.s",   "abs.d",   "add.s",   "add.d",
   "div.s",   "div.d",   "mul.s",   "mul.d",
   "neg.s",   "neg.d",   "sqrt.s",  "sqrt.d",
   "sub.s",   "sub.d",   "recip.s", "recip.d",
   "rsqrt.s", "rsqrt.d", "msub.s",  "msub.d",
   "madd.s",  "madd.d",  "nmadd.s", "nmadd.d",
   "nmsub.s", "nmsub.d"
};

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",
   "ceil.l.s",  "ceil.l.d",
   "floor.l.s", "floor.l.d",
   "round.l.s", "round.l.d",
   "trunc.l.s", "trunc.l.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",
   "cvt.d.l", "cvt.l.s",
   "cvt.l.d", "cvt.s.l",
};

#if defined(__mips_hard_float)
#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"                    \
   );

#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"                    \
   );

#define UNOPld(op)               \
   fd_d = 0;                     \
   __asm__ __volatile__(         \
      "dmtc1 %2, $f0"  "\n\t"    \
      op"    %1, $f0"  "\n\t"    \
      "cfc1  %0, $31"  "\n\t"    \
      : "=r" (fcsr), "=f"(fd_d)  \
      : "r"(fs_l[i])             \
      : "$f0"                    \
   );

#define UNOPdl(op)               \
   fd_l = 0;                     \
   __asm__ __volatile__(         \
      op"    $f0, %2"   "\n\t"   \
      "dmfc1 %1,  $f0"  "\n\t"   \
      "cfc1  %0,  $31"  "\n\t"   \
      : "=r" (fcsr), "=r"(fd_l)  \
      : "f"(fs_d[i])             \
      : "$f0"                    \
   );

#define UNOPls(op)               \
   fd_f = 0;                     \
   __asm__ __volatile__(         \
      "dmtc1 %2, $f0"  "\n\t"    \
      op"    %1, $f0"  "\n\t"    \
      "cfc1  %0, $31"  "\n\t"    \
      : "=r" (fcsr), "=f"(fd_f)  \
      : "r"(fs_l[i])             \
      : "$f0"                    \
   );

#define UNOPsl(op)               \
   fd_l = 0;                     \
   __asm__ __volatile__(         \
      op"    $f0, %2"   "\n\t"   \
      "dmfc1 %1,  $f0"  "\n\t"   \
      "cfc1  %0,  $31"  "\n\t"   \
      : "=r" (fcsr), "=r"(fd_l)  \
      : "f"(fs_f[i])             \
      : "$f0"                    \
   );

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

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

#define TRIOPf(op)                                    \
   fd_f = 0;                                          \
   __asm__ __volatile__(                              \
      op"    %1, %2, %3, %4"  "\n\t"                  \
      "cfc1  %0, $31"         "\n\t"                  \
      : "=r" (fcsr), "=f" (fd_f)                      \
      : "f" (fr_f[i]), "f" (fs_f[i]) , "f" (ft_f[i])  \
   );

#define TRIOPd(op)                                    \
   fd_d = 0;                                          \
   __asm__ __volatile__(                              \
      op"    %1, %2, %3, %4"  "\n\t"                  \
      "cfc1  %0, $31"         "\n\t"                  \
      : "=r" (fcsr), "=f"(fd_d)                       \
      : "f" (fr_d[i]), "f" (fs_d[i]) , "f" (ft_d[i])  \
   );

/* Conditional macros.*/
#define TESTINST1s(instruction, RDval)               \
{                                                    \
   float outf = 0;                                   \
   __asm__ __volatile__(                             \
      ".set        noreorder"                "\n\t"  \
      "mov.s       $f1,   %1"                "\n\t"  \
      "mov.s       $f2,   %2"                "\n\t"  \
      "mtc1        $zero, $f0"               "\n\t"  \
      "c.eq.s      $f1,   $f2"               "\n\t"  \
      instruction" end"instruction"s"#RDval  "\n\t"  \
      "nop"                                  "\n\t"  \
      "add.s       $f0,   $f0, $f1"          "\n\t"  \
      "end"instruction"s"#RDval":"           "\n\t"  \
      "add.s       $f0,   $f0, $f2"          "\n\t"  \
      "mov.s       %0,    $f0"               "\n\t"  \
      ".set        reorder"                  "\n\t"  \
      : "=f" (outf)                                  \
      : "f" (fs_f[i]) , "f" (ft_f[i])                \
      : "$f0", "$f1", "$f2"                          \
   );                                                \
   printf("%s, c.eq.s   out=%f, fs=%f, ft=%f\n",     \
          instruction, outf, fs_f[i], ft_f[i]);      \
}

#define TESTINST1d(instruction, RDval)               \
{                                                    \
   double outd = 0;                                  \
   __asm__ __volatile__(                             \
      ".set        noreorder"                "\n\t"  \
      "mov.d       $f1,   %1"                "\n\t"  \
      "mov.d       $f2,   %2"                "\n\t"  \
      "dmtc1       $zero, $f0"               "\n\t"  \
      "c.eq.d      $f1,   $f2"               "\n\t"  \
      instruction" end"instruction"d"#RDval  "\n\t"  \
      "nop"                                  "\n\t"  \
      "add.d       $f0,   $f0, $f1"          "\n\t"  \
      "end"instruction"d"#RDval":"           "\n\t"  \
      "add.d       $f0,   $f0, $f2"          "\n\t"  \
      "mov.d       %0,    $f0"               "\n\t"  \
      ".set        reorder"                  "\n\t"  \
      : "=f" (outd)                                  \
      : "f" (fs_d[i]) , "f" (ft_d[i])                \
      : "$f0", "$f1", "$f2"                          \
   );                                                \
   printf("%s, c.eq.d   out=%f, fs=%f, ft=%f\n",     \
          instruction, outd, fs_d[i], ft_d[i]);      \
}

#define TESTINST2s(instruction, RDval)               \
{                                                    \
   float outf = 0;                                   \
   __asm__ __volatile__(                             \
      ".set        noreorder"                "\n\t"  \
      "mov.s       $f1,   %1"                "\n\t"  \
      "mov.s       $f2,   %2"                "\n\t"  \
      "mtc1        $zero, $f0"               "\n\t"  \
      "c.eq.s      $f1,   $f2"               "\n\t"  \
      instruction" end"instruction"s"#RDval  "\n\t"  \
      "add.s       $f0,   $f0, $f1"          "\n\t"  \
      "end"instruction"s"#RDval":"           "\n\t"  \
      "add.s       $f0,   $f0, $f2"          "\n\t"  \
      "mov.s       %0, $f0"                  "\n\t"  \
      ".set        reorder"                  "\n\t"  \
      : "=f" (outf)                                  \
      : "f" (fs_f[i]) , "f" (ft_f[i])                \
      : "$f0", "$f1", "$f2"                          \
   );                                                \
   printf("%s, c.eq.s   out=%f, fs=%f, ft=%f\n",     \
          instruction, outf, fs_f[i], ft_f[i]);      \
}

#define TESTINST2d(instruction, RDval)               \
{                                                    \
   double outd = 0;                                  \
   __asm__ __volatile__(                             \
      ".set        noreorder"                "\n\t"  \
      "mov.d       $f1,   %1"                "\n\t"  \
      "mov.d       $f2,   %2"                "\n\t"  \
      "dmtc1       $zero, $f0"               "\n\t"  \
      "c.eq.d      $f1,   $f2"               "\n\t"  \
      instruction" end"instruction"d"#RDval  "\n\t"  \
      "add.d       $f0,   $f0, $f1"          "\n\t"  \
      "end"instruction"d"#RDval":"           "\n\t"  \
      "add.d       $f0,   $f0, $f2"          "\n\t"  \
      "mov.d       %0, $f0"                  "\n\t"  \
      ".set        reorder"                  "\n\t"  \
      : "=f" (outd)                                  \
      : "f" (fs_d[i]) , "f" (ft_d[i])                \
      : "$f0", "$f1", "$f2"                          \
   );                                                \
   printf("%s, c.eq.d   out=%f, fs=%f, ft=%f\n",     \
          instruction, outd, fs_d[i], ft_d[i]);      \
}

#define TESTINST_CONDs(instruction, RDval)       \
{                                                \
   float outf = 0;                               \
   __asm__ __volatile__(                         \
      ".set        noreorder"         "\n\t"     \
      "mov.s       $f1,   %1"         "\n\t"     \
      "mov.s       $f2,   %2"         "\n\t"     \
      "mov.s       $f0,   %1"         "\n\t"     \
      instruction" $f1,   $f2"        "\n\t"     \
      "bc1f end"instruction"s"#RDval  "\n\t"     \
      "nop"                           "\n\t"     \
      "add.s       $f0,   $f0, $f2"   "\n\t"     \
      "end"instruction"s"#RDval":"    "\n\t"     \
      "mov.s       %0,    $f0"        "\n\t"     \
      ".set        reorder"           "\n\t"     \
      : "=f" (outf)                              \
      : "f" (fs_f[i]) , "f" (ft_f[i])            \
      : "$f0", "$f1", "$f2"                      \
   );                                            \
   printf("%s, bc1f   out=%f, fs=%f, ft=%f\n",   \
          instruction, outf, fs_f[i], ft_f[i]);  \
}

#define TESTINST_CONDd(instruction, RDval)       \
{                                                \
   double outd = 0;                              \
   __asm__ __volatile__(                         \
      ".set        noreorder"         "\n\t"     \
      "mov.d       $f1,   %1"         "\n\t"     \
      "mov.d       $f2,   %2"         "\n\t"     \
      "mov.d       $f0,   %1"         "\n\t"     \
      instruction" $f1,   $f2"        "\n\t"     \
      "bc1f end"instruction"d"#RDval  "\n\t"     \
      "nop"                           "\n\t"     \
      "add.d       $f0,   $f0, $f2"   "\n\t"     \
      "end"instruction"d"#RDval":"    "\n\t"     \
      "mov.d       %0,    $f0"        "\n\t"     \
      ".set        reorder"           "\n\t"     \
      : "=f" (outd)                              \
      : "f" (fs_d[i]) , "f" (ft_d[i])            \
      : "$f0", "$f1", "$f2"                      \
   );                                            \
   printf("%s, bc1f   out=%f, fs=%f, ft=%f\n",   \
          instruction, outd, fs_d[i], ft_d[i]);  \
}
#endif