/* $Id: bitops.h,v 1.1 2004/09/17 23:55:34 chris Exp $
**
** Bitoperations & similar things for C
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version 2
** of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
** 02111-1307, USA.
**
*/

#ifndef _BITOPS_H
#define _BITOPS_H

#if defined(__BORLANDC__) || defined(__SUNPRO_C) || (defined(__WATCOMC__) && (__WATCOMC__+1 < 1100+1))
  #define BO_INLINE
#else
  #if defined(__MWERKS__)
    #define BO_INLINE inline
  #else
    #define BO_INLINE __inline
  #endif
#endif

#define CPG_BIG_ENDIAN 0
#define CPG_LITTLE_ENDIAN 1

/* byte operations */
#define rolb(wert) ((cpg_u8_t)(((wert) << 1) | (((wert) >> 7) & 1)))
#define rorb(wert) ((cpg_u8_t)((((cpg_u8_t)wert) >> 1) | ((wert & 1) << 7)))

#if 0
#define p_rclb(wert,cy) do { cpg_u8_t oldcy = *(cy); *(cy) = (*(cpg_u8_t *)(wert) >> 7) & 1; \
                             *(cpg_u8_t *)(wert) = (*(cpg_u8_t *)(wert) << 1) | oldcy; } while(0);
#define p_rcrb(wert,cy) do { cpg_u8_t oldcy = *(cy); *(cy) = *(cpg_u8_t *)(wert) & 1; \
                             *(cpg_u8_t *)(wert) = (*(cpg_u8_t *)(wert) >> 1) | (oldcy << 7); } while(0);
#else
static BO_INLINE void p_rclb(cpg_u8_t *wert,cpg_u8_t *cy)
{
    cpg_u8_t oldcy = *cy;
    *cy = (*wert >> 7) & 1;
    *wert = (*wert << 1) | oldcy;
}
static BO_INLINE void p_rcrb(cpg_u8_t *wert,cpg_u8_t *cy)
{
    cpg_u8_t oldcy = *cy;
    *cy = *wert & 1;
    *wert = (*wert >> 1) | (oldcy << 7);
}
#endif

/* word (16bit) operations */
#define rolw(wert) ((cpg_u16_t)((((cpg_u16_t)wert) << 1) | ((wert >> 15) & 1)))
#define rorw(wert) ((cpg_u16_t)((((cpg_u16_t)wert) >> 1) | ((wert & 1) << 15)))

#if 0
#define p_rclw(wert,cy) do { cpg_u8_t oldcy = *(cy); *(cy) = (*(cpg_u16_t *)(wert) >> 15) & 1; \
                             *(cpg_u16_t *)(wert) = (*(cpg_u16_t *)(wert) << 1) | oldcy; } while(0);
#define p_rcrw(wert,cy) do { cpg_u8_t oldcy = *(cy); *(cy) = *(cpg_u16_t *)(wert) & 1; \
                             *(cpg_u16_t *)(wert) = (*(cpg_u16_t *)(wert) >> 1) | (oldcy << 15); } while(0);
#else
static BO_INLINE void p_rclw(cpg_u16_t *wert,cpg_u8_t *cy)
{
    cpg_u8_t oldcy = *cy;
    *cy = (cpg_u8_t)(*wert >> 15) & 1;
    *wert = (*wert << 1) | oldcy;
}
static BO_INLINE void p_rcrw(cpg_u16_t *wert,cpg_u8_t *cy)
{
    cpg_u8_t oldcy = *cy;
    *cy = (cpg_u8_t)*wert & 1;
    *wert = (*wert >> 1) | ((cpg_u16_t)oldcy << 15);
}
#endif

/* dword (32bit) opearions */
#define rold(wert) ((cpg_u32_t)((((cpg_u32_t)wert) << 1) | ((wert >> 31) & 1)))
#define rord(wert) ((cpg_u32_t)((((cpg_u32_t)wert) >> 1) | ((wert & 1) << 31)))

static BO_INLINE void p_rcld(cpg_u32_t *wert,cpg_u8_t *cy)
{
    cpg_u8_t oldcy = *cy;
    *cy = (cpg_u8_t)(*wert >> 31) & 1;
    *wert = (*wert << 1) | oldcy;
}
static BO_INLINE void p_rcrd(cpg_u32_t *wert,cpg_u8_t *cy)
{
    cpg_u8_t oldcy = *cy;
    *cy = (cpg_u8_t)*wert & 1;
    *wert = (*wert >> 1) | ((cpg_u32_t)oldcy << 31);
}

#if defined(__64BIT__)

