/* vim: set filetype=c:
 *
 * string.h86  -  String handling routines for GCC
 *
 * Copyright (C) 2003-2007 Gero Kuhlmann   <gero@gkminix.han.de>
 *
 *  Many parts of this file have been taken from Linux sources which
 *  carry the following notice:
 *
 *		NO Copyright (C) 1991, 1992 Linus Torvalds,
 *		consider these trivial functions to be PD.
 *
 *  Most string routines use inline assembler code, if the symbol
 *  __USE_STRING_INLINES is defined. Otherwise, library routines get
 *  called instead.
 *
 *  NOTE: All routines in this file assume ES = DS, and DF cleared!!!!
 *
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: string.h86,v 1.11 2007/02/01 12:09:21 gkminix Exp $
 */

#ifndef I386_STRING_H86
#define I386_STRING_H86


/*
 **************************************************************************
 *
 * This file can not be included into assembler source
 */
#ifndef _ASSEMBLY_

#include <common.h86>
#include <i386/misc.h86>



/*
 **************************************************************************
 *
 * Copy a string
 */
__nb_inline__ char *_strcpy(char *dest, const char *src)
{
  __u16 d0, d1, d2;

  __asm__ __volatile__(
	"1:\n\tlodsb\n\t"
	"stosb\n\t"
	"testb %b2,%b2\n\t"
	"jne 1b"
	: "=&S" (d0), "=&D" (d1), "=&a" (d2)
	: "0" (_NP(src)), "1" (_NP(dest))
	: "memory", "cc");
  return(dest);
}

#ifdef __USE_STRING_INLINES
#define strcpy	_strcpy
#else
extern char *strcpy(char *dest, const char *src);
#endif



/*
 **************************************************************************
 *
 * Copy a string with a limited length
 */
__nb_inline__ char *_strncpy(char *dest, const char *src, size_t count)
{
  __u16 d0, d1, d2, d3;

  __asm__ __volatile__(
	"1:\n\tdecw %2\n\t"
	"js 2f\n\t"
	"lodsb\n\t"
	"stosb\n\t"
	"testb %b3,%b3\n\t"
	"jne 1b\n\t"
	"rep\n\t"
	"stosb\n"
	"2:"
	: "=&S" (d0), "=&D" (d1), "=&c" (d2), "=&a" (d3)
	: "0" (_NP(src)), "1" (_NP(dest)), "2" ((__u16)count)
	: "memory", "cc");
  return(dest);
}

#ifdef __USE_STRING_INLINES
#define strncpy	_strncpy
#else
extern char *strncpy(char *dest, const char *src, size_t count);
#endif



/*
 **************************************************************************
 *
 * Concatenate two strings together
 */
__nb_inline__ char *_strcat(char *dest, const char *src)
{
  __u16 d0, d1, d2, d3;

  __asm__ __volatile__(
	"repne\n\t"
	"scasb\n\t"
	"decw %1\n"
	"1:\n\tlodsb\n\t"
	"stosb\n\t"
	"testb %b2,%b2\n\t"
	"jne 1b"
	: "=&S" (d0), "=&D" (d1), "=&a" (d2), "=&c" (d3)
	: "0" (_NP(src)), "1" (_NP(dest)), "2" (0), "3" (0xffffffff)
	:"memory", "cc");
  return(dest);
}

#ifdef __USE_STRING_INLINES
#define strcat	_strcat
#else
extern char *strcat(char *dest, const char *src);
#endif



/*
 **************************************************************************
 *
 * Concatenate two strings together with a limited length
 */
__nb_inline__ char *_strncat(char *dest, const char *src, size_t count)
{
  __u16 d0, d1, d2, d3;

  __asm__ __volatile__(
	"repne\n\t"
	"scasb\n\t"
	"decw %1\n\t"
	"movw %8,%3\n"
	"1:\n\tdecw %3\n\t"
	"js 2f\n\t"
	"lodsb\n\t"
	"stosb\n\t"
	"testb %b2,%b2\n\t"
	"jne 1b\n"
	"2:\n\txorb %b2,%b2\n\t"
	"stosb"
	: "=&S" (d0), "=&D" (d1), "=&a" (d2), "=&c" (d3)
	: "0" (_NP(src)), "1" (_NP(dest)), "2" (0), "3" (0xffffffff),
							"g" ((__u16)count)
	: "memory", "cc");
  return(dest);
}

#ifdef __USE_STRING_INLINES
#define strncat	_strncat
#else
extern char *strncat(char *dest, const char *src, size_t count);
#endif



/*
 **************************************************************************
 *
 * Compare two strings
 */
