/***************************************************************************

    tokenize.h

    Common definitions and macros for tokenizing definitions.

    Copyright Nicola Salmoria and the MAME Team.
    Visit http://mamedev.org for licensing and usage restrictions.

***************************************************************************/

#pragma once

#ifndef __TOKENIZE_H__
#define __TOKENIZE_H__


/***************************************************************************
    CONSTANTS
***************************************************************************/

/* tokens per item */
#define TOKENS_PER_PTR			(1)
#define TOKENS_PER_UINT32		(1)
#define TOKENS_PER_UINT64		(8 / sizeof(FPTR))
#define TOKENS_PER_ATTOTIME		(TOKENS_PER_UINT32 + TOKENS_PER_UINT64)



/***************************************************************************
    TYPE DEFINITIONS
***************************************************************************/

/* include this at the top of your union to get the standard fields */
#define TOKEN_COMMON_FIELDS		\
	FPTR			i;			\
	const char *	stringptr;	\
	const void *	voidptr;	\
	const UINT8 *	ui8ptr;		\
	const INT8 *	i8ptr;		\
	const UINT16 *	ui16ptr;	\
	const INT16 *	i16ptr;		\
	const UINT32 *	ui32ptr;	\
	const INT32 *	i32ptr;		\
	const UINT64 *	ui64ptr;	\
	const INT64 *	i64ptr;		\


/* generic_token can be used when there are no particularly special types */
typedef struct _generic_token generic_token;
struct _generic_token
{
	TOKEN_COMMON_FIELDS
};



/***************************************************************************
    MACROS
***************************************************************************/

/* ----- compile-time token generation macros ----- */

/* GCC and C99 compilers can use designated initializers for type safety */
#if (defined(__GNUC__) && (__GNUC__ >= 3) && !defined(__cplusplus)) || (defined(_STDC_VERSION__) && (__STDC_VERSION__ >= 199901L))
#define TOKEN_VALUE(field,a)	{ .field = (a) }
#else
#define TOKEN_VALUE(field,a)	{ (FPTR)(a) }
#endif

/* token output primitives */
/* note that regardless of the endianness, UINT64s are packed LSW first */
#define TOKEN_PTR(field,p)		TOKEN_VALUE(field, p)
#define TOKEN_STRING(p)			TOKEN_VALUE(stringptr, p)
#define TOKEN_UINT32(a)			TOKEN_VALUE(i, a)
#ifdef PTR64
#define TOKEN_UINT64(a)			TOKEN_VALUE(i, a)
#else
#define TOKEN_UINT64(a)			TOKEN_VALUE(i, (UINT32)(a)), TOKEN_VALUE(i, (UINT32)((a) >> 32))
#endif

/* mask a value to a fixed number of bits and then shift it */
#define SHIFT_AND_MASK32(val, bits, shift)		(((UINT32)(val) & ((1 << (bits)) - 1)) << (shift))
#define SHIFT_AND_MASK64(val, bits, shift)		(((UINT64)(val) & (((UINT64)1 << (bits)) - 1)) << (shift))

/* 32-bit integer packing */
#define TOKEN_UINT32_PACK1(val1, bits1) \
	TOKEN_UINT32(SHIFT_AND_MASK32((val1), (bits1), 0))

#define TOKEN_UINT32_PACK2(val1, bits1, val2, bits2) \
	TOKEN_UINT32(SHIFT_AND_MASK32((val1), (bits1), 0) | \
				 SHIFT_AND_MASK32((val2), (bits2), (bits1)))

#define TOKEN_UINT32_PACK3(val1, bits1, val2, bits2, val3, bits3) \
	TOKEN_UINT32(SHIFT_AND_MASK32((val1), (bits1), 0) | \
				 SHIFT_AND_MASK32((val2), (bits2), (bits1)) | \
				 SHIFT_AND_MASK32((val3), (bits3), (bits1)+(bits2)))

#define TOKEN_UINT32_PACK4(val1, bits1, val2, bits2, val3, bits3, val4, bits4) \
	TOKEN_UINT32(SHIFT_AND_MASK32((val1), (bits1), 0) | \
				 SHIFT_AND_MASK32((val2), (bits2), (bits1)) | \
				 SHIFT_AND_MASK32((val3), (bits3), (bits1)+(bits2)) | \
				 SHIFT_AND_MASK32((val4), (bits4), (bits1)+(bits2)+(bits3)))

