/* ================================================================================== */
/* Copyright (c) 1998-1999 3Com Corporation or its subsidiaries. All rights reserved. */
/* ================================================================================== */

#include "EmulatorCommon.h"
#include "UAE_Utils.h"


// ======================================================================
//	Private functions
// ======================================================================

// ---------------------------------------------------------------------------
// The functions in this file work with two kinds of "pointers": uaecptr's
// and void*'s.  Accessing memory for each kind of pointer is different.
// Since the functions are template functions, we can't know at the time we
// write the function what kind of pointer we'll be dealing with, so we can't
// hard-code the memory-accessing method.  Instead, we provide all of these
// inline accessors and let the compiler sort it out.
// ---------------------------------------------------------------------------

inline char	_get_byte(const void* p)
	{ return *(char*) p; }

inline char	_get_byte(uaecptr p)
	{ return (char) get_byte(p); }

inline void	_put_byte(void* p, const uae_u8 v)
	{ *(uae_u8*) p = v; }

inline void	_put_byte(uaecptr p, const uae_u8 v)
	{ put_byte(p, v); }

inline uae_u16	_get_word(const void* p)
	{ return *(uae_u16*) p; }

inline uae_u16	_get_word(uaecptr p)
	{ return (uae_u16) get_word(p); }

inline void	_put_word(void* p, const uae_u16 v)
	{ *(uae_u16*) p = v; }

inline void	_put_word(uaecptr p, const uae_u16 v)
	{ put_word(p, v); }

inline uae_u32	_get_long(const void* p)
	{ return *(uae_u32*) p; }

inline uae_u32	_get_long(uaecptr p)
	{ return (uae_u32) get_word(p); }

inline void	_put_long(void* p, const uae_u32 v)
	{ *(uae_u32*) p = v; }

inline void	_put_long(uaecptr p, const uae_u32 v)
	{ put_long(p, v); }

inline const void* _get_real_address(const void* p)
	{ return p; }

inline const void* _get_real_address(uaecptr p)
	{ return get_real_address(p); }

template <class T>
inline void _add_delta (T*& v, long delta)
{
	v = (T*) (((char*) v) + delta);
}

inline void _add_delta (uaecptr& v, long delta)
{
	v += delta;
}

template <class T>
inline void _increment (T& v)
{
	_add_delta (v, 1);
}

template <class T>
inline void _decrement (T& v)
{
	_add_delta (v, -1);
}


#pragma mark -

/***********************************************************************
 *
 * FUNCTION:	uae_memset
 *
 * DESCRIPTION:	Same as Std C Library memset function.
 *
 * PARAMETERS:	Same as Std C Library memset function.
 *
 * RETURNED:	Same as Std C Library memset function.
 *
 ***********************************************************************/

uaecptr		uae_memset(uaecptr dst, int val, size_t len)
{
	uaecptr		q = dst;

	uae_u32	longVal = val;
	longVal |= (longVal << 8);
	longVal |= (longVal << 16);

	mem_put_func	longPutter = get_mem_bank(dst).lput;
	mem_put_func	bytePutter = get_mem_bank(dst).bput;

	while ((q & 3) && len > 0)		// while there are leading bytes
	{
		bytePutter(q, val);
		q += sizeof (char);
		len -= sizeof (char);
	}

	while (len >= sizeof (long))	// while there are middle longs
	{
		longPutter(q, longVal);
		q += sizeof (long);
		len -= sizeof (long);
	}

	while (len > 0)					// while there are trailing bytes
	{
		bytePutter(q, val);
		q += sizeof (char);
		len -= sizeof (char);
	}

	return dst;
}


/***********************************************************************
 *
 * FUNCTION:	uae_memchr
 *
 * DESCRIPTION:	Same as Std C Library memchr function.
 *
 * PARAMETERS:	Same as Std C Library memchr function.
 *
 * RETURNED:	Same as Std C Library memchr function.
 *
 ***********************************************************************/

uaecptr	uae_memchr(uaecptr src, int val, size_t len)
{
	uaecptr p = src;

	++len;

	while (--len)
	{
		if (_get_byte (p) == val)
			return p;

		_increment (p);
	}

	return UAE_NULL;
}


/***********************************************************************
 *
 * FUNCTION:	uae_memcmp
 *
 * DESCRIPTION:	Same as Std C Library memcmp function.
 *
 * PARAMETERS:	Same as Std C Library memcmp function.
 *
 * RETURNED:	Same as Std C Library memcmp function.
 *
 ***********************************************************************/

