/****************************************************************************/
/*	Copyright (c) 2009, Bendor Research Pty. Ltd. All rights reserved.		*/
/*	This software is released under the GNU General Public License, version	*/
/*	3, with the exemptions that 1) the header files installed by this 	 	*/
/*	package can be included in closed-source code and 2) the libraries		*/
/*	built by this package can be statically linked into a closed-source 	*/
/*	binary. See the file LICENSE for the details.							*/
/*	This software is provided "AS IS", without any warranty or guarantee.	*/
/*	By using this software you acknowledge that neither Bendor Research nor	*/
/*	any of its personnel or affiliates can be held liable for any damages,	*/
/*	including, but not limited to, loss of business, loss of money, loss of */
/*	life, loss of reputation, arising out of this software being faulty or	*/
/*	not performing as advertised, even if Bendor Research was aware of such	*/
/*	faults in the software. You use this software at your own risk, any and */
/*	all liabilities that arise out of your using this software are strictly	*/
/*	yours.																	*/
/****************************************************************************/

/*
*	Register definitions
*/

#define	NL		r0					// Numerator lower half
#define	NH		r1					// Numerator upper half
#define	DL		r2					// Denominator lower half
#define	DH		r3					// Denominator upper half
#define	QL		r4					// Quotient lower half
#define	QH		r5					// Quotient upper half
#define	TL		r6					// Temporary lower half
#define	TH		r7					// Temporary upper half
#define	SN		r8					// Numerator normalisation shift amount
#define	SD		r9					// Denominator additional shift amount
#define	SQ		r10					// Quotient upper half shift count
#define	LC		r11					// Loop count

/*
*	This is the loop unroll factor.
*	If you set it to 0, then nothing will be unrolled. It will be very
*	slow. Setting it to 1, 2, 3 or 4 will unroll 2, 4, 8 or 16 steps of
*	the loop. Every unroll saves you quite a lot. In fact, the code around
*	the loop is written with unrolling in mind, if you do not unroll the
*	loop, you will execute the unroll related code many times absolutely
*	unnecessarily. Since the inside of a loop is only 7 instructions,
*	unrolling 8 of these is only 56 instructions, it's worth it.
*/

#define	LUF	3

/*
*	Unsigned 64-bit division core
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*	It divides r1:r0 by r3:r2 and returns the quotient in r1:r0
*	and the remainder in r3:r2.
*
*	The function uses the classical subtract and restore algorithm,
*	nothing fancy, really, just the proc is fast.
*
*	IMPORTANT:	Normal C calling conventions permit the use of the
*				r12 register. This function, however, MUST NOT clobber
*				that register because the functions calling this one
*				assume that it is preserved. In fact, r12 contains the
*				return address of the function that called this one.
*/
	.text
	.align	2
	.code	32
	.globl	_bgs_udiv64
	.func	_bgs_udiv64
_bgs_udiv64:
	tst		DH,DH					// Check for division by 0
	tsteq	DL,DL					// in which case we jump to the error
	beq		__trap_div0				// handler
	cmp		NH,DH					// Compare the numerator and the
	cmpeq	NL,DL					// denominator and if the numerator
	movcc	DH,NH					// is less than the denominator,
	movcc	DL,NL					// then the remainder is the numerator
	movcc	NH,#0					// and the quotient should be set
	movcc	NL,#0					// to 0 and
	bxcc	lr						// we can return immediately.
	stmdb	sp!,{r4-r11}			// We do divide, save the registers

	// Normalise the numerator. As we shift it up, we can shift the
	// denumerator with the same amount, since we know that the latter
	// is not larger than the former. Note that in theory we don't have to
	// normalise the numerator, but we do it because the code below is linear.
	// If we want to shift up the denominator until its highest set bit
	// is at the same position as that of the numerator, we'd need to
	// do pretty complex compares. This is faster.

	mov		SN,#0
	mov		LC,#0
	tst		NH,NH
	moveq	LC,#32
	moveq	NH,NL
	moveq	NL,#0
	moveq	DH,DL
	moveq	DL,#0
	tst		NH,NH
	bmi		num_normal
	movs	TH,NH,lsr #16
	addeq	SN,SN,#16
	moveq	NH,NH,lsl #16
	tst		NH,#0xff000000
	addeq	SN,SN,#8
	moveq	NH,NH,lsl #8
	tst		NH,#0xf0000000
	addeq	SN,SN,#4
	moveq	NH,NH,lsl #4
	tst		NH,#0xc0000000
	addeq	SN,SN,#2
	moveq	NH,NH,lsl #2
	tst		NH,#0x80000000
	addeq	SN,SN,#1
	moveq	NH,NH,lsl #1
	rsb		TH,SN,#32
	orr		NH,NH,NL,lsr TH
	mov		NL,NL,lsl SN
	mov		DH,DH,lsl SN
	orr		DH,DH,DL,lsr TH
	mov		DL,DL,lsl SN
num_normal:
	add		SN,SN,LC

	// At this point the numerator is normalised and SN contains the number
	// of bits by which we shifted it up. Since we know that it is larger than
	// the denominator, we also shifted up the denominator by the same amount
	// without fear of overflow. Now shift up the denominator until its top
	// bit becomes set and count the number of extra shifts we need for that.

	mov		SD,#0
	mov		LC,#0
	tst		DH,DH
	moveq	DH,DL
	moveq	DL,#0
	moveq	LC,#32
	tst		DH,DH
	bmi		den_normal
	movs	TH,DH,lsr #16
	addeq	SD,SD,#16
	moveq	DH,DH,lsl #16
	tst		DH,#0xff000000
	addeq	SD,SD,#8
	moveq	DH,DH,lsl #8
	tst		DH,#0xf0000000
	addeq	SD,SD,#4
	moveq	DH,DH,lsl #4
	tst		DH,#0xc0000000
	addeq	SD,SD,#2
	moveq	DH,DH,lsl #2
	tst		DH,#0x80000000
	addeq	SD,SD,#1
	moveq	DH,DH,lsl #1
	rsb		TH,SD,#32
	orr		DH,DH,DL,lsr TH
	mov		DL,DL,lsl SD
