gfiber / barebox / mindspeed / 74944e8936cf3d873f6f16c17d15587a301240be / . / arch / blackfin / lib / divsi3.S

/* | |

* 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 | |

* Rev: $Id: divsi3.S 2794 2007-03-05 05:27:47Z cooloney $ | |

* | |

* 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; |