/* -*- mode: c; c-basic-offset: 8 -*- */ /* earith.c -- Support for bignum arithmetic. */ /* Copyright (c) 1984, Taiichi Yuasa and Masami Hagiya. Copyright (c) 1990, Giuseppe Attardi. Copyright (c) 2001, Juan Jose Garcia Ripoll. ECL is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See file '../Copyright' for full details. */ /* EXTENDED_MUL and EXTENDED_DIV perform 32 bit multiplication and division, respectively. EXTENDED_MUL(D,Q,R,HP,LP) calculates D*Q+R and saves the result into the locations HP and LP. D, Q, and R are 32 bit non-negative integers and HP and LP are word addresses. The word at LP will contain the lower 31 (not 32) bits of the result and its most significant bit is set 0. The word at HP will contain the rest of the result and its MSB is also set 0. EXTENDED_DIV(D,H,L,QP,RP) divides [H:L] by D and saves the quotient and the remainder into the locations QP and RP, respectively. D, H, L are 32 bit non-negative integers and QP and RP are word addresses. Here, [H:L] means the 64 bit integer (imaginary) represented by H and L as follows. 63 62 31 30 0 |0|0||| Although [H:L] is 64 bits, you can assume that the quotient is always represented as 32 bit non-negative integer. */ #include #ifdef CONVEX static void extended_mul(int d, int q, int r, int *hp, int *lp) { long long int ld, lq, lr, z; int zh, zl; ld = d; lq = q; lr = r; z = ld*lq+lr; zl = (z & 0x000000007fffffffLL); zh = (z >> 31LL); *hp = zh; *lp = zl; } static void extended_div(int d, int h, int l, int *qp, int *rp) { long long int lh, ld, ll; ld = d; lh = h; ll = l; lh = (lh << 31LL); lh = (lh | ll); *qp = (lh/ld); *rp = (lh%ld); } #endif /* CONVEX */ #ifdef i386 static void extended_mul(int d, int q, int r, int *hp, int *lp) { asm("pushl %ecx"); asm("movl 8(%ebp),%eax"); asm("mull 12(%ebp)"); asm("addl 16(%ebp),%eax"); asm("adcl $0,%edx"); asm("shll $1,%edx"); asm("btrl $31,%eax"); asm("adcl $0,%edx"); asm("movl 20(%ebp),%ecx"); asm("movl %edx, (%ecx)"); asm("movl 24(%ebp), %ecx"); asm("movl %eax, (%ecx)"); asm("popl %ecx"); } static void extended_div(int d, int h, int l, int *qp, int *rp) { asm("pushl %ebx"); asm("movl 12(%ebp),%edx"); asm("movl 16(%ebp),%eax"); asm("btl $0,%edx"); asm("jae 1f"); asm("btsl $31,%eax"); asm("1: shrl $1,%edx"); asm("idivl 8(%ebp)"); asm("movl 20(%ebp),%ebx"); asm("movl %eax,(%ebx)"); asm("movl 24(%ebp),%ebx"); asm("movl %edx,(%ebx)"); asm("popl %ebx"); } #endif /* i386 */ #ifdef IBMRT static void extended_mul(int d, int q, int r, int *hp, int *lp) { /* d=L750+20, q=L750+24, etc. */ asm(" get r0,L750+20(r13)"); /* get an argument */ asm(" mts r10,r0"); /* put in MQ */ asm(" get r2,L750+24(r13)"); /* get the other argument */ asm(" s r0,r0"); /* zero partial product. set carry to 1. */ asm(" m r0,r2 m r0,r2 m r0,r2 m r0,r2 m r0,r2 m r0,r2 m r0,r2 m r0,r2 m r0,r2 m r0,r2 m r0,r2 m r0,r2 m r0,r2 m r0,r2 m r0,r2 m r0,r2"); /* Now (r0)//mq has the 64 bit product; overflow is ignored. */ asm(" mfs r10,r2"); /* set r2 = low order word of result * so product is in (r0)//(r2). */ /* * Force product into two single precision words. */ asm(" get r3,$1f sli r0,1 ar2,r2 bnc0r r3"); /* branch if carry = 0 */ asm(" oil r0,r0,1 1: sri r2,1"); /* Now add in the third argument. */ asm(" get r4,$2f get r3,L750+28(r13) a r2,r3 bnmr r4"); /* branch if not minus */ asm(" clrbu r2,0 lis r3,1 a r0,r3 2: get r3,L750+32(r13) put r0,0(r3) get r3,L750+36(r13) put r2,0(r3) "); } static void extended_div(int d, int h, int l, int *qp, int *rp) { /* d=L754+20, h=L754+24, etc. */ /* Move arguments into registers. */ asm(" get r0,L754+28(r13)"); /* Low order word of dividend. */ asm(" get r2,L754+24(r13)"); /* High order word of dividend. */ asm(" mttbil r2,15 mftbiu r0,0 sri r2,1 mts r10,r0 get r3,L754+20(r13)") /* Divisor. */ /* Perform 32 steps of division. */ asm(" d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3 d r2,r3"); /* Now MQ has the quotient, R2 the remainder, and R3 is * the unchanged divisor. */ asm(" mttbiu r2,0"); /* Do add-back if necessary. */ asm(" jntb 1f a r2,r3 1: mfs r10,r0 c r2,r3"); /* Remainder : divisor. */ asm(" jne 2f inc r0,1 x r2,r2 2:"); /* Now r0 has the quotient and r2 has the remainder. */ asm(" get r3,L754+32(r13)"); /* Quotient address. */ asm(" put r0,0(r3)"); asm(" get r3,L754+36(r13)"); /* Remainder address. */ asm(" put r2,0(r3)"); } #endif /* IBMRT */ #if defined(NEWS) || defined(MAC) static void extended_mul(int d, int q, int r, int *hp, int *lp) { asm(" move.l d2,-(sp) clr.l d2 move.l (8,fp),d0 mulu.l (12,fp),d1:d0 add.l (16,fp),d0 addx.l d2,d1 lsl.l #1,d0 roxl.l #1,d1 lsr.l #1,d0 move.l (20,fp),a0 move.l d1,(a0) move.l (24,a6),a0 move.l d0,(a0)"); } static void extended_div(int d, int h, int l, int *qp, int *rp) { asm("movem.l (12,fp),#0x303 lsl.l #1,d1 lsr.l #1,d0 roxr.l #1,d1 divu.l (8,fp),d0:d1 move.l d0,(a1) move.l d1,(a0) "); } #endif /* NEWS || MAC */ #ifdef __mips /* earith.s for MIPS R2000 processor by Doug Katzman version 2.1.d dated 7/13/89 15:31 EDT */ /* Register names: #define v0 $2 return value #define v1 $3 #define a0 $4 argument registers #define a1 $5 #define a2 $6 #define a3 $7 #define t7 $15 */ static void extended_mul(unsigned int d, unsigned int q, unsigned int r, unsigned int *hp, unsigned int *lp) { asm("mult $4, $5"); /* [hi:lo] = d * q */ asm("mfhi $5"); /* a1 = hi */ asm("sll $5, 1"); asm("mflo $4"); asm("srl $15, $4, 31"); asm("and $4, 0x7fffffff"); asm("or $5, $15"); asm("addu $4, $6"); /* [a1:a0] += r */ asm("srl $15, $4, 31"); asm("and $4, 0x7fffffff"); asm("addu $5, $15"); asm("sw $5, 0($7)"); /* *hp = a1 */ #ifdef __GNUC__ asm("lw $7, %0" :: "g" (lp)); #else asm("lw $7, 16($sp)"); /* fetch fifth actual argument from stack */ #endif asm("sw $4, 0($7)"); /* *lp = a0 */ } static void extended_div(unsigned int d, unsigned int h, unsigned int l, unsigned int *qp, unsigned int *rp) { asm("sll $6, 1"); asm("li $2, 31"); /* v0 holds number of shifts */ asm("loop: srl $15, $6, 31"); asm("sll $5, 1"); asm("or $5, $15"); asm("sll $6, 1"); asm("subu $15, $5, $4"); /* t = h - d */ asm("bltz $15, underflow"); asm("move $5, $15"); asm("or $6, 1"); asm("underflow: subu $2, 1"); asm("bnez $2, loop"); asm("sw $6, 0($7)"); /* *qp = l */ #ifdef __GNUC__ asm("lw $7, %0" :: "g" (rp)); #else asm("lw $7, 16($sp)"); /* fetch fifth actual argument from stack */ #endif asm("sw $5, 0($7)"); /* *rp = h */ } #endif /* __mips */ #if defined(sun3) || (defined __NeXT) static void extended_mul(int d, int q, int r, int *hp, int *lp) { asm(" movl d2,a7@- clrl d2 movl a6@(8),d0 mulul a6@(12),d1:d0 addl a6@(16),d0 addxl d2,d1 lsll #1,d0 roxll #1,d1 lsrl #1,d0 movl a6@(20),a0 movl d1,a0@ movl a6@(24),a0 movl d0,a0@ movl a7@+,d2 "); } static void extended_div(int d, int h, int l, int *qp, int *rp) { asm("moveml a6@(12),#0x303 lsll #1,d1 lsrl #1,d0 roxrl #1,d1 divul a6@(8),d0:d1 movl d0,a1@ movl d1,a0@ "); } #endif /* sun3 */ /* Possible assembler version: #ifdef sparc _extended_mul: !#PROLOGUE# 0 !#PROLOGUE# 1 save %sp,-96,%sp mov %i0,%o0 call .umul,2 mov %i1,%o1 addcc %o0,%i2,%i0 addx %o1,0,%o1 sll %o1,1,%o1 tst %i0 bge L77003 sethi %hi(0x7fffffff),%o3 or %o3,%lo(0x7fffffff),%o3 ! [internal] and %i0,%o3,%i0 inc %o1 L77003: st %i0,[%i4] st %o1,[%i3] ret restore %g0,0,%o0 #endif sparc */ #if defined(sparc) || defined(APOLLO) || defined(hpux) || defined(UNIGRAPH)n /* for the time being use the C version:*/ static void extended_mul(unsigned int d, unsigned int q, unsigned int r, unsigned int *hp, unsigned int *lp) { register unsigned short dlo = d & 0xffff, dhi = d >> 16, qlo = q & 0xffff, qhi = q >> 16; unsigned int d0 = dhi * qlo + dlo * qhi, d1 = dhi * qhi, d2 = dlo * qlo; d1 = (d1 << 1) + (d0 >> 15); /* add 17 MSB of d0 */ d1 += d2 >> 31; /* add MSB of d2 */ d2 &= 0x7fffffff; /* clear MSB of d2 */ d2 += (d0 & 0x7fff) << 16; /* add 15 LSB of d0: no overflow occurs */ d1 += d2 >> 31; /* add MSB of d2 */ d2 &= 0x7fffffff; /* clear MSB of d2 */ d2 += r; d1 += d2 >> 31; /* add MSB of d2 */ d2 &= 0x7fffffff; /* clear MSB of d2 */ *hp = d1; *lp = d2; } static void extended_div(unsigned int d, unsigned int h, unsigned int l, unsigned int *qp, unsigned int *rp) { int i; int borrow; l = (l << 1) | 1; for (i = 31; i >= 0;) { if (h >= d) { h -= d; borrow = 0; } else borrow = 1; if (i--) h = (h << 1) | ((unsigned)l >> 31); l = (l << 1) | borrow; } l = - l - 1; *qp = l; *rp = h; } #endif /* sparc */ #ifdef vax static void extended_mul(int d, int q, int r, int *hp, int *lp) { asm(" emul 4(ap),8(ap),12(ap),r0"); asm(" ashq $1,r0,r0"); asm(" rotl $-1,r0,r0"); asm(" movl r0,*20(ap)"); asm(" movl r1,*16(ap)"); } static void extended_div(int d, int h, int l, int *qp, int *rp) { asm(" clrl r0"); asm(" movl 8(ap),r1"); asm(" ashq $-1,r0,r0"); asm(" addl2 12(ap),r0"); asm(" ediv 4(ap),r0,*16(ap),*20(ap)"); } #endif /* vax */