/* HOOK. Fixed comments; otherwise impossible to compile */
/*
 * $Log:   V:/Flite/archives/TrueFFS5/Src/REEDSOL.C_V  $
 * 
 *    Rev 1.3   Jul 13 2001 01:10:00   oris
 * Moved saved syndrome array definition (used by d2tst).
 *
 *    Rev 1.2   Apr 09 2001 15:10:20   oris
 * End with an empty line.
 *
 *    Rev 1.1   Apr 01 2001 08:00:14   oris
 * copywrite notice.
 *
 *    Rev 1.0   Feb 04 2001 12:37:38   oris
 * Initial revision.
 *
 */

/************************************************************************/
/*                                                                      */
/*		FAT-FTL Lite Software Development Kit			*/
/*		Copyright (C) M-Systems Ltd. 1995-2001			*/
/*									*/
/************************************************************************/


#include "reedsol.h"

#define T 2			 /* Number of recoverable errors */
#define SYND_LEN (T*2)           /* length of syndrom vector */
#define K512  (((512+1)*8+6)/10) /* number of inf symbols for record
				    of 512 bytes (K512=411) */
#define N512  (K512 + SYND_LEN)  /* code word length for record of 512 bytes */
#define INIT_DEG 510
#define MOD 1023

#define BLOCK_SIZE 512

#ifdef D2TST
byte    saveSyndromForDumping[SYNDROM_BYTES];
#endif /* D2TST */

static short  gfi(short val);
static short  gfmul( short f, short s );
static short  gfdiv( short f, short s );
static short  flog(short val);
static short  alog(short val);

/*------------------------------------------------------------------------------*/
/* Function Name: RTLeightToTen                                                 */
/* Purpose......: convert an array of five 8-bit values into an array of        */
/*                four 10-bit values, from right to left.                       */
/* Returns......: Nothing                                                       */
/*------------------------------------------------------------------------------*/
static void RTLeightToTen(char *reg8, unsigned short reg10[])
{
	reg10[0] =  (reg8[0] & 0xFF)       | ((reg8[1] & 0x03) << 8);
	reg10[1] = ((reg8[1] & 0xFC) >> 2) | ((reg8[2] & 0x0F) << 6);
	reg10[2] = ((reg8[2] & 0xF0) >> 4) | ((reg8[3] & 0x3F) << 4);
	reg10[3] = ((reg8[3] & 0xC0) >> 6) | ((reg8[4] & 0xFF) << 2);
}




/*----------------------------------------------------------------------------*/
static void unpack( short word, short length, short vector[] )
/*                                                                            */
/*   Function unpacks word into vector                                        */
/*                                                                            */
/*   Parameters:                                                              */
/*     word   - word to be unpacked                                           */
/*     vector - array to be filled                                            */
/*     length - number of bits in word                                        */

{
  short i, *ptr;

  ptr = vector + length - 1;
  for( i = 0; i < length; i++ )
  {
    *ptr-- = word & 1;
    word >>= 1;
  }
}


/*----------------------------------------------------------------------------*/
static short pack( short *vector, short length )
/*                                                                            */
/*   Function packs vector into word                                          */
/*                                                                            */
/*   Parameters:                                                              */
/*     vector - array to be packed                                            */
/*     length - number of bits in word                                        */

{
  short tmp, i;

  vector += length - 1;
  tmp = 0;
  i = 1;
  while( length-- > 0 )
  {
    if( *vector-- )
      tmp |= i;
    i <<= 1;
  }
  return( tmp );
}


/*----------------------------------------------------------------------------*/
static short gfi( short val)		/* GF inverse */
{
  return alog((short)(MOD-flog(val)));
}


/*----------------------------------------------------------------------------*/
static short gfmul( short f, short s ) /* GF multiplication */
{
  short i;
  if( f==0 || s==0 )
     return 0;
  else
  {
    i = flog(f) + flog(s);
    if( i > MOD ) i -= MOD;
    return( alog(i) );
  }
}


