/* tGFSR32x4.c
   créé par Yann GUIDON (whygee@f-cpu.org) le 1er août 2007
   version 2007-09-06 : ptit bug 32/64 bits corrigé
   2007-10-3 : typo dans MACRO_tGFSR32x4_SHFT2

   Ce fichier implémente l'algorithme de signature tGFSR32x4,
   il est directement inclus par le programme l'utilisant,
   et compilé de préférence avec
         gcc -Os -fomit-frame-pointer -o test tGFSR32x4.c
   * MMUL est fixé à 144.
   * MACRO_tGFSR32x4 est défini par défaut,
      ne pas hésiter à tester les autres versions.
   * Ne pas oublier de (re)créer machine.h avec machine.sh
*/

#include "def32.h"

#define MMUL (128|16)

#ifndef MACRO_tGFSR32x4
 #define MACRO_tGFSR32x4 MACRO_tGFSR32x4_SHFT
#endif

/********************************
       Macros de signature
 ********************************/

#define MACRO_tGFSR32x4_CMUL(current, last, ptr) { \
  current ^= last;        \
  current ^= ptr;         \
  t = current;            \
  current >>= 1;          \
  t &= 0x01010101;        \
  current &= 0x7F7F7F7F;  \
  t *= MMUL;              \
  current ^= t;           }

/* version optimisée avec MMUL=144 */
#define MACRO_tGFSR32x4_SHFT(current, last, ptr) { \
  current ^= last;        \
  current ^= ptr;         \
  t = current;            \
  current >>= 1;          \
  t &= 0x01010101;        \
  current &= 0x7F7F7F7F;  \
  t <<= 4;                \
  current ^= t;           \
  t <<= 3;                \
  current ^= t;           }

/* version plus adaptée pour certains CPUs RISC */ 
#define MACRO_tGFSR32x4_SHFT2(current, last, ptr) { \
  U32 u;                    \
  current ^= last;          \
  current ^= ptr;           \
  t = current & 0x01010101; \
  current >>= 1;            \
  current &= 0x7F7F7F7F;    \
  u = t << 7;               \
  t <<= 4;                  \
  current ^= u;             \
  current ^= t;             }

/********************************
     La table des constantes
 ********************************/

#define CONST_R0 0x62f08f0a
#define CONST_R1 0x115bac47
#define CONST_R2 0xe293dbb0
#define CONST_R3 0x89865d38

#define ROTATION(x,y,z) (z?((x << z)|(y >> (32-z))):x)

static const U32 tGFSR32x4_const[4*4] = {
 CONVERT_LE32(CONST_R0),
 CONVERT_LE32(CONST_R1),
 CONVERT_LE32(CONST_R2),
 CONVERT_LE32(CONST_R3),

 CONVERT_LE32(ROTATION(CONST_R0,CONST_R3,8)),
 CONVERT_LE32(ROTATION(CONST_R1,CONST_R0,8)),
 CONVERT_LE32(ROTATION(CONST_R2,CONST_R1,8)),
 CONVERT_LE32(ROTATION(CONST_R3,CONST_R2,8)),

 CONVERT_LE32(ROTATION(CONST_R0,CONST_R3,16)),
 CONVERT_LE32(ROTATION(CONST_R1,CONST_R0,16)),
 CONVERT_LE32(ROTATION(CONST_R2,CONST_R1,16)),
 CONVERT_LE32(ROTATION(CONST_R3,CONST_R2,16)),

 CONVERT_LE32(ROTATION(CONST_R0,CONST_R3,24)),
 CONVERT_LE32(ROTATION(CONST_R1,CONST_R0,24)),
 CONVERT_LE32(ROTATION(CONST_R2,CONST_R1,24)),
 CONVERT_LE32(ROTATION(CONST_R3,CONST_R2,24))
};

/********************************
      Routine de signature
 ********************************/

