Coherent4.2.10/include/common/_canon.h

/* (-lgl
 *	Coherent 386 release 4.2
 *	Copyright (c) 1982, 1993 by Mark Williams Company.
 *	All rights reserved. May not be copied without permission.
 *	For copying permission and licensing info, write licensing@mwc.com
 -lgl) */

#ifndef	__COMMON__CANON_H__
#define	__COMMON__CANON_H__

/*
 * This file contains definitions related to canonicalization of numeric
 * formats. Routines are provided for conversions between the native format
 * and numerous other canonical forms, with varying argument patterns so that
 * conversion can be done in a space- and/or time-efficient manner. In
 * particular, separate versions for each routine exists that operate on an
 * lvalue to return a converted value, on an rvalue to return a converted
 * value, and to perform in-place conversion on an lvalue, so that the most
 * efficient machine idioms can be used for particular target machines.
 * The routines in this file are oriented towards systems with 32-bit long-
 * integer arithmetic, but extensions should be trivial.
 */

#include <common/feature.h>
#include <common/_limits.h>

#if	__CHAR_BIT != 8
# error	The canonicalization system only applies to octet-oriented machines
#endif

/*
 * We begin by defining a basic type for the lvalue-oriented manipulations and
 * some fundamental concepts and facilities that build on that to create a
 * bottom layer to the system. We explain the operation of the system entirely
 * in terms of manipulations of octets.
 *
 * We begin by defining the basic notation for describing formats; the basic
 * parameter for a data format is how the octets in the numbers in that format
 * are laid out, which we specify relative to the lowest machine address
 * occupied by the datum (many canonical data formats are based on the notion
 * of transmission order, and we typically expect that "is transmitted before"
 * maps directly onto "has a lower address than" for a structure laid out in
 * machine memory).
 *
 * So, for an M68K, the index of the most-significant byte of a native-format
 * number is always zero, whereas for an Intel-format number the bytes (and
 * thus their indexes) are reversed. If we encode the index of the byte in the
 * canonical order as a (machine-independent) number with each index taking
 * up a byte, then we get a value which when stored in that format will have
 * the values 0, 1, 2, ... stored in consecutive octets of machine memory.
 *
 * The nice thing about this encoding is that is thus reflective; if we have
 * a way of transforming abstract numbers according to this "map", we can
 * apply the transformation to the maps themselves to generate new maps which
 * can be used to encode other numbers or maps. This enables us to better deal
 * with the potential n^2 nature of the conversions by dynamically composing
 * the maps.
 */

typedef unsigned char *	__canon_t;

#define	__IDENTITY_16_MAP	0x0100U
#define __REVERSE_16_MAP	0x0001U
#define	__I386_16_MAP		__IDENTITY_16_MAP
#define	__M68K_16_MAP		__REVERSE_16_MAP
#define	__NET_16_MAP		__REVERSE_16_MAP
#define	__OCOH_16_MAP		__IDENTITY_16_MAP

#define	__IDENTITY_32_MAP	0x03020100UL
#define	__REVERSE_32_MAP	0x00010203UL
#define	__SWAP16_32_MAP		0x01000302UL
#define	__I386_32_MAP		__IDENTITY_32_MAP
#define	__M68K_32_MAP		__REVERSE_32_MAP
#define	__NET_32_MAP		__REVERSE_32_MAP
#define	__OCOH_32_MAP		__SWAP16_32_MAP


/*
 * The following primitive mapping functions use a map, and come in two
 * flavors; r-value oriented, and l-value oriented.  The r-value-oriented
 * transformations have the special property of using only operations that
 * are permitted in the restricted form of integral constant expression that
 * can be used in #if-expressions. Applying the r-value transformations to
 * constants yields other constants.
 *
 * Note that the fundamental transformations have two (nearly) equivalent
 * forms, of which I find the recursive more aesthetically pleasing, so that
 * is the default form. We leave both in here for your amusement.
 */

#define	__OCTET_N_OF_R(r,n)	(((r) >> ((n) * 8)) & 0xFFU)
#define	__OCTET_N_OF_L(l,n)	(((__canon_t) & (l)) [n])
#define	__MAKE_OCTET_N(o,n)	((o) << ((n) * 8))

#define	__CONVERT_OCTET_N_OF_R_VIA_MAP0(r,m,n) \
		__MAKE_OCTET_N (__OCTET_N_OF_R (r, n), __OCTET_N_OF_R (m, n))

#define	__CONVERT_OCTET_N_OF_R_VIA_MAP1(r,m,n) \
		__MAKE_OCTET_N (__OCTET_N_OF_R (r, __OCTET_N_OF_R (m, n)), n)

#define	__CONVERT_OCTET_N_OF_L_VIA_MAP0(l,m,n) \
		__MAKE_OCTET_N (__OCTET_N_OF_L (l, n), __OCTET_N_OF_R (m, n))

#define	__CONVERT_OCTET_N_OF_L_VIA_MAP1(l,m,n) \
		__MAKE_OCTET_N (__OCTET_N_OF_L (l, __OCTET_N_OF_R (m, n)), n)