__nb_inline__ short _strcmp(const char *cs, const char *ct)
{
  __u16 d0, d1;
  register __u16 __res;

  __asm__ __volatile__(
	"1:\n\tlodsb\n\t"
	"scasb\n\t"
	"jne 2f\n\t"
	"testb %b0,%b0\n\t"
	"jne 1b\n\t"
	"xorw %0,%0\n\t"
	"jmp 3f\n"
	"2:\n\tsbbw %0,%0\n\t"
	"orb $1,%b0\n"
	"3:"
	: "=a" (__res), "=&S" (d0), "=&D" (d1)
	: "1" (_NP(cs)), "2" (_NP(ct))
	: "cc");
  return((short)__res);
}

#ifdef __USE_STRING_INLINES
#define strcmp	_strcmp
#else
extern short strcmp(const char *cs, const char *ct);
#endif



/*
 **************************************************************************
 *
 * Compare two strings with limited length
 */
__nb_inline__ short _strncmp(const char *cs, const char *ct, size_t count)
{
  __u16 d0, d1, d2;
  register __u16 __res;

  __asm__ __volatile__(
	"1:\n\tdecw %3\n\t"
	"js 2f\n\t"
	"lodsb\n\t"
	"scasb\n\t"
	"jne 3f\n\t"
	"testb %b0,%b0\n\t"
	"jne 1b\n"
	"2:\n\txorw %0,%0\n\t"
	"jmp 4f\n"
	"3:\n\tsbbw %0,%0\n\t"
	"orb $1,%b0\n"
	"4:"
	: "=a" (__res), "=&S" (d0), "=&D" (d1), "=&c" (d2)
	: "1" (_NP(cs)), "2" (_NP(ct)), "3" ((__u16)count)
	: "cc");
  return((short)__res);
}

#ifdef __USE_STRING_INLINES
#define strncmp	_strncmp
#else
extern short strncmp(const char *cs, const char *ct, size_t count);
#endif



/*
 **************************************************************************
 *
 * Search a char within a string starting at the beginning
 */
__nb_inline__ char *_strchr(const char *s, char c)
{
  __u16 d0;
  register __u16 __res;

  __asm__ __volatile__(
	"movb %b0,%h0\n"
	"1:\n\tlodsb\n\t"
	"cmpb %h0,%b0\n\t"
	"je 2f\n\t"
	"testb %b0,%b0\n\t"
	"jne 1b\n\t"
	"movw $1,%1\n"
	"2:\n\tmovw %1,%0\n\t"
	"decw %0"
	: "=a" (__res), "=&S" (d0)
	: "1" (_NP(s)), "0" ((__u8)c)
	: "cc");
  return((char *)((unsigned long)__res));
}

#ifdef __USE_STRING_INLINES
#define strchr	_strchr
#else
extern char *strchr(const char *s, char c);
#endif



/*
 **************************************************************************
 *
 * Search a char within a string starting at the end
 */
__nb_inline__ char *_strrchr(const char *s, char c)
{
  __u16 d0, d1;
  register __u16 __res;

  __asm__ __volatile__(
	"movb %b2,%h2\n"
	"1:\n\tlodsb\n\t"
	"cmpb %h2,%b2\n\t"
	"jne 2f\n\t"
	"leaw -1(%1),%0\n"
	"2:\n\ttestb %b2,%b2\n\t"
	"jne 1b"
	: "=g" (__res), "=&S" (d0), "=&a" (d1)
	: "0" (0), "1" (_NP(s)), "2" ((__u8)c)
	: "cc");
  return((char *)((unsigned long)__res));
}

#ifdef __USE_STRING_INLINES
#define strrchr	_strrchr
#else
extern char *strrchr(const char *s, char c);
#endif



/*
 **************************************************************************
 *
 * Determine the length of a string
 */
__nb_inline__ size_t _strlen(const char * s)
{
  __u16 d0;
  register __u16 __res;

  __asm__ __volatile__(
	"repne\n\t"
	"scasb\n\t"
	"notw %0\n\t"
	"decw %0"
	: "=c" (__res), "=&D" (d0)
	: "1" (_NP(s)), "a" (0), "0" (0xffffffff)
	: "cc");
  return((__u16)__res);
}

#ifdef __USE_STRING_INLINES
#define strlen	_strlen
#else
extern size_t strlen(const char *s);
#endif



/*
 **************************************************************************
 *
 * Copy memory
 */
