/*
 * Copyright (C) 1991, 1992 Free Software Foundation, Inc.
 * This file is part of the GNU C Library.
 * 
 * The GNU C Library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 * 
 * The GNU C Library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 * 
 * You should have received a copy of the GNU Library General Public
 * License along with the GNU C Library; see the file COPYING.LIB in the
 * root directory of this distribution.  If not, write to the, 1992
 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <linux/types.h>
#include <linux/string.h>

#define op_t	unsigned long int
#define OPSIZ	(sizeof(op_t))

/* Type to use for unaligned operations.  */
typedef unsigned char byte;

/* Optimal type for storing bytes in registers.  */
#define reg_char	char

#define MERGE(w0, sh_1, w1, sh_2) (((w0) << (sh_1)) | ((w1) >> (sh_2)))

/* Copy exactly NBYTES bytes from SRC_BP to DST_BP,
   without any assumptions about alignment of the pointers.  */
#define BYTE_COPY_FWD(dst_bp, src_bp, nbytes)                                 \
  do									      \
    {									      \
      size_t __nbytes = (nbytes);                                             \
      while (__nbytes > 0)                                                    \
	{								      \
	  byte __x = ((byte *) src_bp)[0];                                    \
	  src_bp += 1;							      \
	  __nbytes -= 1;						      \
	  ((byte *) dst_bp)[0] = __x;                                         \
	  dst_bp += 1;							      \
	}								      \
    } while (0)

/* Copy exactly NBYTES_TO_COPY bytes from SRC_END_PTR to DST_END_PTR,
   beginning at the bytes right before the pointers and continuing towards
   smaller addresses.  Don't assume anything about alignment of the
   pointers.  */
#define BYTE_COPY_BWD(dst_ep, src_ep, nbytes)                                 \
  do									      \
    {									      \
      size_t __nbytes = (nbytes);                                             \
      while (__nbytes > 0)                                                    \
	{								      \
	  byte __x;							      \
	  src_ep -= 1;							      \
	  __x = ((byte *) src_ep)[0];                                         \
	  dst_ep -= 1;							      \
	  __nbytes -= 1;						      \
	  ((byte *) dst_ep)[0] = __x;                                         \
	}								      \
    } while (0)

/* Copy *up to* NBYTES bytes from SRC_BP to DST_BP, with
   the assumption that DST_BP is aligned on an OPSIZ multiple.	If
   not all bytes could be easily copied, store remaining number of bytes
   in NBYTES_LEFT, otherwise store 0.  */
extern void _wordcopy_fwd_aligned (long int, long int, size_t);
extern void _wordcopy_fwd_dest_aligned (long int, long int, size_t);
#define WORD_COPY_FWD(dst_bp, src_bp, nbytes_left, nbytes)                    \
  do									      \
    {									      \
      if (src_bp % OPSIZ == 0)                                                \
	_wordcopy_fwd_aligned (dst_bp, src_bp, (nbytes) / OPSIZ);             \
      else								      \
	_wordcopy_fwd_dest_aligned (dst_bp, src_bp, (nbytes) / OPSIZ);        \
      src_bp += (nbytes) & -OPSIZ;                                            \
      dst_bp += (nbytes) & -OPSIZ;                                            \
      (nbytes_left) = (nbytes) % OPSIZ;                                       \
    } while (0)

/* Copy *up to* NBYTES_TO_COPY bytes from SRC_END_PTR to DST_END_PTR,
   beginning at the words (of type op_t) right before the pointers and
   continuing towards smaller addresses.  May take advantage of that
   DST_END_PTR is aligned on an OPSIZ multiple.  If not all bytes could be
   easily copied, store remaining number of bytes in NBYTES_REMAINING,
   otherwise store 0.  */
extern void _wordcopy_bwd_aligned (long int, long int, size_t);
extern void _wordcopy_bwd_dest_aligned (long int, long int, size_t);
#define WORD_COPY_BWD(dst_ep, src_ep, nbytes_left, nbytes)                    \
  do									      \
    {									      \
      if (src_ep % OPSIZ == 0)                                                \
	_wordcopy_bwd_aligned (dst_ep, src_ep, (nbytes) / OPSIZ);             \
      else								      \
	_wordcopy_bwd_dest_aligned (dst_ep, src_ep, (nbytes) / OPSIZ);        \
      src_ep -= (nbytes) & -OPSIZ;                                            \
      dst_ep -= (nbytes) & -OPSIZ;                                            \
      (nbytes_left) = (nbytes) % OPSIZ;                                       \
    } while (0)


/* Threshold value for when to enter the unrolled loops.  */
#define OP_T_THRES	16

#if	defined(__mc68020__) || defined(mc68020)

#undef	OP_T_THRES
#define OP_T_THRES	16

/* WORD_COPY_FWD and WORD_COPY_BWD are not symmetric on the 68020,
   because of its weird instruction overlap characteristics.  */