/*----------------------------------------------------------------------------*/
static short gfdiv( short f, short s ) /* GF division */
{
  return gfmul(f,gfi(s));
}




/*----------------------------------------------------------------------------*/
static void residue_to_syndrom( short reg[], short realsynd[] )
{
   short i,l,alpha,x,s,x4;
   short deg,deg4;


   for(i=0,deg=INIT_DEG;i<SYND_LEN;i++,deg++)
   {
      s = reg[0];
      alpha = x = alog(deg);
      deg4 = deg+deg;
      if( deg4 >= MOD ) deg4 -= MOD;
      deg4 += deg4;
      if( deg4 >= MOD ) deg4 -= MOD;
      x4 = alog(deg4);

      for(l=1;l<SYND_LEN;l++)
      {
	s ^= gfmul( reg[l], x );
	x  = gfmul( alpha, x );
      }

      realsynd[i] = gfdiv( s, x4 );
   }
}


/*----------------------------------------------------------------------------*/
static short alog(short i)
{
  short j=0, val=1;

  for( ; j < i ; j++ )
  {
    val <<= 1 ;

    if ( val > 0x3FF )
    {
      if ( val & 8 )   val -= (0x400+7);
      else             val -= (0x400-9);
    }
  }

  return val ;
}


static short flog(short val)
{
  short j, val1;

  if (val == 0)
    return (short)0xFFFF;

  j=0;
  val1=1;

  for( ; j <= MOD ; j++ )
  {
    if (val1 == val)
      return j;

    val1 <<= 1 ;

    if ( val1 > 0x3FF )
    {
      if ( val1 & 8 )   val1 -= (0x400+7);
      else              val1 -= (0x400-9);
    }

  }

  return 0;
}



/*----------------------------------------------------------------------------*/
static short convert_to_byte_patterns( short *locators, short *values,
				short noferr, short *blocs, short *bvals )
{
  static short mask[] = { 0x0, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff };

  short i,j,n, n0, n1, tmp;
  short n_bit, n_byte, k_bit, nb;

  for( i = 0, nb = 0; i< noferr; i++)
  {
    n = locators[i];
    tmp = values[i];
    n_bit = n *10 - 6 ;
    n_byte = n_bit >> 3;
    k_bit  = n_bit - (n_byte<<3);
    n_byte++;
    if( k_bit == 7 )
    {
      /* 3 corrupted bytes */
      blocs[nb] = n_byte+1;
      bvals[nb++] = tmp & 1 ? 0x80 : 0;

      tmp >>= 1;
      blocs[nb] = n_byte;
      bvals[nb++] = tmp & 0xff;

      tmp >>= 8;
      bvals[nb++] = tmp & 0xff;
    }
    else
    {
      n0 = 8 - k_bit;
      n1 = 10 - n0;

      blocs[nb] = n_byte;
      bvals[nb++] = (tmp & mask[n1]) << (8 - n1);

      tmp >>= n1;
      blocs[nb] = n_byte - 1;
      bvals[nb++] = (tmp & mask[n0]);
    }
  }

  for( i = 0, j = -1; i < nb; i++ )
  {
    if( bvals[i] == 0 ) continue;
    if( (blocs[i] == blocs[j]) && ( j>= 0 ) )
    {
      bvals[j] |= bvals[i];
    }
    else
    {
      j++;
      blocs[j] = blocs[i];
      bvals[j] = bvals[i];
    }
  }
  return j+1;
}


/*----------------------------------------------------------------------------*/
static short deg512( short x )
{
  short i;
  short l,m;

  l = flog(x);
  for( i=0;i<9;i++)
  {
    m = 0;
    if( (l & 0x200) )
      m = 1;
    l =  ( ( l << 1 ) & 0x3FF  ) | m;
  }
  return alog(l);
}