/* qword (64bit) opearions */
#define rolq(wert) ((cpg_u64_t)((((cpg_u64_t)wert) << 1) | ((wert >> 63) & 1)))
#define rorq(wert) ((cpg_u64_t)((((cpg_u64_t)wert) >> 1) | ((wert & 1) << 63)))

#endif

/* rotate more than 1 */
#define rotl32(X,C) (((X)<<((C)&0x1F))|((X)>>(32-((C)&0x1F))))  /* rotate left (32 bits) */
#define rotr32(X,C) (((X)>>((C)&0x1F))|((X)<<(32-((C)&0x1F))))  /* rotate right(32 bits) */


/* bit operations */
#define get_bit(bitnum,value) ((value >> bitnum) & 1)
#define set_bit(bitnum,value) do { value |= (1 << bitnum); } while (0)
#define clr_bit(bitnum,value) do { value &= ~(1 << bitnum); } while (0)

/*
 *  Endianness Conversion Functions
 */

/* ----------------------- Machine Type is Little Endian ----------------- */
#if defined(_CPG_LITTLE_ENDIAN_) && !defined(_CPG_NO_UNALIGN_)

#define read_le16(where)        (*(cpg_u16_t *)(where))
#define write_le16(where, what) (*(cpg_u16_t *)(where) = what)
#define read_le32(where)        (*(cpg_u32_t *)(where))
#define write_le32(where, what) (*(cpg_u32_t *)(where) = what)

#endif /* #if defined(_CPG_LITTLE_ENDIAN_) && !defined(_CPG_NO_UNALIGN_) */
#if defined(_CPG_BIG_ENDIAN_) || defined(_CPG_NO_UNALIGN_)
#if defined(__GNUC__) && (defined(__ppc__) || defined(__powerpc__))

/*#warning "using PPC inline assembler for little-endian access"*/
static inline cpg_u16_t read_le16 (const cpg_u16_t *where)
{
  cpg_u16_t res;
  __asm__ volatile ("lhbrx %0,0,%1" : "=r"(res) : "r"(where) );
  return(res);
}

static inline void write_le16(cpg_u16_t *where,cpg_u16_t what)
{
  __asm__ volatile ("sthbrx %0,0,%1" : : "r"(what), "r"(where) : "memory" );
}

static inline cpg_u32_t read_le32(const cpg_u32_t *where)
{
  cpg_u32_t res;
  __asm__ volatile ("lwbrx %0,0,%1" : "=r"(res) : "r"(where) );
  return(res);
}

static inline void write_le32(cpg_u32_t *where, cpg_u32_t what)
{
  __asm__ volatile ("stwbrx %0,0,%1" : : "r"(what), "r"(where) : "memory" );
}

#else /* above powerpc version, below generic */

static BO_INLINE cpg_u16_t read_le16(const cpg_u16_t *where)
{
  const cpg_u8_t *ww = (const cpg_u8_t *)where;
  return(*ww | (*(ww+1) << 8));
}

static BO_INLINE void write_le16(cpg_u16_t *where, cpg_u16_t what)
{
  cpg_u8_t *ww = (cpg_u8_t *)where;
  *ww = what;
  *(ww+1) = what >> 8;
}

static BO_INLINE cpg_u32_t read_le32(const cpg_u32_t *where)
{
  const cpg_u8_t *ww = (const cpg_u8_t *)where;
  return(*ww | ((cpg_u32_t)*(ww+1) << 8) | ((cpg_u32_t)*(ww+2) << 16) | ((cpg_u32_t)*(ww+3) << 24));
}

static BO_INLINE void write_le32(cpg_u32_t *where,cpg_u32_t what)
{
  cpg_u8_t *ww = (cpg_u8_t *)where;
  *ww = what;
  *(ww+1) = what >> 8;
  *(ww+2) = what >> 16;
  *(ww+3) = what >> 24;
}

#endif /* ! (defined(__GNUC__) && defined(__ppc__)) */
#endif /* #if defined(_CPG_BIG_ENDIAN_) || defined(_CPG_NO_UNALIGN_) */
#if defined(_CPG_BIG_ENDIAN_) && !defined(_CPG_NO_UNALIGN_)

#define read_be16(where)        (*(cpg_u16_t *)(where))
#define write_be16(where, what) (*(cpg_u16_t *)(where) = what)
#define read_be32(where)        (*(cpg_u32_t *)(where))
#define write_be32(where, what) (*(cpg_u32_t *)(where) = what)