template <class T1, class T2>
int		uae_memcmp(T1 src1, T2 src2, size_t len)
{
	T1	p1 = src1;
	T2	p2 = src2;

	++len;

	while (--len)
	{
		unsigned char	ch1 = _get_byte (p1);
		unsigned char	ch2 = _get_byte (p2);
		
		if (ch1 != ch2)
			return (ch1 < ch2) ? -1 : 1;

		_increment (p1);
		_increment (p2);
	}

	return 0;
}

	// Instantiate uae_memcmp's that work with:
	//
	//			dest		source
	//			------		------
	//			void*		uaecptr
	//			const void*	uaecptr
	//			uaecptr		void*
	//			uaecptr		const void*
	//			uaecptr		uaecptr

template int		uae_memcmp<void*, uaecptr>			(void* dst, uaecptr src, size_t len);
template int		uae_memcmp<const void*, uaecptr>	(const void* dst, uaecptr src, size_t len);
template int		uae_memcmp<uaecptr, void*>			(uaecptr dst, void* src, size_t len);
template int		uae_memcmp<uaecptr, const void*>	(uaecptr dst, const void* src, size_t len);
template int		uae_memcmp<uaecptr, uaecptr>		(uaecptr dst, uaecptr src, size_t len);


/***********************************************************************
 *
 * FUNCTION:	uae_memcpy
 *
 * DESCRIPTION:	Same as Std C Library memcpy function.
 *
 * PARAMETERS:	Same as Std C Library memcpy function.
 *
 * RETURNED:	Same as Std C Library memcpy function.
 *
 ***********************************************************************/

template <class T1, class T2>
T1		uae_memcpy (T1 dst, T2 src, size_t len)
{
	T1		q = dst;
	T2		p = src;

	while (len--)
	{
		_put_byte(q, _get_byte(p));
		_increment (q);
		_increment (p);
	}

	return dst;
}

	// Instantiate uae_memcpy's that work with:
	//
	//			dest		source
	//			------		------
	//			void*		uaecptr
	//			uaecptr		void*
	//			uaecptr		const void*
	//			uaecptr		uaecptr

template void*		uae_memcpy<void*, uaecptr>			(void* dst, uaecptr src, size_t len);
template uaecptr	uae_memcpy<uaecptr, void*>			(uaecptr dst, void* src, size_t len);
template uaecptr	uae_memcpy<uaecptr, const void*>	(uaecptr dst, const void* src, size_t len);
template uaecptr	uae_memcpy<uaecptr, uaecptr>		(uaecptr dst, uaecptr src, size_t len);


/***********************************************************************
 *
 * FUNCTION:	uae_memmove
 *
 * DESCRIPTION:	Same as Std C Library memmove function.
 *
 * PARAMETERS:	Same as Std C Library memmove function.
 *
 * RETURNED:	Same as Std C Library memmove function.
 *
 ***********************************************************************/

template <class T1, class T2>
T1		uae_memmove (T1 dst, T2 src, size_t len)
{
	T1		q = dst;
	T2		p = src;

	Bool	backward = _get_real_address(dst) <= _get_real_address(src);

	if (backward)
	{
		while (len--)
		{
			_put_byte(q, _get_byte(p));
			_increment (q);
			_increment (p);
		}
	}
	else
	{
		_add_delta (q, len);
		_add_delta (p, len);

		while (len--)
		{
			_decrement (q);
			_decrement (p);
			_put_byte(q, _get_byte(p));
		}
	}

	return dst;
}

	// Instantiate uae_memmove's that work with:
	//
	//			dest		source
	//			------		------
	//			void*		uaecptr
	//			uaecptr		void*
	//			uaecptr		const void*
	//			uaecptr		uaecptr

template void*		uae_memmove<void*, uaecptr>			(void* dst, uaecptr src, size_t len);
template uaecptr	uae_memmove<uaecptr, void*>			(uaecptr dst, void* src, size_t len);
template uaecptr	uae_memmove<uaecptr, const void*>	(uaecptr dst, const void* src, size_t len);
template uaecptr	uae_memmove<uaecptr, uaecptr>		(uaecptr dst, uaecptr src, size_t len);


#pragma mark -

/***********************************************************************
 *
 * FUNCTION:	uae_strlen
 *
 * DESCRIPTION:	Same as Std C Library strlen function.
 *
 * PARAMETERS:	Same as Std C Library strlen function.
 *
 * RETURNED:	Same as Std C Library strlen function.
 *
 ***********************************************************************/