den_normal:
	add		SD,SD,LC

	// Now both numerator and denominator has their top bit set.
	// SN contains the common shift amount, SD contains the additional
	// shift amount for the denominator. SD+1 will be our loop count.
	// The looping is a bit tricky. In the loop only the lower half of
	// the quotient is used. If the loop count is >= 32, then we do a
	// 32-iteration spin first. Then we move the quotient's lower half to
	// its upper half and spin the loop for the remaining number of times.
	// Since we unrolled the loop to a certain factor, we split this second
	// round into a bit where the full unrolled loop should be run and to
	// a partial one, where only some of the steps in the unrolled version
	// should be done. Furthermore, at the end of each unrolled block we
	// check if the numerator became 0, in which case we can stop the
	// whole thing.

	mov		QL,#0				// Prepare the quotient
	mov		SQ,#-1				// Indicate the first 32-bit block
	add		SD,SD,#1			// Loop count is shift count + 1
	subs	LC,SD,#32			// Check if it is more than 31
	bmi		outer				// If not, go directly to the end of the loop
	mov		SD,LC				// If yes, keep the difference for further use
	mov		LC,#32>>LUF			// Load the initial loop count

	// This is the actual division loop

loop:

	// This is a block containing 2^LUF steps of the division

	.rept	1<<LUF
0:
	subs	TL,NL,DL			// Subtract the denominator from the numerator
	sbcs	TH,NH,DH
	movcs	NL,TL				// If we succeeded, keep the result
	movcs	NH,TH
	adc		QL,QL,QL			// Shift the result in
	movs	DH,DH,lsr #1		// Shift the denominator to the right
	mov		DL,DL,rrx
1:
	.endr

	// An unrolled block finished

	tst		NL,NL				// Check if the numerator became
	tsteq	NH,NH				// zero during the last round
	beq		numzero				// If yes, we abort the whole looping
	subs	LC,LC,#1			// Otherwise decrement the loop count
	bne		loop				// and loop if we have any more to do

	// The inner loop of 2^LUF * LC steps finished

outer:
	tst		SQ,SQ				// Was it the first block?
	movmi	QH,QL				// If it was, move the result
	movmi	QL,#0				// up to the upper word
	movmi	SQ,SD				// and remember the shift value
	movs	LC,SD,lsr #LUF		// How many full unrolled loops left?
	subne	SD,SD,LC,lsl #LUF	// Keep track of the remaining single steps
	bne		loop				// and do the complete block ones
	movs	LC,SD				// Number of single steps
	beq		done				// If 0, we're done
	mov		SD,#0				// Indicate that this is the last outer loop
	rsb		LC,LC,#1<<LUF		// Number of steps not to be done in the block
	adr		TH,loop				// Start address of the loop
	mov		TL,#1b-0b			// Length of a step
	mla		TH,TL,LC,TH			// Entry address into the block
	mov		LC,#1				// We don't want to loop at all
	bx		TH					// Jump into the middle of the block

	// The numerator became zero

numzero:
	sub		LC,LC,#1			// Adjust the loop count
	mov		LC,LC,lsl #LUF
	mov		QL,QL,lsl LC		// Adjust the quotient
	tst		SQ,SQ				// Did it happen in the first block?
	movmi	QH,QL				// If it yes, move the result
	movmi	QL,#0				// up to the upper word
	movmi	SQ,SD				// and remember the shift value
	movpl	QL,QL,lsl SD		// If not, adjust the quotient

	// When we are here, the whole division is done but there are bits and
	// pieces in various registers. This is what we have:
	// SN contains a shift amount, 0 - 63
	// NH:NL contains the remainder, shifted to the left by SN bits
	// SQ contains a shift amount, 0 - 32
	// If SQ is 0, then QH contains the lower 32 bits of the quotient
	// QL is zero and the quotient is only 32 bits. If SQ is 32,
	// then QH:QL contain the quotient. If SQ is any other value,
	// then QL contains the lower SQ bits of the quotient and QH
	// contains the upper 32 bits of it and should be shifted into
	// place.
	// DH:DL does not hold anything useful.
	// We want the final quotient in NH:NL and the remainder in DH:DL.

done:
	cmp		SN,#32				// Do we have to shift more than 31 bits?
	movge	NL,NH				// If yes, we shift words then
	movge	NH,#0
	subges	SN,SN,#32			// decrement the shift amount and set the Z bit
	rsbne	LC,SN,#32			// If we have to shift, calc the complementary
	movne	NL,NL,lsr SN		// and shift the remainder down
	orrne	NL,NL,NH,lsl LC
	movne	NH,NH,lsr SN
	mov		DH,NH				// Move the remainder into its final place
	mov		DL,NL
	tst		SQ,SQ				// Test SQ
	moveq	NL,QH				// If 0, swap the quotient halves and
	moveq	NH,QL				// move them into place
	movne	NL,QL				// Otherwise move the quotient to place
	movne	NH,QH
	rsbne	LC,SQ,#32			// and calculate the complementary shift amount
	orrne	NL,NL,NH,lsl SQ		// If we have to shift at all, do the shifting
	movne	NH,NH,lsr LC		// Now everything is finished.
	ldmia	sp!,{r4-r11}		// Restore the registers
	bx		lr					// and return
	.endfunc

