/*
   fichier def64 : Définit les opérations à utiliser
       pour manipuler des données 64 bits avec GCC 3.x
   créé par Yann GUIDON le 16 mai 2006
   version 23 février : ajout de U64_SHLi/SHRi
   version 24 avril : mmintrin.h & GCC4.1.1
   version 12 août

   exemple de compilation :
     gcc -Os -W -Wall -momit-leaf-frame-pointer -march=pentium3 nomdufichier.c
*/

#ifndef ___DEF64_H___
#include "machine/machine.h"

#define PTR_CAST (long)

#if defined (__MMX__)
  #if __GNUC__ > 3
    #include <mmintrin.h>
    #define GCCTYPE_IN  (__m64) /* le type interne de mmintrin.h */
    #define GCCTYPE_OUT (U64)
  #else /* sinon, définition vide */
    #define GCCTYPE_IN
    #define GCCTYPE_OUT
  #endif

  #define U64_XOR(x,y)   GCCTYPE_OUT __builtin_ia32_pxor (GCCTYPE_IN x, GCCTYPE_IN y)
  #define U64_OR(x,y)    GCCTYPE_OUT __builtin_ia32_por  (GCCTYPE_IN x, GCCTYPE_IN y)
  #define U64_AND(x,y)   GCCTYPE_OUT __builtin_ia32_pand (GCCTYPE_IN x, GCCTYPE_IN y)
  #define U64_ANDN(x,y)  GCCTYPE_OUT __builtin_ia32_pandn(GCCTYPE_IN x, GCCTYPE_IN y)

/* 3 cas particuliers pour le décalage : */

/* - Si y est un registre MMX : tout baigne */
  #define U64_SHR(x,y)   GCCTYPE_OUT __builtin_ia32_psrlq(x, y)
  #define U64_SHL(x,y)   GCCTYPE_OUT __builtin_ia32_psllq(x, y)

/* - Si y est un entier constant : il faut ruser */
  #define U64_SHRi(x,y) ({ unsigned tmp=y; \
    GCCTYPE_OUT __builtin_ia32_psrlq(GCCTYPE_IN x, tmp); })
  #define U64_SHLi(x,y) ({ unsigned tmp=y; \
    GCCTYPE_OUT __builtin_ia32_psllq(GCCTYPE_IN x, tmp); })

/* - Si y est un registre normal : la tuile !
Comme movd est indisponible dans les intrinsèques, il faut le définir. */

/* /!\ output : x, input : y */
#define U64_MOVD(x,y)           \
   __asm__ __volatile__ ( "\t"  \
     "movd %1, %0\n\t"          \
   : "=X" (x)                   \
   : "r" (y) );

#define U64_SHLr(x, y) ({\
   U64 r;                \
   U64_MOVD(r,y);        \
   U64_SHL(x, r); })

#define U64_SHRr(x, y) ({\
   U64 r;                \
   U64_MOVD(r,y);        \
   U64_SHR(x, r); })

#else
  #define U64_XOR(x,y)   (x ^ y)
  #define U64_OR(x,y)    (x | y)
  #define U64_AND(x,y)   (x & y)
  #define U64_ANDN(x,y)  (~x & y)
  #define U64_SHR(x,y)   (x >> y)
  #define U64_SHL(x,y)   (x << y)
  #define U64_SHRi(x,y)  U64_SHR(x,y)
  #define U64_SHLi(x,y)  U64_SHL(x,y)   
  #define U64_SHRr(x,y)  U64_SHR(x,y)
  #define U64_SHLr(x,y)  U64_SHL(x,y)   
#endif

/* adaptation des sens de décalage */

#ifndef WORDS_BIGENDIAN
  #define LOCAL_SHL(x,y)   U64_SHL(x,y)
  #define LOCAL_SHR(x,y)   U64_SHR(x,y)
  #define LOCAL_SHLi(x,y)  U64_SHLi(x,y)
  #define LOCAL_SHRi(x,y)  U64_SHRi(x,y)
  #define LOCAL_SHLr(x,y)  U64_SHLr(x,y)
  #define LOCAL_SHRr(x,y)  U64_SHRr(x,y)
  #define CONVERT_LE64(x)  (x)
#else
  #define LOCAL_SHL(x,y)   U64_SHR(x,y)
  #define LOCAL_SHR(x,y)   U64_SHL(x,y)
  #define LOCAL_SHLi(x,y)  U64_SHRi(x,y)
  #define LOCAL_SHRi(x,y)  U64_SHLi(x,y)
  #define LOCAL_SHLr(x,y)  U64_SHRr(x,y)
  #define LOCAL_SHRr(x,y)  U64_SHLr(x,y)
  #define CONVERT_LE64(x) \
({ /* honteusement pompé du /usr/include/linux/byteorder/swab.h de Faré */ \
  U64 __x = (x); \
  ((U64)( \
    (U64)(((U64)(__x) & (U64)0x00000000000000ffULL) << 56) |   \
    (U64)(((U64)(__x) & (U64)0x000000000000ff00ULL) << 40) |   \
    (U64)(((U64)(__x) & (U64)0x0000000000ff0000ULL) << 24) |   \
    (U64)(((U64)(__x) & (U64)0x00000000ff000000ULL) <<  8) |   \
    (U64)(((U64)(__x) & (U64)0x000000ff00000000ULL) >>  8) |   \
    (U64)(((U64)(__x) & (U64)0x0000ff0000000000ULL) >> 24) |   \
    (U64)(((U64)(__x) & (U64)0x00ff000000000000ULL) >> 40) |   \
    (U64)(((U64)(__x) & (U64)0xff00000000000000ULL) >> 56) )); \
})
#endif

#endif /* ___DEF64_H___ */
