mirror of
https://github.com/netwide-assembler/nasm.git
synced 2025-09-22 10:43:39 -04:00
Merge the macros used in the assembler and disassembler. Simplify and slightly correct the byte order/unaligned handling macros. Use <stdbit.h> from C23 if available for bytesex.h and ilog2.h. Signed-off-by: H. Peter Anvin (Intel) <hpa@zytor.com>
404 lines
12 KiB
C
404 lines
12 KiB
C
/* ----------------------------------------------------------------------- *
|
|
*
|
|
* Copyright 1996-2025 The NASM Authors - All Rights Reserved
|
|
* See the file AUTHORS included with the NASM distribution for
|
|
* the specific copyright holders.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following
|
|
* conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials provided
|
|
* with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* ----------------------------------------------------------------------- */
|
|
|
|
/*
|
|
* bytesex.h - byte order helper functions
|
|
*
|
|
* In this function, be careful about getting X86_MEMORY versus
|
|
* LITTLE_ENDIAN correct: X86_MEMORY also means we are allowed to
|
|
* do unaligned memory references; it is opportunistic.
|
|
*/
|
|
|
|
#ifndef NASM_BYTEORD_H
|
|
#define NASM_BYTEORD_H
|
|
|
|
#include "compiler.h"
|
|
|
|
/*
|
|
* Endian control functions which work on a single integer
|
|
*/
|
|
/* Last resort implementations */
|
|
#define CPU_TO_LE(w,x) \
|
|
uint ## w ## _t xx = (x); \
|
|
union { \
|
|
uint ## w ## _t v; \
|
|
uint8_t c[sizeof(xx)]; \
|
|
} u; \
|
|
size_t i; \
|
|
for (i = 0; i < sizeof(xx); i++) { \
|
|
u.c[i] = (uint8_t)xx; \
|
|
xx >>= 8; \
|
|
} \
|
|
return u.v
|
|
|
|
#define LE_TO_CPU(w,x) \
|
|
uint ## w ## _t xx = 0; \
|
|
union { \
|
|
uint ## w ## _t v; \
|
|
uint8_t c[sizeof(xx)]; \
|
|
} u; \
|
|
u.v = (x); \
|
|
for (i = 0; i < sizeof(xx); i++) \
|
|
xx += (uint ## w ## _t)x.c[i] << (i << 3); \
|
|
\
|
|
return xx
|
|
|
|
#ifndef HAVE_CPU_TO_LE16
|
|
static inline uint16_t cpu_to_le16(uint16_t v)
|
|
{
|
|
# ifdef WORDS_LITTLEENDIAN
|
|
return v;
|
|
# elif defined(HAVE___CPU_TO_LE16)
|
|
return __cpu_to_le16(v);
|
|
# elif defined(HAVE_HTOLE16)
|
|
return htole16(v);
|
|
# elif defined(WORDS_BIGENDIAN)
|
|
# ifdef HAVE___BSWAP_16
|
|
return __bswap_16(v);
|
|
# elif defined(HAVE___BUILTIN_BSWAP16)
|
|
return __builtin_bswap16(v);
|
|
# elif defined(HAVE__BYTESWAP_UINT16)
|
|
return _byteswap_uint16(v);
|
|
# else
|
|
v = (v << 8) | (v >> 8);
|
|
return v;
|
|
# endif
|
|
# else
|
|
CPU_TO_LE(16);
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
#ifndef HAVE_CPU_TO_LE32
|
|
static inline uint32_t cpu_to_le32(uint32_t v)
|
|
{
|
|
# ifdef WORDS_LITTLEENDIAN
|
|
return v;
|
|
# elif defined(HAVE___CPU_TO_LE32)
|
|
return __cpu_to_le32(v);
|
|
# elif defined(HAVE_HTOLE32)
|
|
return htole32(v);
|
|
# elif defined(WORDS_BIGENDIAN)
|
|
# ifdef HAVE___BSWAP_32
|
|
return __bswap_32(v);
|
|
# elif defined(HAVE___BUILTIN_BSWAP32)
|
|
return __builtin_bswap32(v);
|
|
# elif defined(HAVE__BYTESWAP_UINT32)
|
|
return _byteswap_uint32(v);
|
|
# else
|
|
v = ((v << 8) & UINT32_C(0xff00ff00)) |
|
|
((v >> 8) & UINT32_C(0x00ff00ff));
|
|
return (v << 16) | (v >> 16);
|
|
# endif
|
|
# else
|
|
CPU_TO_LE(32);
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
#ifndef HAVE_CPU_TO_LE64
|
|
static inline uint64_t cpu_to_le64(uint64_t v)
|
|
{
|
|
#ifdef WORDS_LITTLEENDIAN
|
|
return v;
|
|
# elif defined(HAVE___CPU_TO_LE64)
|
|
return __cpu_to_le64(v);
|
|
# elif defined(HAVE_HTOLE64)
|
|
return htole64(v);
|
|
# elif defined(WORDS_BIGENDIAN)
|
|
# ifdef HAVE___BSWAP_64
|
|
return __bswap_64(v);
|
|
# elif defined(HAVE___BUILTIN_BSWAP64)
|
|
return __builtin_bswap64(v);
|
|
# elif defined(HAVE__BYTESWAP_UINT64)
|
|
return _byteswap_uint64(v);
|
|
# else
|
|
v = ((v << 8) & UINT64_C(0xff00ff00ff00ff00)) |
|
|
((v >> 8) & UINT64_C(0x00ff00ff00ff00ff));
|
|
v = ((v << 16) & UINT64_C(0xffff0000ffff0000)) |
|
|
((v >> 16) & UINT64_C(0x0000ffff0000ffff));
|
|
return (v << 32) | (v >> 32);
|
|
# endif
|
|
# else
|
|
CPU_TO_LE(64);
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
#ifndef HAVE_LE16_TO_CPU
|
|
static inline uint16_t le16_to_cpu(uint16_t v)
|
|
{
|
|
#ifdef WORDS_LITTLEENDIAN
|
|
return v;
|
|
# elif defined(HAVE___LE16_TO_CPU)
|
|
return __le16_to_cpu(v);
|
|
# elif defined(HAVE_LE16TOH)
|
|
return le64toh(v);
|
|
# elif defined(WORDS_BIGENDIAN)
|
|
return cpu_to_le16(v);
|
|
# else
|
|
LE_TO_CPU(16);
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
#ifndef HAVE_LE32_TO_CPU
|
|
static inline uint32_t le32_to_cpu(uint32_t v)
|
|
{
|
|
#ifdef WORDS_LITTLEENDIAN
|
|
return v;
|
|
# elif defined(HAVE___LE32_TO_CPU)
|
|
return __le32_to_cpu(v);
|
|
# elif defined(HAVE_LE32TOH)
|
|
return le64toh(v);
|
|
# elif defined(WORDS_BIGENDIAN)
|
|
return cpu_to_le32(v);
|
|
# else
|
|
LE_TO_CPU(32);
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
#ifndef HAVE_LE64_TO_CPU
|
|
static inline uint64_t le64_to_cpu(uint64_t v)
|
|
{
|
|
#ifdef WORDS_LITTLEENDIAN
|
|
return v;
|
|
# elif defined(HAVE___LE64_TO_CPU)
|
|
return __le64_to_cpu(v);
|
|
# elif defined(HAVE_LE64TOH)
|
|
return le64toh(v);
|
|
# elif defined(WORDS_BIGENDIAN)
|
|
return cpu_to_le64(v);
|
|
# else
|
|
LE_TO_CPU(64);
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Accessors for unaligned littleendian objects. These intentionally
|
|
* take an arbitrary pointer type, such that e.g. getu32() can be
|
|
* correctly executed on a void * or uint8_t *.
|
|
*/
|
|
#define getu8(p) (*(const uint8_t *)(p))
|
|
#define setu8(p,v) (*(uint8_t *)(p) = (v))
|
|
|
|
/* Unaligned object referencing */
|
|
#if X86_MEMORY
|
|
|
|
#define getu16(p) (*(const uint16_t *)(p))
|
|
#define getu32(p) (*(const uint32_t *)(p))
|
|
#define getu64(p) (*(const uint64_t *)(p))
|
|
|
|
#define setu16(p,v) (*(uint16_t *)(p) = (v))
|
|
#define setu32(p,v) (*(uint32_t *)(p) = (v))
|
|
#define setu64(p,v) (*(uint64_t *)(p) = (v))
|
|
|
|
#elif defined(__GNUC__)
|
|
|
|
struct unaligned16 {
|
|
uint16_t v;
|
|
} __attribute__((packed));
|
|
static inline uint16_t getu16(const void *p)
|
|
{
|
|
return le16_to_cpu(((const struct unaligned16 *)p)->v);
|
|
}
|
|
static inline uint16_t setu16(void *p, uint16_t v)
|
|
{
|
|
((struct unaligned16 *)p)->v = cpu_to_le16(v);
|
|
return v;
|
|
}
|
|
|
|
struct unaligned32 {
|
|
uint32_t v;
|
|
} __attribute__((packed));
|
|
static inline uint32_t getu32(const void *p)
|
|
{
|
|
return le32_to_cpu(((const struct unaligned32 *)p)->v);
|
|
}
|
|
static inline uint32_t setu32(void *p, uint32_t v)
|
|
{
|
|
((struct unaligned32 *)p)->v = cpu_to_le32(v);
|
|
return v;
|
|
}
|
|
|
|
struct unaligned64 {
|
|
uint64_t v;
|
|
} __attribute__((packed));
|
|
static inline uint64_t getu64(const void *p)
|
|
{
|
|
return le64_to_cpu(((const struct unaligned64 *)p)->v);
|
|
}
|
|
static inline uint64_t setu64(void *p, uint64_t v)
|
|
{
|
|
((struct unaligned64 *)p)->v = cpu_to_le64(v);
|
|
return v;
|
|
}
|
|
|
|
#elif defined(_MSC_VER)
|
|
|
|
static inline uint16_t getu16(const void *p)
|
|
{
|
|
const uint16_t _unaligned *pp = p;
|
|
return le16_to_cpu(*pp);
|
|
}
|
|
static inline uint16_t setu16(void *p, uint16_t v)
|
|
{
|
|
uint16_t _unaligned *pp = p;
|
|
*pp = cpu_to_le16(v);
|
|
return v;
|
|
}
|
|
|
|
static inline uint32_t getu32(const void *p)
|
|
{
|
|
const uint32_t _unaligned *pp = p;
|
|
return le32_to_cpu(*pp);
|
|
}
|
|
static inline uint32_t setu32(void *p, uint32_t v)
|
|
{
|
|
uint32_t _unaligned *pp = p;
|
|
*pp = cpu_to_le32(v);
|
|
return v;
|
|
}
|
|
|
|
static inline uint64_t getu64(const void *p)
|
|
{
|
|
const uint64_t _unaligned *pp = p;
|
|
return le64_to_cpu(*pp);
|
|
}
|
|
static inline uint64_t setu64(void *p, uint64_t v)
|
|
{
|
|
uint32_t _unaligned *pp = p;
|
|
*pp = cpu_to_le64(v);
|
|
return v;
|
|
}
|
|
|
|
#else
|
|
|
|
/* No idea, do it the slow way... */
|
|
|
|
static inline uint16_t getu16(const void *p)
|
|
{
|
|
const uint8_t *pp = p;
|
|
return pp[0] + (pp[1] << 8);
|
|
}
|
|
static inline uint16_t setu16(void *p, uint16_t v)
|
|
{
|
|
uint8_t *pp = p;
|
|
pp[0] = (uint8_t)v;
|
|
pp[1] = (uint8_t)(v >> 8);
|
|
return v;
|
|
}
|
|
|
|
static inline uint32_t getu32(const void *p)
|
|
{
|
|
const uint8_t *pp = p;
|
|
return getu16(pp) + ((uint32_t)getu16(pp+2) << 16);
|
|
}
|
|
static inline uint32_t setu32(void *p, uint32_t v)
|
|
{
|
|
uint8_t *pp = p;
|
|
setu16(pp, (uint16_t)v);
|
|
setu16(pp+2, (uint16_t)(v >> 16));
|
|
return v;
|
|
}
|
|
|
|
static inline uint64_t getu64(const void *p)
|
|
{
|
|
const uint8_t *pp = p;
|
|
return getu32(pp) + ((uint64_t)getu32(pp+4) << 32);
|
|
}
|
|
static inline uint64_t setu64(void *p, uint64_t v)
|
|
{
|
|
uint8_t *pp = p;
|
|
setu32(pp, (uint32_t)v);
|
|
setu32(pp+4, (uint32_t)(v >> 32));
|
|
return v;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* Signed versions */
|
|
#define gets8(p) ((int8_t) getu8(p))
|
|
#define gets16(p) ((int16_t)getu16(p))
|
|
#define gets32(p) ((int32_t)getu32(p))
|
|
#define gets64(p) ((int64_t)getu64(p))
|
|
|
|
#define sets8(p,v) ((int8_t) setu8((p), (uint8_t)(v)))
|
|
#define sets16(p,v) ((int16_t)setu16((p),(uint16_t)(v)))
|
|
#define sets32(p,v) ((int32_t)setu32((p),(uint32_t)(v)))
|
|
#define sets64(p,v) ((int64_t)setu64((p),(uint64_t)(v)))
|
|
|
|
/*
|
|
* Some handy macros that will probably be of use in more than one
|
|
* output format: convert integers into little-endian byte packed
|
|
* format in memory, advancing the pointer.
|
|
*/
|
|
|
|
#define WRITECHAR(p,v) \
|
|
do { \
|
|
uint8_t *_wc_p = (uint8_t *)(p); \
|
|
setu8(_wc_p, (v)); \
|
|
(p) = (void *)(_wc_p+1); \
|
|
} while (0)
|
|
|
|
#define WRITESHORT(p,v) \
|
|
do { \
|
|
uint8_t *_wc_p = (uint8_t *)(p); \
|
|
setu16(_wc_p, (v)); \
|
|
(p) = (void *)(_wc_p+2); \
|
|
} while (0)
|
|
|
|
#define WRITELONG(p,v) \
|
|
do { \
|
|
uint8_t *_wc_p = (uint8_t *)(p); \
|
|
setu32(_wc_p, (v)); \
|
|
(p) = (void *)(_wc_p+4); \
|
|
} while (0)
|
|
|
|
#define WRITEDLONG(p,v) \
|
|
do { \
|
|
uint8_t *_wc_p = (uint8_t *)(p); \
|
|
setu64(_wc_p, (v)); \
|
|
(p) = (void *)(_wc_p+8); \
|
|
} while (0)
|
|
|
|
#define WRITEADDR(p,v,s) \
|
|
do { \
|
|
const uint64_t _wa_v = cpu_to_le64(v); \
|
|
(p) = mempcpy((p), &_wa_v, (s)); \
|
|
} while (0)
|
|
|
|
#endif /* NASM_BYTESEX_H */
|