217 строки
6.2 KiB
ArmAsm
217 строки
6.2 KiB
ArmAsm
/*
|
|
* File: arch/blackfin/lib/divsi3.S
|
|
* Based on:
|
|
* Author:
|
|
*
|
|
* Created:
|
|
* Description: 16 / 32 bit signed division.
|
|
* Special cases :
|
|
* 1) If(numerator == 0)
|
|
* return 0
|
|
* 2) If(denominator ==0)
|
|
* return positive max = 0x7fffffff
|
|
* 3) If(numerator == denominator)
|
|
* return 1
|
|
* 4) If(denominator ==1)
|
|
* return numerator
|
|
* 5) If(denominator == -1)
|
|
* return -numerator
|
|
*
|
|
* Operand : R0 - Numerator (i)
|
|
* R1 - Denominator (i)
|
|
* R0 - Quotient (o)
|
|
* Registers Used : R2-R7,P0-P2
|
|
*
|
|
* Modified:
|
|
* Copyright 2004-2006 Analog Devices Inc.
|
|
*
|
|
* Bugs: Enter bugs at http://blackfin.uclinux.org/
|
|
*
|
|
* 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, see the file COPYING, or write
|
|
* to the Free Software Foundation, Inc.,
|
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
.global ___divsi3;
|
|
|
|
#ifdef CONFIG_ARITHMETIC_OPS_L1
|
|
.section .l1.text
|
|
#else
|
|
.text
|
|
#endif
|
|
|
|
.align 2;
|
|
___divsi3 :
|
|
|
|
|
|
R3 = R0 ^ R1;
|
|
R0 = ABS R0;
|
|
|
|
CC = V;
|
|
|
|
r3 = rot r3 by -1;
|
|
r1 = abs r1; /* now both positive, r3.30 means "negate result",
|
|
** r3.31 means overflow, add one to result
|
|
*/
|
|
cc = r0 < r1;
|
|
if cc jump .Lret_zero;
|
|
r2 = r1 >> 15;
|
|
cc = r2;
|
|
if cc jump .Lidents;
|
|
r2 = r1 << 16;
|
|
cc = r2 <= r0;
|
|
if cc jump .Lidents;
|
|
|
|
DIVS(R0, R1);
|
|
DIVQ(R0, R1);
|
|
DIVQ(R0, R1);
|
|
DIVQ(R0, R1);
|
|
DIVQ(R0, R1);
|
|
DIVQ(R0, R1);
|
|
DIVQ(R0, R1);
|
|
DIVQ(R0, R1);
|
|
DIVQ(R0, R1);
|
|
DIVQ(R0, R1);
|
|
DIVQ(R0, R1);
|
|
DIVQ(R0, R1);
|
|
DIVQ(R0, R1);
|
|
DIVQ(R0, R1);
|
|
DIVQ(R0, R1);
|
|
DIVQ(R0, R1);
|
|
DIVQ(R0, R1);
|
|
|
|
R0 = R0.L (Z);
|
|
r1 = r3 >> 31; /* add overflow issue back in */
|
|
r0 = r0 + r1;
|
|
r1 = -r0;
|
|
cc = bittst(r3, 30);
|
|
if cc r0 = r1;
|
|
RTS;
|
|
|
|
/* Can't use the primitives. Test common identities.
|
|
** If the identity is true, return the value in R2.
|
|
*/
|
|
|
|
.Lidents:
|
|
CC = R1 == 0; /* check for divide by zero */
|
|
IF CC JUMP .Lident_return;
|
|
|
|
CC = R0 == 0; /* check for division of zero */
|
|
IF CC JUMP .Lzero_return;
|
|
|
|
CC = R0 == R1; /* check for identical operands */
|
|
IF CC JUMP .Lident_return;
|
|
|
|
CC = R1 == 1; /* check for divide by 1 */
|
|
IF CC JUMP .Lident_return;
|
|
|
|
R2.L = ONES R1;
|
|
R2 = R2.L (Z);
|
|
CC = R2 == 1;
|
|
IF CC JUMP .Lpower_of_two;
|
|
|
|
/* Identities haven't helped either.
|
|
** Perform the full division process.
|
|
*/
|
|
|
|
P1 = 31; /* Set loop counter */
|
|
|
|
[--SP] = (R7:5); /* Push registers R5-R7 */
|
|
R2 = -R1;
|
|
[--SP] = R2;
|
|
R2 = R0 << 1; /* R2 lsw of dividend */
|
|
R6 = R0 ^ R1; /* Get sign */
|
|
R5 = R6 >> 31; /* Shift sign to LSB */
|
|
|
|
R0 = 0 ; /* Clear msw partial remainder */
|
|
R2 = R2 | R5; /* Shift quotient bit */
|
|
R6 = R0 ^ R1; /* Get new quotient bit */
|
|
|
|
LSETUP(.Llst,.Llend) LC0 = P1; /* Setup loop */
|
|
.Llst: R7 = R2 >> 31; /* record copy of carry from R2 */
|
|
R2 = R2 << 1; /* Shift 64 bit dividend up by 1 bit */
|
|
R0 = R0 << 1 || R5 = [SP];
|
|
R0 = R0 | R7; /* and add carry */
|
|
CC = R6 < 0; /* Check quotient(AQ) */
|
|
/* we might be subtracting divisor (AQ==0) */
|
|
IF CC R5 = R1; /* or we might be adding divisor (AQ==1)*/
|
|
R0 = R0 + R5; /* do add or subtract, as indicated by AQ */
|
|
R6 = R0 ^ R1; /* Generate next quotient bit */
|
|
R5 = R6 >> 31;
|
|
/* Assume AQ==1, shift in zero */
|
|
BITTGL(R5,0); /* tweak AQ to be what we want to shift in */
|
|
.Llend: R2 = R2 + R5; /* and then set shifted-in value to
|
|
** tweaked AQ.
|
|
*/
|
|
r1 = r3 >> 31;
|
|
r2 = r2 + r1;
|
|
cc = bittst(r3,30);
|
|
r0 = -r2;
|
|
if !cc r0 = r2;
|
|
SP += 4;
|
|
(R7:5)= [SP++]; /* Pop registers R6-R7 */
|
|
RTS;
|
|
|
|
.Lident_return:
|
|
CC = R1 == 0; /* check for divide by zero => 0x7fffffff */
|
|
R2 = -1 (X);
|
|
R2 >>= 1;
|
|
IF CC JUMP .Ltrue_ident_return;
|
|
|
|
CC = R0 == R1; /* check for identical operands => 1 */
|
|
R2 = 1 (Z);
|
|
IF CC JUMP .Ltrue_ident_return;
|
|
|
|
R2 = R0; /* assume divide by 1 => numerator */
|
|
/*FALLTHRU*/
|
|
|
|
.Ltrue_ident_return:
|
|
R0 = R2; /* Return an identity value */
|
|
R2 = -R2;
|
|
CC = bittst(R3,30);
|
|
IF CC R0 = R2;
|
|
.Lzero_return:
|
|
RTS; /* ...including zero */
|
|
|
|
.Lpower_of_two:
|
|
/* Y has a single bit set, which means it's a power of two.
|
|
** That means we can perform the division just by shifting
|
|
** X to the right the appropriate number of bits
|
|
*/
|
|
|
|
/* signbits returns the number of sign bits, minus one.
|
|
** 1=>30, 2=>29, ..., 0x40000000=>0. Which means we need
|
|
** to shift right n-signbits spaces. It also means 0x80000000
|
|
** is a special case, because that *also* gives a signbits of 0
|
|
*/
|
|
|
|
R2 = R0 >> 31;
|
|
CC = R1 < 0;
|
|
IF CC JUMP .Ltrue_ident_return;
|
|
|
|
R1.l = SIGNBITS R1;
|
|
R1 = R1.L (Z);
|
|
R1 += -30;
|
|
R0 = LSHIFT R0 by R1.L;
|
|
r1 = r3 >> 31;
|
|
r0 = r0 + r1;
|
|
R2 = -R0; // negate result if necessary
|
|
CC = bittst(R3,30);
|
|
IF CC R0 = R2;
|
|
RTS;
|
|
|
|
.Lret_zero:
|
|
R0 = 0;
|
|
RTS;
|