mirror of
				https://source.denx.de/u-boot/u-boot.git
				synced 2025-11-04 02:11:25 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			214 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			214 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
/* SPDX-License-Identifier: GPL-2.0 */
 | 
						|
/*
 | 
						|
 *  linux/arch/arm/lib/div64.S
 | 
						|
 *
 | 
						|
 *  Optimized computation of 64-bit dividend / 32-bit divisor
 | 
						|
 *
 | 
						|
 *  Author:	Nicolas Pitre
 | 
						|
 *  Created:	Oct 5, 2003
 | 
						|
 *  Copyright:	Monta Vista Software, Inc.
 | 
						|
 */
 | 
						|
 | 
						|
#include <linux/linkage.h>
 | 
						|
#include <asm/assembler.h>
 | 
						|
#ifdef __UBOOT__
 | 
						|
#define UNWIND(x...)
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef __ARMEB__
 | 
						|
#define xh r0
 | 
						|
#define xl r1
 | 
						|
#define yh r2
 | 
						|
#define yl r3
 | 
						|
#else
 | 
						|
#define xl r0
 | 
						|
#define xh r1
 | 
						|
#define yl r2
 | 
						|
#define yh r3
 | 
						|
#endif
 | 
						|
 | 
						|
/*
 | 
						|
 * __do_div64: perform a division with 64-bit dividend and 32-bit divisor.
 | 
						|
 *
 | 
						|
 * Note: Calling convention is totally non standard for optimal code.
 | 
						|
 *       This is meant to be used by do_div() from include/asm/div64.h only.
 | 
						|
 *
 | 
						|
 * Input parameters:
 | 
						|
 *	xh-xl	= dividend (clobbered)
 | 
						|
 *	r4	= divisor (preserved)
 | 
						|
 *
 | 
						|
 * Output values:
 | 
						|
 *	yh-yl	= result
 | 
						|
 *	xh	= remainder
 | 
						|
 *
 | 
						|
 * Clobbered regs: xl, ip
 | 
						|
 */
 | 
						|
 | 
						|
.pushsection .text.__do_div64, "ax"
 | 
						|
ENTRY(__do_div64)
 | 
						|
UNWIND(.fnstart)
 | 
						|
 | 
						|
	@ Test for easy paths first.
 | 
						|
	subs	ip, r4, #1
 | 
						|
	bls	9f			@ divisor is 0 or 1
 | 
						|
	tst	ip, r4
 | 
						|
	beq	8f			@ divisor is power of 2
 | 
						|
 | 
						|
	@ See if we need to handle upper 32-bit result.
 | 
						|
	cmp	xh, r4
 | 
						|
	mov	yh, #0
 | 
						|
	blo	3f
 | 
						|
 | 
						|
	@ Align divisor with upper part of dividend.
 | 
						|
	@ The aligned divisor is stored in yl preserving the original.
 | 
						|
	@ The bit position is stored in ip.
 | 
						|
 | 
						|
#if __LINUX_ARM_ARCH__ >= 5
 | 
						|
 | 
						|
	clz	yl, r4
 | 
						|
	clz	ip, xh
 | 
						|
	sub	yl, yl, ip
 | 
						|
	mov	ip, #1
 | 
						|
	mov	ip, ip, lsl yl
 | 
						|
	mov	yl, r4, lsl yl
 | 
						|
 | 
						|
#else
 | 
						|
 | 
						|
	mov	yl, r4
 | 
						|
	mov	ip, #1
 | 
						|
1:	cmp	yl, #0x80000000
 | 
						|
	cmpcc	yl, xh
 | 
						|
	movcc	yl, yl, lsl #1
 | 
						|
	movcc	ip, ip, lsl #1
 | 
						|
	bcc	1b
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
	@ The division loop for needed upper bit positions.
 | 
						|
	@ Break out early if dividend reaches 0.
 | 
						|
2:	cmp	xh, yl
 | 
						|
	orrcs	yh, yh, ip
 | 
						|
	subscs	xh, xh, yl
 | 
						|
	movsne	ip, ip, lsr #1
 | 
						|
	mov	yl, yl, lsr #1
 | 
						|
	bne	2b
 | 
						|
 | 
						|
	@ See if we need to handle lower 32-bit result.
 | 
						|
3:	cmp	xh, #0
 | 
						|
	mov	yl, #0
 | 
						|
	cmpeq	xl, r4
 | 
						|
	movlo	xh, xl
 | 
						|
	retlo	lr
 | 
						|
 | 
						|
	@ The division loop for lower bit positions.
 | 
						|
	@ Here we shift remainer bits leftwards rather than moving the
 | 
						|
	@ divisor for comparisons, considering the carry-out bit as well.
 | 
						|
	mov	ip, #0x80000000
 | 
						|