/* 64-bit integer packing */
#define TOKEN_UINT64_PACK1(val1, bits1) \
	TOKEN_UINT64(SHIFT_AND_MASK64((val1), (bits1), 0))

#define TOKEN_UINT64_PACK2(val1, bits1, val2, bits2) \
	TOKEN_UINT64(SHIFT_AND_MASK64((val1), (bits1), 0) | \
				 SHIFT_AND_MASK64((val2), (bits2), (bits1)))

#define TOKEN_UINT64_PACK3(val1, bits1, val2, bits2, val3, bits3) \
	TOKEN_UINT64(SHIFT_AND_MASK64((val1), (bits1), 0) | \
				 SHIFT_AND_MASK64((val2), (bits2), (bits1)) | \
				 SHIFT_AND_MASK64((val3), (bits3), (bits1)+(bits2)))

#define TOKEN_UINT64_PACK4(val1, bits1, val2, bits2, val3, bits3, val4, bits4) \
	TOKEN_UINT64(SHIFT_AND_MASK64((val1), (bits1), 0) | \
				 SHIFT_AND_MASK64((val2), (bits2), (bits1)) | \
				 SHIFT_AND_MASK64((val3), (bits3), (bits1)+(bits2)) | \
				 SHIFT_AND_MASK64((val4), (bits4), (bits1)+(bits2)+(bits3)))



/* ----- run-time token extraction macros ----- */

/* token fetch and advance primitives */
#define TOKEN_GET_PTR(tp,field)		(((tp)++)->field)
#define TOKEN_GET_STRING(tp)		(((tp)++)->stringptr)
#define TOKEN_GET_UINT32(tp)		(((tp)++)->i)
#ifdef PTR64
#define TOKEN_EXTRACT_UINT64(tp,a)	do { (a) = (tp)->i; (tp)++; } while (0)
#else
#define TOKEN_EXTRACT_UINT64(tp,a)	do { (a) = (tp)[0].i | ((UINT64)(tp)[1].i << 32); (tp) += 2; } while (0)
#endif

/* token unfetch primitives */
#define TOKEN_UNGET_PTR(tp)			((tp)--)
#define TOKEN_UNGET_STRING(tp)		((tp)--)
#define TOKEN_UNGET_UINT32(tp)		((tp)--)
#define TOKEN_UNGET_UINT64(tp)		((tp) -= 8 / sizeof(FPTR))

/* token skip primitives */
#define TOKEN_SKIP_PTR(tp)			((tp)++)
#define TOKEN_SKIP_STRING(tp)		((tp)++)
#define TOKEN_SKIP_UINT32(tp)		((tp)++)
#define TOKEN_SKIP_UINT64(tp)		((tp) += 8 / sizeof(FPTR))

/* extract a value from a fixed number of bits; if bits is negative, treat it as a signed value */
#define UNSHIFT_AND_MASK32(src, val, bits, shift)	do { \
	if ((bits) < 0) \
		(val) = token_sign_extend32((src) >> (shift), (UINT8)-(bits)); \
	else \
		(val) = token_zero_extend32((src) >> (shift), (UINT8)(bits)); \
} while (0)

#define UNSHIFT_AND_MASK64(src, val, bits, shift)	do { \
	if ((bits) < 0) \
		(val) = token_sign_extend64((src) >> (shift), (UINT8)-(bits)); \
	else \
		(val) = token_zero_extend64((src) >> (shift), (UINT8)(bits)); \
} while (0)

/* cheesy inline absolute value */
#define TOKENABS(v)		(((v) < 0) ? -(v) : (v))

/* 32-bit integer unpacking */
#define TOKEN_GET_UINT32_UNPACK1(tp, val1, bits1) do { \
	UINT32 token32 = TOKEN_GET_UINT32(tp); \
	UNSHIFT_AND_MASK32(token32, val1, (bits1), 0); \
} while (0)

#define TOKEN_GET_UINT32_UNPACK2(tp, val1, bits1, val2, bits2) do { \
	UINT32 token32 = TOKEN_GET_UINT32(tp); \
	UINT8 shift = 0; \
	UNSHIFT_AND_MASK32(token32, val1, (bits1), shift); shift += TOKENABS(bits1); \
	UNSHIFT_AND_MASK32(token32, val2, (bits2), shift); \
} while (0)