#undef	WORD_COPY_FWD
#define WORD_COPY_FWD(dst_bp, src_bp, nbytes_left, nbytes)                    \
  do									      \
    {									      \
      size_t __nwords = (nbytes) / sizeof (op_t);                             \
      size_t __nblocks = __nwords / 8 + 1;				      \
      dst_bp -= (8 - __nwords % 8) * sizeof (op_t);                           \
      src_bp -= (8 - __nwords % 8) * sizeof (op_t);                           \
      switch (__nwords % 8)                                                   \
	do								      \
	  {								      \
	    ((op_t *) dst_bp)[0] = ((op_t *) src_bp)[0];                      \
	  case 7:							      \
	    ((op_t *) dst_bp)[1] = ((op_t *) src_bp)[1];                      \
	  case 6:							      \
	    ((op_t *) dst_bp)[2] = ((op_t *) src_bp)[2];                      \
	  case 5:							      \
	    ((op_t *) dst_bp)[3] = ((op_t *) src_bp)[3];                      \
	  case 4:							      \
	    ((op_t *) dst_bp)[4] = ((op_t *) src_bp)[4];                      \
	  case 3:							      \
	    ((op_t *) dst_bp)[5] = ((op_t *) src_bp)[5];                      \
	  case 2:							      \
	    ((op_t *) dst_bp)[6] = ((op_t *) src_bp)[6];                      \
	  case 1:							      \
	    ((op_t *) dst_bp)[7] = ((op_t *) src_bp)[7];                      \
	  case 0:							      \
	    src_bp += 32;						      \
	    dst_bp += 32;						      \
	    __nblocks--;						      \
	  }								      \
      while (__nblocks != 0);                                                 \
      (nbytes_left) = (nbytes) % sizeof (op_t);                               \
    } while (0)

#undef	WORD_COPY_BWD
#define WORD_COPY_BWD(dst_ep, src_ep, nbytes_left, nbytes)                    \
  do									      \
    {									      \
      size_t __nblocks = (nbytes) / 32 + 1;                                   \
      switch ((nbytes) / sizeof (op_t) % 8)                                   \
	do								      \
	  {								      \
	    *--((op_t *) dst_ep) = *--((op_t *) src_ep);                      \
	  case 7:							      \
	    *--((op_t *) dst_ep) = *--((op_t *) src_ep);                      \
	  case 6:							      \
	    *--((op_t *) dst_ep) = *--((op_t *) src_ep);                      \
	  case 5:							      \
	    *--((op_t *) dst_ep) = *--((op_t *) src_ep);                      \
	  case 4:							      \
	    *--((op_t *) dst_ep) = *--((op_t *) src_ep);                      \
	  case 3:							      \
	    *--((op_t *) dst_ep) = *--((op_t *) src_ep);                      \
	  case 2:							      \
	    *--((op_t *) dst_ep) = *--((op_t *) src_ep);                      \
	  case 1:							      \
	    *--((op_t *) dst_ep) = *--((op_t *) src_ep);                      \
	  case 0:							      \
	    __nblocks--;						      \
	  }								      \
      while (__nblocks != 0);                                                 \
      (nbytes_left) = (nbytes) % sizeof (op_t);                               \
    } while (0)

#endif

void * memset(void * dstpp, int c, size_t len)
{
  long int dstp = (long int) dstpp;

  if (len >= 8)
    {
      size_t xlen;
      op_t cccc;

      cccc = (unsigned char) c;
      cccc |= cccc << 8;
      cccc |= cccc << 16;

      /* There are at least some bytes to set.
	 No need to test for LEN == 0 in this alignment loop.  */
      while (dstp % OPSIZ != 0)
	{
	  ((byte *) dstp)[0] = c;
	  dstp += 1;
	  len -= 1;
	}

      /* Write 8 `op_t' per iteration until less than 8 `op_t' remain.  */
      xlen = len / (OPSIZ * 8);
      while (xlen > 0)
	{
	  ((op_t *) dstp)[0] = cccc;
	  ((op_t *) dstp)[1] = cccc;
	  ((op_t *) dstp)[2] = cccc;
	  ((op_t *) dstp)[3] = cccc;
	  ((op_t *) dstp)[4] = cccc;
	  ((op_t *) dstp)[5] = cccc;
	  ((op_t *) dstp)[6] = cccc;
	  ((op_t *) dstp)[7] = cccc;
	  dstp += 8 * OPSIZ;
	  xlen -= 1;
	}
      len %= OPSIZ * 8;

      /* Write 1 `op_t' per iteration until less than OPSIZ bytes remain.  */
      xlen = len / OPSIZ;
      while (xlen > 0)
	{
	  ((op_t *) dstp)[0] = cccc;
	  dstp += OPSIZ;
	  xlen -= 1;
	}
      len %= OPSIZ;
    }

  /* Write the last few bytes.	*/
  while (len > 0)
    {
      ((byte *) dstp)[0] = c;
      dstp += 1;
      len -= 1;
    }

  return dstpp;
}

