/* (-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__) */