#endif /* #if defined(_CPG_BIG_ENDIAN_) && !defined(_CPG_NO_UNALIGN_) */
#if defined(_CPG_LITTLE_ENDIAN_) || defined(_CPG_NO_UNALIGN_)

static BO_INLINE cpg_u16_t read_be16(const cpg_u16_t *where)
{
  const cpg_u8_t *ww = (const cpg_u8_t *)where;
  return(*ww << 8 | (*(ww+1) ));
}

static BO_INLINE void write_be16(cpg_u16_t *where, cpg_u16_t what)
{
  cpg_u8_t *ww = (cpg_u8_t *)where;
  *ww = what >> 8;
  *(ww+1) = (cpg_u8_t)what;
}

static BO_INLINE cpg_u32_t read_be32(const cpg_u32_t *where)
{
  const cpg_u8_t *ww = (const cpg_u8_t *)where;
  return(((cpg_u32_t)*ww << 24) | ((cpg_u32_t)*(ww+1) << 16) | ((cpg_u32_t)*(ww+2) << 8) | *(ww+3));
}

static BO_INLINE void write_be32(cpg_u32_t *where, cpg_u32_t what)
{
  cpg_u8_t *ww = (cpg_u8_t *)where;
  *ww = (cpg_u8_t)(what >> 24);
  *(ww+1) = (cpg_u8_t)(what >> 16);
  *(ww+2) = (cpg_u8_t)(what >> 8);
  *(ww+3) = (cpg_u8_t)what;
}

#endif /* #if defined(_CPG_LITTLE_ENDIAN_) || defined(_CPG_NO_UNALIGN_) */

#define add_be16(where, what)   write_be16((where), (cpg_u16_t)(read_be16((where)) + (what)))
#define sub_be16(where, what)   write_be16((where), (cpg_u16_t)(read_be16((where)) - (what)))
#define inc_be16(where)         write_be16((where), (cpg_u16_t)(read_be16((where)) + 1))
#define dec_be16(where)         write_be16((where), (cpg_u16_t)(read_be16((where)) - 1))

#define add_be32(where, what)   write_be32((where), (cpg_u32_t)(read_be32((where)) + (what)))
#define sub_be32(where, what)   write_be32((where), (cpg_u32_t)(read_be32((where)) - (what)))
#define inc_be32(where)         write_be32((where), (cpg_u32_t)(read_be32((where)) + 1))
#define dec_be32(where)         write_be32((where), (cpg_u32_t)(read_be32((where)) - 1))

#define add_le16(where, what)   write_le16((where), (cpg_u16_t)(read_le16((where)) + (what)))
#define sub_le16(where, what)   write_le16((where), (cpg_u16_t)(read_le16((where)) - (what)))
#define inc_le16(where)         write_le16((where), (cpg_u16_t)(read_le16((where)) + 1))
#define dec_le16(where)         write_le16((where), (cpg_u16_t)(read_le16((where)) - 1))

#define add_le32(where, what)   write_le32((where), (cpg_u32_t)(read_le16((where)) + (what)))
#define sub_le32(where, what)   write_le32((where), (cpg_u32_t)(read_le16((where)) - (what)))
#define inc_le32(where)         write_le32((where), (cpg_u32_t)(read_le16((where)) + 1))
#define dec_le32(where)         write_le32((where), (cpg_u32_t)(read_le16((where)) - 1))

#define read16(endian, where)   (endian == CPG_BIG_ENDIAN \
                                     ? read_be16(where) \
                                       : read_le16(where))
#define read32(endian, where)   (endian == CPG_BIG_ENDIAN \
                                     ? read_be32(where) \
                                       : read_le32(where))
#define write16(endian, where, what)  do { if (endian == CPG_BIG_ENDIAN) \
                                                  write_be16(where, what); else \
                                                  write_le16(where, what); } while (0)
#define write32(endian, where, what)  do { if (endian == CPG_BIG_ENDIAN) \
                                                  write_be32(where, what); else \
                                                  write_le32(where, what); } while (0)

#define inc16(endian, where)  do { if (endian == CPG_BIG_ENDIAN) \
                                          inc_be16(where); else inc_le16(where); } while (0)
#define inc32(endian, where)  do { if (endian == CPG_BIG_ENDIAN) \
                                          inc_be32(where); else inc_le32(where); } while (0)

#define dec16(endian, where)  do { if (endian == CPG_BIG_ENDIAN) \
                                          dec_be16(where); else dec_le16(where); } while (0)
#define dec32(endian, where)  do { if (endian == CPG_BIG_ENDIAN) \
                                          dec_be32(where); else dec_le32(where): } while (0)

#endif /* #ifndef _BITOPS_H */