#define TOKEN_GET_UINT32_UNPACK3(tp, val1, bits1, val2, bits2, val3, bits3) do { \
	UINT32 token32 = TOKEN_GET_UINT32(tp); \
	UINT8 shift = 0; \
	UNSHIFT_AND_MASK32(token32, val1, (bits1), shift); shift += TOKENABS(bits1); \
	UNSHIFT_AND_MASK32(token32, val2, (bits2), shift); shift += TOKENABS(bits2); \
	UNSHIFT_AND_MASK32(token32, val3, (bits3), shift); \
} while (0)

#define TOKEN_GET_UINT32_UNPACK4(tp, val1, bits1, val2, bits2, val3, bits3, val4, bits4) do { \
	UINT32 token32 = TOKEN_GET_UINT32(tp); \
	UINT8 shift = 0; \
	UNSHIFT_AND_MASK32(token32, val1, (bits1), shift); shift += TOKENABS(bits1); \
	UNSHIFT_AND_MASK32(token32, val2, (bits2), shift); shift += TOKENABS(bits2); \
	UNSHIFT_AND_MASK32(token32, val3, (bits3), shift); shift += TOKENABS(bits3); \
	UNSHIFT_AND_MASK32(token32, val4, (bits4), shift); \
} while (0)

/* 64-bit integer unpacking */
#define TOKEN_GET_UINT64_UNPACK1(tp, val1, bits1) do { \
	UINT64 token64; \
	TOKEN_EXTRACT_UINT64(tp, token64); \
	UNSHIFT_AND_MASK64(token64, val1, (bits1), 0); \
} while (0)

#define TOKEN_GET_UINT64_UNPACK2(tp, val1, bits1, val2, bits2) do { \
	UINT64 token64; \
	UINT8 shift = 0; \
	TOKEN_EXTRACT_UINT64(tp, token64); \
	UNSHIFT_AND_MASK64(token64, val1, (bits1), shift); shift += TOKENABS(bits1); \
	UNSHIFT_AND_MASK64(token64, val2, (bits2), shift); \
} while (0)

#define TOKEN_GET_UINT64_UNPACK3(tp, val1, bits1, val2, bits2, val3, bits3) do { \
	UINT64 token64; \
	UINT8 shift = 0; \
	TOKEN_EXTRACT_UINT64(tp, token64); \
	UNSHIFT_AND_MASK64(token64, val1, (bits1), shift); shift += TOKENABS(bits1); \
	UNSHIFT_AND_MASK64(token64, val2, (bits2), shift); shift += TOKENABS(bits2); \
	UNSHIFT_AND_MASK64(token64, val3, (bits3), shift); \
} while (0)

#define TOKEN_GET_UINT64_UNPACK4(tp, val1, bits1, val2, bits2, val3, bits3, val4, bits4) do { \
	UINT64 token64; \
	UINT8 shift = 0; \
	TOKEN_EXTRACT_UINT64(tp, token64); \
	UNSHIFT_AND_MASK64(token64, val1, (bits1), shift); shift += TOKENABS(bits1); \
	UNSHIFT_AND_MASK64(token64, val2, (bits2), shift); shift += TOKENABS(bits2); \
	UNSHIFT_AND_MASK64(token64, val3, (bits3), shift); shift += TOKENABS(bits3); \
	UNSHIFT_AND_MASK64(token64, val4, (bits4), shift); \
} while (0)



/***************************************************************************
    INLINE FUNCTIONS
***************************************************************************/

INLINE UINT32 token_zero_extend32(UINT32 val, UINT8 bits)
{
	return val & (((UINT32)1 << bits) - 1);
}

INLINE INT32 token_sign_extend32(UINT32 val, UINT8 bits)
{
	return (INT32)(val << (32 - bits)) >> (32 - bits);
}

INLINE UINT64 token_zero_extend64(UINT64 val, UINT8 bits)
{
	return val & (((UINT64)1 << bits) - 1);
}

INLINE INT64 token_sign_extend64(UINT64 val, UINT8 bits)
{
	return (INT64)(val << (64 - bits)) >> (64 - bits);
}

#endif
