#include <float.h>
#include <stdio.h>
#include <assert.h>
#include "opcodes.h"


#define L2F(insn,  initial, target,round)                                \
({                                                                       \
   register unsigned long source asm("2") =  initial;                    \
   register typeof(target) _t asm("f0");                                 \
   asm volatile(insn(round,0,0,2) :"=f" (_t):"d"(source));               \
   _t;                                                                   \
})

#define F2L(insn, initial, type, round, cc)                              \
({                                                                       \
   register type source asm("f0") =  initial;                            \
   register unsigned long target asm ("2") = 0;                          \
   asm volatile(insn(round,0,2,0)                                        \
                "ipm %1\n\t"                                             \
                "srl %1,28\n\t"                                          \
 		:"=d" (target), "=d" (cc) :"f"(source):"cc");            \
   target;                                                               \
})


#define DO_INSN_L2F32(insn, round)                                       \
({                                                                       \
   float f32;                                                            \
   printf(#insn " %f\n", L2F(insn, 0, f32, round));                      \
   printf(#insn " %f\n", L2F(insn, 1, f32, round));                      \
   printf(#insn " %f\n", L2F(insn, 0xffffffffUL, f32, round));           \
   printf(#insn " %f\n", L2F(insn, 0x80000000UL, f32, round));           \
   printf(#insn " %f\n", L2F(insn, 0x7fffffffUL, f32, round));           \
   printf(#insn " %f\n", L2F(insn, 0x100000000UL, f32, round));          \
   printf(#insn " %f\n", L2F(insn, 0xffffffffffffffffUL, f32, round));   \
   printf(#insn " %f\n", L2F(insn, 0x8000000000000000UL, f32, round));   \
   printf(#insn " %f\n", L2F(insn, 0x7fffffffffffffffUL, f32, round));   \
})

#define DO_INSN_L2F64(insn, round)                                       \
({                                                                       \
   double f64;                                                           \
   printf(#insn " %f\n", L2F(insn, 0, f64, round));                      \
   printf(#insn " %f\n", L2F(insn, 1, f64, round));                      \
   printf(#insn " %f\n", L2F(insn, 0xffffffffUL, f64, round));           \
   printf(#insn " %f\n", L2F(insn, 0x80000000UL, f64, round));           \
   printf(#insn " %f\n", L2F(insn, 0x7fffffffUL, f64, round));           \
   printf(#insn " %f\n", L2F(insn, 0x100000000UL, f64, round));          \
   printf(#insn " %f\n", L2F(insn, 0xffffffffffffffffUL, f64, round));   \
   printf(#insn " %f\n", L2F(insn, 0x8000000000000000UL, f64, round));   \
   printf(#insn " %f\n", L2F(insn, 0x7fffffffffffffffUL, f64, round));   \
})

#define DO_INSN_L2F128(insn, round)                                      \
({                                                                       \
   long double f128;                                                     \
   printf(#insn " %Lf\n", L2F(insn, 0, f128, round));                    \
   printf(#insn " %Lf\n", L2F(insn, 1, f128, round));                    \
   printf(#insn " %Lf\n", L2F(insn, 0xffffffffUL, f128, round));         \
   printf(#insn " %Lf\n", L2F(insn, 0x80000000UL, f128, round));         \
   printf(#insn " %Lf\n", L2F(insn, 0x7fffffffUL, f128, round));         \
   printf(#insn " %Lf\n", L2F(insn, 0x100000000UL, f128, round));        \
   printf(#insn " %Lf\n", L2F(insn, 0xffffffffffffffffUL, f128, round)); \
   printf(#insn " %Lf\n", L2F(insn, 0x8000000000000000UL, f128, round)); \
   printf(#insn " %Lf\n", L2F(insn, 0x7fffffffffffffffUL, f128, round)); \
})

#define DO_INSN_F2L(insn, round, type)                                   \
({                                                                       \
   int cc;                                                               \
   printf(#insn " %lu ", F2L(insn, -1.1, type, round, cc));              \
   printf("cc=%d\n", cc);                                                \
   printf(#insn " %lu ", F2L(insn, 0, type, round, cc));                 \
   printf("cc=%d\n", cc);                                                \
   printf(#insn " %lu ", F2L(insn, 1, type, round, cc));                 \
   printf("cc=%d\n", cc);                                                \
   printf(#insn " %lu ", F2L(insn, 1.4, type, round, cc));               \
   printf("cc=%d\n", cc);                                                \
   printf(#insn " %lu ", F2L(insn, 1.5, type, round, cc));               \
   printf("cc=%d\n", cc);                                                \
   printf(#insn " %lu ", F2L(insn, 1.6, type, round, cc));               \
   printf("cc=%d\n", cc);                                                \
   printf(#insn " %lu ", F2L(insn, 1.6E+4, type, round, cc));            \
   printf("cc=%d\n", cc);                                                \
   printf(#insn " %lu ", F2L(insn, 1.6E+8, type, round, cc));            \
   printf("cc=%d\n", cc);                                                \
   printf(#insn " %lu ", F2L(insn, 1.6E+12, type, round, cc));           \
   printf("cc=%d\n", cc);                                                \
   printf(#insn " %lu ", F2L(insn, 1.6E+20, type, round, cc));           \
   printf("cc=%d\n", cc);                                                \
   printf(#insn " %lu ", F2L(insn, 1.6E+200, type, round, cc));          \
   printf("cc=%d\n", cc);                                                \
   printf(#insn " %lu ", F2L(insn, 1.6E+2000L, type, round, cc));        \
   printf("cc=%d\n", cc);                                                \
   printf(#insn " %lu ", F2L(insn, 1.6E-4, type, round, cc));            \
   printf("cc=%d\n", cc);                                                \
   printf(#insn " %lu ", F2L(insn, FLT_MIN, type, round, cc));           \
   printf("cc=%d\n", cc);                                                \
   printf(#insn " %lu ", F2L(insn, FLT_MAX, type, round, cc));           \
   printf("cc=%d\n", cc);                                                \
   printf(#insn " %lu ", F2L(insn, DBL_MIN, type, round, cc));           \
   printf("cc=%d\n", cc);                                                \
   printf(#insn " %lu ", F2L(insn, DBL_MAX, type, round, cc));           \
   printf("cc=%d\n", cc);                                                \
   printf(#insn " %lu ", F2L(insn, LDBL_MIN, type, round, cc));          \
   printf("cc=%d\n", cc);                                                \
   printf(#insn " %lu ", F2L(insn, LDBL_MAX, type, round, cc));          \
   printf("cc=%d\n", cc);                                                \
})

#define DO_L2F(round)                                                    \
({                                                                       \
   DO_INSN_L2F32(CELFBR, round);                                         \
   DO_INSN_L2F32(CELGBR, round);                                         \
   DO_INSN_L2F64(CDLFBR, round);                                         \
   DO_INSN_L2F64(CDLGBR, round);                                         \
   DO_INSN_L2F128(CXLFBR, round);                                        \
   DO_INSN_L2F128(CXLGBR, round);                                        \
})

#define DO_F2L(round)                                                    \
({                                                                       \
   DO_INSN_F2L(CLFEBR, round, float);                                    \
   DO_INSN_F2L(CLGEBR, round, float);                                    \
   DO_INSN_F2L(CLFDBR, round, double);                                   \
   DO_INSN_F2L(CLGDBR, round, double);                                   \
   DO_INSN_F2L(CLFXBR, round, long double);                              \
   DO_INSN_F2L(CLGXBR, round, long double);                              \
})


int main()
{
   assert(sizeof(long double) == 16);
   DO_L2F(4);
   DO_F2L(4);

   DO_L2F(5);
   DO_F2L(5);

   DO_L2F(6);
   DO_F2L(6);

   DO_L2F(7);
   DO_F2L(7);

   return 0;
}
