Linux 0.12 OS. math - div.c
3549 ワード
static void shift_left(int * c)
{
__asm__ __volatile__("movl (%0),%%eax ; addl %%eax,(%0)
\t"
"movl 4(%0),%%eax ; adcl %%eax,4(%0)
\t"
"movl 8(%0),%%eax ; adcl %%eax,8(%0)
\t"
"movl 12(%0),%%eax ; adcl %%eax,12(%0)"
::"r" ((long) c));
}
static void shift_right(int * c)
{
__asm__("shrl $1,12(%0) ; rcrl $1,8(%0) ; rcrl $1,4(%0) ; rcrl $1,(%0)"
::"r" ((long) c));
}
static int try_sub(int * a, int * b)
{
char ok;
/*
b = b -a, ok = ~CF.
sbb
*/
__asm__ __volatile__("movl (%1),%%eax ; subl %%eax,(%2)
\t"
"movl 4(%1),%%eax ; sbbl %%eax,4(%2)
\t"
"movl 8(%1),%%eax ; sbbl %%eax,8(%2)
\t"
"movl 12(%1),%%eax ; sbbl %%eax,12(%2)
\t"
"setae %%al":"=a" (ok):"c" ((long) a),"d" ((long) b)); /* setae: ~CF */
return ok;
}
static void div64(int * a, int * b, int * c)
{
int tmp[4];
int i;
unsigned int mask = 0;
/*
: c = a/b.
c[1]=c[0]=0, 64 .
4 :
a = 1011 0011
b = 1001 0100
c = 1 0011 0101
1001 0100 | 1011 0011 a
- 1001 0100 ... b>>0
0001 1111 000 a
- 0100 1010 0 ... b>>1
- 0010 0101 00 ... b>>2
- 0001 0010 100 ... b>>3
0000 1100 1000 a
- 0000 1001 0100 ... b>>4
0000 0011 0100 00 a
- 0000 0100 1010 0 ... b>>5
- 0000 0010 0101 00 ... b>>6
0000 0000 1111 0000 a
- 0000 0001 0010 100 ... b>>7
- 0000 0000 1001 0100 ... b>>8
0000 0000 0101 1100 a
, c , 8
*/
c += 4; /* */
for (i = 0 ; i<64 ; i++) {
if (!(mask >>= 1)) { /* mask c */
c--;
mask = 0x80000000; /* c , 32 , 32 ,
*/
}
tmp[0] = a[0]; tmp[1] = a[1];
tmp[2] = a[2]; tmp[3] = a[3];
if (try_sub(b,tmp)) { /* a-b , mask 1, a */
*c |= mask;
a[0] = tmp[0]; a[1] = tmp[1];
a[2] = tmp[2]; a[3] = tmp[3];
}
shift_right(b); /* */
}
}
void fdiv(const temp_real * src1, const temp_real * src2, temp_real * result)
{
int i,sign;
int a[4],b[4],tmp[4] = {0,0,0,0};
sign = (src1->exponent ^ src2->exponent) & 0x8000; /* */
if (!(src2->a || src2->b)) { /* 0 */
set_ZE(); /* 8.5.3 Divide-by-zero exception (#Z) */
return;
}
i = (src1->exponent & 0x7fff) - (src2->exponent & 0x7fff) + 16383; /* */
if (i<0) { /* 0 */
set_UE(); /* 8.5.5 Numeric underflow exception (#U) */
result->exponent = sign;
result->a = result->b = 0;
return;
}
a[0] = a[1] = 0; /* a */
a[2] = src1->a;
a[3] = src1->b;
b[0] = b[1] = 0; /* b */
b[2] = src2->a;
b[3] = src2->b;
while (b[3] >= 0) { /* 0 */
i++; /* --, ++ */
shift_left(b);
}
div64(a,b,tmp); /* tmp = a/b */
if (tmp[0] || tmp[1] || tmp[2] || tmp[3]) {
while (i && tmp[3] >= 0) { /* */
i--;
shift_left(tmp);
}
if (tmp[3] >= 0) /* */
set_DE(); /* 8.5.2 Denormal operand exception (#D) */
} else /* 0 */
i = 0;
if (i>0x7fff) { /* */
set_OE();
return;
}
if (tmp[0] || tmp[1]) /* */
set_PE(); /* 8.5.6 Inexact-result (Precision) exception (#P) */
result->exponent = i | sign;
result->a = tmp[2];
result->b = tmp[3];
}