__nb_inline__ void *__memcpy(void *to, const void *from, size_t n)
{
  __u16 d0, d1, d2;

  __asm__ __volatile__(
	"shrw %0\n\t"
	"rep\n\tmovsw\n\t"
	"jnc 1f\n\t"
	"movsb\n"
	"1:"
	: "=&c" (d0), "=&D" (d1), "=&S" (d2)
	: "0" ((__u16)n), "1" (_NP(to)), "2" (_NP(from))
	: "memory", "cc");
  return(to);
}


/*
 * This looks horribly ugly, but the compiler can optimize it totally,
 * as the count is constant.
 */
__nb_inline__ void *__constant_memcpy(void *to, const void *from, size_t n)
{
	switch (n) {
		case 0:
			return(to);
		case 1:
			*(__u8 *)to = *(const __u8 *)from;
			return(to);
		case 2:
			*(__u16 *)to = *(const __u16 *)from;
			return(to);
		case 3:
			*(__u16 *)to = *(const __u16 *)from;
			*(2+(__u8 *)to) = *(2+(const __u8 *)from);
			return(to);
		case 4:
			*(__u32 *)to = *(const __u32 *)from;
			return(to);
		case 6:	/* for Ethernet addresses */
			*(__u32 *)to = *(const __u32 *)from;
			*(2+(__u16 *)to) = *(2+(const __u16 *)from);
			return(to);
		case 8:
			*(__u32 *)to = *(const __u32 *)from;
			*(1+(__u32 *)to) = *(1+(const __u32 *)from);
			return(to);
	}

#define COMMON(x) \
  __asm__ __volatile__( \
	"rep\n\tmovsw" \
	x \
	: "=&c" (d0), "=&D" (d1), "=&S" (d2) \
	: "0" ((__u16)(n / 2)), "1" (_NP(to)), "2" (_NP(from)) \
	: "memory", "cc");
  {
	__u16 d0, d1, d2;

	if ((n % 2) == 0) {
		COMMON("");
		return(to);
	} else {
		COMMON("\n\tmovsb");
		return(to);
	}
  }
  
#undef COMMON
}

 
#define _memcpy(t, f, n) \
(__builtin_constant_p(n) ? \
 __constant_memcpy((t),(f),(n)) : \
 __memcpy((t),(f),(n)))

#ifdef __USE_STRING_INLINES
#define memcpy	_memcpy
#else
extern void *memcpy(void *to, const void *from, size_t n);
#endif



/*
 **************************************************************************
 *
 * Set memory with a value
 */
__nb_inline__ void *__memset_generic(void *s, unsigned char c, size_t n)
{
  __u16 d0, d1;

  __asm__ __volatile__(
	"movb %%al,%%ah\n\t"
	"shrw %0\n\t"
	"rep\n\tstosb\n\t"
	"jnc 1f\n\t"
	"stosb\n\t"
	"1:"
	: "=&c" (d0), "=&D" (d1)
	: "a" ((__u8)c), "0" ((__u16)n), "1" (_NP(s))
	: "memory", "cc");
  return(s);
}


#define __constant_n_memset(s,c,n) __memset_generic((s),(c),(n))


/*
 * memset(x,0,y) is a reasonably common thing to do, so we want to fill
 * things 16 bits at a time even when we don't know the size of the
 * area at compile-time..
 */
__nb_inline__ void *__constant_c_memset(void *s, unsigned char c, size_t n)
{
  __u16 d0, d1, d2;

  d2 = ((c & 0xff) << 8) | (c & 0xff);
  __asm__ __volatile__(
	"shrw %0\n\t"
	"rep\n\tstosw\n\t"
	"jnc 1f\n\t"
	"stosb\n\t"
	"1:"
	: "=&c" (d0), "=&D" (d1)
	: "a" (d2), "0" ((__u16)n), "1" (_NP(s))
	: "memory", "cc");
  return(s);	
}


/*
 * This looks horribly ugly, but the compiler can optimize it totally,
 * as we by now know that both pattern and count is constant..
 */