/*----------------------------------------------------------------------------*/
static short decoder_for_2_errors( short s[], short lerr[], short verr[] )
{
  /* decoder for correcting up to 2 errors */
  short i,j,k,temp,delta;
  short ind, x1, x2;
  short r1, r2, r3, j1, j2;
  short sigma1, sigma2;
  short xu[10], ku[10];
  short yd, yn;

  ind = 0;
  for(i=0;i<SYND_LEN;i++)
    if( s[i] != 0 )
      ind++;                /* ind = number of nonzero syndrom symbols */

  if( ind == 0 ) return 0;  /* no errors */

  if( ind < 4 )
    goto two_or_more_errors;


/* checking s1/s0 = s2/s1 = s3/s2 = alpha**j for some j */

  r1 = gfdiv( s[1], s[0] );
  r2 = gfdiv( s[2], s[1] );
  r3 = gfdiv( s[3], s[2] );

  if( r1 != r2 || r2 != r3)
    goto two_or_more_errors;

  j = flog(r1);
  if( j > 414 )
    goto two_or_more_errors;

  lerr[0] = j;

/*  pattern = (s0/s1)**(510+1) * s1

	  or

    pattern = (s0/s1)**(512 - 1 )  * s1 */

  temp = gfi( r1 );

#ifndef NT5PORT
  {
    int i;

    for (i = 0; i < 9; i++)
      temp = gfmul( temp, temp );  /* deg = 512 */
  }
#else /*NT5PORT*/
  for (i = 0; i < 9; i++)
  {
      temp = gfmul( temp, temp );  /* deg = 512 */
  }
#endif /*NT5PORT*/

  verr[0] = gfmul( gfmul(temp, r1), s[1] );

  return 1;    /* 1 error */

two_or_more_errors:

  delta = gfmul( s[0], s[2] ) ^ gfmul( s[1], s[1] );

  if( delta == 0 )
    return -1;  /* uncorrectable error */

  temp = gfmul( s[1], s[3] ) ^ gfmul( s[2], s[2] );

  if( temp == 0 )
    return -1;  /* uncorrectable error */

  sigma2 = gfdiv( temp, delta );

  temp = gfmul( s[1], s[2] ) ^ gfmul( s[0], s[3] );

  if( temp == 0 )
    return -1;  /* uncorrectable error */

  sigma1 = gfdiv( temp, delta );

  k = gfdiv( sigma2, gfmul( sigma1, sigma1 ) );

  unpack( k, 10, ku );

  if( ku[2] != 0 )
    return -1;

  xu[4] = ku[9];
  xu[5] = ku[0] ^ ku[1];
  xu[6] = ku[6] ^ ku[9];
  xu[3] = ku[4] ^ ku[9];
  xu[1] = ku[3] ^ ku[4] ^ ku[6];
  xu[0] = ku[0] ^ xu[1];
  xu[8] = ku[8] ^ xu[0];
  xu[7] = ku[7] ^ xu[3] ^ xu[8];
  xu[2] = ku[5] ^ xu[7] ^ xu[5] ^ xu[0];
  xu[9] = 0;

  x1 = pack( xu, 10 );
  x2 = x1 | 1;

  x1 = gfmul( sigma1, x1 );
  x2 = gfmul( sigma1, x2 );


  j1 = flog(x1);
  j2 = flog(x2);

  if( (j1 > 414) || (j2 > 414) )
    return -1;


  r1 = x1 ^ x2;
  r2 = deg512( x1 );
  temp = gfmul( x1, x1 );
  r2 = gfdiv( r2, temp );
  yd = gfmul( r2, r1 );

  if( yd == 0 )
    return -1;

  yn = gfmul( s[0], x2 ) ^ s[1];
  if( yn == 0 )
    return -1;

  verr[0] = gfdiv( yn, yd );

  r2 = deg512( x2 );
  temp = gfmul( x2, x2 );
  r2 = gfdiv( r2, temp );
  yd = gfmul( r2, r1 );

  if( yd == 0 )
    return -1;

  yn = gfmul( s[0], x1 ) ^ s[1];
  if( yn == 0 )
    return -1;

  verr[1] = gfdiv( yn, yd );

  if( j1 > j2 ) {
    lerr[0] = j2;
    lerr[1] = j1;
    temp = verr[0];
    verr[0] = verr[1];
    verr[1] = temp;
  }
  else
  {
    lerr[0] = j1;
    lerr[1] = j2;
  }

  return 2;
}