size_t	uae_strlen(uaecptr str)
{
    uaecptr	eos = str;

    while (_get_byte(eos))
    	_increment (eos);

    return ((size_t) (eos - str));
}


/***********************************************************************
 *
 * FUNCTION:	uae_strcpy
 *
 * DESCRIPTION:	Same as Std C Library strcpy function.
 *
 * PARAMETERS:	Same as Std C Library strcpy function.
 *
 * RETURNED:	Same as Std C Library strcpy function.
 *
 ***********************************************************************/

template <class T1, class T2>
T1	uae_strcpy(T1 dst, T2 src)
{
	T1		q = dst;
	T2		p = src;
	char	ch;

	do {
		ch = _get_byte (p);
		_increment (p);
		_put_byte (q, ch);
		_increment (q);
	} while (ch);

	return dst;
}

	// Instantiate uae_strcpy's that work with:
	//
	//			dest		source
	//			------		------
	//			char*		uaecptr
	//			uaecptr		char*
	//			uaecptr		const char*
	//			uaecptr		uaecptr

template char*		uae_strcpy<char*, uaecptr>			(char* dst, uaecptr src);
template uaecptr	uae_strcpy<uaecptr, char*>			(uaecptr dst, char* src);
template uaecptr	uae_strcpy<uaecptr, const char*>	(uaecptr dst, const char* src);
template uaecptr	uae_strcpy<uaecptr, uaecptr>		(uaecptr dst, uaecptr src);


/***********************************************************************
 *
 * FUNCTION:	uae_strncpy
 *
 * DESCRIPTION:	Same as Std C Library strncpy function.
 *
 * PARAMETERS:	Same as Std C Library strncpy function.
 *
 * RETURNED:	Same as Std C Library strncpy function.
 *
 ***********************************************************************/

template <class T1, class T2>
T1	uae_strncpy(T1 dst, T2 src, size_t len)
{
	T1		q = dst;
	T2		p = src;

	++len;

	while (--len)
	{
		char	ch = _get_byte(p);
		_increment (p);

		_put_byte (q, ch);
		_increment (q);

		if (!ch)
		{
			while (--len)
			{
				_put_byte (q, 0);
				_increment (q);
			}

			break;
		}
	}

	return dst;
}

	// Instantiate uae_strncpy's that work with:
	//
	//			dest		source
	//			------		------
	//			char*		uaecptr
	//			uaecptr		char*
	//			uaecptr		const char*
	//			uaecptr		uaecptr

template char*		uae_strncpy<char*, uaecptr>			(char* dst, uaecptr src, size_t len);
template uaecptr	uae_strncpy<uaecptr, char*>			(uaecptr dst, char* src, size_t len);
template uaecptr	uae_strncpy<uaecptr, const char*>	(uaecptr dst, const char* src, size_t len);
template uaecptr	uae_strncpy<uaecptr, uaecptr>		(uaecptr dst, uaecptr src, size_t len);


/***********************************************************************
 *
 * FUNCTION:	uae_strcat
 *
 * DESCRIPTION:	Same as Std C Library strcat function.
 *
 * PARAMETERS:	Same as Std C Library strcat function.
 *
 * RETURNED:	Same as Std C Library strcat function.
 *
 ***********************************************************************/

template <class T1, class T2>
T1	uae_strcat(T1 dst, T2 src)
{
	T1	q = dst;
	T2	p = src;
	
	while (_get_byte (q))
		_increment(q);
	
	_decrement (q);

	while (_get_byte (p))
	{
		_put_byte (q, _get_byte (p));
		_increment(q);
		_increment(p);
	}

	_put_byte (q, 0);

	return dst;
}

	// Instantiate uae_strcat's that work with:
	//
	//			dest		source
	//			------		------
	//			char*		uaecptr
	//			uaecptr		char*
	//			uaecptr		const char*
	//			uaecptr		uaecptr

template char*		uae_strcat<char*, uaecptr>			(char* dst, uaecptr src);
template uaecptr	uae_strcat<uaecptr, char*>			(uaecptr dst, char* src);
template uaecptr	uae_strcat<uaecptr, const char*>	(uaecptr dst, const char* src);
template uaecptr	uae_strcat<uaecptr, uaecptr>		(uaecptr dst, uaecptr src);


