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

#include <arm_func.h>

	afunc	memcmp_fast
	mov		r3,r0					// Move one address to R3
	cmp		r2,#8					// Test is at least 8 bytes long?
	bhs		1f						// If yes, prepare for word loads
0:
	subs	r2,r2,#1				// Any more bytes to test ?
	movmi	r0,#0					// If not, we can return
	bxmi	lr						// with 0 immediately
	ldrb	r0,[r3],#1				// Fetch from one area
	ldrb	r12,[r1],#1				// Fetch from the other
	subs	r0,r0,r12				// Make a difference
	beq		0b						// If they are equal, keep going
	bx		lr						// Otherwise return

	// We have at least 8 bytes to process

1:
	tst		r3,#3					// Alignment of the first area
	beq		0f
	ldrb	r0,[r3],#1				// Misaligned, fetch a byte
	ldrb	r12,[r1],#1				// from both memory areas
	subs	r0,r0,r12				// Compare them
	bxne	lr						// and return if they differ
	sub		r2,r2,#1				// Decrement the byte count
	b		1b						// and keep testing

	// First address is aligned
	// R2 is bytes remaining, R3 is the first address, R1 is the second

0:
	stmdb	sp!,{r4,r5,r6}			// We need some registers
	mov		r4,#0					// Misalignment count
	tst		r1,#3					// Second area aligned?
	beq		2f						// Yes, no misalignment at all!
	ldrb	r5,[r1],#1				// Get a byte
	mov		r4,#8					// Increment misalignment count
	tst		r1,#3					// Is it now aligned?
	beq		1f						// If yes, start working
	ldrb	r12,[r1],#1				// Get a byte
	orr		r5,r5,r12,lsl #8		// Put it into its place
	mov		r4,#16					// Increment misalignment count
	tst		r1,#3					// Is it now aligned?
	beq		1f						// If yes, start working
	ldrb	r12,[r1],#1				// Get a byte
	orr		r5,r5, r12,lsl #16		// Put it into its place
	mov		r4,#24					// Increment misalignment count

	// Second address was not aligned, now it is.
	// The partial word is in R5, the shift amount is in R4,
	// the opposite shift amount will be R6.

1:
	rsb		r6,r4,#32				// Other direction shift count
	sub		r2,r2,#4				// Protect against overreading
0:
	subs	r2,r2,#4				// 4 more bytes left?
	bmi		1f						// If not finish
	ldr		r0,[r3],#4				// Fetch a word from the first source
	ldr		r12,[r1],#4				// Fetch a word from the second source
	orr		r5,r5, r12,lsl r4		// Build a full word
	cmp		r0,r5					// Match ?
	moveq	r5,r12,lsr r6			// Build the next partial word
	beq		0b						// and do it again if they matched
	mov		r4,r0					// Move the first source

	// Differing words.
	// The first source word is in R4, the second is in R5

0:
	and		r0,r4,#0xff				// Get next byte from first src
	and		r12,r5,#0xff			// Get next byte from second src
	subs	r0,r0,r12				// Create the difference
	ldmneia	sp!,{r4-r6}				// If they were different, then
	bxne	lr						// return with the result
	mov		r4,r4,lsr #8			// Otherwise, prepare for the
	mov		r5,r5,lsr #8			// next byte in both sources
	b		0b						// and keep hunting for the difference

	// Both addresses are now aligned

2:
	subs	r2,r2,#4				// Do we have a full word ?
	bmi		0f						// If not, leave it
	ldr		r4,[r3],#4				// Fetch from the first area
	ldr		r5,[r1],#4				// Fetch from the second area
	cmp		r4,r5					// Test them
	beq		2b						// and keep going if they match
	b		0b

	// We do not have a full word remaining

0:
	sub		r2,r2,#4				// Coming from aligned case, make it
	mov		r4,#0					// identical to the aligned one
1:
	add		r2,r2,#8				// Restore remaining byte count

	// Here we drop back to byte by byte compare. At this point
	// R1 is the second address, R2 is the remaining byte count,
	// R3 if the first address, R4 is the shift count and R5 has
	// R4/8 bytes already fetched from the second area
1:
	mov		r0,#0					// Prepare for return on equal
0:
	subs	r2,r2,#1				// Any bytes left?
	ldmmiia	sp!,{r4-r6}				// If not, they're equal, so
	bxmi	lr						// return with the 0
	ldrb	r0,[r3],#1				// Otherwise, fetch a byte from the first
	subs	r4,r4,#8				// Check if we have already fetched second
	ldrmib	r5,[r1],#1				// If not, fetch a byte from the second
	and		r12,r5,#0xff			// Prepare the second byte
	mov		r5,r5,lsr #8			// and process the already fetched ones
	subs	r0,r0,r12				// Create the difference
	beq		0b						// If there's none, keep going
	ldmia	sp!,{r4-r6}				// Otherwise we return with
	bx		lr						// the difference