__nb_inline__ void *__constant_c_and_n_memset(void *s, unsigned char c, size_t n)
{
	switch (n) {
		case 0:
			return(s);
		case 1:
			*(__u8 *)s = c;
			return(s);
		case 2:
			*(__u16 *)s = ((c & 0xff) << 8) | (c & 0xff);
			return(s);
		case 3:
			*(__u16 *)s = ((c & 0xff) << 8) | (c & 0xff);
			*(2+(__u8 *)s) = c;
			return(s);
		case 4:
			*(__u32 *)s = ((c & 0xff) << 24) | ((c & 0xff) << 16) |
			              ((c & 0xff) << 8) | (c & 0xff);
		case 6:	/* for Ethernet addresses */
			*(__u32 *)s = ((c & 0xff) << 24) | ((c & 0xff) << 16) |
			              ((c & 0xff) << 8) | (c & 0xff);
			*(2+(__u16 *)s) = ((c & 0xff) << 8) | (c & 0xff);
			return(s);
		case 8:
			*(__u32 *)s = ((c & 0xff) << 24) | ((c & 0xff) << 16) |
			              ((c & 0xff) << 8) | (c & 0xff);
			*(1+(__u32 *)s) = ((c & 0xff) << 24) | ((c & 0xff) << 16) |
			                  ((c & 0xff) << 8) | (c & 0xff);
			return(s);
	}
#define COMMON(x) \
  __asm__ __volatile__( \
	"rep\n\tstosw" \
	x \
	: "=&c" (d0), "=&D" (d1) \
	: "a" (d2),"0" ((__u16)(n / 2)),"1" (_NP(s)) \
	: "memory", "cc")
  {
	__u16 d0, d1, d2;

	d2 = ((c & 0xff) << 8) | (c & 0xff);
	if ((n % 2) == 0) {
		COMMON("");
		return(s);
	} else {
		COMMON("\n\tstosb");
		return(s);
	}
  }
  
#undef COMMON
}


#define __constant_c_x_memset(s, c, n) \
(__builtin_constant_p(n) ? \
 __constant_c_and_n_memset((s),(c),(n)) : \
 __constant_c_memset((s),(c),(n)))


#define __memset(s, c, n) \
(__builtin_constant_p(n) ? \
 __constant_n_memset((s),(c),(n)) : \
 __memset_generic((s),(c),(n)))

#define _memset(s, c, n) \
(__builtin_constant_p(c) ? \
 __constant_c_x_memset((s),(c),(n)) : \
 __memset((s),(c),(n)))

#ifdef __USE_STRING_INLINES
#define memset	_memset
#else
extern void *memset(void *s, unsigned char c, size_t n);
#endif



/*
 **************************************************************************
 *
 * Compare two memory blocks
 */
__nb_inline__ short _memcmp(const void *cs, const void *ct, size_t n)
{
  __u16 d0, d1, d2;
  register __u16 __res;

  __asm__ __volatile__(
	"repe\n\tcmpsb\n\t"
	"jne 2f\n\t"
	"4:\n\txorw %0,%0\n\t"
	"jmp 3f\n"
	"2:\n\tsbbw %0,%0\n\t"
	"orb $1,%b0\n"
	"3:"
	: "=a" (__res), "=&S" (d0), "=&D" (d1), "=&c" (d2)
	: "1" (_NP(cs)), "2" (_NP(ct)), "3" ((__u16)n)
	: "cc");
  return((short)__res);
}

#ifdef __USE_STRING_INLINES
#define memcmp	_memcmp
#else
extern short memcmp(const void *cs, const void *ct, size_t n);
#endif



/*
 **************************************************************************
 *
 * Copy from far memory into far memory
 */
__nb_inline__ void _fmemcpy(__fptr tomem, const __fptr frommem, size_t n)
{
  __u16 d0, d1, d2;

  __asm__ __volatile__(
	"pushw %%ds\n\t"
	"pushw %%es\n\t"
	"les %4,%%di\n\t"
	"lds %5,%%si\n\t"
	"shrw %0\n\t"
	"rep\n\tmovsw\n\t"
	"jnc 1f\n\t"
	"movsb\n"
	"1:\n\tpopw %%es\n\t"
	"popw %%ds\n\t"
	: "=&c" (d0), "=&D" (d1), "=&S" (d2)
	: "0" ((__u16)n), "m" ((__u32)tomem), "m" ((__u32)frommem)
	: "memory", "cc");
}

#ifdef __USE_STRING_INLINES
#define fmemcpy	_fmemcpy
#else
extern void fmemcpy(__fptr tomem, const __fptr frommem, size_t n);
#endif



/*
 **************************************************************************
 *
 * Copy from/to extended memory
 */
__nb_inline__ short movemem(__lptr src, __lptr dest, __long n)
{
  __u32 d0, d1;
  register __u16 __res;

  __asm__ __volatile__(
	"call movmem\n\t"
	"setnc %b0\n\t"
	"cbtw"
	: "=&a" (__res), "=&b" (d0), "=&c" (d1)
	: "0" ((__u32)src), "1" ((__u32)dest), "2" ((__u32)n)
	: "memory", "cc");
  return((short)__res);
}

#define lmove(d,s,n)	movemem(far2linear(MK_FP(getds(), (int)(s))), \
						(__lptr)(d), (__long)(n))


#endif /* _ASSEMBLY_ */

#endif

