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

/* The FLOGR insn reads from register R2 and writes to register R1 and
   R1 + 1. So we need to distinguish three cases:

   (1) All three registers R1, R1 + 1, and R2 are distinct
   (2) R2 == R1
   (3) R2 == R1 + 1

   These are tested by flogr1, flogr2, and flogr3, respectively. */

/* Call FLOGR on INPUT. The results are returned through the parms. */

/* R2 != R1 && R2 != R1 + 1 */
void
flogr1(unsigned long input, unsigned long *bitpos, unsigned long *modval,
       unsigned int *cc)
{
   unsigned int psw;
   register unsigned long value asm("4") = input;

   asm volatile ( FLOGR(2,4)
                  "ipm   %[psw]\n\t"
                  "stg   2, %[bitpos]\n\t"
                  "stg   3, %[modval]\n\t"
                  : [bitpos]"=m"(*bitpos), [modval]"=m"(*modval),
                    [psw]"=&d"(psw)
                  : [val] "d"(value)
                  : "2", "3", "cc");

   *cc = psw >> 28;
#if 0
   printf("value = %lx,  bitpos = %lu,  modval = %lx,  cc = %d\n",
          value, *bitpos, *modval, *cc);
#endif
}

/* R2 == R1 */
void
flogr2(unsigned long input, unsigned long *bitpos, unsigned long *modval,
       unsigned int *cc)
{
   unsigned int psw;
   register unsigned long value asm("2") = input;

   asm volatile ( FLOGR(2,2)
                  "ipm   %[psw]\n\t"
                  "stg   2, %[bitpos]\n\t"
                  "stg   3, %[modval]\n\t"
                  : [bitpos]"=m"(*bitpos), [modval]"=m"(*modval),
                    [psw]"=&d"(psw), [val] "+d"(value)
                  :
                  : "3", "cc");

   *cc = psw >> 28;
#if 0
   printf("value = %lx,  bitpos = %lu,  modval = %lx,  cc = %d\n",
          value, *bitpos, *modval, *cc);
#endif
}

/* R2 == R1 + 1 */
void
flogr3(unsigned long input, unsigned long *bitpos, unsigned long *modval,
       unsigned int *cc)
{
   unsigned int psw;
   register unsigned long value asm("3") = input;

   asm volatile ( FLOGR(2,3)
                  "ipm   %[psw]\n\t"
                  "stg   2, %[bitpos]\n\t"
                  "stg   3, %[modval]\n\t"
                  : [bitpos]"=m"(*bitpos), [modval]"=m"(*modval),
                    [psw]"=&d"(psw), [val] "+d"(value)
                  :
                  : "2", "cc");

   *cc = psw >> 28;
#if 0
   printf("value = %lx,  bitpos = %lu,  modval = %lx,  cc = %d\n",
          value, *bitpos, *modval, *cc);
#endif
}

void
runtest(void (*func)(unsigned long, unsigned long *, unsigned long *,
                     unsigned int *))
{
   unsigned long bitpos, modval, value;
   unsigned int cc;
   int i;

   /* Value 0 is special */
   value = 0;
   func(value, &bitpos, &modval, &cc);
   if (modval != 0)  fprintf(stderr, "modval is wrong for %lx\n", value);
   if (bitpos != 64) fprintf(stderr, "bitpos is wrong for %lx\n", value);
   if (cc != 0)      fprintf(stderr, "cc is wrong for %lx\n", value);

   /* Test with exactly 1 bit set */
   for (i = 0; i < 64; ++i) {
     value = 1ull << i;
     func(value, &bitpos, &modval, &cc);
     if (modval != 0) fprintf(stderr, "modval is wrong for %lx\n", value);
     if (bitpos != 63 - i) fprintf(stderr, "bitpos is wrong for %lx\n", value);
     if (cc != 2)          fprintf(stderr, "cc is wrong for %lx\n", value);
   }

   /* Test with all bits 1 right from first 1 bit */
   for (i = 1; i < 64; ++i) {
     value = 1ull << i;
     value = value | (value - 1);
     func(value, &bitpos, &modval, &cc);
     if (modval != (value >> 1)) fprintf(stderr, "modval is wrong for %lx\n", value);
     if (bitpos != 63 - i) fprintf(stderr, "bitpos is wrong for %lx\n", value);
     if (cc != 2)          fprintf(stderr, "cc is wrong for %lx\n", value);
   }
}


int main()
{
   runtest(flogr1);
   runtest(flogr2);
   runtest(flogr3);

   return 0;
}