#define	__CONVERT_R_16(r,m) \
		(__CONVERT_OCTET_N_OF_R_VIA_MAP1 (r, m, 0) | \
		 __CONVERT_OCTET_N_OF_R_VIA_MAP1 (r, m, 1))

#define	__CONVERT_L_16(l,m) \
		(__CONVERT_OCTET_N_OF_L_VIA_MAP1 (l, m, 0) | \
		 __CONVERT_OCTET_N_OF_L_VIA_MAP1 (l, m, 1))

#define	__CONVERT_R_32(r,m) \
		(__CONVERT_OCTET_N_OF_R_VIA_MAP1 (r, m, 0) | \
		 __CONVERT_OCTET_N_OF_R_VIA_MAP1 (r, m, 1) | \
		 __CONVERT_OCTET_N_OF_R_VIA_MAP1 (r, m, 2) | \
		 __CONVERT_OCTET_N_OF_R_VIA_MAP1 (r, m, 3))

#define	__CONVERT_L_32(l,m) \
		(__CONVERT_OCTET_N_OF_L_VIA_MAP1 (l, m, 0) | \
		 __CONVERT_OCTET_N_OF_L_VIA_MAP1 (l, m, 1) | \
		 __CONVERT_OCTET_N_OF_L_VIA_MAP1 (l, m, 2) | \
		 __CONVERT_OCTET_N_OF_L_VIA_MAP1 (l, m, 3))

/*
 * Here, we use rather more specific feature-tests to see about escaping to
 * special hand-coded routines or inlines, which is highly translator-specific
 * in addition to being machine-specific.
 */

#if	__GNUC__ && _I386

#if	__SHRT_BIT != 16 || __LONG_BIT != 32
# error	For GCC on i386, short should be 16 bits and a long should be 32.
#endif

#include <common/ccompat.h>
#include <common/xdebug.h>

/*
 * We supply two versions of some of the following depending on whether or not
 * you care about not being able to use the %ebp, %esi, and %edi registers as
 * operands.
 */

__LOCAL__ __INLINE__ unsigned short __swap_bytes (unsigned short _number) {
	unsigned short	_result;
#if	1
	__NON_ISO (asm) ("rolw $8, %0" : "=r" (_result) : "0" (_number));
#else
	__NON_ISO (asm) ("xchg %h0, %b0" : "=q" (_result) : "0" (_number));
#endif
	return _result;
}

__LOCAL__ __INLINE__ unsigned long __swap_words (unsigned long _number) {
	unsigned long	_result;
	__NON_ISO (asm) ("roll $16, %0" : "=r" (_result) : "0" (_number));
	return _result;
}

/*
 * On the i486 processor we have the BSWAP instruction, but the following
 * works on the i386 as well.
 */

__LOCAL__ __INLINE__ unsigned long __reverse_long (unsigned long _number) {
	unsigned long	_result;
#if	1
	__NON_ISO (asm) ("rolw $8, %0\n"
			 "roll $16, %0\n"
			 "rolw $8, %0\n" : "=r" (_result) : "0" (_number));
#else
	__NON_ISO (asm) ("xchg %h0, %b0\n"
			 "rorl $16, %0\n"
			 "xchg %h0, %b0\n" : "=q" (_result) : "0" (_number));
#endif
	return _result;
}

#undef	__CONVERT_L_16
#define	__CONVERT_L_16(l,m) \
		((m) == __REVERSE_16_MAP ? __swap_bytes (l) : \
		 __CONVERT_OCTET_N_OF_L_VIA_MAP1 (l, m, 0) | \
		 __CONVERT_OCTET_N_OF_L_VIA_MAP1 (l, m, 1))

#undef	__CONVERT_L_32
#define	__CONVERT_L_32(l,m) \
		((m) == __SWAP16_32_MAP ? __swap_words (l) : \
		 (m) == __REVERSE_32_MAP ? __reverse_long (l) : \
		 __CONVERT_OCTET_N_OF_L_VIA_MAP1 (l, m, 0) | \
		 __CONVERT_OCTET_N_OF_L_VIA_MAP1 (l, m, 1) | \
		 __CONVERT_OCTET_N_OF_L_VIA_MAP1 (l, m, 2) | \
		 __CONVERT_OCTET_N_OF_L_VIA_MAP1 (l, m, 3))

#endif	/* __GNUC__ && _I386 */


/*
 * Now, use a variety of feature-tests to figure out the native format for the
 * host for which we are compiling.
 */

#if	__MSDOS__ || _I386

# define	__NATIVE_16_MAP		__I386_16_MAP
# define	__NATIVE_32_MAP		__I386_32_MAP

#else

# error	What is the native endianness of your system?

#endif

#define	__CANON_FOO(value,l_or_r,type, map,ident) \
		((map) == (ident) ? value : \
			__CONCAT4 (__CONVERT_, l_or_r, _, type) \
				(value, map))

#define	__CANONICALIZE(value,l_or_r,machine,type) \
		__CANON_FOO (value, l_or_r, type, \
			     __CONCAT (__CONVERT_R_, type) \
				(__CONCAT3 (__NATIVE_, type, _MAP), \
				 __CONCAT4 (machine, _, type, _MAP)), \
			     __CONCAT3 (__IDENTITY_, type, _MAP))


#endif	/* ! defined (__COMMON__CANON_H__) */