U16 tGFSR32x4_signature(U8 *p, int size) {
/* - p est l'adresse des octets à signer
   - size est le nombre d'octets à signer
  Ils sont normalement dans des registres. */

  register U32 t, r0, r1, r3;
#ifndef i386
  register U32  r2, *R2;
  /* séparés pour réduire les risques d'erreurs
    sur plateformes 64 bits genre Alpha */
  #define ASSIGN_R2 R2 = (U32*)    /* un pointeur = un pointeur */
#else
  register U32 r2; /* utilisé aussi comme pointeur */
  #define R2 ((U32*)r2) /* R2 remplace *ptr_table */
  #define ASSIGN_R2 r2 = PTR_CAST  /* un entier = un entier */
/* à laisser en mémoire ou sur la pile : */
volatile
#endif
  U32 u, offset, mask;
/* mask est remplacé par r1 dans le prologue */

  /* Prologue */

  offset = PTR_CAST p & 3; /* calcul de l'alignement */
  ASSIGN_R2 (tGFSR32x4_const+(offset<<2));
  p = (U8*) ((PTR_CAST p) & -4L);  /* aligne p sur 4 octets */

  /* charge les premiers registres */
  r0 = R2[0];
  r3 = R2[3];

  /* création du masque pour traiter le premier mot */
  r1 = 0xFFFFFFFF;
  r1 = LOCAL_SHL32(r1, (offset << 3));

#ifdef DEBUG
  printf("align=%d mask=%08X ", offset, r1);
#endif

  /* calcule une première étape */
  MACRO_tGFSR32x4(r0, r3, *(U32*)p)

  /* décompte un mot entier et compense avec l'offset : */
  size += offset - 4;

  if (size > 0) {
    /*  MIX(r0) */
    r0 &=  r1;
    r1  = ~r1 & R2[0];
    r0 |=  r1;

    /* finit de charger les registres */
    r1 = R2[1];
    r2 = R2[2]; /* en dernier */

    /* La boucle principale */
    while (size >= 16) {
      size -= 16;
      MACRO_tGFSR32x4(r1, r0, *(U32*)(p+4 ))
      MACRO_tGFSR32x4(r2, r1, *(U32*)(p+8 ))
      MACRO_tGFSR32x4(r3, r2, *(U32*)(p+12))
      MACRO_tGFSR32x4(r0, r3, *(U32*)(p+16))
      p+=16;
    }

/* macro locale de sélection des octets */
#define MIX(rX) { \
    u   &=  mask; \
    rX  &= ~mask; \
    rX  |=  u;    }

    /* Epilogue */
    if (size > 0) {
      /* construction du masque */
      mask = LOCAL_SHL32(0xFFFFFFFF,8);
      t    = SHIFT_MASK32((size - 1),3) << 3;
      mask = LOCAL_SHL32(mask,t);

#ifdef DEBUG
  printf(" E %08X ", mask);
#endif

      u=r1;
      MACRO_tGFSR32x4(r1, r0, *(U32*)(p+4))
      if (size > 4) {
        u=r2;
        MACRO_tGFSR32x4(r2, r1, *(U32*)(p+8))
        if (size > 8) {
          u=r3;
          MACRO_tGFSR32x4(r3, r2, *(U32*)(p+12))
          if (size > 12) {
            u=r0;
            MACRO_tGFSR32x4(r0, r3, *(U32*)(p+16))
            MIX(r0)  /* size=13,14,15 */
          }
          else
            MIX(r3)  /* size=9,10,11,12 */
        }
        else
          MIX(r2)    /* size=5,6,7,8 */
      }
      else
        MIX(r1)      /* size=1,2,3,4 */
    }
#ifdef DEBUG
    else
  printf(" |          ");
#endif

  } /* if size>0 */

  else {
    /* version court-circuit, peu employée */
    if (size > -4) {
      /* Il faut encore tronquer le masque.
         Deux décalages en sens opposés sont plus
         simples qu'un masque supplémentaire. */
      size = (-size) << 3;
      r1 = LOCAL_SHR32(LOCAL_SHL32(r1, size), size);

#ifdef DEBUG
  printf(" P %08X ", r1);
#endif

      /*  MIX(r0) */
      r0 &=  r1;
      r1  = ~r1 & R2[0];
      r0 |=  r1;
    }
    else { /* size=0 && offset==0 */
#ifdef DEBUG
  printf(" Q          ");
#endif
      r0 = R2[0];
    }
    /* finit de charger les registres avant la compaction */
    r1 = R2[1];
    r2 = R2[2]; /* en dernier */
  }

#ifdef DEBUG

#ifndef WORDS_BIGENDIAN
#undef  ROTATION
#define ROTATION(x,y,z) (z?((x >> z)|(y << (32-z))):x)
#endif
  printf(" | %08X %08X %08X %08X\n",
    tGFSR32x4_const[0]^ROTATION(r0,r1,offset*8),
    tGFSR32x4_const[1]^ROTATION(r1,r2,offset*8),
    tGFSR32x4_const[2]^ROTATION(r2,r3,offset*8),
    tGFSR32x4_const[3]^ROTATION(r3,r0,offset*8));
#endif

  /* empêche GCC d'"optimiser" le code */
  return (r0^r1^r2^r3) & 0xFFFF ;
}
#undef MIX