/***********************************************************************
 *
 * FUNCTION:	uae_strncat
 *
 * DESCRIPTION:	Same as Std C Library strncat function.
 *
 * PARAMETERS:	Same as Std C Library strncat function.
 *
 * RETURNED:	Same as Std C Library strncat function.
 *
 ***********************************************************************/

template <class T1, class T2>
T1	uae_strncat(T1 dst, T2 src, size_t len)
{
	T1		q = dst;
	T2		p = src;

	while (_get_byte (q))
		_increment (q);

	_decrement (q);
	++len;

	while (--len)
	{
		char	ch = _get_byte(p);
		_increment (p);

		_put_byte (q, ch);
		_increment (q);

		if (!ch)
			return dst;
	}

	_put_byte (q, 0);

	return dst;
}

	// Instantiate uae_strncat's that work with:
	//
	//			dest		source
	//			------		------
	//			char*		uaecptr
	//			uaecptr		char*
	//			uaecptr		const char*
	//			uaecptr		uaecptr

template char*		uae_strncat<char*, uaecptr>			(char* dst, uaecptr src, size_t len);
template uaecptr	uae_strncat<uaecptr, char*>			(uaecptr dst, char* src, size_t len);
template uaecptr	uae_strncat<uaecptr, const char*>	(uaecptr dst, const char* src, size_t len);
template uaecptr	uae_strncat<uaecptr, uaecptr>		(uaecptr dst, uaecptr src, size_t len);


/***********************************************************************
 *
 * FUNCTION:	uae_strcmp
 *
 * DESCRIPTION:	Same as Std C Library strcmp function.
 *
 * PARAMETERS:	Same as Std C Library strcmp function.
 *
 * RETURNED:	Same as Std C Library strcmp function.
 *
 ***********************************************************************/

template <class T1, class T2>
int	uae_strcmp(T1 dst, T2 src)
{
	T1	p1 = dst;
	T2	p2 = src;
	
	while (1)
	{
		unsigned char	c1 = _get_byte (p1);
		unsigned char	c2 = _get_byte (p2);

		if (c1 != c2)
			return (c1 - c2);
		else if (!c1)
			break;

		_increment (p1);
		_increment (p2);
	}

	return 0;
}

	// Instantiate uae_strcmp's that work with:
	//
	//			dest		source
	//			------		------
	//			char*		uaecptr
	//			const char*	uaecptr
	//			uaecptr		char*
	//			uaecptr		const char*
	//			uaecptr		uaecptr

template int		uae_strcmp<char*, uaecptr>			(char* dst, uaecptr src);
template int		uae_strcmp<const char*, uaecptr>	(const char* dst, uaecptr src);
template int		uae_strcmp<uaecptr, char*>			(uaecptr dst, char* src);
template int		uae_strcmp<uaecptr, const char*>	(uaecptr dst, const char* src);
template int		uae_strcmp<uaecptr, uaecptr>		(uaecptr dst, uaecptr src);


/***********************************************************************
 *
 * FUNCTION:	uae_strncmp
 *
 * DESCRIPTION:	Same as Std C Library strncmp function.
 *
 * PARAMETERS:	Same as Std C Library strncmp function.
 *
 * RETURNED:	Same as Std C Library strncmp function.
 *
 ***********************************************************************/

template <class T1, class T2>
int	uae_strncmp(T1 dst, T2 src, size_t len)
{
	T1	p1 = dst;
	T2	p2 = src;
	
	++len;
	
	while (--len)
	{
		unsigned char	c1 = _get_byte (p1);
		unsigned char	c2 = _get_byte (p2);

		if (c1 != c2)
			return (c1 - c2);
		else if (!c1)
			break;

		_increment (p1);
		_increment (p2);
	}

	return 0;
}

	// Instantiate uae_strncmp's that work with:
	//
	//			dest		source
	//			------		------
	//			char*		uaecptr
	//			const char*	uaecptr
	//			uaecptr		char*
	//			uaecptr		const char*
	//			uaecptr		uaecptr

template int		uae_strncmp<char*, uaecptr>			(char* dst, uaecptr src, size_t len);
template int		uae_strncmp<const char*, uaecptr>	(const char* dst, uaecptr src, size_t len);
template int		uae_strncmp<uaecptr, char*>			(uaecptr dst, char* src, size_t len);
template int		uae_strncmp<uaecptr, const char*>	(uaecptr dst, const char* src, size_t len);
template int		uae_strncmp<uaecptr, uaecptr>		(uaecptr dst, uaecptr src, size_t len);
