/* 386/builtin.h (emx+gcc) */
/** @file
 * EMX 0.9d-fix04
 */

#ifndef _I386_BUILTIN_H
#define _I386_BUILTIN_H

#if defined (__cplusplus)
extern "C" {
#endif

static __inline__ signed char __cxchg (__volatile__ signed char *p,
                                       signed char v)
{
  __asm__ __volatile__ ("xchgb %0, %1" : "=m"(*p), "=r"(v) : "1"(v));
  return v;
}

static __inline__ short __sxchg (__volatile__ short *p, short v)
{
  __asm__ __volatile__ ("xchgw %0, %1" : "=m"(*p), "=r"(v) : "1"(v));
  return v;
}

static __inline__ int __lxchg (__volatile__ int *p, int v)
{
  __asm__ __volatile__ ("xchgl %0, %1" : "=m"(*p), "=r"(v) : "1"(v));
  return v;
}

static __inline__ void __enable (void)
{
  __asm__ __volatile__ ("sti");
}

static __inline__ void __disable (void)
{
  __asm__ __volatile__ ("cli");
}

#define __ROTATE_FUN(F,I,T) \
  static __inline__ T F (T value, int shift) \
  { \
    __asm__ (I " %b2, %0" : "=g"(value) : "0"(value), "c"(shift) : "cc"); \
    return value; \
  } \
  static __inline__ T F##1 (T value) \
  { \
    __asm__ (I " $1, %0" : "=g"(value) : "0"(value) : "cc"); \
    return value; \
  }

#define __ROTATE(V,S,F) ((__builtin_constant_p (S) && (int)(S) == 1) \
                         ? F##1 (V) : F (V, S))

__ROTATE_FUN (__crotr, "rorb", unsigned char)
__ROTATE_FUN (__srotr, "rorw", unsigned short)
__ROTATE_FUN (__lrotr, "rorl", unsigned long)

__ROTATE_FUN (__crotl, "rolb", unsigned char)
__ROTATE_FUN (__srotl, "rolw", unsigned short)
__ROTATE_FUN (__lrotl, "roll", unsigned long)

#define _crotr(V,S) __ROTATE (V, S, __crotr)
#define _srotr(V,S) __ROTATE (V, S, __srotr)
#define _lrotr(V,S) __ROTATE (V, S, __lrotr)
#define _crotl(V,S) __ROTATE (V, S, __crotl)
#define _srotl(V,S) __ROTATE (V, S, __srotl)
#define _lrotl(V,S) __ROTATE (V, S, __lrotl)

#define _rotr(V,S) _lrotr (V, S)
#define _rotl(V,S) _lrotl (V, S)


static __inline__ int __fls (int v)
{
  int r;

  __asm__ __volatile__ ("bsrl %1, %0;"
                        "jnz 1f;"
                        "movl $-1, %0;"
                        ".align 2, 0x90;"
                        "1:"
                        : "=r"(r) : "r"(v) : "cc");
  return r + 1;
}

/* Quick routines similar to div() and friends, but inline */

static __inline__ long __ldivmod (long num, long den, long *rem)
{
  long q, r;
  __asm__ ("cltd; idivl %2"
           : "=a" (q), "=&d" (r)
           : "r?m" (den), "a" (num));
  *rem = r;
  return q;
}

static __inline__ unsigned long __uldivmod (unsigned long num,
  unsigned long den, unsigned long *rem)
{
  unsigned long q, r;
  __asm__ ("xorl %%edx,%%edx; divl %2"
           : "=a" (q), "=&d" (r)
           : "r?m" (den), "a" (num));
  *rem = r;
  return q;
}

/*
    Divide a 64-bit integer by a 32-bit one:

    A*2^32 + B    A            B + (A mod 32)
    ---------- = --- * 2^32 + ----------------
        C         C                  C
*/
static __inline__ long long __lldivmod (long long num, long den, long *rem)
{
  long long q;
  long r;
  __asm__ ("	movl	%%eax,%%esi;"
           "	movl	%%edx,%%eax;"
           "	pushl	%%edx;"
           "	cltd;"
           "	idivl	%2;"
           "	;"
/* Now ensure remainder is smallest of possible two values (negative and
   positive). For this we compare the remainder with positive and negative
   denominator/2; if it is smaller than one and bigger than another we
   consider it optimal, otherwise it can be made smaller by adding or
   subtracting denominator to it. This is done to ensure no overflow
   will occur at next division. */
           "	movl	%2,%%ecx;"
           "	sarl	$1,%%ecx;"	/* ecx = den/2 */
           "	cmpl	%%ecx,%%edx;"
           "	setl	%%bl;"
           "	negl	%%ecx;"
           "	cmpl	%%ecx,%%edx;"
           "	setl	%%bh;"
           "	xorb	%%bh,%%bl;"
           "	jnz	1f;"		/* Remainder is between -den/2...den/2 */
           "	;"
/* If remainder has same sign as denominator, we have to do r -= den; q++;
   otherwise we have to do r += den; q--; */
           "	movl	%2,%%ebx;"	/* ebx = den */
           "	xorl	%%edx,%%ebx;"	/* r ^ den */
           "	js	0f;"		/* Different signs */
           "	subl	%2,%%edx;"	/* r -= den */
           "	addl	$1,%%eax;"	/* q++ */
           "	adcl	$0,%%edx;"
           "	jmp	1f;"
           "	;"
           "0:	addl	%2,%%edx;"	/* r += den */
           "	subl	$1,%%eax;"	/* q-- */
           "	sbbl	$0,%%edx;"
           "	;"
           "1:	xchgl	%%eax,%%esi;"
           "	idivl	%2;"
           "	;"
           "	movl	%%edx,%1;"
           "	cltd;"
           "	addl	%%esi,%%edx;"
           "	;"
/* Check if numerator has the same sign as remainder; if they have different
   sign we should make the remainder have same sign as numerator to comply
   with ANSI standard, which says we always should truncate the quotient
   towards zero. */
           "	popl	%%ebx;"		/* ebx = num >> 32 */
           "	xorl	%1,%%ebx;"	/* sign(r) ^ sign(num) */
           "	jns	3f;"		/* jump if same sign */
           "	;"
/* If remainder has same sign as denominator, we have to do r -= den; q++;
   otherwise we have to do r += den; q--; */
           "	movl	%2,%%ebx;"
           "	xorl	%1,%%ebx;"	/* r ^ den */
           "	js	2f;"		/* Different signs */
           "	subl	%2,%1;"		/* r -= den */
           "	addl	$1,%%eax;"	/* q++ */
           "	adcl	$0,%%edx;"
           "	jmp	3f;"
           "	;"
           "2:	addl	%2,%1;"		/* r += den */
           "	subl	$1,%%eax;"	/* q-- */
           "	sbbl	$0,%%edx;"
           "	;"
           "3:	;"
           : "=A" (q), "=&c" (r)
           : "r" (den), "A" (num)
           : "ebx", "esi");
  *rem = r;
  return q;
}

/*
    Same as __lldivmod except that if A < C, we can do just one division
    instead of two because the result is always a 32-bit integer.
*/
static __inline__ unsigned long long __ulldivmod (unsigned long long num,
  unsigned long den, unsigned long *rem)
{
  unsigned long long q;
  unsigned long r;
  __asm__ ("	movl	%%eax,%1;"
           "	movl	%%edx,%%eax;"
           "	xorl	%%edx,%%edx;"
           "	divl	%2;"
           "	xchgl	%%eax,%%ecx;"
           "	divl	%2;"
           "	xchgl	%%edx,%1;"
           : "=A" (q), "=c" (r)
           : "r?m" (den), "A" (num));
  *rem = r;
  return q;
}

#if defined (__cplusplus)
}
#endif

#endif /* not _I386_BUILTIN_H */