/*------------------------------------------------------------------------------*/
/* Function Name: flDecodeEDC                                                   */
/* Purpose......: Trys to correct errors.                                       */
/*                errorSyndrom[] should contain the syndrom as 5 bytes and one  */
/*                parity byte. (identical to the output of calcEDCSyndrom()).   */
/*                Upon returning, errorNum will contain the number of errors,   */
/*                errorLocs[] will contain error locations, and                 */
/*                errorVals[] will contain error values (to be XORed with the   */
/*                data).                                                        */
/*                Parity error is relevant only if there are other errors, and  */
/*                the EDC code fails parity check.                              */
/*                NOTE! Only the first errorNum indexes of the above two arrays */
/*                      are relevant. The others contain garbage.               */
/* Returns......: The error status.                                             */
/*                NOTE! If the error status is NO_EDC_ERROR upon return, ignore */
/*                      the value of the arguments.                             */
/*------------------------------------------------------------------------------*/
EDCstatus flDecodeEDC(char *errorSyndrom, char *errorsNum,
		    short errorLocs[3*T],  short errorVals[3*T])
{
  short noferr;                         /* number of errors */
  short dec_parity;                     /* parity byte of decoded word */
  short rec_parity;                     /* parity byte of received word */
  short realsynd[SYND_LEN];             /* real syndrom calculated from residue */
  short locators[T],                    /* error locators */
  values[T];                            /* error values */
  short reg[SYND_LEN];                  /* register for main division procedure */
  int i;

  RTLeightToTen(errorSyndrom, (unsigned short *)reg);
  rec_parity = errorSyndrom[5] & 0xFF;  /* The parity byte */

  residue_to_syndrom(reg, realsynd);
  noferr = decoder_for_2_errors(realsynd, locators, values);

  if(noferr == 0)
    return NO_EDC_ERROR;                /* No error found */

  if(noferr < 0)                        /* If an uncorrectable error was found */
    return UNCORRECTABLE_ERROR;

  for (i=0;i<noferr;i++)
    locators[i] = N512 - 1 - locators[i];

  *errorsNum = (char)convert_to_byte_patterns(locators, values, noferr, errorLocs, errorVals);

  for(dec_parity=i=0; i < *errorsNum; i++)/* Calculate the parity for all the */
  {                                       /*   errors found: */
    if(errorLocs[i] <= 512)
      dec_parity ^= errorVals[i];
  }

  if(dec_parity != rec_parity)
    return UNCORRECTABLE_ERROR;         /* Parity error */
  else
    return CORRECTABLE_ERROR;
}


/*------------------------------------------------------------------------------*/
/* Function Name: flCheckAndFixEDC                                              */
/* Purpose......: Decodes the EDC syndrom and fixs the errors if possible.      */
/*                block[] should contain 512 bytes of data.                     */
/*                NOTE! Call this function only if errors where detected by     */
/*                      syndCalc or by the ASIC module.                         */
/* Returns......: The error status.                                             */
/*------------------------------------------------------------------------------*/
EDCstatus flCheckAndFixEDC(char FAR1 *block, char *syndrom, FLBoolean byteSwap)
{
  char errorsNum;
  short errorLocs[3*T];
  short errorVals[3*T];
  EDCstatus status;

  status = flDecodeEDC(syndrom, &errorsNum, errorLocs, errorVals);

  if(status == CORRECTABLE_ERROR)       /* Fix the errors if possible */
  {
    int i;

    for (i=0; i < errorsNum; i++)
      if( (errorLocs[i] ^ byteSwap) < BLOCK_SIZE )  /* Fix only in Data Area */
        block[errorLocs[i] ^ byteSwap] ^= errorVals[i];

    return NO_EDC_ERROR;                /* All errors are fixed */
  }
  else
    return status;                      /* Uncorrectable error */
}