4:	movs	xl, xl, lsl #1
 | 
						|
	adcs	xh, xh, xh
 | 
						|
	beq	6f
 | 
						|
	cmpcc	xh, r4
 | 
						|
5:	orrcs	yl, yl, ip
 | 
						|
	subcs	xh, xh, r4
 | 
						|
	movs	ip, ip, lsr #1
 | 
						|
	bne	4b
 | 
						|
	ret	lr
 | 
						|
 | 
						|
	@ The top part of remainder became zero.  If carry is set
 | 
						|
	@ (the 33th bit) this is a false positive so resume the loop.
 | 
						|
	@ Otherwise, if lower part is also null then we are done.
 | 
						|
6:	bcs	5b
 | 
						|
	cmp	xl, #0
 | 
						|
	reteq	lr
 | 
						|
 | 
						|
	@ We still have remainer bits in the low part.  Bring them up.
 | 
						|
 | 
						|
#if __LINUX_ARM_ARCH__ >= 5
 | 
						|
 | 
						|
	clz	xh, xl			@ we know xh is zero here so...
 | 
						|
	add	xh, xh, #1
 | 
						|
	mov	xl, xl, lsl xh
 | 
						|
	mov	ip, ip, lsr xh
 | 
						|
 | 
						|
#else
 | 
						|
 | 
						|
7:	movs	xl, xl, lsl #1
 | 
						|
	mov	ip, ip, lsr #1
 | 
						|
	bcc	7b
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
	@ Current remainder is now 1.  It is worthless to compare with
 | 
						|
	@ divisor at this point since divisor can not be smaller than 3 here.
 | 
						|
	@ If possible, branch for another shift in the division loop.
 | 
						|
	@ If no bit position left then we are done.
 | 
						|
	movs	ip, ip, lsr #1
 | 
						|
	mov	xh, #1
 | 
						|
	bne	4b
 | 
						|
	ret	lr
 | 
						|
 | 
						|
8:	@ Division by a power of 2: determine what that divisor order is
 | 
						|
	@ then simply shift values around
 | 
						|
 | 
						|
#if __LINUX_ARM_ARCH__ >= 5
 | 
						|
 | 
						|
	clz	ip, r4
 | 
						|
	rsb	ip, ip, #31
 | 
						|
 | 
						|
#else
 | 
						|
 | 
						|
	mov	yl, r4
 | 
						|
	cmp	r4, #(1 << 16)
 | 
						|
	mov	ip, #0
 | 
						|
	movhs	yl, yl, lsr #16
 | 
						|
	movhs	ip, #16
 | 
						|
 | 
						|
	cmp	yl, #(1 << 8)
 | 
						|
	movhs	yl, yl, lsr #8
 | 
						|
	addhs	ip, ip, #8
 | 
						|
 | 
						|
	cmp	yl, #(1 << 4)
 | 
						|
	movhs	yl, yl, lsr #4
 | 
						|
	addhs	ip, ip, #4
 | 
						|
 | 
						|
	cmp	yl, #(1 << 2)
 | 
						|
	addhi	ip, ip, #3
 | 
						|
	addls	ip, ip, yl, lsr #1
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
	mov	yh, xh, lsr ip
 | 
						|
	mov	yl, xl, lsr ip
 | 
						|
	rsb	ip, ip, #32
 | 
						|
 ARM(	orr	yl, yl, xh, lsl ip	)
 | 
						|
 THUMB(	lsl	xh, xh, ip		)
 | 
						|
 THUMB(	orr	yl, yl, xh		)
 | 
						|
	mov	xh, xl, lsl ip
 | 
						|
	mov	xh, xh, lsr ip
 | 
						|
	ret	lr
 | 
						|
 | 
						|
	@ eq -> division by 1: obvious enough...
 | 
						|
9:	moveq	yl, xl
 | 
						|
	moveq	yh, xh
 | 
						|
	moveq	xh, #0
 | 
						|
	reteq	lr
 | 
						|
UNWIND(.fnend)
 | 
						|
 | 
						|
UNWIND(.fnstart)
 | 
						|
UNWIND(.pad #4)
 | 
						|
UNWIND(.save {lr})
 | 
						|
Ldiv0_64:
 | 
						|
	@ Division by 0:
 | 
						|
	str	lr, [sp, #-8]!
 | 
						|
	bl	__div0
 | 
						|
 | 
						|
	@ as wrong as it could be...
 | 
						|
	mov	yl, #0
 | 
						|
	mov	yh, #0
 | 
						|
	mov	xh, #0
 | 
						|
	ldr	pc, [sp], #8
 | 
						|
 | 
						|
UNWIND(.fnend)
 | 
						|
ENDPROC(__do_div64)
 | 
						|
.popsection
 |