/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
 */


#include <sys/param.h>
#include <sys/errno.h>
#include <sys/asm_linkage.h>
#include <sys/vtrace.h>
#include <sys/machthread.h>
#include <sys/clock.h>
#include <sys/asi.h>
#include <sys/fsr.h>
#include <sys/privregs.h>
#include <sys/machasi.h>
#include <sys/niagaraasi.h>

#if !defined(lint)
#include "assym.h"
#endif	/* lint */


/*
 * Pseudo-code to aid in understanding the control flow of the
 * bcopy/kcopy routine.
 *
 *	! WARNING : <Register usage convention>
 *	! In kcopy() the %o5, holds previous error handler and a flag
 *	! LOFAULT_SET (low bits). The %o5 is null in bcopy().
 *	! The %o5 is not available for any other use.
 *
 * On entry:
 *	! Determine whether to use the FP register version or the
 *	! the leaf routine version depending on the size of the copy.
 *	! Set up error handling accordingly.
 *	! The transition point depends on FP_COPY
 *	! For both versions %o5 is reserved
 *
 * kcopy():
 *	if(length > FP_COPY)
 *		go to regular_kcopy
 *
 *	! Setup_leaf_rtn_error_handler
 *	%o5 = curthread->t_lofault;		! save existing handler in %o5
 *	%o5 |= LOFAULT_SET;			! ORed with LOFAULT_SET flag
 *	curthread->t_lofault = .sm_copyerr;
 *	goto small_bcopy();
 *
 * regular_kcopy:
 *	save_registers()
 *	%o5 = curthread->t_lofault;		! save existing handler in %o5
 *	%o5 |= LOFAULT_SET;			! ORed with LOFAULT_SET flag
 *	curthread->t_lofault = .copyerr;
 *	goto do_copy();
 *
 * bcopy():
 *	if(length > FP_COPY)
 *		go to regular_bcopy
 *
 *	! Setup_leaf_rtn_error_handler
 *	%o5 = curthread->t_lofault;		! save existing handler in %o5
 *	curthread->t_lofault = .sm_copyerr;
 *	goto small_bcopy();
 *
 * regular_bcopy:
 *	%o5 = curthread->t_lofault;		! save existing handler in %o5
 *	curthread->t_lofault = .copyerr;
 *	goto do_copy();
 *
 * small_bcopy:
 *	! handle copies smaller than FP_COPY
 *	restore t_lofault handler
 *	exit
 *
 * do_copy:
 *	! handle copies larger than FP_COPY
 *	save fp_regs
 * 	blockcopy;
 *	restore fp_regs
 *	restore t_lofault handler if came from kcopy();
 *
 *
 * In leaf lofault handler:
 *	curthread->t_lofault = (%o5 & ~LOFAULT_SET);	! restore old t_lofault
 *	return (errno)
 *
 * In lofault handler:
 *	curthread->t_lofault = (%o5 & ~LOFAULT_SET);	! restore old t_lofault
 *	restore fp_regs
 *	return (errno)
 *
 *
 *
 * For all of bcopy/copyin/copyout the copy logic is specialized according
 * to how the src and dst is aligned and how much data needs to be moved.
 * The following comments apply to the N2/RF code (#if !defined(NIAGARA_IMPL))
 * 
 * N2/RF Flow :
 *
 * if (count < FP_COPY) {  (584 bytes)
 *   set small fault handler (no register window save/restore)
 *   if count < SHORTCOPY  (7 bytes)
 *	copy bytes; go to short_exit
 *   else
 *   determine dst alignment, move minimum bytes/halfwords to
 *   get dst aligned on long word boundary
 *     if( src is on long word boundary ) {
 * medlong:					   src/dst aligned on 8 bytes
 *	 copy with ldx/stx in 4-way unrolled loop;
 *       copy final 0-31 bytes; go to short_exit
 *     } else {					src/dst not aligned on 8 bytes
 *     if src is word aligned, ld/st words in 32-byte chunks
 *     if src is half word aligned, ld half, ld word, ld half; pack 
 *		into long word, store long words in 32-byte chunks
 *     if src is byte aligned, ld byte,half,word parts;  pack into long
 *	   word, store long words in 32-byte chunks
 *     move final 0-31 bytes according to src alignment;  go to short_exit
 * short_exit:
 *     restore trap handler if needed, retl
 * else {					   More than FP_COPY bytes
 *     set fault handler
 *     disable kernel preemption
 *     save registers, save FP registers if in use
 *     move bytes to align destination register on long word boundary
 *     if(src is on long word boundary) {	   src/dst aligned on 8 bytes
 *       align dst on 64 byte boundary;  use 8-way test for each of 8 possible
 *       src alignments relative to a 64 byte boundary to select the
 *       16-way unrolled loop (128 bytes) to use for
 *       block load, fmovd, block-init-store, block-store, fmovd operations
 *       then go to remain_stuff.
 * remain_stuff: move remaining bytes. go to long_exit
 *     } else {
 *       setup alignaddr for faligndata instructions
 *       align dst on 64 byte boundary; use 8-way test for each of 8 possible
 *       src alignments to nearest long word relative to 64 byte boundary to
 *       select the 8-way unrolled loop (64 bytes) to use for
 *       block load, falign, fmovd, block-store loop
 *	 (only use block-init-store when src/dst on 8 byte boundaries.)
 *       goto unalign_done.
 * unalign_done:
 *       move remaining bytes for unaligned cases. go to long_exit
 * long_exit:
 *       restore %gsr, FP regs (either from stack or set to zero),
 *       restore trap handler, check for kernel preemption request,
 *       handle if needed, ret.
 * }
 *
 * Other platforms include hw_bcopy_limit_[1248] to control the exact
 * point where the FP register code is used. On those platforms, the
 * FP register code did not leave data in L2 cache, potentially affecting
 * performance more than the gain/loss from the algorithm difference.
 * For N2/RF, block store places data in the L2 cache, so use or non-use
 * of the FP registers has no effect on L2 cache behavior.
 * The cost for testing hw_bcopy_limit_* according to different
 * alignments exceeds 50 cycles for all cases, even when hw_bcopy_limits
 * were not used. That cost was judged too high relative to the benefits,
 * so the hw_bcopy_limit option is omitted from this code.
 */

/*
 * Less then or equal this number of bytes we will always copy byte-for-byte
 */
#define	SMALL_LIMIT	7

/*
 * LOFAULT_SET : Flag set by kzero and kcopy to indicate that t_lofault
 * handler was set
 */
#define	LOFAULT_SET 2

/*
 * This define is to align data for the unaligned source cases.
 * The data1, data2 and data3 is merged into data1 and data2.
 * The data3 is preserved for next merge.
 */
#define	ALIGN_DATA(data1, data2, data3, lshift, rshift, tmp)	\
	sllx	data1, lshift, data1				;\
	srlx	data2, rshift, tmp				;\
	or	data1, tmp, data1				;\
	sllx	data2, lshift, data2				;\
	srlx	data3, rshift, tmp				;\
	or	data2, tmp, data2
/*
 * This macro is to align the data. Basically it merges
 * data1 and data2 to form double word.
 */
#define	ALIGN_DATA_EW(data1, data2, lshift, rshift, tmp)	\
	sllx	data1, lshift, data1				;\
	srlx	data2, rshift, tmp				;\
	or	data1, tmp, data1

#if !defined(NIAGARA_IMPL)
/*
 * Flags set in the lower bits of the t_lofault address:
 * FPUSED_FLAG: The FP registers were in use and must be restored
 * LOFAULT_SET: Set for bcopy calls, cleared for kcopy calls
 * COPY_FLAGS: Both of the above
 *
 * Other flags:
 * KPREEMPT_FLAG: kpreempt needs to be called
 */
#define	FPUSED_FLAG	1
#define	LOFAULT_SET	2
#define	COPY_FLAGS	(FPUSED_FLAG | LOFAULT_SET)
#define	KPREEMPT_FLAG	4

#define	ALIGN_OFF_1_7			\
	faligndata %d0, %d2, %d48	;\
	faligndata %d2, %d4, %d50	;\
	faligndata %d4, %d6, %d52	;\
	faligndata %d6, %d8, %d54	;\
	faligndata %d8, %d10, %d56	;\
	faligndata %d10, %d12, %d58	;\
	faligndata %d12, %d14, %d60	;\
	faligndata %d14, %d16, %d62

#define	ALIGN_OFF_8_15			\
	faligndata %d2, %d4, %d48	;\
	faligndata %d4, %d6, %d50	;\
	faligndata %d6, %d8, %d52	;\
	faligndata %d8, %d10, %d54	;\
	faligndata %d10, %d12, %d56	;\
	faligndata %d12, %d14, %d58	;\
	faligndata %d14, %d16, %d60	;\
	faligndata %d16, %d18, %d62

#define	ALIGN_OFF_16_23			\
	faligndata %d4, %d6, %d48	;\
	faligndata %d6, %d8, %d50	;\
	faligndata %d8, %d10, %d52	;\
	faligndata %d10, %d12, %d54	;\
	faligndata %d12, %d14, %d56	;\
	faligndata %d14, %d16, %d58	;\
	faligndata %d16, %d18, %d60	;\
	faligndata %d18, %d20, %d62

#define	ALIGN_OFF_24_31			\
	faligndata %d6, %d8, %d48	;\
	faligndata %d8, %d10, %d50	;\
	faligndata %d10, %d12, %d52	;\
	faligndata %d12, %d14, %d54	;\
	faligndata %d14, %d16, %d56	;\
	faligndata %d16, %d18, %d58	;\
	faligndata %d18, %d20, %d60	;\
	faligndata %d20, %d22, %d62

#define	ALIGN_OFF_32_39			\
	faligndata %d8, %d10, %d48	;\
	faligndata %d10, %d12, %d50	;\
	faligndata %d12, %d14, %d52	;\
	faligndata %d14, %d16, %d54	;\
	faligndata %d16, %d18, %d56	;\
	faligndata %d18, %d20, %d58	;\
	faligndata %d20, %d22, %d60	;\
	faligndata %d22, %d24, %d62

#define	ALIGN_OFF_40_47			\
	faligndata %d10, %d12, %d48	;\
	faligndata %d12, %d14, %d50	;\
	faligndata %d14, %d16, %d52	;\
	faligndata %d16, %d18, %d54	;\
	faligndata %d18, %d20, %d56	;\
	faligndata %d20, %d22, %d58	;\
	faligndata %d22, %d24, %d60	;\
	faligndata %d24, %d26, %d62

#define	ALIGN_OFF_48_55			\
	faligndata %d12, %d14, %d48	;\
	faligndata %d14, %d16, %d50	;\
	faligndata %d16, %d18, %d52	;\
	faligndata %d18, %d20, %d54	;\
	faligndata %d20, %d22, %d56	;\
	faligndata %d22, %d24, %d58	;\
	faligndata %d24, %d26, %d60	;\
	faligndata %d26, %d28, %d62

#define	ALIGN_OFF_56_63			\
	faligndata %d14, %d16, %d48	;\
	faligndata %d16, %d18, %d50	;\
	faligndata %d18, %d20, %d52	;\
	faligndata %d20, %d22, %d54	;\
	faligndata %d22, %d24, %d56	;\
	faligndata %d24, %d26, %d58	;\
	faligndata %d26, %d28, %d60	;\
	faligndata %d28, %d30, %d62

/*
 * FP_COPY indicates the minimum number of bytes needed
 * to justify using FP/VIS-accelerated memory operations.
 * The FPBLK code assumes a minimum number of bytes are available
 * to be moved on entry.  Check that code carefully before
 * reducing FP_COPY below 256.
 */
#define FP_COPY			584
#define SHORTCOPY		7
#define ASI_STBI_P		ASI_BLK_INIT_ST_QUAD_LDD_P
#define ASI_STBI_AIUS		ASI_BLK_INIT_QUAD_LDD_AIUS
#define CACHE_LINE		64
#define	VIS_BLOCKSIZE		64

/*
 * Size of stack frame in order to accomodate a 64-byte aligned
 * floating-point register save area and 2 64-bit temp locations.
 * All copy functions use three quadrants of fp registers; to assure a
 * block-aligned three block buffer in which to save we must reserve
 * four blocks on stack.
 *
 *    _______________________________________ <-- %fp + STACK_BIAS
 *    | We may need to preserve 3 quadrants |
 *    | of fp regs, but since we do so with |
 *    | BST/BLD we need room in which to    |
 *    | align to VIS_BLOCKSIZE bytes.  So   |
 *    | this area is 4 * VIS_BLOCKSIZE.     | <--  - SAVED_FPREGS_OFFSET
 *    |-------------------------------------|
 *    | 8 bytes to save %fprs		    | <--  - SAVED_FPRS_OFFSET
 *    |-------------------------------------|
 *    | 8 bytes to save %gsr		    | <--  - SAVED_GSR_OFFSET
 *    ---------------------------------------
 */
#define HWCOPYFRAMESIZE		((VIS_BLOCKSIZE * (3 + 1)) + (2 * 8))
#define SAVED_FPREGS_OFFSET	(VIS_BLOCKSIZE * 4)
#define SAVED_FPREGS_ADJUST	((VIS_BLOCKSIZE * 3) + 1)
#define SAVED_FPRS_OFFSET	(SAVED_FPREGS_OFFSET + 8)
#define SAVED_GSR_OFFSET	(SAVED_FPRS_OFFSET + 8)

/*
 * In FP copies if we do not have preserved data to restore over
 * the fp regs we used then we must zero those regs to avoid
 * exposing portions of the data to later threads (data security).
 */
#define	FZERO				\
	fzero	%f0			;\
	fzero	%f2			;\
	faddd	%f0, %f2, %f4		;\
	fmuld	%f0, %f2, %f6		;\
	faddd	%f0, %f2, %f8		;\
	fmuld	%f0, %f2, %f10		;\
	faddd	%f0, %f2, %f12		;\
	fmuld	%f0, %f2, %f14		;\
	faddd	%f0, %f2, %f16		;\
	fmuld	%f0, %f2, %f18		;\
	faddd	%f0, %f2, %f20		;\
	fmuld	%f0, %f2, %f22		;\
	faddd	%f0, %f2, %f24		;\
	fmuld	%f0, %f2, %f26		;\
	faddd	%f0, %f2, %f28		;\
	fmuld	%f0, %f2, %f30		;\
	faddd	%f0, %f2, %f48		;\
	fmuld	%f0, %f2, %f50		;\
	faddd	%f0, %f2, %f52		;\
	fmuld	%f0, %f2, %f54		;\
	faddd	%f0, %f2, %f56		;\
	fmuld	%f0, %f2, %f58		;\
	faddd	%f0, %f2, %f60		;\
	fmuld	%f0, %f2, %f62

#if !defined(lint)

/*
 * Macros to save and restore fp registers to/from the stack.
 * Used to save and restore in-use fp registers when we want to use FP.
 */
#define BST_FP_TOSTACK(tmp1)					\
	/* membar #Sync	*/					;\
	add	%fp, STACK_BIAS - SAVED_FPREGS_ADJUST, tmp1	;\
	and	tmp1, -VIS_BLOCKSIZE, tmp1 /* block align */	;\
	stda	%f0, [tmp1]ASI_BLK_P				;\
	add	tmp1, VIS_BLOCKSIZE, tmp1			;\
	stda	%f16, [tmp1]ASI_BLK_P				;\
	add	tmp1, VIS_BLOCKSIZE, tmp1			;\
	stda	%f48, [tmp1]ASI_BLK_P				;\
	membar	#Sync

#define	BLD_FP_FROMSTACK(tmp1)					\
	/* membar #Sync - provided at copy completion */	;\
	add	%fp, STACK_BIAS - SAVED_FPREGS_ADJUST, tmp1	;\
	and	tmp1, -VIS_BLOCKSIZE, tmp1 /* block align */	;\
	ldda	[tmp1]ASI_BLK_P, %f0				;\
	add	tmp1, VIS_BLOCKSIZE, tmp1			;\
	ldda	[tmp1]ASI_BLK_P, %f16				;\
	add	tmp1, VIS_BLOCKSIZE, tmp1			;\
	ldda	[tmp1]ASI_BLK_P, %f48				;\
	membar	#Sync
#endif	/* NIAGARA_IMPL */

#endif	/* lint */
/*
 * Copy a block of storage, returning an error code if `from' or
 * `to' takes a kernel pagefault which cannot be resolved.
 * Returns errno value on pagefault error, 0 if all ok
 */

#if defined(lint)

/* ARGSUSED */
int
kcopy(const void *from, void *to, size_t count)
{ return(0); }

#else	/* lint */

	.seg	".text"
	.align	4

	ENTRY(kcopy)
#if !defined(NIAGARA_IMPL)
	cmp	%o2, FP_COPY			! check for small copy/leaf case
	bgt,pt	%ncc, .kcopy_more		!
	nop
.kcopy_small:					! setup error handler
	sethi	%hi(.sm_copyerr), %o4
	or	%o4, %lo(.sm_copyerr), %o4	! .sm_copyerr is lofault value
	ldn	[THREAD_REG + T_LOFAULT], %o5	! save existing handler
	! Note that we carefully do *not* flag the setting of
	! t_lofault.
	membar	#Sync				! sync error barrier
	b	.sm_do_copy			! common code
	stn	%o4, [THREAD_REG + T_LOFAULT]	! set t_lofault


.kcopy_more:
	save	%sp, -SA(MINFRAME + HWCOPYFRAMESIZE), %sp
	sethi	%hi(.copyerr), %l7		! copyerr is lofault value
	or	%l7, %lo(.copyerr), %l7
	ldn	[THREAD_REG + T_LOFAULT], %o5	! save existing handler
	! Note that we carefully do *not* flag the setting of
	! t_lofault.
	membar	#Sync				! sync error barrier
	b	.do_copy			! common code
	stn	%l7, [THREAD_REG + T_LOFAULT]	! set t_lofault

/*
 * We got here because of a fault during a small kcopy or bcopy.
 * if a fault handler existed when bcopy was called.
 * No floating point registers are used by the small copies.
 * Small copies are from a leaf routine
 * Errno value is in %g1.
 */
.sm_copyerr:
	! The kcopy will always set a t_lofault handler. If it fires,
	! we're expected to just return the error code and not to
	! invoke any existing error handler. As far as bcopy is concerned,
	! we only set t_lofault if there was an existing lofault handler.
	! In that case we're expected to invoke the previously existing
	! handler after resetting the t_lofault value.
	btst	LOFAULT_SET, %o5
	membar	#Sync				! sync error barrier
	andn	%o5, LOFAULT_SET, %o5		! clear fault flag
	bnz,pn	%ncc, 3f
	stn	%o5, [THREAD_REG + T_LOFAULT]	! restore old t_lofault
	retl
	mov	%g1, %o0
3:
	! We're here via bcopy. There must have been an error handler
	! in place otherwise we would have died a nasty death already.
	jmp	%o5				! goto real handler
	mov	%g0, %o0
/*
 *  end of .sm_copyerr
 */

/*
 * We got here because of a fault during kcopy or bcopy if a fault
 * handler existed when bcopy was called.
 * stack and fp registers need to be restored
 * Errno value is in %g1.
 */
.copyerr:
	sethi	%hi(.copyerr2), %l1
	or	%l1, %lo(.copyerr2), %l1
	membar	#Sync				! sync error barrier
	stn	%l1, [THREAD_REG + T_LOFAULT]	! set t_lofault
	btst	FPUSED_FLAG, %o5
	bz,pt	%xcc, 1f
	and	%o5, LOFAULT_SET, %l1	! copy flag to %l1

	membar	#Sync				! sync error barrier
	wr	%l5, 0, %gsr
	btst	FPRS_FEF, %g5
	bz,pt	%icc, 4f
	nop
	! restore fpregs from stack
	BLD_FP_FROMSTACK(%o2)
	ba,pt	%ncc, 2f
	wr	%g5, 0, %fprs		! restore fprs
4:
	FZERO
	wr	%g5, 0, %fprs		! restore fprs
2:
	ldn	[THREAD_REG + T_LWP], %o2
	brnz,pt	%o2, 1f
	nop

	ldsb	[THREAD_REG + T_PREEMPT], %l0
	deccc	%l0
	bnz,pn	%ncc, 1f
	stb	%l0, [THREAD_REG + T_PREEMPT]

	! Check for a kernel preemption request
	ldn	[THREAD_REG + T_CPU], %l0
	ldub	[%l0 + CPU_KPRUNRUN], %l0
	brnz,a,pt	%l0, 1f	! Need to call kpreempt?
	or	%l1, KPREEMPT_FLAG, %l1	! If so, set the flag

	! The kcopy will always set a t_lofault handler. If it fires,
	! we're expected to just return the error code and not to
	! invoke any existing error handler. As far as bcopy is concerned,
	! we only set t_lofault if there was an existing lofault handler.
	! In that case we're expected to invoke the previously existing
	! handler after resetting the t_lofault value.
1:
	andn	%o5, COPY_FLAGS, %o5	! remove flags from lofault address
	membar	#Sync				! sync error barrier
	stn	%o5, [THREAD_REG + T_LOFAULT]	! restore old t_lofault

	! call kpreempt if necessary
	btst	KPREEMPT_FLAG, %l1
	bz,pt	%icc, 2f
	nop
	call	kpreempt
	rdpr	%pil, %o0	! pass %pil
2:
	btst	LOFAULT_SET, %l1
	bnz,pn	%ncc, 3f
	nop
	ret
	restore	%g1, 0, %o0
3:
	! We're here via bcopy. There must have been an error handler
	! in place otherwise we would have died a nasty death already.
	jmp	%o5				! goto real handler
	restore	%g0, 0, %o0			! dispose of copy window

/*
 * We got here because of a fault in .copyerr.  We can't safely restore fp
 * state, so we panic.
 */
fp_panic_msg:
	.asciz	"Unable to restore fp state after copy operation"

	.align	4
.copyerr2:
	set	fp_panic_msg, %o0
	call	panic
	nop
/*
 *  end of .copyerr
 */

#else	/* NIAGARA_IMPL */
	save	%sp, -SA(MINFRAME), %sp
	set	.copyerr, %l7			! copyerr is lofault value
	ldn	[THREAD_REG + T_LOFAULT], %o5	! save existing handler
	or	%o5, LOFAULT_SET, %o5
	membar	#Sync				! sync error barrier
	b	.do_copy			! common code
	stn	%l7, [THREAD_REG + T_LOFAULT]	! set t_lofault

/*
 * We got here because of a fault during kcopy.
 * Errno value is in %g1.
 */
.copyerr:
	! The kcopy() *always* sets a t_lofault handler and it ORs LOFAULT_SET
	! into %o5 to indicate it has set t_lofault handler. Need to clear
	! LOFAULT_SET flag before restoring the error handler.
	andn	%o5, LOFAULT_SET, %o5
	membar	#Sync				! sync error barrier
	stn	%o5, [THREAD_REG + T_LOFAULT]	! restore old t_lofault
	ret
	restore	%g1, 0, %o0
#endif	/* NIAGARA_IMPL */

	SET_SIZE(kcopy)
#endif	/* lint */


/*
 * Copy a block of storage - must not overlap (from + len <= to).
 */
#if defined(lint)

/* ARGSUSED */
void
bcopy(const void *from, void *to, size_t count)
{}

#else	/* lint */

	ENTRY(bcopy)
#if !defined(NIAGARA_IMPL)
	cmp	%o2, FP_COPY			! check for small copy/leaf case
	bgt,pt	%ncc, .bcopy_more		!
	nop
.bcopy_small:					! setup error handler
	ldn	[THREAD_REG + T_LOFAULT], %o5	! save existing handler
	tst	%o5
	bz,pt	%icc, .sm_do_copy
	sethi	%hi(.sm_copyerr), %o4
	or	%o4, %lo(.sm_copyerr), %o4	! .sm_copyerr is lofault value
	membar	#Sync				! sync error barrier
	stn	%o4, [THREAD_REG + T_LOFAULT]	! set t_lofault
	or	%o5, LOFAULT_SET, %o5		! Error should trampoline
.sm_do_copy:
	mov	%o0, %g1		! save %o0
	cmp	%o2, SHORTCOPY		! make sure there is enough to align
	ble,pt	%ncc, .bc_smallest
	andcc	%o1, 0x7, %o3		! is dest long aligned
	bnz,pn	%ncc, .bc_align
	andcc	%o1, 1, %o3		! is dest byte aligned

! Destination is long word aligned
.bc_al_src:
	andcc	%o0, 7, %o3
	brnz,pt	%o3, .bc_src_dst_unal8
	nop
/*
 * Special case for handling when src and dest are both long word aligned
 * and total data to move is less than FP_COPY bytes
 * Also handles finish up for large block moves, so may be less than 32 bytes
 */
.bc_medlong:
	subcc	%o2, 31, %o2		! adjust length to allow cc test
	ble,pt	%ncc, .bc_medl31
	nop
.bc_medl32:
	ldx	[%o0], %o4		! move 32 bytes
	subcc	%o2, 32, %o2		! decrement length count by 32
	stx	%o4, [%o1]
	ldx	[%o0+8], %o4
	stx	%o4, [%o1+8]
	ldx	[%o0+16], %o4
	add	%o0, 32, %o0		! increase src ptr by 32
	stx	%o4, [%o1+16]
	ldx	[%o0-8], %o4
	add	%o1, 32, %o1		! increase dst ptr by 32
	bgu,pt	%ncc, .bc_medl32	! repeat if at least 32 bytes left
	stx	%o4, [%o1-8]
.bc_medl31:
	addcc	%o2, 24, %o2		! adjust count to be off by 7
	ble,pt	%ncc, .bc_medl7		! skip if 7 or fewer bytes left
	nop
.bc_medl8:
	ldx	[%o0], %o4		! move 8 bytes
	add	%o0, 8, %o0		! increase src ptr by 8
	subcc	%o2, 8, %o2		! decrease count by 8
	add	%o1, 8, %o1		! increase dst ptr by 8
	bgu,pt	%ncc, .bc_medl8
	stx	%o4, [%o1-8]
.bc_medl7:
	addcc	%o2, 7, %o2		! finish adjustment of remaining count
	bnz,pt	%ncc, .bc_small4	! do final bytes if not finished

.bc_smallx:				! finish up and exit
	tst	%o5
	bz,pt	%ncc, .bc_sm_done
	andn	%o5, COPY_FLAGS, %o5	! remove flags from lofault address
	membar	#Sync			! sync error barrier
	stn	%o5, [THREAD_REG + T_LOFAULT]	! restore old t_lofault
.bc_sm_done:
	retl
	mov	%g0, %o0

.bc_small4:
	cmp	%o2, 4
	blt,pt	%ncc, .bc_small3x	! skip if less than 4 bytes left
	nop				!
	ld	[%o0], %o4		! move 4 bytes
	add	%o0, 4, %o0		! increase src ptr by 4
	add	%o1, 4, %o1		! increase dst ptr by 4
	subcc	%o2, 4, %o2		! decrease count by 4
	bz,pt	%ncc, .bc_smallx
	stw	%o4, [%o1-4]

.bc_small3x:				! Exactly 1, 2, or 3 bytes remain
	subcc	%o2, 1, %o2		! reduce count for cc test
	ldub	[%o0], %o4		! load one byte
	bz,pt	%ncc, .bc_smallx
	stb	%o4, [%o1]		! store one byte
	ldub	[%o0+1], %o4		! load second byte
	subcc	%o2, 1, %o2
	bz,pt	%ncc, .bc_smallx
	stb	%o4, [%o1+1]		! store second byte
	ldub	[%o0+2], %o4		! load third byte
	ba	.bc_smallx
	stb	%o4, [%o1+2]		! store third byte

.bc_smallest:				! 7 or fewer bytes remain
	tst	%o2
	bz,pt	%ncc, .bc_smallx
	cmp	%o2, 4
	blt,pt	%ncc, .bc_small3x
	nop
	ldub	[%o0], %o4		! read byte
	subcc	%o2, 4, %o2		! reduce count by 4
	stb	%o4, [%o1]		! write byte
	ldub	[%o0+1], %o4		! repeat for total of 4 bytes
	add	%o0, 4, %o0		! advance src by 4
	stb	%o4, [%o1+1]
	ldub	[%o0-2], %o4
	add	%o1, 4, %o1		! advance dst by 4
	stb	%o4, [%o1-2]
	ldub	[%o0-1], %o4
	bnz,pt	%ncc, .bc_small3x
	stb	%o4, [%o1-1]
	ba	.bc_smallx
	nop

/*
 * Align destination to long word boundary
 */
.bc_align:				! byte align test in prior branch delay
	bnz,pt	%ncc, .bc_al_d1
.bc_al_d1f:				! dest is now half word aligned
	andcc	%o1, 2, %o3
	bnz,pt	%ncc, .bc_al_d2
.bc_al_d2f:				! dest is now word aligned
	andcc	%o1, 4, %o3		! is dest longword aligned?
	bz,pt	%ncc, .bc_al_src
	nop
.bc_al_d4:				! dest is word aligned;  src is unknown
	ldub	[%o0], %o4		! move a word (src align unknown)
	ldub	[%o0+1], %o3
	sll	%o4, 24, %o4		! position
	sll	%o3, 16, %o3		! position
	or	%o4, %o3, %o3		! merge
	ldub	[%o0+2], %o4
	sll	%o4, 8, %o4		! position
	or	%o4, %o3, %o3		! merge
	ldub	[%o0+3], %o4
	or	%o4, %o3, %o4		! merge
	stw	%o4,[%o1]		! store four bytes
	add	%o0, 4, %o0		! adjust src by 4
	add	%o1, 4, %o1		! adjust dest by 4
	sub	%o2, 4, %o2		! adjust count by 4
	andcc	%o0, 7, %o3		! check for src long word alignment
	brz,pt	%o3, .bc_medlong
.bc_src_dst_unal8:
	! dst is 8-byte aligned, src is not
	! Size is less than FP_COPY
	! Following code is to select for alignment
	andcc	%o0, 0x3, %o3		! test word alignment
	bz,pt	%ncc, .bc_medword
	nop
	andcc	%o0, 0x1, %o3		! test halfword alignment
	bnz,pt	%ncc, .bc_med_byte	! go to byte move if not halfword
	andcc	%o0, 0x2, %o3		! test which byte alignment
	ba	.bc_medhalf
	nop
.bc_al_d1:				! align dest to half word
	ldub	[%o0], %o4		! move a byte
	add	%o0, 1, %o0
	stb	%o4, [%o1]
	add	%o1, 1, %o1
	andcc	%o1, 2, %o3
	bz,pt	%ncc, .bc_al_d2f
	sub	%o2, 1, %o2
.bc_al_d2:				! align dest to word
	ldub	[%o0], %o4		! move a half-word (src align unknown)
	ldub	[%o0+1], %o3
	sll	%o4, 8, %o4		! position
	or	%o4, %o3, %o4		! merge
	sth	%o4, [%o1]
	add	%o0, 2, %o0
	add	%o1, 2, %o1
	andcc	%o1, 4, %o3		! is dest longword aligned?
	bz,pt	%ncc, .bc_al_src
	sub	%o2, 2, %o2
	ba	.bc_al_d4
	nop
/*
 * Handle all cases where src and dest are aligned on word
 * boundaries. Use unrolled loops for better performance.
 * This option wins over standard large data move when 
 * source and destination is in cache for medium
 * to short data moves.
 */
.bc_medword:
	subcc	%o2, 31, %o2		! adjust length to allow cc test
	ble,pt	%ncc, .bc_medw31
	nop
.bc_medw32:
	ld	[%o0], %o4		! move a block of 32 bytes
	stw	%o4, [%o1]
	ld	[%o0+4], %o4
	stw	%o4, [%o1+4]
	ld	[%o0+8], %o4
	stw	%o4, [%o1+8]
	ld	[%o0+12], %o4
	stw	%o4, [%o1+12]
	ld	[%o0+16], %o4
	stw	%o4, [%o1+16]
	ld	[%o0+20], %o4
	subcc	%o2, 32, %o2		! decrement length count
	stw	%o4, [%o1+20]
	ld	[%o0+24], %o4
	add	%o0, 32, %o0		! increase src ptr by 32
	stw	%o4, [%o1+24]
	ld	[%o0-4], %o4
	add	%o1, 32, %o1		! increase dst ptr by 32
	bgu,pt	%ncc, .bc_medw32	! repeat if at least 32 bytes left
	stw	%o4, [%o1-4]
.bc_medw31:
	addcc	%o2, 24, %o2		! adjust count to be off by 7
	ble,pt	%ncc, .bc_medw7		! skip if 7 or fewer bytes left
	nop				!
.bc_medw15:
	ld	[%o0], %o4		! move a block of 8 bytes
	subcc	%o2, 8, %o2		! decrement length count
	stw	%o4, [%o1]
	add	%o0, 8, %o0		! increase src ptr by 8
	ld	[%o0-4], %o4
	add	%o1, 8, %o1		! increase dst ptr by 8
	bgu,pt	%ncc, .bc_medw15
	stw	%o4, [%o1-4]
.bc_medw7:
	addcc	%o2, 7, %o2		! finish adjustment of remaining count
	bz,pt	%ncc, .bc_smallx	! exit if finished
	cmp	%o2, 4
	blt,pt	%ncc, .bc_small3x	! skip if less than 4 bytes left
	nop				!
	ld	[%o0], %o4		! move 4 bytes
	add	%o0, 4, %o0		! increase src ptr by 4
	add	%o1, 4, %o1		! increase dst ptr by 4
	subcc	%o2, 4, %o2		! decrease count by 4
	bnz	.bc_small3x
	stw	%o4, [%o1-4]
	ba	.bc_smallx
	nop

.bc_medhalf:
	subcc	%o2, 31, %o2		! adjust length to allow cc test
	ble,pt	%ncc, .bc_medh31
	nop
.bc_medh32:				! load and store block of 32 bytes
	subcc	%o2, 32, %o2		! decrement length count

	lduh	[%o0], %o4		! move 32 bytes
	lduw	[%o0+2], %o3
	sllx	%o4, 48, %o4
	sllx	%o3, 16, %o3
	or	%o4, %o3, %o3
	lduh	[%o0+6], %o4
	or	%o4, %o3, %o4
	stx	%o4, [%o1]

	lduh	[%o0+8], %o4
	lduw	[%o0+10], %o3
	sllx	%o4, 48, %o4
	sllx	%o3, 16, %o3
	or	%o4, %o3, %o3
	lduh	[%o0+14], %o4
	or	%o4, %o3, %o4
	stx	%o4, [%o1+8]

	lduh	[%o0+16], %o4
	lduw	[%o0+18], %o3
	sllx	%o4, 48, %o4
	sllx	%o3, 16, %o3
	or	%o4, %o3, %o3
	lduh	[%o0+22], %o4
	or	%o4, %o3, %o4
	stx	%o4, [%o1+16]

	add	%o0, 32, %o0		! increase src ptr by 32
	add	%o1, 32, %o1		! increase dst ptr by 32

	lduh	[%o0-8], %o4
	lduw	[%o0-6], %o3
	sllx	%o4, 48, %o4
	sllx	%o3, 16, %o3
	or	%o4, %o3, %o3
	lduh	[%o0-2], %o4
	or	%o3, %o4, %o4
	bgu,pt	%ncc, .bc_medh32	! repeat if at least 32 bytes left
	stx	%o4, [%o1-8]

.bc_medh31:
	addcc	%o2, 24, %o2		! adjust count to be off by 7
	ble,pt	%ncc, .bc_medh7		! skip if 7 or fewer bytes left
	nop				!
.bc_medh15:
	lduh	[%o0], %o4		! move 16 bytes
	subcc	%o2, 8, %o2		! decrement length count
	lduw	[%o0+2], %o3
	sllx	%o4, 48, %o4
	sllx	%o3, 16, %o3
	or	%o4, %o3, %o3
	add	%o1, 8, %o1		! increase dst ptr by 8
	lduh	[%o0+6], %o4
	add	%o0, 8, %o0		! increase src ptr by 8
	or	%o4, %o3, %o4
	bgu,pt	%ncc, .bc_medh15
	stx	%o4, [%o1-8]
.bc_medh7:
	addcc	%o2, 7, %o2		! finish adjustment of remaining count
	bz,pt	%ncc, .bc_smallx	! exit if finished
	cmp	%o2, 4
	blt,pt	%ncc, .bc_small3x	! skip if less than 4 bytes left
	nop				!
	lduh	[%o0], %o4
	sll	%o4, 16, %o4
	lduh	[%o0+2], %o3
	or	%o3, %o4, %o4
	subcc	%o2, 4, %o2
	add	%o0, 4, %o0
	add	%o1, 4, %o1
	bnz	.bc_small3x
	stw	%o4, [%o1-4]
	ba	.bc_smallx
	nop

	.align 16
.bc_med_byte:
	bnz,pt	%ncc, .bc_medbh32a	! go to correct byte move
	subcc	%o2, 31, %o2		! adjust length to allow cc test
	ble,pt	%ncc, .bc_medb31
	nop
.bc_medb32:				! Alignment 1 or 5
	subcc	%o2, 32, %o2		! decrement length count

	ldub	[%o0], %o4		! load and store a block of 32 bytes
	sllx	%o4, 56, %o3
	lduh	[%o0+1], %o4
	sllx	%o4, 40, %o4
	or	%o4, %o3, %o3
	lduw	[%o0+3], %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0+7], %o4
	or	%o4, %o3, %o4
	stx	%o4, [%o1]

	ldub	[%o0+8], %o4
	sllx	%o4, 56, %o3
	lduh	[%o0+9], %o4
	sllx	%o4, 40, %o4
	or	%o4, %o3, %o3
	lduw	[%o0+11], %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0+15], %o4
	or	%o4, %o3, %o4
	stx	%o4, [%o1+8]

	ldub	[%o0+16], %o4
	sllx	%o4, 56, %o3
	lduh	[%o0+17], %o4
	sllx	%o4, 40, %o4
	or	%o4, %o3, %o3
	lduw	[%o0+19], %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0+23], %o4
	or	%o4, %o3, %o4
	stx	%o4, [%o1+16]

	add	%o0, 32, %o0		! increase src ptr by 32
	add	%o1, 32, %o1		! increase dst ptr by 32

	ldub	[%o0-8], %o4
	sllx	%o4, 56, %o3
	lduh	[%o0-7], %o4
	sllx	%o4, 40, %o4
	or	%o4, %o3, %o3
	lduw	[%o0-5], %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0-1], %o4
	or	%o4, %o3, %o4
	bgu,pt	%ncc, .bc_medb32	! repeat if at least 32 bytes left
	stx	%o4, [%o1-8]

.bc_medb31:				! 31 or fewer bytes remaining
	addcc	%o2, 24, %o2		! adjust count to be off by 7
	ble,pt	%ncc, .bc_medb7		! skip if 7 or fewer bytes left
	nop				!
.bc_medb15:

	ldub	[%o0], %o4		! load and store a block of 8 bytes
	subcc	%o2, 8, %o2		! decrement length count
	sllx	%o4, 56, %o3
	lduh	[%o0+1], %o4
	sllx	%o4, 40, %o4
	or	%o4, %o3, %o3
	lduw	[%o0+3], %o4
	add	%o1, 8, %o1		! increase dst ptr by 16
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0+7], %o4
	add	%o0, 8, %o0		! increase src ptr by 16
	or	%o4, %o3, %o4
	bgu,pt	%ncc, .bc_medb15
	stx	%o4, [%o1-8]
.bc_medb7:
	addcc	%o2, 7, %o2		! finish adjustment of remaining count
	bz,pt	%ncc, .bc_smallx	! exit if finished
	cmp	%o2, 4
	blt,pt	%ncc, .bc_small3x	! skip if less than 4 bytes left
	nop				!
	ldub	[%o0], %o4		! move 4 bytes
	sll	%o4, 24, %o3
	lduh	[%o0+1], %o4
	sll	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0+3], %o4
	or	%o4, %o3, %o4
	subcc	%o2, 4, %o2
	add	%o0, 4, %o0
	add	%o1, 4, %o1
	bnz	.bc_small3x
	stw	%o4, [%o1-4]
	ba	.bc_smallx
	nop

	.align 16
.bc_medbh32a:				! Alignment 3 or 7
	ble,pt	%ncc, .bc_medbh31
	nop
.bc_medbh32:				! Alignment 3 or 7
	subcc	%o2, 32, %o2		! decrement length count

	ldub	[%o0], %o4		! load and store a block of 32 bytes
	sllx	%o4, 56, %o3
	lduw	[%o0+1], %o4
	sllx	%o4, 24, %o4
	or	%o4, %o3, %o3
	lduh	[%o0+5], %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0+7], %o4
	or	%o4, %o3, %o4
	stx	%o4, [%o1]

	ldub	[%o0+8], %o4
	sllx	%o4, 56, %o3
	lduw	[%o0+9], %o4
	sllx	%o4, 24, %o4
	or	%o4, %o3, %o3
	lduh	[%o0+13], %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0+15], %o4
	or	%o4, %o3, %o4
	stx	%o4, [%o1+8]

	ldub	[%o0+16], %o4
	sllx	%o4, 56, %o3
	lduw	[%o0+17], %o4
	sllx	%o4, 24, %o4
	or	%o4, %o3, %o3
	lduh	[%o0+21], %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0+23], %o4
	or	%o4, %o3, %o4
	stx	%o4, [%o1+16]

	add	%o0, 32, %o0		! increase src ptr by 32
	add	%o1, 32, %o1		! increase dst ptr by 32

	ldub	[%o0-8], %o4
	sllx	%o4, 56, %o3
	lduw	[%o0-7], %o4
	sllx	%o4, 24, %o4
	or	%o4, %o3, %o3
	lduh	[%o0-3], %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0-1], %o4
	or	%o4, %o3, %o4
	bgu,pt	%ncc, .bc_medbh32	! repeat if at least 32 bytes left
	stx	%o4, [%o1-8]

.bc_medbh31:
	addcc	%o2, 24, %o2		! adjust count to be off by 7
	ble,pt	%ncc, .bc_medb7		! skip if 7 or fewer bytes left
	nop				!
.bc_medbh15:
	ldub	[%o0], %o4		! load and store a block of 8 bytes
	sllx	%o4, 56, %o3
	lduw	[%o0+1], %o4
	sllx	%o4, 24, %o4
	or	%o4, %o3, %o3
	lduh	[%o0+5], %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0+7], %o4
	or	%o4, %o3, %o4
	stx	%o4, [%o1]
	subcc	%o2, 8, %o2		! decrement length count
	add	%o1, 8, %o1		! increase dst ptr by 8
	add	%o0, 8, %o0		! increase src ptr by 8
	bgu,pt	%ncc, .bc_medbh15
	stx	%o4, [%o1-8]
	ba	.bc_medb7
	nop
	
	SET_SIZE(bcopy)
/*
 * The _more entry points are not intended to be used directly by
 * any caller from outside this file.  They are provided to allow
 * profiling and dtrace of the portions of the copy code that uses
 * the floating point registers.
*/
	ENTRY(bcopy_more)
.bcopy_more:
	save	%sp, -SA(MINFRAME + HWCOPYFRAMESIZE), %sp
	ldn	[THREAD_REG + T_LOFAULT], %o5	! save existing handler
	brz,pt	%o5, .do_copy
	nop
	sethi	%hi(.copyerr), %l7		! copyerr is lofault value
	or	%l7, %lo(.copyerr), %l7
	membar	#Sync				! sync error barrier
	stn	%l7, [THREAD_REG + T_LOFAULT]	! set t_lofault
	! We've already captured whether t_lofault was zero on entry.
	! We need to mark ourselves as being from bcopy since both
	! kcopy and bcopy use the same code path. If LOFAULT_SET is
	! set and the saved lofault was zero, we won't reset lofault on
	! returning.
	or	%o5, LOFAULT_SET, %o5
.do_copy:
	ldn	[THREAD_REG + T_LWP], %o3
	brnz,pt	%o3, 1f
	nop
/*
 * kpreempt_disable();
 */
	ldsb	[THREAD_REG +T_PREEMPT], %o3
	inc	%o3
	stb	%o3, [THREAD_REG + T_PREEMPT]
1:
/*
 * Following code is for large copies. We know there is at
 * least FP_COPY bytes available. FP regs are used, so
 *  we save registers and fp regs before starting
 */
	rd	%fprs, %g5		! check for unused fp
	or	%o5,FPUSED_FLAG,%o5
	! if fprs.fef == 0, set it.
	! Setting it when already set costs more than checking
	andcc	%g5, FPRS_FEF, %g5	! test FEF, fprs.du = fprs.dl = 0
	bz,pt	%ncc, .bc_fp_unused
	prefetch [%i0 + (1 * CACHE_LINE)], #one_read
	BST_FP_TOSTACK(%o3)
	ba	.bc_fp_ready
.bc_fp_unused:
	andcc	%i1, 1, %o3		! is dest byte aligned
	wr	%g0, FPRS_FEF, %fprs	! fprs.fef = 1
.bc_fp_ready:
	rd	%gsr, %l5		! save %gsr value
	bnz,pt	%ncc, .bc_big_d1
.bc_big_d1f:				! dest is now half word aligned
	andcc	%i1, 2, %o3
	bnz,pt	%ncc, .bc_big_d2
.bc_big_d2f:				! dest is now word aligned
	andcc	%i1, 4, %o3
	bnz,pt	%ncc, .bc_big_d4
.bc_big_d4f:				! dest is now long word aligned
	andcc	%i0, 7, %o3		! is src long word aligned
	brnz,pt	%o3, .bc_big_unal8
	prefetch [%i0 + (2 * CACHE_LINE)], #one_read
	
	! Src and dst are long word aligned
	! align dst to 64 byte boundary
	andcc	%i1, 0x3f, %o3		! %o3 == 0 means dst is 64 byte aligned
	brz,pn	%o3, .bc_al_to_64
	nop
	sub	%o3, 64, %o3		! %o3 has negative bytes to move
	add	%i2, %o3, %i2		! adjust remaining count
	andcc	%o3, 8, %o4		! odd long words to move?
	brz,pt	%o4, .bc_al_to_16
	nop
	add	%o3, 8, %o3
	ldx	[%i0], %o4
	add	%i0, 8, %i0		! increment src ptr
	add	%i1, 8, %i1		! increment dst ptr
	stx	%o4, [%i1-8]
! Dest is aligned on 16 bytes, src 8 byte aligned
.bc_al_to_16:
	andcc	%o3, 0x30, %o4		! pair of long words to move?
	brz,pt	%o4, .bc_al_to_64
	nop
.bc_al_mv_16:
	add	%o3, 16, %o3
	ldx	[%i0], %o4
	stx	%o4, [%i1]
	ldx	[%i0+8], %o4
	add	%i0, 16, %i0		! increment src ptr
	stx	%o4, [%i1+8]
	andcc	%o3, 48, %o4
	brnz,pt	%o4, .bc_al_mv_16
	add	%i1, 16, %i1		! increment dst ptr
! Dest is aligned on 64 bytes, src 8 byte aligned
.bc_al_to_64:
	! Determine source alignment
	! to correct 8 byte offset
	andcc	%i0, 32, %o3
	brnz,pn	%o3, .bc_aln_1
	andcc	%i0, 16, %o3
	brnz,pn	%o3, .bc_aln_01
	andcc	%i0, 8, %o3
	brz,pn	%o3, .bc_aln_000
	prefetch [%i0 + (3 * CACHE_LINE)], #one_read
	ba	.bc_aln_001
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read

.bc_aln_01:
	brnz,pn	%o3, .bc_aln_011
	prefetch [%i0 + (3 * CACHE_LINE)], #one_read
	ba	.bc_aln_010
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
.bc_aln_1:
	andcc	%i0, 16, %o3
	brnz,pn	%o3, .bc_aln_11
	andcc	%i0, 8, %o3
	brnz,pn	%o3, .bc_aln_101
	prefetch [%i0 + (3 * CACHE_LINE)], #one_read
	ba	.bc_aln_100
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
.bc_aln_11:
	brz,pn	%o3, .bc_aln_110
	prefetch [%i0 + (3 * CACHE_LINE)], #one_read

.bc_aln_111:
! Alignment off by 8 bytes
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	ldd	[%i0], %d0
	add	%i0, 8, %i0
	sub	%i2, 8, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.bc_aln_111_loop:
	ldda	[%i0]ASI_BLK_P,%d16		! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d2
	fmovd	%d18, %d4
	fmovd	%d20, %d6
	fmovd	%d22, %d8
	fmovd	%d24, %d10
	fmovd	%d26, %d12
	fmovd	%d28, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_P	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_P
	add	%i0, 64, %i0
	fmovd	%d30, %d0
	bgt,pt	%ncc, .bc_aln_111_loop
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	add	%i1, %i0, %i1

	std	%d0, [%i1]
	ba	.bc_remain_stuff
	add	%i1, 8, %i1
	! END OF aln_111

.bc_aln_110:
! Alignment off by 16 bytes
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	ldd	[%i0], %d0
	ldd	[%i0+8], %d2
	add	%i0, 16, %i0
	sub	%i2, 16, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.bc_aln_110_loop:
	ldda	[%i0]ASI_BLK_P,%d16		! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d4
	fmovd	%d18, %d6
	fmovd	%d20, %d8
	fmovd	%d22, %d10
	fmovd	%d24, %d12
	fmovd	%d26, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_P	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_P
	add	%i0, 64, %i0
	fmovd	%d28, %d0
	fmovd	%d30, %d2
	bgt,pt	%ncc, .bc_aln_110_loop
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	add	%i1, %i0, %i1

	std	%d0, [%i1]
	std	%d2, [%i1+8]
	ba	.bc_remain_stuff
	add	%i1, 16, %i1
	! END OF aln_110

.bc_aln_101:
! Alignment off by 24 bytes
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	ldd	[%i0], %d0
	ldd	[%i0+8], %d2
	ldd	[%i0+16], %d4
	add	%i0, 24, %i0
	sub	%i2, 24, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.bc_aln_101_loop:
	ldda	[%i0]ASI_BLK_P,%d16	! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d6
	fmovd	%d18, %d8
	fmovd	%d20, %d10
	fmovd	%d22, %d12
	fmovd	%d24, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_P	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_P
	add	%i0, 64, %i0
	fmovd	%d26, %d0
	fmovd	%d28, %d2
	fmovd	%d30, %d4
	bgt,pt	%ncc, .bc_aln_101_loop
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	add	%i1, %i0, %i1

	std	%d0, [%i1]
	std	%d2, [%i1+8]
	std	%d4, [%i1+16]
	ba	.bc_remain_stuff
	add	%i1, 24, %i1
	! END OF aln_101

.bc_aln_100:
! Alignment off by 32 bytes
	ldd	[%i0], %d0
	ldd	[%i0+8], %d2
	ldd	[%i0+16],%d4
	ldd	[%i0+24],%d6
	add	%i0, 32, %i0
	sub	%i2, 32, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.bc_aln_100_loop:
	ldda	[%i0]ASI_BLK_P,%d16	! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d8
	fmovd	%d18, %d10
	fmovd	%d20, %d12
	fmovd	%d22, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_P	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_P
	add	%i0, 64, %i0
	fmovd	%d24, %d0
	fmovd	%d26, %d2
	fmovd	%d28, %d4
	fmovd	%d30, %d6
	bgt,pt	%ncc, .bc_aln_100_loop
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	add	%i1, %i0, %i1

	std	%d0, [%i1]
	std	%d2, [%i1+8]
	std	%d4, [%i1+16]
	std	%d6, [%i1+24]
	ba	.bc_remain_stuff
	add	%i1, 32, %i1
	! END OF aln_100

.bc_aln_011:
! Alignment off by 40 bytes
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	ldd	[%i0], %d0
	ldd	[%i0+8], %d2
	ldd	[%i0+16], %d4
	ldd	[%i0+24], %d6
	ldd	[%i0+32], %d8
	add	%i0, 40, %i0
	sub	%i2, 40, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.bc_aln_011_loop:
	ldda	[%i0]ASI_BLK_P,%d16	! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d10
	fmovd	%d18, %d12
	fmovd	%d20, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_P	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_P
	add	%i0, 64, %i0
	fmovd	%d22, %d0
	fmovd	%d24, %d2
	fmovd	%d26, %d4
	fmovd	%d28, %d6
	fmovd	%d30, %d8
	bgt,pt	%ncc, .bc_aln_011_loop
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	add	%i1, %i0, %i1

	std	%d0, [%i1]
	std	%d2, [%i1+8]
	std	%d4, [%i1+16]
	std	%d6, [%i1+24]
	std	%d8, [%i1+32]
	ba	.bc_remain_stuff
	add	%i1, 40, %i1
	! END OF aln_011

.bc_aln_010:
! Alignment off by 48 bytes
	ldd	[%i0], %d0
	ldd	[%i0+8], %d2
	ldd	[%i0+16], %d4
	ldd	[%i0+24], %d6
	ldd	[%i0+32], %d8
	ldd	[%i0+40], %d10
	add	%i0, 48, %i0
	sub	%i2, 48, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.bc_aln_010_loop:
	ldda	[%i0]ASI_BLK_P,%d16	! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d12
	fmovd	%d18, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_P	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_P
	add	%i0, 64, %i0
	fmovd	%d20, %d0
	fmovd	%d22, %d2
	fmovd	%d24, %d4
	fmovd	%d26, %d6
	fmovd	%d28, %d8
	fmovd	%d30, %d10
	bgt,pt	%ncc, .bc_aln_010_loop
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	add	%i1, %i0, %i1

	std	%d0, [%i1]
	std	%d2, [%i1+8]
	std	%d4, [%i1+16]
	std	%d6, [%i1+24]
	std	%d8, [%i1+32]
	std	%d10, [%i1+40]
	ba	.bc_remain_stuff
	add	%i1, 48, %i1
	! END OF aln_010

.bc_aln_001:
! Alignment off by 56 bytes
	ldd	[%i0], %d0
	ldd	[%i0+8], %d2
	ldd	[%i0+16], %d4
	ldd	[%i0+24], %d6
	ldd	[%i0+32], %d8
	ldd	[%i0+40], %d10
	ldd	[%i0+48], %d12
	add	%i0, 56, %i0
	sub	%i2, 56, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.bc_aln_001_loop:
	ldda	[%i0]ASI_BLK_P,%d16	! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_P	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_P
	add	%i0, 64, %i0
	fmovd	%d18, %d0
	fmovd	%d20, %d2
	fmovd	%d22, %d4
	fmovd	%d24, %d6
	fmovd	%d26, %d8
	fmovd	%d28, %d10
	fmovd	%d30, %d12
	bgt,pt	%ncc, .bc_aln_001_loop
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	add	%i1, %i0, %i1

	std	%d0, [%i1]
	std	%d2, [%i1+8]
	std	%d4, [%i1+16]
	std	%d6, [%i1+24]
	std	%d8, [%i1+32]
	std	%d10, [%i1+40]
	std	%d12, [%i1+48]
	ba	.bc_remain_stuff
	add	%i1, 56, %i1
	! END OF aln_001

.bc_aln_000:
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.bc_aln_000_loop:
	ldda	[%i0]ASI_BLK_P,%d0
	subcc	%o3, 64, %o3
	stxa	%g0,[%i0+%i1]ASI_STBI_P	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_P
	add	%i0, 64, %i0
	bgt,pt	%ncc, .bc_aln_000_loop
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	add	%i1, %i0, %i1

	! END OF aln_000

.bc_remain_stuff:
	subcc	%i2, 31, %i2		! adjust length to allow cc test
	ble,pt	%ncc, .bc_aln_31
	nop
.bc_aln_32:
	ldx	[%i0], %o4		! move 32 bytes
	subcc	%i2, 32, %i2		! decrement length count by 32
	stx	%o4, [%i1]
	ldx	[%i0+8], %o4
	stx	%o4, [%i1+8]
	ldx	[%i0+16], %o4
	add	%i0, 32, %i0		! increase src ptr by 32
	stx	%o4, [%i1+16]
	ldx	[%i0-8], %o4
	add	%i1, 32, %i1		! increase dst ptr by 32
	bgu,pt	%ncc, .bc_aln_32	! repeat if at least 32 bytes left
	stx	%o4, [%i1-8]
.bc_aln_31:
	addcc	%i2, 24, %i2		! adjust count to be off by 7
	ble,pt	%ncc, .bc_aln_7		! skip if 7 or fewer bytes left
	nop				!
.bc_aln_15:
	ldx	[%i0], %o4		! move 8 bytes
	add	%i0, 8, %i0		! increase src ptr by 8
	subcc	%i2, 8, %i2		! decrease count by 8
	add	%i1, 8, %i1		! increase dst ptr by 8
	bgu,pt	%ncc, .bc_aln_15
	stx	%o4, [%i1-8]		!
.bc_aln_7:
	addcc	%i2, 7, %i2		! finish adjustment of remaining count
	bz,pt	%ncc, .bc_exit		! exit if finished
	cmp	%i2, 4
	blt,pt	%ncc, .bc_unaln3x	! skip if less than 4 bytes left
	nop				!
	ld	[%i0], %o4		! move 4 bytes
	add	%i0, 4, %i0		! increase src ptr by 4
	add	%i1, 4, %i1		! increase dst ptr by 4
	subcc	%i2, 4, %i2		! decrease count by 4
	bnz	.bc_unaln3x
	stw	%o4, [%i1-4]
	ba	.bc_exit
	nop

	! destination alignment code
.bc_big_d1:
	ldub	[%i0], %o4		! move a byte
	add	%i0, 1, %i0
	stb	%o4, [%i1]
	add	%i1, 1, %i1
	andcc	%i1, 2, %o3
	bz,pt	%ncc, .bc_big_d2f
	sub	%i2, 1, %i2
.bc_big_d2:
	ldub	[%i0], %o4		! move a half-word (src align unknown)
	ldub	[%i0+1], %o3
	add	%i0, 2, %i0
	sll	%o4, 8, %o4		! position
	or	%o4, %o3, %o4		! merge
	sth	%o4, [%i1]
	add	%i1, 2, %i1
	andcc	%i1, 4, %o3
	bz,pt	%ncc, .bc_big_d4f
	sub	%i2, 2, %i2
.bc_big_d4:
	ldub	[%i0], %o4		! move a word (src align unknown)
	ldub	[%i0+1], %o3
	sll	%o4, 24, %o4		! position
	sll	%o3, 16, %o3		! position
	or	%o4, %o3, %o3		! merge
	ldub	[%i0+2], %o4
	sll	%o4, 8, %o4		! position
	or	%o4, %o3, %o3		! merge
	ldub	[%i0+3], %o4
	or	%o4, %o3, %o4		! merge
	stw	%o4,[%i1]		! store four bytes
	add	%i0, 4, %i0		! adjust src by 4
	add	%i1, 4, %i1		! adjust dest by 4
	ba	.bc_big_d4f
	sub	%i2, 4, %i2		! adjust count by 4


	! Dst is on 8 byte boundary; src is not;
.bc_big_unal8:
	andcc	%i1, 0x3f, %o3		! is dst 64-byte block aligned?
	bz	%ncc, .bc_unalnsrc
	sub	%o3, 64, %o3		! %o3 will be multiple of 8
	neg	%o3			! bytes until dest is 64 byte aligned
	sub	%i2, %o3, %i2		! update cnt with bytes to be moved
	! Move bytes according to source alignment
	andcc	%i0, 0x1, %o4
	bnz	%ncc, .bc_unalnbyte	! check for byte alignment
	nop
	andcc	%i0, 2, %o4		! check for half word alignment
	bnz	%ncc, .bc_unalnhalf
	nop
	! Src is word aligned, move bytes until dest 64 byte aligned
.bc_unalnword:
	ld	[%i0], %o4		! load 4 bytes
	stw	%o4, [%i1]		! and store 4 bytes
	ld	[%i0+4], %o4		! load 4 bytes
	add	%i0, 8, %i0		! increase src ptr by 8
	stw	%o4, [%i1+4]		! and store 4 bytes
	subcc	%o3, 8, %o3		! decrease count by 8
	bnz	%ncc, .bc_unalnword
	add	%i1, 8, %i1		! increase dst ptr by 8
	ba	.bc_unalnsrc
	nop

	! Src is half-word aligned, move bytes until dest 64 byte aligned
.bc_unalnhalf:
	lduh	[%i0], %o4		! load 2 bytes
	sllx	%o4, 32, %i3		! shift left
	lduw	[%i0+2], %o4
	or	%o4, %i3, %i3
	sllx	%i3, 16, %i3
	lduh	[%i0+6], %o4
	or	%o4, %i3, %i3
	stx	%i3, [%i1]
	add	%i0, 8, %i0
	subcc	%o3, 8, %o3
	bnz	%ncc, .bc_unalnhalf
	add	%i1, 8, %i1
	ba	.bc_unalnsrc
	nop

	! Src is Byte aligned, move bytes until dest 64 byte aligned
.bc_unalnbyte:
	sub	%i1, %i0, %i1		! share pointer advance
.bc_unalnbyte_loop:
	ldub	[%i0], %o4
	sllx	%o4, 56, %i3
	lduh	[%i0+1], %o4
	sllx	%o4, 40, %o4
	or	%o4, %i3, %i3
	lduh	[%i0+3], %o4
	sllx	%o4, 24, %o4
	or	%o4, %i3, %i3
	lduh	[%i0+5], %o4
	sllx	%o4, 8, %o4
	or	%o4, %i3, %i3
	ldub	[%i0+7], %o4
	or	%o4, %i3, %i3
	stx	%i3, [%i1+%i0]
	subcc	%o3, 8, %o3
	bnz	%ncc, .bc_unalnbyte_loop
	add	%i0, 8, %i0
	add	%i1,%i0, %i1		! restore pointer

	! Destination is now block (64 byte aligned), src is not 8 byte aligned
.bc_unalnsrc:
	andn	%i2, 0x3f, %i3		! %i3 is multiple of block size
	and	%i2, 0x3f, %i2		! residue bytes in %i2
	add	%i2, 64, %i2		! Insure we don't load beyond
	sub	%i3, 64, %i3		! end of source buffer

	andn	%i0, 0x3f, %o4		! %o4 has block aligned src address
	prefetch [%o4 + (3 * CACHE_LINE)], #one_read
	alignaddr %i0, %g0, %g0		! generate %gsr
	add	%i0, %i3, %i0		! advance %i0 to after blocks
	!
	! Determine source alignment to correct 8 byte offset
	andcc	%i0, 0x20, %o3
	brnz,pn	%o3, .bc_unaln_1
	andcc	%i0, 0x10, %o3
	brnz,pn	%o3, .bc_unaln_01
	andcc	%i0, 0x08, %o3
	brz,a	%o3, .bc_unaln_000
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read
	ba	.bc_unaln_001
	nop
.bc_unaln_01:
	brnz,a	%o3, .bc_unaln_011
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read
	ba	.bc_unaln_010
	nop
.bc_unaln_1:
	brnz,pn	%o3, .bc_unaln_11
	andcc	%i0, 0x08, %o3
	brnz,a	%o3, .bc_unaln_101
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read
	ba	.bc_unaln_100
	nop
.bc_unaln_11:
	brz,pn	%o3, .bc_unaln_110
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read

.bc_unaln_111:
	ldd	[%o4+56], %d14
.bc_unaln_111_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_P, %d16
	faligndata %d14, %d16, %d48
	faligndata %d16, %d18, %d50
	faligndata %d18, %d20, %d52
	faligndata %d20, %d22, %d54
	faligndata %d22, %d24, %d56
	faligndata %d24, %d26, %d58
	faligndata %d26, %d28, %d60
	faligndata %d28, %d30, %d62
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_P
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .bc_unaln_111_loop
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read
	ba	.bc_unaln_done
	nop

.bc_unaln_110:
	ldd	[%o4+48], %d12
	ldd	[%o4+56], %d14
.bc_unaln_110_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_P, %d16
	faligndata %d12, %d14, %d48
	faligndata %d14, %d16, %d50
	faligndata %d16, %d18, %d52
	faligndata %d18, %d20, %d54
	faligndata %d20, %d22, %d56
	faligndata %d22, %d24, %d58
	faligndata %d24, %d26, %d60
	faligndata %d26, %d28, %d62
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_P
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .bc_unaln_110_loop
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read
	ba	.bc_unaln_done
	nop

.bc_unaln_101:
	ldd	[%o4+40], %d10
	ldd	[%o4+48], %d12
	ldd	[%o4+56], %d14
.bc_unaln_101_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_P, %d16
	faligndata %d10, %d12, %d48
	faligndata %d12, %d14, %d50
	faligndata %d14, %d16, %d52
	faligndata %d16, %d18, %d54
	faligndata %d18, %d20, %d56
	faligndata %d20, %d22, %d58
	faligndata %d22, %d24, %d60
	faligndata %d24, %d26, %d62
	fmovd	%d26, %d10
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_P
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .bc_unaln_101_loop
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read
	ba	.bc_unaln_done
	nop

.bc_unaln_100:
	ldd	[%o4+32], %d8
	ldd	[%o4+40], %d10
	ldd	[%o4+48], %d12
	ldd	[%o4+56], %d14
.bc_unaln_100_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_P, %d16
	faligndata %d8, %d10, %d48
	faligndata %d10, %d12, %d50
	faligndata %d12, %d14, %d52
	faligndata %d14, %d16, %d54
	faligndata %d16, %d18, %d56
	faligndata %d18, %d20, %d58
	faligndata %d20, %d22, %d60
	faligndata %d22, %d24, %d62
	fmovd	%d24, %d8
	fmovd	%d26, %d10
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_P
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .bc_unaln_100_loop
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read
	ba	.bc_unaln_done
	nop

.bc_unaln_011:
	ldd	[%o4+24], %d6
	ldd	[%o4+32], %d8
	ldd	[%o4+40], %d10
	ldd	[%o4+48], %d12
	ldd	[%o4+56], %d14
.bc_unaln_011_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_P, %d16
	faligndata %d6, %d8, %d48
	faligndata %d8, %d10, %d50
	faligndata %d10, %d12, %d52
	faligndata %d12, %d14, %d54
	faligndata %d14, %d16, %d56
	faligndata %d16, %d18, %d58
	faligndata %d18, %d20, %d60
	faligndata %d20, %d22, %d62
	fmovd	%d22, %d6
	fmovd	%d24, %d8
	fmovd	%d26, %d10
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_P
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .bc_unaln_011_loop
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read
	ba	.bc_unaln_done
	nop

.bc_unaln_010:
	ldd	[%o4+16], %d4
	ldd	[%o4+24], %d6
	ldd	[%o4+32], %d8
	ldd	[%o4+40], %d10
	ldd	[%o4+48], %d12
	ldd	[%o4+56], %d14
.bc_unaln_010_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_P, %d16
	faligndata %d4, %d6, %d48
	faligndata %d6, %d8, %d50
	faligndata %d8, %d10, %d52
	faligndata %d10, %d12, %d54
	faligndata %d12, %d14, %d56
	faligndata %d14, %d16, %d58
	faligndata %d16, %d18, %d60
	faligndata %d18, %d20, %d62
	fmovd	%d20, %d4
	fmovd	%d22, %d6
	fmovd	%d24, %d8
	fmovd	%d26, %d10
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_P
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .bc_unaln_010_loop
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read
	ba	.bc_unaln_done
	nop

.bc_unaln_001:
	ldd	[%o4+8], %d2
	ldd	[%o4+16], %d4
	ldd	[%o4+24], %d6
	ldd	[%o4+32], %d8
	ldd	[%o4+40], %d10
	ldd	[%o4+48], %d12
	ldd	[%o4+56], %d14
.bc_unaln_001_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_P, %d16
	faligndata %d2, %d4, %d48
	faligndata %d4, %d6, %d50
	faligndata %d6, %d8, %d52
	faligndata %d8, %d10, %d54
	faligndata %d10, %d12, %d56
	faligndata %d12, %d14, %d58
	faligndata %d14, %d16, %d60
	faligndata %d16, %d18, %d62
	fmovd	%d18, %d2
	fmovd	%d20, %d4
	fmovd	%d22, %d6
	fmovd	%d24, %d8
	fmovd	%d26, %d10
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_P
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .bc_unaln_001_loop
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read
	ba	.bc_unaln_done
	nop

.bc_unaln_000:
	ldda	[%o4]ASI_BLK_P, %d0
.bc_unaln_000_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_P, %d16
	faligndata %d0, %d2, %d48
	faligndata %d2, %d4, %d50
	faligndata %d4, %d6, %d52
	faligndata %d6, %d8, %d54
	faligndata %d8, %d10, %d56
	faligndata %d10, %d12, %d58
	faligndata %d12, %d14, %d60
	faligndata %d14, %d16, %d62
	fmovd	%d16, %d0
	fmovd	%d18, %d2
	fmovd	%d20, %d4
	fmovd	%d22, %d6
	fmovd	%d24, %d8
	fmovd	%d26, %d10
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_P
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .bc_unaln_000_loop
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read

.bc_unaln_done:
	! Handle trailing bytes, 64 to 127
	! Dest long word aligned, Src not long word aligned
	cmp	%i2, 15
	bleu	%ncc, .bc_unaln_short

	andn	%i2, 0x7, %i3		! %i3 is multiple of 8
	and	%i2, 0x7, %i2		! residue bytes in %i2
	add	%i2, 8, %i2
	sub	%i3, 8, %i3		! insure we don't load past end of src
	andn	%i0, 0x7, %o4		! %o4 has long word aligned src address
	add	%i0, %i3, %i0		! advance %i0 to after multiple of 8
	ldd	[%o4], %d0		! fetch partial word
.bc_unaln_by8:
	ldd	[%o4+8], %d2
	add	%o4, 8, %o4
	faligndata %d0, %d2, %d16
	subcc	%i3, 8, %i3
	std	%d16, [%i1]
	fmovd	%d2, %d0
	bgu,pt	%ncc, .bc_unaln_by8
	add	%i1, 8, %i1

.bc_unaln_short:
	cmp	%i2, 8
	blt,pt	%ncc, .bc_unalnfin
	nop
	ldub	[%i0], %o4
	sll	%o4, 24, %o3
	ldub	[%i0+1], %o4
	sll	%o4, 16, %o4
	or	%o4, %o3, %o3
	ldub	[%i0+2], %o4
	sll	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%i0+3], %o4
	or	%o4, %o3, %o3
	stw	%o3, [%i1]
	ldub	[%i0+4], %o4
	sll	%o4, 24, %o3
	ldub	[%i0+5], %o4
	sll	%o4, 16, %o4
	or	%o4, %o3, %o3
	ldub	[%i0+6], %o4
	sll	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%i0+7], %o4
	or	%o4, %o3, %o3
	stw	%o3, [%i1+4]
	add	%i0, 8, %i0
	add	%i1, 8, %i1
	sub	%i2, 8, %i2
.bc_unalnfin:
	cmp	%i2, 4
	blt,pt	%ncc, .bc_unalnz
	tst	%i2
	ldub	[%i0], %o3		! read byte
	subcc	%i2, 4, %i2		! reduce count by 4
	sll	%o3, 24, %o3		! position
	ldub	[%i0+1], %o4
	sll	%o4, 16, %o4		! position
	or	%o4, %o3, %o3		! merge
	ldub	[%i0+2], %o4
	sll	%o4, 8, %o4		! position
	or	%o4, %o3, %o3		! merge
	add	%i1, 4, %i1		! advance dst by 4
	ldub	[%i0+3], %o4
	add	%i0, 4, %i0		! advance src by 4
	or	%o4, %o3, %o4		! merge
	bnz,pt	%ncc, .bc_unaln3x
	stw	%o4, [%i1-4]
	ba	.bc_exit
	nop
.bc_unalnz:
	bz,pt	%ncc, .bc_exit
.bc_unaln3x:				! Exactly 1, 2, or 3 bytes remain
	subcc	%i2, 1, %i2		! reduce count for cc test
	ldub	[%i0], %o4		! load one byte
	bz,pt	%ncc, .bc_exit
	stb	%o4, [%i1]		! store one byte
	ldub	[%i0+1], %o4		! load second byte
	subcc	%i2, 1, %i2
	bz,pt	%ncc, .bc_exit
	stb	%o4, [%i1+1]		! store second byte
	ldub	[%i0+2], %o4		! load third byte
	stb	%o4, [%i1+2]		! store third byte
.bc_exit:
	wr	%l5, %g0, %gsr		! restore %gsr
	brnz	%g5, .bc_fp_restore
	and	%o5, COPY_FLAGS, %l1	! save flags in %l1
	FZERO
	wr	%g5, %g0, %fprs
	ba,pt	%ncc, .bc_ex2
	nop
.bc_fp_restore:
	BLD_FP_FROMSTACK(%o4)
.bc_ex2:
	ldn	[THREAD_REG + T_LWP], %o2
	brnz,pt	%o2, 1f
	nop

	ldsb	[THREAD_REG + T_PREEMPT], %l0
	deccc	%l0
	bnz,pn	%ncc, 1f
	stb	%l0, [THREAD_REG + T_PREEMPT]

	! Check for a kernel preemption request
	ldn	[THREAD_REG + T_CPU], %l0
	ldub	[%l0 + CPU_KPRUNRUN], %l0
	brnz,a,pt	%l0, 1f	! Need to call kpreempt?
	or	%l1, KPREEMPT_FLAG, %l1	! If so, set the flag
1:
	btst	LOFAULT_SET, %l1
	bz,pn	%icc, 3f
	andncc	%o5, COPY_FLAGS, %o5
	! Here via bcopy. Check to see if the handler was NULL.
	! If so, just return quietly. Otherwise, reset the
	! handler and return.
	bz,pn %ncc, 2f
	nop
	membar	#Sync
	stn	%o5, [THREAD_REG + T_LOFAULT]	! restore old t_lofault
2:
	btst	KPREEMPT_FLAG, %l1
	bz,pt	%icc, 3f
	nop
	call	kpreempt
	rdpr	%pil, %o0		! pass %pil
3:
	ret
	restore	%g0, 0, %o0
	
	SET_SIZE(bcopy_more)


#else	/* NIAGARA_IMPL */
	save	%sp, -SA(MINFRAME), %sp
	clr	%o5			! flag LOFAULT_SET is not set for bcopy
.do_copy:
	cmp	%i2, 12			! for small counts
	blu	%ncc, .bytecp		! just copy bytes
	.empty

	cmp	%i2, 128		! for less than 128 bytes
	blu,pn	%ncc, .bcb_punt		! no block st/quad ld
	nop

	set	use_hw_bcopy, %o2
	ld	[%o2], %o2
	brz,pn	%o2, .bcb_punt
	nop

	subcc	%i1, %i0, %i3
	bneg,a,pn %ncc, 1f
	neg	%i3
1:
	/*
	 * Compare against 256 since we should be checking block addresses
	 * and (dest & ~63) - (src & ~63) can be 3 blocks even if
	 * src = dest + (64 * 3) + 63.
	 */
	cmp	%i3, 256
	blu,pn	%ncc, .bcb_punt
	nop

	/*
	 * Copy that reach here have at least 2 blocks of data to copy.
	 */
.do_blockcopy:
	! Swap src/dst since the code below is memcpy code
	! and memcpy/bcopy have different calling sequences
	mov	%i1, %i5
	mov	%i0, %i1
	mov	%i5, %i0

	! Block (64 bytes) align the destination.
	andcc	%i0, 0x3f, %i3		! is dst aligned on a 64 bytes
	bz	%xcc, .chksrc		! dst is already double aligned
	sub	%i3, 0x40, %i3
	neg	%i3			! bytes till dst 64 bytes aligned
	sub	%i2, %i3, %i2		! update i2 with new count

	! Based on source and destination alignment do
	! either 8 bytes, 4 bytes, 2 bytes or byte copy.

	! Is dst & src 8B aligned
	or	%i0, %i1, %o2
	andcc	%o2, 0x7, %g0
	bz	%ncc, .alewdcp
	nop

	! Is dst & src 4B aligned
	andcc	%o2, 0x3, %g0
	bz	%ncc, .alwdcp
	nop

	! Is dst & src 2B aligned
	andcc	%o2, 0x1, %g0
	bz	%ncc, .alhlfwdcp
	nop

	! 1B aligned
1:	ldub	[%i1], %o2
	stb	%o2, [%i0]
	inc	%i1
	deccc	%i3
	bgu,pt	%ncc, 1b
	inc	%i0

	ba	.chksrc
	nop

	! dst & src 4B aligned
.alwdcp:
	ld	[%i1], %o2
	st	%o2, [%i0]
	add	%i1, 0x4, %i1
	subcc	%i3, 0x4, %i3
	bgu,pt	%ncc, .alwdcp
	add	%i0, 0x4, %i0

	ba	.chksrc
	nop

	! dst & src 2B aligned
.alhlfwdcp:
	lduh	[%i1], %o2
	stuh	%o2, [%i0]
	add	%i1, 0x2, %i1
	subcc	%i3, 0x2, %i3
	bgu,pt	%ncc, .alhlfwdcp
	add	%i0, 0x2, %i0

	ba	.chksrc
	nop

	! dst & src 8B aligned
.alewdcp:
	ldx	[%i1], %o2
	stx	%o2, [%i0]
	add	%i1, 0x8, %i1
	subcc	%i3, 0x8, %i3
	bgu,pt	%ncc, .alewdcp
	add	%i0, 0x8, %i0

	! Now Destination is block (64 bytes) aligned
.chksrc:
	andn	%i2, 0x3f, %i3		! %i3 count is multiple of block size
	sub	%i2, %i3, %i2		! Residue bytes in %i2

	mov	ASI_BLK_INIT_ST_QUAD_LDD_P, %asi

	andcc	%i1, 0xf, %o2		! is src quadword aligned
	bz,pn	%xcc, .blkcpy		! src offset in %o2
	nop
	cmp	%o2, 0x8
	bg	.cpy_upper_double
	nop
	bl	.cpy_lower_double
	nop

	! Falls through when source offset is equal to 8 i.e.
	! source is double word aligned.
	! In this case no shift/merge of data is required
	sub	%i1, %o2, %i1		! align the src at 16 bytes.
	andn	%i1, 0x3f, %l0		! %l0 has block aligned source
	prefetch [%l0+0x0], #one_read
	ldda	[%i1+0x0]%asi, %l2
loop0:
	ldda	[%i1+0x10]%asi, %l4
	prefetch [%l0+0x40], #one_read

	stxa	%l3, [%i0+0x0]%asi
	stxa	%l4, [%i0+0x8]%asi

	ldda	[%i1+0x20]%asi, %l2
	stxa	%l5, [%i0+0x10]%asi
	stxa	%l2, [%i0+0x18]%asi

	ldda	[%i1+0x30]%asi, %l4
	stxa	%l3, [%i0+0x20]%asi
	stxa	%l4, [%i0+0x28]%asi

	ldda	[%i1+0x40]%asi, %l2
	stxa	%l5, [%i0+0x30]%asi
	stxa	%l2, [%i0+0x38]%asi

	add	%l0, 0x40, %l0
	add	%i1, 0x40, %i1
	subcc	%i3, 0x40, %i3
	bgu,pt	%xcc, loop0
	add	%i0, 0x40, %i0
	ba	.blkdone
	add	%i1, %o2, %i1		! increment the source by src offset
					! the src offset was stored in %o2

.cpy_lower_double:
	sub	%i1, %o2, %i1		! align the src at 16 bytes.
	sll	%o2, 3, %o0		! %o0 left shift
	mov	0x40, %o1
	sub	%o1, %o0, %o1		! %o1 right shift = (64 - left shift)
	andn	%i1, 0x3f, %l0		! %l0 has block aligned source
	prefetch [%l0+0x0], #one_read
	ldda	[%i1+0x0]%asi, %l2	! partial data in %l2 and %l3 has
					! complete data
loop1:
	ldda	[%i1+0x10]%asi, %l4	! %l4 has partial data for this read.
	ALIGN_DATA(%l2, %l3, %l4, %o0, %o1, %l6)	! merge %l2, %l3 and %l4
							! into %l2 and %l3
	prefetch [%l0+0x40], #one_read
	stxa	%l2, [%i0+0x0]%asi
	stxa	%l3, [%i0+0x8]%asi

	ldda	[%i1+0x20]%asi, %l2
	ALIGN_DATA(%l4, %l5, %l2, %o0, %o1, %l6)	! merge %l2 with %l5 and
	stxa	%l4, [%i0+0x10]%asi			! %l4 from previous read
	stxa	%l5, [%i0+0x18]%asi			! into %l4 and %l5

	! Repeat the same for next 32 bytes.

	ldda	[%i1+0x30]%asi, %l4
	ALIGN_DATA(%l2, %l3, %l4, %o0, %o1, %l6)
	stxa	%l2, [%i0+0x20]%asi
	stxa	%l3, [%i0+0x28]%asi

	ldda	[%i1+0x40]%asi, %l2
	ALIGN_DATA(%l4, %l5, %l2, %o0, %o1, %l6)
	stxa	%l4, [%i0+0x30]%asi
	stxa	%l5, [%i0+0x38]%asi

	add	%l0, 0x40, %l0
	add	%i1, 0x40, %i1
	subcc	%i3, 0x40, %i3
	bgu,pt	%xcc, loop1
	add	%i0, 0x40, %i0
	ba	.blkdone
	add	%i1, %o2, %i1		! increment the source by src offset
					! the src offset was stored in %o2

.cpy_upper_double:
	sub	%i1, %o2, %i1		! align the src at 16 bytes.
	mov	0x8, %o0
	sub	%o2, %o0, %o0
	sll	%o0, 3, %o0		! %o0 left shift
	mov	0x40, %o1
	sub	%o1, %o0, %o1		! %o1 right shift = (64 - left shift)
	andn	%i1, 0x3f, %l0		! %l0 has block aligned source
	prefetch [%l0+0x0], #one_read
	ldda	[%i1+0x0]%asi, %l2	! partial data in %l3 for this read and
					! no data in %l2
loop2:
	ldda	[%i1+0x10]%asi, %l4	! %l4 has complete data and %l5 has
					! partial
	ALIGN_DATA(%l3, %l4, %l5, %o0, %o1, %l6)	! merge %l3, %l4 and %l5
							! into %l3 and %l4
	prefetch [%l0+0x40], #one_read
	stxa	%l3, [%i0+0x0]%asi
	stxa	%l4, [%i0+0x8]%asi

	ldda	[%i1+0x20]%asi, %l2
	ALIGN_DATA(%l5, %l2, %l3, %o0, %o1, %l6)	! merge %l2 and %l3 with
	stxa	%l5, [%i0+0x10]%asi			! %l5 from previous read
	stxa	%l2, [%i0+0x18]%asi			! into %l5 and %l2

	! Repeat the same for next 32 bytes.

	ldda	[%i1+0x30]%asi, %l4
	ALIGN_DATA(%l3, %l4, %l5, %o0, %o1, %l6)
	stxa	%l3, [%i0+0x20]%asi
	stxa	%l4, [%i0+0x28]%asi

	ldda	[%i1+0x40]%asi, %l2
	ALIGN_DATA(%l5, %l2, %l3, %o0, %o1, %l6)
	stxa	%l5, [%i0+0x30]%asi
	stxa	%l2, [%i0+0x38]%asi

	add	%l0, 0x40, %l0
	add	%i1, 0x40, %i1
	subcc	%i3, 0x40, %i3
	bgu,pt	%xcc, loop2
	add	%i0, 0x40, %i0
	ba	.blkdone
	add	%i1, %o2, %i1		! increment the source by src offset
					! the src offset was stored in %o2


	! Both Source and Destination are block aligned.
	! Do fast copy using ASI_BLK_INIT_ST_QUAD_LDD_P
.blkcpy:
	prefetch [%i1+0x0], #one_read
1:
	ldda	[%i1+0x0]%asi, %l0
	ldda	[%i1+0x10]%asi, %l2
	prefetch [%i1+0x40], #one_read

	stxa	%l0, [%i0+0x0]%asi
	ldda	[%i1+0x20]%asi, %l4
	ldda	[%i1+0x30]%asi, %l6

	stxa	%l1, [%i0+0x8]%asi
	stxa	%l2, [%i0+0x10]%asi
	stxa	%l3, [%i0+0x18]%asi
	stxa	%l4, [%i0+0x20]%asi
	stxa	%l5, [%i0+0x28]%asi
	stxa	%l6, [%i0+0x30]%asi
	stxa	%l7, [%i0+0x38]%asi

	add	%i1, 0x40, %i1
	subcc	%i3, 0x40, %i3
	bgu,pt	%xcc, 1b
	add	%i0, 0x40, %i0

.blkdone:
	membar	#Sync

	brz,pt	%i2, .blkexit
	nop

	! Handle trailing bytes
	cmp	%i2, 0x8
	blu,pt	%ncc, .residue
	nop

	! Can we do some 8B ops
	or	%i1, %i0, %o2
	andcc	%o2, 0x7, %g0
	bnz	%ncc, .last4
	nop

	! Do 8byte ops as long as possible
.last8:
	ldx	[%i1], %o2
	stx	%o2, [%i0]
	add	%i1, 0x8, %i1
	sub	%i2, 0x8, %i2
	cmp	%i2, 0x8
	bgu,pt	%ncc, .last8
	add	%i0, 0x8, %i0

	brz,pt	%i2, .blkexit
	nop

	ba	.residue
	nop

.last4:
	! Can we do 4B ops
	andcc	%o2, 0x3, %g0
	bnz	%ncc, .last2
	nop
1:
	ld	[%i1], %o2
	st	%o2, [%i0]
	add	%i1, 0x4, %i1
	sub	%i2, 0x4, %i2
	cmp	%i2, 0x4
	bgu,pt	%ncc, 1b
	add	%i0, 0x4, %i0

	brz,pt	%i2, .blkexit
	nop

	ba	.residue
	nop

.last2:
	! Can we do 2B ops
	andcc	%o2, 0x1, %g0
	bnz	%ncc, .residue
	nop

1:
	lduh	[%i1], %o2
	stuh	%o2, [%i0]
	add	%i1, 0x2, %i1
	sub	%i2, 0x2, %i2
	cmp	%i2, 0x2
	bgu,pt	%ncc, 1b
	add	%i0, 0x2, %i0

	brz,pt	%i2, .blkexit
	nop

.residue:
	ldub	[%i1], %o2
	stb	%o2, [%i0]
	inc	%i1
	deccc	%i2
	bgu,pt	%ncc, .residue
	inc	%i0

.blkexit:

	membar	#Sync				! sync error barrier
	! Restore t_lofault handler, if came here from kcopy().
	tst	%o5
	bz	%ncc, 1f
	andn	%o5, LOFAULT_SET, %o5
	stn	%o5, [THREAD_REG + T_LOFAULT]	! restore old t_lofault
1:
	ret
	restore	%g0, 0, %o0


.bcb_punt:
	!
	! use aligned transfers where possible
	!
	xor	%i0, %i1, %o4		! xor from and to address
	btst	7, %o4			! if lower three bits zero
	bz	.aldoubcp		! can align on double boundary
	.empty	! assembler complaints about label

	xor	%i0, %i1, %o4		! xor from and to address
	btst	3, %o4			! if lower two bits zero
	bz	.alwordcp		! can align on word boundary
	btst	3, %i0			! delay slot, from address unaligned?
	!
	! use aligned reads and writes where possible
	! this differs from wordcp in that it copes
	! with odd alignment between source and destnation
	! using word reads and writes with the proper shifts
	! in between to align transfers to and from memory
	! i0 - src address, i1 - dest address, i2 - count
	! i3, i4 - tmps for used generating complete word
	! i5 (word to write)
	! l0 size in bits of upper part of source word (US)
	! l1 size in bits of lower part of source word (LS = 32 - US)
	! l2 size in bits of upper part of destination word (UD)
	! l3 size in bits of lower part of destination word (LD = 32 - UD)
	! l4 number of bytes leftover after aligned transfers complete
	! l5 the number 32
	!
	mov	32, %l5			! load an oft-needed constant
	bz	.align_dst_only
	btst	3, %i1			! is destnation address aligned?
	clr	%i4			! clear registers used in either case
	bz	.align_src_only
	clr	%l0
	!
	! both source and destination addresses are unaligned
	!
1:					! align source
	ldub	[%i0], %i3		! read a byte from source address
	add	%i0, 1, %i0		! increment source address
	or	%i4, %i3, %i4		! or in with previous bytes (if any)
	btst	3, %i0			! is source aligned?
	add	%l0, 8, %l0		! increment size of upper source (US)
	bnz,a	1b
	sll	%i4, 8, %i4		! make room for next byte

	sub	%l5, %l0, %l1		! generate shift left count (LS)
	sll	%i4, %l1, %i4		! prepare to get rest
	ld	[%i0], %i3		! read a word
	add	%i0, 4, %i0		! increment source address
	srl	%i3, %l0, %i5		! upper src bits into lower dst bits
	or	%i4, %i5, %i5		! merge
	mov	24, %l3			! align destination
1:
	srl	%i5, %l3, %i4		! prepare to write a single byte
	stb	%i4, [%i1]		! write a byte
	add	%i1, 1, %i1		! increment destination address
	sub	%i2, 1, %i2		! decrement count
	btst	3, %i1			! is destination aligned?
	bnz,a	1b
	sub	%l3, 8, %l3		! delay slot, decrement shift count (LD)
	sub	%l5, %l3, %l2		! generate shift left count (UD)
	sll	%i5, %l2, %i5		! move leftover into upper bytes
	cmp	%l2, %l0		! cmp # reqd to fill dst w old src left
	bgu	%ncc, .more_needed	! need more to fill than we have
	nop

	sll	%i3, %l1, %i3		! clear upper used byte(s)
	srl	%i3, %l1, %i3
	! get the odd bytes between alignments
	sub	%l0, %l2, %l0		! regenerate shift count
	sub	%l5, %l0, %l1		! generate new shift left count (LS)
	and	%i2, 3, %l4		! must do remaining bytes if count%4 > 0
	andn	%i2, 3, %i2		! # of aligned bytes that can be moved
	srl	%i3, %l0, %i4
	or	%i5, %i4, %i5
	st	%i5, [%i1]		! write a word
	subcc	%i2, 4, %i2		! decrement count
	bz	%ncc, .unalign_out
	add	%i1, 4, %i1		! increment destination address

	b	2f
	sll	%i3, %l1, %i5		! get leftover into upper bits
.more_needed:
	sll	%i3, %l0, %i3		! save remaining byte(s)
	srl	%i3, %l0, %i3
	sub	%l2, %l0, %l1		! regenerate shift count
	sub	%l5, %l1, %l0		! generate new shift left count
	sll	%i3, %l1, %i4		! move to fill empty space
	b	3f
	or	%i5, %i4, %i5		! merge to complete word
	!
	! the source address is aligned and destination is not
	!
.align_dst_only:
	ld	[%i0], %i4		! read a word
	add	%i0, 4, %i0		! increment source address
	mov	24, %l0			! initial shift alignment count
1:
	srl	%i4, %l0, %i3		! prepare to write a single byte
	stb	%i3, [%i1]		! write a byte
	add	%i1, 1, %i1		! increment destination address
	sub	%i2, 1, %i2		! decrement count
	btst	3, %i1			! is destination aligned?
	bnz,a	1b
	sub	%l0, 8, %l0		! delay slot, decrement shift count
.xfer:
	sub	%l5, %l0, %l1		! generate shift left count
	sll	%i4, %l1, %i5		! get leftover
3:
	and	%i2, 3, %l4		! must do remaining bytes if count%4 > 0
	andn	%i2, 3, %i2		! # of aligned bytes that can be moved
2:
	ld	[%i0], %i3		! read a source word
	add	%i0, 4, %i0		! increment source address
	srl	%i3, %l0, %i4		! upper src bits into lower dst bits
	or	%i5, %i4, %i5		! merge with upper dest bits (leftover)
	st	%i5, [%i1]		! write a destination word
	subcc	%i2, 4, %i2		! decrement count
	bz	%ncc, .unalign_out	! check if done
	add	%i1, 4, %i1		! increment destination address
	b	2b			! loop
	sll	%i3, %l1, %i5		! get leftover
.unalign_out:
	tst	%l4			! any bytes leftover?
	bz	%ncc, .cpdone
	.empty				! allow next instruction in delay slot
1:
	sub	%l0, 8, %l0		! decrement shift
	srl	%i3, %l0, %i4		! upper src byte into lower dst byte
	stb	%i4, [%i1]		! write a byte
	subcc	%l4, 1, %l4		! decrement count
	bz	%ncc, .cpdone		! done?
	add	%i1, 1, %i1		! increment destination
	tst	%l0			! any more previously read bytes
	bnz	%ncc, 1b		! we have leftover bytes
	mov	%l4, %i2		! delay slot, mv cnt where dbytecp wants
	b	.dbytecp		! let dbytecp do the rest
	sub	%i0, %i1, %i0		! i0 gets the difference of src and dst
	!
	! the destination address is aligned and the source is not
	!
.align_src_only:
	ldub	[%i0], %i3		! read a byte from source address
	add	%i0, 1, %i0		! increment source address
	or	%i4, %i3, %i4		! or in with previous bytes (if any)
	btst	3, %i0			! is source aligned?
	add	%l0, 8, %l0		! increment shift count (US)
	bnz,a	.align_src_only
	sll	%i4, 8, %i4		! make room for next byte
	b,a	.xfer
	!
	! if from address unaligned for double-word moves,
	! move bytes till it is, if count is < 56 it could take
	! longer to align the thing than to do the transfer
	! in word size chunks right away
	!
.aldoubcp:
	cmp	%i2, 56			! if count < 56, use wordcp, it takes
	blu,a	%ncc, .alwordcp		! longer to align doubles than words
	mov	3, %o0			! mask for word alignment
	call	.alignit		! copy bytes until aligned
	mov	7, %o0			! mask for double alignment
	!
	! source and destination are now double-word aligned
	! i3 has aligned count returned by alignit
	!
	and	%i2, 7, %i2		! unaligned leftover count
	sub	%i0, %i1, %i0		! i0 gets the difference of src and dst
5:
	ldx	[%i0+%i1], %o4		! read from address
	stx	%o4, [%i1]		! write at destination address
	subcc	%i3, 8, %i3		! dec count
	bgu	%ncc, 5b
	add	%i1, 8, %i1		! delay slot, inc to address
	cmp	%i2, 4			! see if we can copy a word
	blu	%ncc, .dbytecp		! if 3 or less bytes use bytecp
	.empty
	!
	! for leftover bytes we fall into wordcp, if needed
	!
.wordcp:
	and	%i2, 3, %i2		! unaligned leftover count
5:
	ld	[%i0+%i1], %o4		! read from address
	st	%o4, [%i1]		! write at destination address
	subcc	%i3, 4, %i3		! dec count
	bgu	%ncc, 5b
	add	%i1, 4, %i1		! delay slot, inc to address
	b,a	.dbytecp

	! we come here to align copies on word boundaries
.alwordcp:
	call	.alignit		! go word-align it
	mov	3, %o0			! bits that must be zero to be aligned
	b	.wordcp
	sub	%i0, %i1, %i0		! i0 gets the difference of src and dst

	!
	! byte copy, works with any alignment
	!
.bytecp:
	b	.dbytecp
	sub	%i0, %i1, %i0		! i0 gets difference of src and dst

	!
	! differenced byte copy, works with any alignment
	! assumes dest in %i1 and (source - dest) in %i0
	!
1:
	stb	%o4, [%i1]		! write to address
	inc	%i1			! inc to address
.dbytecp:
	deccc	%i2			! dec count
	bgeu,a	%ncc, 1b		! loop till done
	ldub	[%i0+%i1], %o4		! read from address
.cpdone:

	membar	#Sync				! sync error barrier
	! Restore t_lofault handler, if came here from kcopy().
	tst	%o5
	bz	%ncc, 1f
	andn	%o5, LOFAULT_SET, %o5
	stn	%o5, [THREAD_REG + T_LOFAULT]	! restore old t_lofault
1:
	ret
	restore %g0, 0, %o0		! return (0)

/*
 * Common code used to align transfers on word and doubleword
 * boundaries.  Aligns source and destination and returns a count
 * of aligned bytes to transfer in %i3
 */
1:
	inc	%i0			! inc from
	stb	%o4, [%i1]		! write a byte
	inc	%i1			! inc to
	dec	%i2			! dec count
.alignit:
	btst	%o0, %i0		! %o0 is bit mask to check for alignment
	bnz,a	1b
	ldub	[%i0], %o4		! read next byte

	retl
	andn	%i2, %o0, %i3		! return size of aligned bytes
	
	SET_SIZE(bcopy)

#endif	/* NIAGARA_IMPL */

#endif	/* lint */

/*
 * Block copy with possibly overlapped operands.
 */

#if defined(lint)

/*ARGSUSED*/
void
ovbcopy(const void *from, void *to, size_t count)
{}

#else	/* lint */

	ENTRY(ovbcopy)
	tst	%o2			! check count
	bgu,a	%ncc, 1f		! nothing to do or bad arguments
	subcc	%o0, %o1, %o3		! difference of from and to address

	retl				! return
	nop
1:
	bneg,a	%ncc, 2f
	neg	%o3			! if < 0, make it positive
2:	cmp	%o2, %o3		! cmp size and abs(from - to)
	bleu	%ncc, bcopy		! if size <= abs(diff): use bcopy,
	.empty				!   no overlap
	cmp	%o0, %o1		! compare from and to addresses
	blu	%ncc, .ov_bkwd		! if from < to, copy backwards
	nop
	!
	! Copy forwards.
	!
.ov_fwd:
	ldub	[%o0], %o3		! read from address
	inc	%o0			! inc from address
	stb	%o3, [%o1]		! write to address
	deccc	%o2			! dec count
	bgu	%ncc, .ov_fwd		! loop till done
	inc	%o1			! inc to address

	retl				! return
	nop
	!
	! Copy backwards.
	!
.ov_bkwd:
	deccc	%o2			! dec count
	ldub	[%o0 + %o2], %o3	! get byte at end of src
	bgu	%ncc, .ov_bkwd		! loop till done
	stb	%o3, [%o1 + %o2]	! delay slot, store at end of dst

	retl				! return
	nop
	SET_SIZE(ovbcopy)

#endif	/* lint */

/*
 * hwblkpagecopy()
 *
 * Copies exactly one page.  This routine assumes the caller (ppcopy)
 * has already disabled kernel preemption and has checked
 * use_hw_bcopy.
 */
#ifdef lint
/*ARGSUSED*/
void
hwblkpagecopy(const void *src, void *dst)
{ }
#else /* lint */
	ENTRY(hwblkpagecopy)
	save	%sp, -SA(MINFRAME), %sp

	! %i0 - source address (arg)
	! %i1 - destination address (arg)
	! %i2 - length of region (not arg)

	set	PAGESIZE, %i2

	/*
	 * Copying exactly one page and PAGESIZE is in mutliple of 0x80. 
	 */
	mov	ASI_BLK_INIT_ST_QUAD_LDD_P, %asi
	prefetch [%i0+0x0], #one_read
	prefetch [%i0+0x40], #one_read
1:
	prefetch [%i0+0x80], #one_read
	prefetch [%i0+0xc0], #one_read
	ldda	[%i0+0x0]%asi, %l0
	ldda	[%i0+0x10]%asi, %l2
	ldda	[%i0+0x20]%asi, %l4
	ldda	[%i0+0x30]%asi, %l6
	stxa	%l0, [%i1+0x0]%asi
	stxa	%l1, [%i1+0x8]%asi
	stxa	%l2, [%i1+0x10]%asi
	stxa	%l3, [%i1+0x18]%asi
	stxa	%l4, [%i1+0x20]%asi
	stxa	%l5, [%i1+0x28]%asi
	stxa	%l6, [%i1+0x30]%asi
	stxa	%l7, [%i1+0x38]%asi
	ldda	[%i0+0x40]%asi, %l0
	ldda	[%i0+0x50]%asi, %l2
	ldda	[%i0+0x60]%asi, %l4
	ldda	[%i0+0x70]%asi, %l6
	stxa	%l0, [%i1+0x40]%asi
	stxa	%l1, [%i1+0x48]%asi
	stxa	%l2, [%i1+0x50]%asi
	stxa	%l3, [%i1+0x58]%asi
	stxa	%l4, [%i1+0x60]%asi
	stxa	%l5, [%i1+0x68]%asi
	stxa	%l6, [%i1+0x70]%asi
	stxa	%l7, [%i1+0x78]%asi

	add	%i0, 0x80, %i0
	subcc	%i2, 0x80, %i2
	bgu,pt	%xcc, 1b
	add	%i1, 0x80, %i1

	membar #Sync
	ret
	restore	%g0, 0, %o0
	SET_SIZE(hwblkpagecopy)
#endif	/* lint */


/*
 * Transfer data to and from user space -
 * Note that these routines can cause faults
 * It is assumed that the kernel has nothing at
 * less than KERNELBASE in the virtual address space.
 *
 * Note that copyin(9F) and copyout(9F) are part of the
 * DDI/DKI which specifies that they return '-1' on "errors."
 *
 * Sigh.
 *
 * So there's two extremely similar routines - xcopyin() and xcopyout()
 * which return the errno that we've faithfully computed.  This
 * allows other callers (e.g. uiomove(9F)) to work correctly.
 * Given that these are used pretty heavily, we expand the calling
 * sequences inline for all flavours (rather than making wrappers).
 *
 * There are also stub routines for xcopyout_little and xcopyin_little,
 * which currently are intended to handle requests of <= 16 bytes from
 * do_unaligned. Future enhancement to make them handle 8k pages efficiently
 * is left as an exercise...
 */

/*
 * Copy user data to kernel space (copyOP/xcopyOP/copyOP_noerr)
 *
 * General theory of operation:
 *
 * None of the copyops routines grab a window until it's decided that
 * we need to do a HW block copy operation. This saves a window
 * spill/fill when we're called during socket ops. The typical IO
 * path won't cause spill/fill traps.
 *
 * This code uses a set of 4 limits for the maximum size that will
 * be copied given a particular input/output address alignment.
 * the default limits are:
 *
 * single byte aligned - 256 (hw_copy_limit_1)
 * two byte aligned - 512 (hw_copy_limit_2)
 * four byte aligned - 1024 (hw_copy_limit_4)
 * eight byte aligned - 1024 (hw_copy_limit_8)
 *
 * If the value for a particular limit is zero, the copy will be done
 * via the copy loops rather than block store/quad load instructions.
 *
 * Flow:
 *
 * If count == zero return zero.
 *
 * Store the previous lo_fault handler into %g6.
 * Place our secondary lofault handler into %g5.
 * Place the address of our nowindow fault handler into %o3.
 * Place the address of the windowed fault handler into %o4.
 * --> We'll use this handler if we end up grabbing a window
 * --> before we use block initializing store and quad load ASIs
 *
 * If count is less than or equal to SMALL_LIMIT (7) we
 * always do a byte for byte copy.
 *
 * If count is > SMALL_LIMIT, we check the alignment of the input
 * and output pointers. Based on the alignment we check count
 * against a limit based on detected alignment.  If we exceed the
 * alignment value we copy via block initializing store and quad
 * load instructions.
 *
 * If we don't exceed one of the limits, we store -count in %o3,
 * we store the number of chunks (8, 4, 2 or 1 byte) operated
 * on in our basic copy loop in %o2. Following this we branch 
 * to the appropriate copy loop and copy that many chunks.
 * Since we've been adding the chunk size to %o3 each time through
 * as well as decrementing %o2, we can tell if any data is
 * is left to be copied by examining %o3. If that is zero, we're
 * done and can go home. If not, we figure out what the largest
 * chunk size left to be copied is and branch to that copy loop
 * unless there's only one byte left. We load that as we're
 * branching to code that stores it just before we return.
 *
 * Fault handlers are invoked if we reference memory that has no
 * current mapping.  All forms share the same copyio_fault handler.
 * This routine handles fixing up the stack and general housecleaning.
 * Each copy operation has a simple fault handler that is then called
 * to do the work specific to the invidual operation.  The handler
 * for copyOP and xcopyOP are found at the end of individual function.
 * The handlers for xcopyOP_little are found at the end of xcopyin_little.
 * The handlers for copyOP_noerr are found at the end of copyin_noerr.
 */

/*
 * Copy kernel data to user space (copyout/xcopyout/xcopyout_little).
 */

#if defined(lint)

/*ARGSUSED*/
int
copyout(const void *kaddr, void *uaddr, size_t count)
{ return (0); }

#else	/* lint */

/*
 * We save the arguments in the following registers in case of a fault:
 * 	kaddr - %g2
 * 	uaddr - %g3
 * 	count - %g4
 */
#define	SAVE_SRC	%g2
#define	SAVE_DST	%g3
#define	SAVE_COUNT	%g4

#define	REAL_LOFAULT		%g5
#define	SAVED_LOFAULT		%g6

/*
 * Generic copyio fault handler.  This is the first line of defense when a 
 * fault occurs in (x)copyin/(x)copyout.  In order for this to function
 * properly, the value of the 'real' lofault handler should be in REAL_LOFAULT.
 * This allows us to share common code for all the flavors of the copy
 * operations, including the _noerr versions.
 *
 * Note that this function will restore the original input parameters before
 * calling REAL_LOFAULT.  So the real handler can vector to the appropriate
 * member of the t_copyop structure, if needed.
 */
	ENTRY(copyio_fault)
#if !defined(NIAGARA_IMPL)
	btst	FPUSED_FLAG, SAVED_LOFAULT
	bz	1f
	andn	SAVED_LOFAULT, FPUSED_FLAG, SAVED_LOFAULT

	wr	%l5, 0, %gsr		! restore gsr

	btst	FPRS_FEF, %g1
	bz	%icc, 4f
	nop

	! restore fpregs from stack
	BLD_FP_FROMSTACK(%o2)

	ba,pt	%ncc, 1f
	nop
4:
	FZERO				! zero all of the fpregs
	wr	%g1, %g0, %fprs		! restore fprs
1:
	restore
	mov	SAVE_SRC, %o0
	mov	SAVE_DST, %o1
	jmp	REAL_LOFAULT
	mov	SAVE_COUNT, %o2

#else	/* NIAGARA_IMPL */
	membar	#Sync
	stn	SAVED_LOFAULT, [THREAD_REG + T_LOFAULT]	! restore old t_lofault
	restore
	mov	SAVE_SRC, %o0
	mov	SAVE_DST, %o1
	jmp	REAL_LOFAULT
	mov	SAVE_COUNT, %o2

#endif	/* NIAGARA_IMPL */

	SET_SIZE(copyio_fault)

	ENTRY(copyio_fault_nowindow)
	membar	#Sync
	stn	SAVED_LOFAULT, [THREAD_REG + T_LOFAULT]	! restore old t_lofault

	mov	SAVE_SRC, %o0
	mov	SAVE_DST, %o1
	jmp	REAL_LOFAULT
	mov	SAVE_COUNT, %o2
	SET_SIZE(copyio_fault_nowindow)

	ENTRY(copyout)
	sethi	%hi(.copyout_err), REAL_LOFAULT
	or	REAL_LOFAULT, %lo(.copyout_err), REAL_LOFAULT

#if !defined(NIAGARA_IMPL)
.do_copyout:
	tst	%o2			! check for zero count;  quick exit
	bz,pt	%ncc, .co_smallqx
	mov	%o0, SAVE_SRC
	mov	%o1, SAVE_DST
	mov	%o2, SAVE_COUNT
	cmp	%o2, FP_COPY		! check for small copy/leaf case
	bgt,pt	%ncc, .co_copy_more
	ldn	[THREAD_REG + T_LOFAULT], SAVED_LOFAULT
/*
 * Small copy out code
 * 
 */
	sethi	%hi(copyio_fault_nowindow), %o3
	or	%o3, %lo(copyio_fault_nowindow), %o3
	membar	#Sync
	stn	%o3, [THREAD_REG + T_LOFAULT]

	mov	ASI_USER, %asi
	cmp	%o2, SHORTCOPY		! make sure there is enough to align
	ble,pt	%ncc, .co_smallest
	andcc	%o1, 0x7, %o3		! is dest long word aligned
	bnz,pn	%ncc, .co_align
	andcc	%o1, 1, %o3		! is dest byte aligned

! Destination is long word aligned
! 8 cases for src alignment; load parts, store long words
.co_al_src:
	andcc	%o0, 7, %o3
	brnz,pt	%o3, .co_src_dst_unal8
	nop
/*
 * Special case for handling when src and dest are both long word aligned
 * and total data to move is less than FP_COPY bytes
 * Also handles finish up for large block moves, so may be less than 32 bytes
 */
.co_medlong:
	subcc	%o2, 31, %o2		! adjust length to allow cc test
	ble,pt	%ncc, .co_medl31
	nop
.co_medl32:
	ldx	[%o0], %o4		! move 32 bytes
	subcc	%o2, 32, %o2		! decrement length count by 32
	stxa	%o4, [%o1]%asi
	ldx	[%o0+8], %o4
	stxa	%o4, [%o1+8]%asi
	ldx	[%o0+16], %o4
	add	%o0, 32, %o0		! increase src ptr by 32
	stxa	%o4, [%o1+16]%asi
	ldx	[%o0-8], %o4
	add	%o1, 32, %o1		! increase dst ptr by 32
	bgu,pt	%ncc, .co_medl32	! repeat if at least 32 bytes left
	stxa	%o4, [%o1-8]%asi
.co_medl31:
	addcc	%o2, 24, %o2		! adjust count to be off by 7
	ble,pt	%ncc, .co_medl7		! skip if 7 or fewer bytes left
	nop
.co_medl8:
	ldx	[%o0], %o4		! move 8 bytes
	add	%o0, 8, %o0		! increase src ptr by 8
	subcc	%o2, 8, %o2		! decrease count by 8
	add	%o1, 8, %o1		! increase dst ptr by 8
	bgu,pt	%ncc, .co_medl8
	stxa	%o4, [%o1-8]%asi
.co_medl7:
	addcc	%o2, 7, %o2		! finish adjustment of remaining count
	bnz,pt	%ncc, .co_small4	! do final bytes if not finished

.co_smallx:				! finish up and exit
	membar	#Sync
	stn	SAVED_LOFAULT, [THREAD_REG + T_LOFAULT]
.co_smallqx:
	retl
	mov	%g0, %o0

.co_small4:
	cmp	%o2, 4
	blt,pt	%ncc, .co_small3x	! skip if less than 4 bytes left
	nop				!
	ld	[%o0], %o4		! move 4 bytes
	add	%o0, 4, %o0		! increase src ptr by 4
	add	%o1, 4, %o1		! increase dst ptr by 4
	subcc	%o2, 4, %o2		! decrease count by 4
	bz,pt	%ncc, .co_smallx
	stwa	%o4, [%o1-4]%asi

.co_small3x:				! Exactly 1, 2, or 3 bytes remain
	subcc	%o2, 1, %o2		! reduce count for cc test
	ldub	[%o0], %o4		! load one byte
	bz,pt	%ncc, .co_smallx
	stba	%o4, [%o1]%asi		! store one byte
	ldub	[%o0+1], %o4		! load second byte
	subcc	%o2, 1, %o2
	bz,pt	%ncc, .co_smallx
	stba	%o4, [%o1+1]%asi	! store second byte
	ldub	[%o0+2], %o4		! load third byte
	ba	.co_smallx
	stba	%o4, [%o1+2]%asi	! store third byte

.co_smallest:				! 7 or fewer bytes remain
	cmp	%o2, 4
	blt,pt	%ncc, .co_small3x
	nop
	ldub	[%o0], %o4		! read byte
	subcc	%o2, 4, %o2		! reduce count by 4
	stba	%o4, [%o1]%asi		! write byte
	ldub	[%o0+1], %o4		! repeat for total of 4 bytes
	add	%o0, 4, %o0		! advance src by 4
	stba	%o4, [%o1+1]%asi
	ldub	[%o0-2], %o4
	add	%o1, 4, %o1		! advance dst by 4
	stba	%o4, [%o1-2]%asi
	ldub	[%o0-1], %o4
	bnz,pt	%ncc, .co_small3x
	stba	%o4, [%o1-1]%asi
	membar	#Sync
	stn	SAVED_LOFAULT, [THREAD_REG + T_LOFAULT]
	retl
	mov	%g0, %o0

.co_align:				! byte align test in prior branch delay
	bnz,pt	%ncc, .co_al_d1
.co_al_d1f:				! dest is now half word aligned
	andcc	%o1, 2, %o3
	bnz,pt	%ncc, .co_al_d2
.co_al_d2f:				! dest is now word aligned
	andcc	%o1, 4, %o3		! is dest longword aligned?
	bz,pt	%ncc, .co_al_src
	nop
.co_al_d4:				! dest is word aligned;  src is unknown
	ldub	[%o0], %o4		! move a word (src align unknown)
	ldub	[%o0+1], %o3
	sll	%o4, 24, %o4		! position
	sll	%o3, 16, %o3		! position
	or	%o4, %o3, %o3		! merge
	ldub	[%o0+2], %o4
	sll	%o4, 8, %o4		! position
	or	%o4, %o3, %o3		! merge
	ldub	[%o0+3], %o4
	or	%o4, %o3, %o4		! merge
	stwa	%o4,[%o1]%asi		! store four bytes
	add	%o0, 4, %o0		! adjust src by 4
	add	%o1, 4, %o1		! adjust dest by 4
	sub	%o2, 4, %o2		! adjust count by 4
	andcc	%o0, 7, %o3		! check for src long word alignment
	brz,pt	%o3, .co_medlong
.co_src_dst_unal8:
	! dst is 8-byte aligned, src is not
	! Size is less than FP_COPY
	! Following code is to select for alignment
	andcc	%o0, 0x3, %o3		! test word alignment
	bz,pt	%ncc, .co_medword
	nop
	andcc	%o0, 0x1, %o3		! test halfword alignment
	bnz,pt	%ncc, .co_med_byte	! go to byte move if not halfword
	andcc	%o0, 0x2, %o3		! test which byte alignment
	ba	.co_medhalf
	nop
.co_al_d1:				! align dest to half word
	ldub	[%o0], %o4		! move a byte
	add	%o0, 1, %o0
	stba	%o4, [%o1]%asi
	add	%o1, 1, %o1
	andcc	%o1, 2, %o3
	bz,pt	%ncc, .co_al_d2f
	sub	%o2, 1, %o2
.co_al_d2:				! align dest to word
	ldub	[%o0], %o4		! move a half-word (src align unknown)
	ldub	[%o0+1], %o3
	sll	%o4, 8, %o4		! position
	or	%o4, %o3, %o4		! merge
	stha	%o4, [%o1]%asi
	add	%o0, 2, %o0
	add	%o1, 2, %o1
	andcc	%o1, 4, %o3		! is dest longword aligned?
	bz,pt	%ncc, .co_al_src
	sub	%o2, 2, %o2
	ba	.co_al_d4
	nop
/*
 * Handle all cases where src and dest are aligned on word
 * boundaries. Use unrolled loops for better performance.
 * This option wins over standard large data move when 
 * source and destination is in cache for medium
 * to short data moves.
 */
.co_medword:
	subcc	%o2, 31, %o2		! adjust length to allow cc test
	ble,pt	%ncc, .co_medw31
	nop
.co_medw32:
	ld	[%o0], %o4		! move a block of 32 bytes
	stwa	%o4, [%o1]%asi
	ld	[%o0+4], %o4
	stwa	%o4, [%o1+4]%asi
	ld	[%o0+8], %o4
	stwa	%o4, [%o1+8]%asi
	ld	[%o0+12], %o4
	stwa	%o4, [%o1+12]%asi
	ld	[%o0+16], %o4
	stwa	%o4, [%o1+16]%asi
	ld	[%o0+20], %o4
	subcc	%o2, 32, %o2		! decrement length count
	stwa	%o4, [%o1+20]%asi
	ld	[%o0+24], %o4
	add	%o0, 32, %o0		! increase src ptr by 32
	stwa	%o4, [%o1+24]%asi
	ld	[%o0-4], %o4
	add	%o1, 32, %o1		! increase dst ptr by 32
	bgu,pt	%ncc, .co_medw32	! repeat if at least 32 bytes left
	stwa	%o4, [%o1-4]%asi
.co_medw31:
	addcc	%o2, 24, %o2		! adjust count to be off by 7
	ble,pt	%ncc, .co_medw7		! skip if 7 or fewer bytes left
	nop				!
.co_medw15:
	ld	[%o0], %o4		! move a block of 8 bytes
	subcc	%o2, 8, %o2		! decrement length count
	stwa	%o4, [%o1]%asi
	add	%o0, 8, %o0		! increase src ptr by 8
	ld	[%o0-4], %o4
	add	%o1, 8, %o1		! increase dst ptr by 8
	bgu,pt	%ncc, .co_medw15
	stwa	%o4, [%o1-4]%asi
.co_medw7:
	addcc	%o2, 7, %o2		! finish adjustment of remaining count
	bz,pt	%ncc, .co_smallx	! exit if finished
	cmp	%o2, 4
	blt,pt	%ncc, .co_small3x	! skip if less than 4 bytes left
	nop				!
	ld	[%o0], %o4		! move 4 bytes
	add	%o0, 4, %o0		! increase src ptr by 4
	add	%o1, 4, %o1		! increase dst ptr by 4
	subcc	%o2, 4, %o2		! decrease count by 4
	bnz	.co_small3x
	stwa	%o4, [%o1-4]%asi
	membar	#Sync
	stn	SAVED_LOFAULT, [THREAD_REG + T_LOFAULT]
	retl
	mov	%g0, %o0

.co_medhalf:
	subcc	%o2, 31, %o2		! adjust length to allow cc test
	ble,pt	%ncc, .co_medh31
	nop
.co_medh32:				! load and store block of 32 bytes

	lduh	[%o0], %o4		! move 32 bytes
	subcc	%o2, 32, %o2		! decrement length count
	lduw	[%o0+2], %o3
	sllx	%o4, 48, %o4
	sllx	%o3, 16, %o3
	or	%o4, %o3, %o3
	lduh	[%o0+6], %o4
	or	%o4, %o3, %o4
	stxa	%o4, [%o1]%asi

	lduh	[%o0+8], %o4
	lduw	[%o0+10], %o3
	sllx	%o4, 48, %o4
	sllx	%o3, 16, %o3
	or	%o4, %o3, %o3
	lduh	[%o0+14], %o4
	or	%o4, %o3, %o4
	stxa	%o4, [%o1+8]%asi

	lduh	[%o0+16], %o4
	lduw	[%o0+18], %o3
	sllx	%o4, 48, %o4
	sllx	%o3, 16, %o3
	or	%o4, %o3, %o3
	lduh	[%o0+22], %o4
	or	%o4, %o3, %o4
	stxa	%o4, [%o1+16]%asi

	add	%o0, 32, %o0		! increase src ptr by 32
	add	%o1, 32, %o1		! increase dst ptr by 32

	lduh	[%o0-8], %o4
	lduw	[%o0-6], %o3
	sllx	%o4, 48, %o4
	sllx	%o3, 16, %o3
	or	%o4, %o3, %o3
	lduh	[%o0-2], %o4
	or	%o3, %o4, %o4
	bgu,pt	%ncc, .co_medh32	! repeat if at least 32 bytes left
	stxa	%o4, [%o1-8]%asi

.co_medh31:
	addcc	%o2, 24, %o2		! adjust count to be off by 7
	ble,pt	%ncc, .co_medh7		! skip if 7 or fewer bytes left
	nop				!
.co_medh15:
	lduh	[%o0], %o4		! move 16 bytes
	subcc	%o2, 8, %o2		! decrement length count
	lduw	[%o0+2], %o3
	sllx	%o4, 48, %o4
	sllx	%o3, 16, %o3
	or	%o4, %o3, %o3
	add	%o1, 8, %o1		! increase dst ptr by 8
	lduh	[%o0+6], %o4
	add	%o0, 8, %o0		! increase src ptr by 8
	or	%o4, %o3, %o4
	bgu,pt	%ncc, .co_medh15
	stxa	%o4, [%o1-8]%asi
.co_medh7:
	addcc	%o2, 7, %o2		! finish adjustment of remaining count
	bz,pt	%ncc, .co_smallx	! exit if finished
	cmp	%o2, 4
	blt,pt	%ncc, .co_small3x	! skip if less than 4 bytes left
	nop				!
	lduh	[%o0], %o4
	sll	%o4, 16, %o4
	lduh	[%o0+2], %o3
	or	%o3, %o4, %o4
	subcc	%o2, 4, %o2
	add	%o0, 4, %o0
	add	%o1, 4, %o1
	bnz	.co_small3x
	stwa	%o4, [%o1-4]%asi
	membar	#Sync
	stn	SAVED_LOFAULT, [THREAD_REG + T_LOFAULT]
	retl
	mov	%g0, %o0

	.align 16
.co_med_byte:
	bnz,pt	%ncc, .co_medbh32a	! go to correct byte move
	subcc	%o2, 31, %o2		! adjust length to allow cc test
	ble,pt	%ncc, .co_medb31
	nop
.co_medb32:				! Alignment 1 or 5
	subcc	%o2, 32, %o2		! decrement length count

	ldub	[%o0], %o4		! load and store a block of 32 bytes
	sllx	%o4, 56, %o3
	lduh	[%o0+1], %o4
	sllx	%o4, 40, %o4
	or	%o4, %o3, %o3
	lduw	[%o0+3], %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0+7], %o4
	or	%o4, %o3, %o4
	stxa	%o4, [%o1]%asi

	ldub	[%o0+8], %o4
	sllx	%o4, 56, %o3
	lduh	[%o0+9], %o4
	sllx	%o4, 40, %o4
	or	%o4, %o3, %o3
	lduw	[%o0+11], %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0+15], %o4
	or	%o4, %o3, %o4
	stxa	%o4, [%o1+8]%asi

	ldub	[%o0+16], %o4
	sllx	%o4, 56, %o3
	lduh	[%o0+17], %o4
	sllx	%o4, 40, %o4
	or	%o4, %o3, %o3
	lduw	[%o0+19], %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0+23], %o4
	or	%o4, %o3, %o4
	stxa	%o4, [%o1+16]%asi

	add	%o0, 32, %o0		! increase src ptr by 32
	add	%o1, 32, %o1		! increase dst ptr by 32

	ldub	[%o0-8], %o4
	sllx	%o4, 56, %o3
	lduh	[%o0-7], %o4
	sllx	%o4, 40, %o4
	or	%o4, %o3, %o3
	lduw	[%o0-5], %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0-1], %o4
	or	%o4, %o3, %o4
	bgu,pt	%ncc, .co_medb32	! repeat if at least 32 bytes left
	stxa	%o4, [%o1-8]%asi

.co_medb31:				! 31 or fewer bytes remaining
	addcc	%o2, 24, %o2		! adjust count to be off by 7
	ble,pt	%ncc, .co_medb7		! skip if 7 or fewer bytes left
	nop				!
.co_medb15:

	ldub	[%o0], %o4		! load and store a block of 8 bytes
	subcc	%o2, 8, %o2		! decrement length count
	sllx	%o4, 56, %o3
	lduh	[%o0+1], %o4
	sllx	%o4, 40, %o4
	or	%o4, %o3, %o3
	lduw	[%o0+3], %o4
	add	%o1, 8, %o1		! increase dst ptr by 16
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0+7], %o4
	add	%o0, 8, %o0		! increase src ptr by 16
	or	%o4, %o3, %o4
	bgu,pt	%ncc, .co_medb15
	stxa	%o4, [%o1-8]%asi
.co_medb7:
	addcc	%o2, 7, %o2		! finish adjustment of remaining count
	bz,pt	%ncc, .co_smallx	! exit if finished
	cmp	%o2, 4
	blt,pt	%ncc, .co_small3x	! skip if less than 4 bytes left
	nop				!
	ldub	[%o0], %o4		! move 4 bytes
	sll	%o4, 24, %o3
	lduh	[%o0+1], %o4
	sll	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0+3], %o4
	or	%o4, %o3, %o4
	subcc	%o2, 4, %o2
	add	%o0, 4, %o0
	add	%o1, 4, %o1
	bnz	.co_small3x
	stwa	%o4, [%o1-4]%asi
	membar	#Sync
	stn	SAVED_LOFAULT, [THREAD_REG + T_LOFAULT]
	retl
	mov	%g0, %o0

	.align 16
.co_medbh32a:
	ble,pt	%ncc, .co_medbh31
	nop
.co_medbh32:				! Alignment 3 or 7
	subcc	%o2, 32, %o2		! decrement length count

	ldub	[%o0], %o4		! load and store a block of 32 bytes
	sllx	%o4, 56, %o3
	lduw	[%o0+1], %o4
	sllx	%o4, 24, %o4
	or	%o4, %o3, %o3
	lduh	[%o0+5], %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0+7], %o4
	or	%o4, %o3, %o4
	stxa	%o4, [%o1]%asi

	ldub	[%o0+8], %o4
	sllx	%o4, 56, %o3
	lduw	[%o0+9], %o4
	sllx	%o4, 24, %o4
	or	%o4, %o3, %o3
	lduh	[%o0+13], %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0+15], %o4
	or	%o4, %o3, %o4
	stxa	%o4, [%o1+8]%asi

	ldub	[%o0+16], %o4
	sllx	%o4, 56, %o3
	lduw	[%o0+17], %o4
	sllx	%o4, 24, %o4
	or	%o4, %o3, %o3
	lduh	[%o0+21], %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0+23], %o4
	or	%o4, %o3, %o4
	stxa	%o4, [%o1+16]%asi

	add	%o0, 32, %o0		! increase src ptr by 32
	add	%o1, 32, %o1		! increase dst ptr by 32

	ldub	[%o0-8], %o4
	sllx	%o4, 56, %o3
	lduw	[%o0-7], %o4
	sllx	%o4, 24, %o4
	or	%o4, %o3, %o3
	lduh	[%o0-3], %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0-1], %o4
	or	%o4, %o3, %o4
	bgu,pt	%ncc, .co_medbh32	! repeat if at least 32 bytes left
	stxa	%o4, [%o1-8]%asi

.co_medbh31:
	addcc	%o2, 24, %o2		! adjust count to be off by 7
	ble,pt	%ncc, .co_medb7		! skip if 7 or fewer bytes left
	nop				!
.co_medbh15:
	ldub	[%o0], %o4		! load and store a block of 8 bytes
	sllx	%o4, 56, %o3
	lduw	[%o0+1], %o4
	sllx	%o4, 24, %o4
	or	%o4, %o3, %o3
	lduh	[%o0+5], %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%o0+7], %o4
	or	%o4, %o3, %o4
	stxa	%o4, [%o1]%asi
	subcc	%o2, 8, %o2		! decrement length count
	add	%o1, 8, %o1		! increase dst ptr by 8
	add	%o0, 8, %o0		! increase src ptr by 8
	bgu,pt	%ncc, .co_medbh15
	stxa	%o4, [%o1-8]%asi
	ba	.co_medb7
	nop
/*
 * End of small copy (no window) code
 */

/*
 * Long copy code
 */
.co_copy_more:
	sethi	%hi(copyio_fault), %o3
	or	%o3, %lo(copyio_fault), %o3
	membar	#Sync
	stn	%o3, [THREAD_REG + T_LOFAULT]

/*
 * Following code is for large copies. We know there is at
 * least FP_COPY bytes available. FP regs are used, so
 *  we save registers and fp regs before starting
 */
	save	%sp, -SA(MINFRAME + HWCOPYFRAMESIZE), %sp
	or	SAVED_LOFAULT, FPUSED_FLAG, SAVED_LOFAULT
	rd	%fprs, %g1		! check for unused fp
	! if fprs.fef == 0, set it.
	! Setting it when already set costs more than checking
	andcc	%g1, FPRS_FEF, %g1	! test FEF, fprs.du = fprs.dl = 0
	bz,pt	%ncc, .co_fp_unused
	mov	ASI_USER, %asi
	BST_FP_TOSTACK(%o3)
	ba	.co_fp_ready
.co_fp_unused:
	prefetch [%i0 + (1 * CACHE_LINE)], #one_read
	wr	%g0, FPRS_FEF, %fprs	! fprs.fef = 1
.co_fp_ready:
	rd	%gsr, %l5		! save %gsr value
	andcc	%i1, 1, %o3		! is dest byte aligned
	bnz,pt	%ncc, .co_big_d1
.co_big_d1f:				! dest is now half word aligned
	andcc	%i1, 2, %o3
	bnz,pt	%ncc, .co_big_d2
.co_big_d2f:				! dest is now word aligned
	andcc	%i1, 4, %o3		! is dest longword aligned
	bnz,pt	%ncc, .co_big_d4
.co_big_d4f:				! dest is now long word aligned
	andcc	%i0, 7, %o3		! is src long word aligned
	brnz,pt	%o3, .co_big_unal8
	prefetch [%i0 + (2 * CACHE_LINE)], #one_read
	! Src and dst are long word aligned
	! align dst to 64 byte boundary
	andcc	%i1, 0x3f, %o3		! %o3 == 0 means dst is 64 byte aligned
	brz,pn	%o3, .co_al_to_64
	nop
	sub	%o3, 64, %o3		! %o3 has negative bytes to move
	add	%i2, %o3, %i2		! adjust remaining count
	andcc	%o3, 8, %o4		! odd long words to move?
	brz,pt	%o4, .co_al_to_16
	nop
	add	%o3, 8, %o3
	ldx	[%i0], %o4
	add	%i0, 8, %i0		! increment src ptr
	stxa	%o4, [%i1]ASI_USER
	add	%i1, 8, %i1		! increment dst ptr
! Dest is aligned on 16 bytes, src 8 byte aligned
.co_al_to_16:
	andcc	%o3, 0x30, %o4		! move to move?
	brz,pt	%o4, .co_al_to_64
	nop
.co_al_mv_16:
	add	%o3, 16, %o3
	ldx	[%i0], %o4
	stxa	%o4, [%i1]ASI_USER
	add	%i0, 16, %i0		! increment src ptr
	ldx	[%i0-8], %o4
	add	%i1, 8, %i1		! increment dst ptr
	stxa	%o4, [%i1]ASI_USER
	andcc	%o3, 0x30, %o4
	brnz,pt	%o4, .co_al_mv_16
	add	%i1, 8, %i1		! increment dst ptr
! Dest is aligned on 64 bytes, src 8 byte aligned
.co_al_to_64:
	! Determine source alignment
	! to correct 8 byte offset
	andcc	%i0, 32, %o3
	brnz,pn	%o3, .co_aln_1
	andcc	%i0, 16, %o3
	brnz,pn	%o3, .co_aln_01
	andcc	%i0, 8, %o3
	brz,pn	%o3, .co_aln_000
	prefetch [%i0 + (3 * CACHE_LINE)], #one_read
	ba	.co_aln_001
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
.co_aln_01:
	brnz,pn	%o3, .co_aln_011
	prefetch [%i0 + (3 * CACHE_LINE)], #one_read
	ba	.co_aln_010
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
.co_aln_1:
	andcc	%i0, 16, %o3
	brnz,pn	%o3, .co_aln_11
	andcc	%i0, 8, %o3
	brnz,pn	%o3, .co_aln_101
	prefetch [%i0 + (3 * CACHE_LINE)], #one_read
	ba	.co_aln_100
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
.co_aln_11:
	brz,pn	%o3, .co_aln_110
	prefetch [%i0 + (3 * CACHE_LINE)], #one_read

.co_aln_111:
! Alignment off by 8 bytes
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	ldd	[%i0], %d0
	add	%i0, 8, %i0
	sub	%i2, 8, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.co_aln_111_loop:
	ldda	[%i0]ASI_BLK_P,%d16		! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d2
	fmovd	%d18, %d4
	fmovd	%d20, %d6
	fmovd	%d22, %d8
	fmovd	%d24, %d10
	fmovd	%d26, %d12
	fmovd	%d28, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_AIUS	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_AIUS
	add	%i0, 64, %i0
	fmovd	%d30, %d0
	bgt,pt	%ncc, .co_aln_111_loop
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	add	%i1, %i0, %i1

	stda	%d0, [%i1]ASI_USER
	ba	.co_remain_stuff
	add	%i1, 8, %i1
	! END OF aln_111

.co_aln_110:
! Alignment off by 16 bytes
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	ldd	[%i0], %d0
	ldd	[%i0+8], %d2
	add	%i0, 16, %i0
	sub	%i2, 16, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.co_aln_110_loop:
	ldda	[%i0]ASI_BLK_P,%d16		! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d4
	fmovd	%d18, %d6
	fmovd	%d20, %d8
	fmovd	%d22, %d10
	fmovd	%d24, %d12
	fmovd	%d26, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_AIUS	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_AIUS
	add	%i0, 64, %i0
	fmovd	%d28, %d0
	fmovd	%d30, %d2
	bgt,pt	%ncc, .co_aln_110_loop
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	add	%i1, %i0, %i1

	stda	%d0, [%i1]%asi
	stda	%d2, [%i1+8]%asi
	ba	.co_remain_stuff
	add	%i1, 16, %i1
	! END OF aln_110

.co_aln_101:
! Alignment off by 24 bytes
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	ldd	[%i0], %d0
	ldd	[%i0+8], %d2
	ldd	[%i0+16], %d4
	add	%i0, 24, %i0
	sub	%i2, 24, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.co_aln_101_loop:
	ldda	[%i0]ASI_BLK_P,%d16	! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d6
	fmovd	%d18, %d8
	fmovd	%d20, %d10
	fmovd	%d22, %d12
	fmovd	%d24, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_AIUS	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_AIUS
	add	%i0, 64, %i0
	fmovd	%d26, %d0
	fmovd	%d28, %d2
	fmovd	%d30, %d4
	bgt,pt	%ncc, .co_aln_101_loop
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	add	%i1, %i0, %i1

	stda	%d0, [%i1]%asi
	stda	%d2, [%i1+8]%asi
	stda	%d4, [%i1+16]%asi
	ba	.co_remain_stuff
	add	%i1, 24, %i1
	! END OF aln_101

.co_aln_100:
! Alignment off by 32 bytes
	ldd	[%i0], %d0
	ldd	[%i0+8], %d2
	ldd	[%i0+16],%d4
	ldd	[%i0+24],%d6
	add	%i0, 32, %i0
	sub	%i2, 32, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.co_aln_100_loop:
	ldda	[%i0]ASI_BLK_P,%d16	! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d8
	fmovd	%d18, %d10
	fmovd	%d20, %d12
	fmovd	%d22, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_AIUS	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_AIUS
	add	%i0, 64, %i0
	fmovd	%d24, %d0
	fmovd	%d26, %d2
	fmovd	%d28, %d4
	fmovd	%d30, %d6
	bgt,pt	%ncc, .co_aln_100_loop
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	add	%i1, %i0, %i1

	stda	%d0, [%i1]%asi
	stda	%d2, [%i1+8]%asi
	stda	%d4, [%i1+16]%asi
	stda	%d6, [%i1+24]%asi
	ba	.co_remain_stuff
	add	%i1, 32, %i1
	! END OF aln_100

.co_aln_011:
! Alignment off by 40 bytes
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	ldd	[%i0], %d0
	ldd	[%i0+8], %d2
	ldd	[%i0+16], %d4
	ldd	[%i0+24], %d6
	ldd	[%i0+32], %d8
	add	%i0, 40, %i0
	sub	%i2, 40, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.co_aln_011_loop:
	ldda	[%i0]ASI_BLK_P,%d16	! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d10
	fmovd	%d18, %d12
	fmovd	%d20, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_AIUS	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_AIUS
	add	%i0, 64, %i0
	fmovd	%d22, %d0
	fmovd	%d24, %d2
	fmovd	%d26, %d4
	fmovd	%d28, %d6
	fmovd	%d30, %d8
	bgt,pt	%ncc, .co_aln_011_loop
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	add	%i1, %i0, %i1

	stda	%d0, [%i1]%asi
	stda	%d2, [%i1+8]%asi
	stda	%d4, [%i1+16]%asi
	stda	%d6, [%i1+24]%asi
	stda	%d8, [%i1+32]%asi
	ba	.co_remain_stuff
	add	%i1, 40, %i1
	! END OF aln_011

.co_aln_010:
! Alignment off by 48 bytes
	ldd	[%i0], %d0
	ldd	[%i0+8], %d2
	ldd	[%i0+16], %d4
	ldd	[%i0+24], %d6
	ldd	[%i0+32], %d8
	ldd	[%i0+40], %d10
	add	%i0, 48, %i0
	sub	%i2, 48, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.co_aln_010_loop:
	ldda	[%i0]ASI_BLK_P,%d16	! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d12
	fmovd	%d18, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_AIUS	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_AIUS
	add	%i0, 64, %i0
	fmovd	%d20, %d0
	fmovd	%d22, %d2
	fmovd	%d24, %d4
	fmovd	%d26, %d6
	fmovd	%d28, %d8
	fmovd	%d30, %d10
	bgt,pt	%ncc, .co_aln_010_loop
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	add	%i1, %i0, %i1

	stda	%d0, [%i1]%asi
	stda	%d2, [%i1+8]%asi
	stda	%d4, [%i1+16]%asi
	stda	%d6, [%i1+24]%asi
	stda	%d8, [%i1+32]%asi
	stda	%d10, [%i1+40]%asi
	ba	.co_remain_stuff
	add	%i1, 48, %i1
	! END OF aln_010

.co_aln_001:
! Alignment off by 56 bytes
	ldd	[%i0], %d0
	ldd	[%i0+8], %d2
	ldd	[%i0+16], %d4
	ldd	[%i0+24], %d6
	ldd	[%i0+32], %d8
	ldd	[%i0+40], %d10
	ldd	[%i0+48], %d12
	add	%i0, 56, %i0
	sub	%i2, 56, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.co_aln_001_loop:
	ldda	[%i0]ASI_BLK_P,%d16	! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_AIUS	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_AIUS
	add	%i0, 64, %i0
	fmovd	%d18, %d0
	fmovd	%d20, %d2
	fmovd	%d22, %d4
	fmovd	%d24, %d6
	fmovd	%d26, %d8
	fmovd	%d28, %d10
	fmovd	%d30, %d12
	bgt,pt	%ncc, .co_aln_001_loop
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	add	%i1, %i0, %i1

	stda	%d0, [%i1]%asi
	stda	%d2, [%i1+8]%asi
	stda	%d4, [%i1+16]%asi
	stda	%d6, [%i1+24]%asi
	stda	%d8, [%i1+32]%asi
	stda	%d10, [%i1+40]%asi
	stda	%d12, [%i1+48]%asi
	ba	.co_remain_stuff
	add	%i1, 56, %i1
	! END OF aln_001

.co_aln_000:
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.co_aln_000_loop:
	ldda	[%i0]ASI_BLK_P,%d0
	subcc	%o3, 64, %o3
	stxa	%g0,[%i0+%i1]ASI_STBI_AIUS	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_AIUS
	add	%i0, 64, %i0
	bgt,pt	%ncc, .co_aln_000_loop
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read
	add	%i1, %i0, %i1

	! END OF aln_000

.co_remain_stuff:
	subcc	%i2, 31, %i2		! adjust length to allow cc test
	ble,pt	%ncc, .co_aln_31
	nop
.co_aln_32:
	ldx	[%i0], %o4		! move 32 bytes
	subcc	%i2, 32, %i2		! decrement length count by 32
	stxa	%o4, [%i1]%asi
	ldx	[%i0+8], %o4
	stxa	%o4, [%i1+8]%asi
	ldx	[%i0+16], %o4
	add	%i0, 32, %i0		! increase src ptr by 32
	stxa	%o4, [%i1+16]%asi
	ldx	[%i0-8], %o4
	add	%i1, 32, %i1		! increase dst ptr by 32
	bgu,pt	%ncc, .co_aln_32	! repeat if at least 32 bytes left
	stxa	%o4, [%i1-8]%asi
.co_aln_31:
	addcc	%i2, 24, %i2		! adjust count to be off by 7
	ble,pt	%ncc, .co_aln_7		! skip if 7 or fewer bytes left
	nop				!
.co_aln_15:
	ldx	[%i0], %o4		! move 8 bytes
	add	%i0, 8, %i0		! increase src ptr by 8
	subcc	%i2, 8, %i2		! decrease count by 8
	add	%i1, 8, %i1		! increase dst ptr by 8
	bgu,pt	%ncc, .co_aln_15
	stxa	%o4, [%i1-8]%asi
.co_aln_7:
	addcc	%i2, 7, %i2		! finish adjustment of remaining count
	bz,pt	%ncc, .co_exit		! exit if finished
	cmp	%i2, 4
	blt,pt	%ncc, .co_unaln3x	! skip if less than 4 bytes left
	nop				!
	ld	[%i0], %o4		! move 4 bytes
	add	%i0, 4, %i0		! increase src ptr by 4
	add	%i1, 4, %i1		! increase dst ptr by 4
	subcc	%i2, 4, %i2		! decrease count by 4
	bnz	.co_unaln3x
	stwa	%o4, [%i1-4]%asi
	ba	.co_exit
	nop

	! destination alignment code
.co_big_d1:
	ldub	[%i0], %o4		! move a byte
	add	%i0, 1, %i0
	stba	%o4, [%i1]ASI_USER
	add	%i1, 1, %i1
	andcc	%i1, 2, %o3
	bz,pt	%ncc, .co_big_d2f
	sub	%i2, 1, %i2
.co_big_d2:
	ldub	[%i0], %o4		! move a half-word (src align unknown)
	ldub	[%i0+1], %o3
	add	%i0, 2, %i0
	sll	%o4, 8, %o4		! position
	or	%o4, %o3, %o4		! merge
	stha	%o4, [%i1]ASI_USER
	add	%i1, 2, %i1
	andcc	%i1, 4, %o3		! is dest longword aligned
	bz,pt	%ncc, .co_big_d4f
	sub	%i2, 2, %i2
.co_big_d4:				! dest is at least word aligned
	nop
	ldub	[%i0], %o4		! move a word (src align unknown)
	ldub	[%i0+1], %o3
	sll	%o4, 24, %o4		! position
	sll	%o3, 16, %o3		! position
	or	%o4, %o3, %o3		! merge
	ldub	[%i0+2], %o4
	sll	%o4, 8, %o4		! position
	or	%o4, %o3, %o3		! merge
	ldub	[%i0+3], %o4
	or	%o4, %o3, %o4		! merge
	stwa	%o4,[%i1]ASI_USER	! store four bytes
	add	%i0, 4, %i0		! adjust src by 4
	add	%i1, 4, %i1		! adjust dest by 4
	ba	.co_big_d4f
	sub	%i2, 4, %i2		! adjust count by 4


	! Dst is on 8 byte boundary; src is not;
.co_big_unal8:
	andcc	%i1, 0x3f, %o3		! is dst 64-byte block aligned?
	bz	%ncc, .co_unalnsrc
	sub	%o3, 64, %o3		! %o3 will be multiple of 8
	neg	%o3			! bytes until dest is 64 byte aligned
	sub	%i2, %o3, %i2		! update cnt with bytes to be moved
	! Move bytes according to source alignment
	andcc	%i0, 0x1, %o4
	bnz	%ncc, .co_unalnbyte	! check for byte alignment
	nop
	andcc	%i0, 2, %o4		! check for half word alignment
	bnz	%ncc, .co_unalnhalf
	nop
	! Src is word aligned, move bytes until dest 64 byte aligned
.co_unalnword:
	ld	[%i0], %o4		! load 4 bytes
	stwa	%o4, [%i1]%asi		! and store 4 bytes
	ld	[%i0+4], %o4		! load 4 bytes
	add	%i0, 8, %i0		! increase src ptr by 8
	stwa	%o4, [%i1+4]%asi	! and store 4 bytes
	subcc	%o3, 8, %o3		! decrease count by 8
	bnz	%ncc, .co_unalnword
	add	%i1, 8, %i1		! increase dst ptr by 8
	ba	.co_unalnsrc
	nop

	! Src is half-word aligned, move bytes until dest 64 byte aligned
.co_unalnhalf:
	lduh	[%i0], %o4		! load 2 bytes
	sllx	%o4, 32, %i3		! shift left
	lduw	[%i0+2], %o4
	or	%o4, %i3, %i3
	sllx	%i3, 16, %i3
	lduh	[%i0+6], %o4
	or	%o4, %i3, %i3
	stxa	%i3, [%i1]ASI_USER
	add	%i0, 8, %i0
	subcc	%o3, 8, %o3
	bnz	%ncc, .co_unalnhalf
	add	%i1, 8, %i1
	ba	.co_unalnsrc
	nop

	! Src is Byte aligned, move bytes until dest 64 byte aligned
.co_unalnbyte:
	sub	%i1, %i0, %i1		! share pointer advance
.co_unalnbyte_loop:
	ldub	[%i0], %o4
	sllx	%o4, 56, %i3
	lduh	[%i0+1], %o4
	sllx	%o4, 40, %o4
	or	%o4, %i3, %i3
	lduh	[%i0+3], %o4
	sllx	%o4, 24, %o4
	or	%o4, %i3, %i3
	lduh	[%i0+5], %o4
	sllx	%o4, 8, %o4
	or	%o4, %i3, %i3
	ldub	[%i0+7], %o4
	or	%o4, %i3, %i3
	stxa	%i3, [%i1+%i0]ASI_USER
	subcc	%o3, 8, %o3
	bnz	%ncc, .co_unalnbyte_loop
	add	%i0, 8, %i0
	add	%i1,%i0, %i1		! restore pointer

	! Destination is now block (64 byte aligned), src is not 8 byte aligned
.co_unalnsrc:
	andn	%i2, 0x3f, %i3		! %i3 is multiple of block size
	and	%i2, 0x3f, %i2		! residue bytes in %i2
	add	%i2, 64, %i2		! Insure we don't load beyond
	sub	%i3, 64, %i3		! end of source buffer

	andn	%i0, 0x3f, %o4		! %o4 has block aligned src address
	prefetch [%o4 + (3 * CACHE_LINE)], #one_read
	alignaddr %i0, %g0, %g0		! generate %gsr
	add	%i0, %i3, %i0		! advance %i0 to after blocks
	!
	! Determine source alignment to correct 8 byte offset
	andcc	%i0, 0x20, %o3
	brnz,pn	%o3, .co_unaln_1
	andcc	%i0, 0x10, %o3
	brnz,pn	%o3, .co_unaln_01
	andcc	%i0, 0x08, %o3
	brz,a	%o3, .co_unaln_000
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read
	ba	.co_unaln_001
	nop
.co_unaln_01:
	brnz,a	%o3, .co_unaln_011
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read
	ba	.co_unaln_010
	nop
.co_unaln_1:
	brnz,pn	%o3, .co_unaln_11
	andcc	%i0, 0x08, %o3
	brnz,a	%o3, .co_unaln_101
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read
	ba	.co_unaln_100
	nop
.co_unaln_11:
	brz,pn	%o3, .co_unaln_110
	prefetch [%i0 + (4 * CACHE_LINE)], #one_read

.co_unaln_111:
	ldd	[%o4+56], %d14
.co_unaln_111_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_P, %d16
	faligndata %d14, %d16, %d48
	faligndata %d16, %d18, %d50
	faligndata %d18, %d20, %d52
	faligndata %d20, %d22, %d54
	faligndata %d22, %d24, %d56
	faligndata %d24, %d26, %d58
	faligndata %d26, %d28, %d60
	faligndata %d28, %d30, %d62
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_AIUS
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .co_unaln_111_loop
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read
	ba	.co_unaln_done
	nop

.co_unaln_110:
	ldd	[%o4+48], %d12
	ldd	[%o4+56], %d14
.co_unaln_110_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_P, %d16
	faligndata %d12, %d14, %d48
	faligndata %d14, %d16, %d50
	faligndata %d16, %d18, %d52
	faligndata %d18, %d20, %d54
	faligndata %d20, %d22, %d56
	faligndata %d22, %d24, %d58
	faligndata %d24, %d26, %d60
	faligndata %d26, %d28, %d62
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_AIUS
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .co_unaln_110_loop
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read
	ba	.co_unaln_done
	nop

.co_unaln_101:
	ldd	[%o4+40], %d10
	ldd	[%o4+48], %d12
	ldd	[%o4+56], %d14
.co_unaln_101_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_P, %d16
	faligndata %d10, %d12, %d48
	faligndata %d12, %d14, %d50
	faligndata %d14, %d16, %d52
	faligndata %d16, %d18, %d54
	faligndata %d18, %d20, %d56
	faligndata %d20, %d22, %d58
	faligndata %d22, %d24, %d60
	faligndata %d24, %d26, %d62
	fmovd	%d26, %d10
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_AIUS
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .co_unaln_101_loop
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read
	ba	.co_unaln_done
	nop

.co_unaln_100:
	ldd	[%o4+32], %d8
	ldd	[%o4+40], %d10
	ldd	[%o4+48], %d12
	ldd	[%o4+56], %d14
.co_unaln_100_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_P, %d16
	faligndata %d8, %d10, %d48
	faligndata %d10, %d12, %d50
	faligndata %d12, %d14, %d52
	faligndata %d14, %d16, %d54
	faligndata %d16, %d18, %d56
	faligndata %d18, %d20, %d58
	faligndata %d20, %d22, %d60
	faligndata %d22, %d24, %d62
	fmovd	%d24, %d8
	fmovd	%d26, %d10
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_AIUS
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .co_unaln_100_loop
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read
	ba	.co_unaln_done
	nop

.co_unaln_011:
	ldd	[%o4+24], %d6
	ldd	[%o4+32], %d8
	ldd	[%o4+40], %d10
	ldd	[%o4+48], %d12
	ldd	[%o4+56], %d14
.co_unaln_011_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_P, %d16
	faligndata %d6, %d8, %d48
	faligndata %d8, %d10, %d50
	faligndata %d10, %d12, %d52
	faligndata %d12, %d14, %d54
	faligndata %d14, %d16, %d56
	faligndata %d16, %d18, %d58
	faligndata %d18, %d20, %d60
	faligndata %d20, %d22, %d62
	fmovd	%d22, %d6
	fmovd	%d24, %d8
	fmovd	%d26, %d10
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_AIUS
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .co_unaln_011_loop
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read
	ba	.co_unaln_done
	nop

.co_unaln_010:
	ldd	[%o4+16], %d4
	ldd	[%o4+24], %d6
	ldd	[%o4+32], %d8
	ldd	[%o4+40], %d10
	ldd	[%o4+48], %d12
	ldd	[%o4+56], %d14
.co_unaln_010_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_P, %d16
	faligndata %d4, %d6, %d48
	faligndata %d6, %d8, %d50
	faligndata %d8, %d10, %d52
	faligndata %d10, %d12, %d54
	faligndata %d12, %d14, %d56
	faligndata %d14, %d16, %d58
	faligndata %d16, %d18, %d60
	faligndata %d18, %d20, %d62
	fmovd	%d20, %d4
	fmovd	%d22, %d6
	fmovd	%d24, %d8
	fmovd	%d26, %d10
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_AIUS
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .co_unaln_010_loop
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read
	ba	.co_unaln_done
	nop

.co_unaln_001:
	ldd	[%o4+8], %d2
	ldd	[%o4+16], %d4
	ldd	[%o4+24], %d6
	ldd	[%o4+32], %d8
	ldd	[%o4+40], %d10
	ldd	[%o4+48], %d12
	ldd	[%o4+56], %d14
.co_unaln_001_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_P, %d16
	faligndata %d2, %d4, %d48
	faligndata %d4, %d6, %d50
	faligndata %d6, %d8, %d52
	faligndata %d8, %d10, %d54
	faligndata %d10, %d12, %d56
	faligndata %d12, %d14, %d58
	faligndata %d14, %d16, %d60
	faligndata %d16, %d18, %d62
	fmovd	%d18, %d2
	fmovd	%d20, %d4
	fmovd	%d22, %d6
	fmovd	%d24, %d8
	fmovd	%d26, %d10
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_AIUS
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .co_unaln_001_loop
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read
	ba	.co_unaln_done
	nop

.co_unaln_000:
	ldda	[%o4]ASI_BLK_P, %d0
.co_unaln_000_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_P, %d16
	faligndata %d0, %d2, %d48
	faligndata %d2, %d4, %d50
	faligndata %d4, %d6, %d52
	faligndata %d6, %d8, %d54
	faligndata %d8, %d10, %d56
	faligndata %d10, %d12, %d58
	faligndata %d12, %d14, %d60
	faligndata %d14, %d16, %d62
	fmovd	%d16, %d0
	fmovd	%d18, %d2
	fmovd	%d20, %d4
	fmovd	%d22, %d6
	fmovd	%d24, %d8
	fmovd	%d26, %d10
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_AIUS
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .co_unaln_000_loop
	prefetch [%o4 + (4 * CACHE_LINE)], #one_read

.co_unaln_done:
	! Handle trailing bytes, 64 to 127
	! Dest long word aligned, Src not long word aligned
	cmp	%i2, 15
	bleu	%ncc, .co_unaln_short

	andn	%i2, 0x7, %i3		! %i3 is multiple of 8
	and	%i2, 0x7, %i2		! residue bytes in %i2
	add	%i2, 8, %i2
	sub	%i3, 8, %i3		! insure we don't load past end of src
	andn	%i0, 0x7, %o4		! %o4 has long word aligned src address
	add	%i0, %i3, %i0		! advance %i0 to after multiple of 8
	ldd	[%o4], %d0		! fetch partial word
.co_unaln_by8:
	ldd	[%o4+8], %d2
	add	%o4, 8, %o4
	faligndata %d0, %d2, %d16
	subcc	%i3, 8, %i3
	stda	%d16, [%i1]%asi
	fmovd	%d2, %d0
	bgu,pt	%ncc, .co_unaln_by8
	add	%i1, 8, %i1

.co_unaln_short:
	cmp	%i2, 8
	blt,pt	%ncc, .co_unalnfin
	nop
	ldub	[%i0], %o4
	sll	%o4, 24, %o3
	ldub	[%i0+1], %o4
	sll	%o4, 16, %o4
	or	%o4, %o3, %o3
	ldub	[%i0+2], %o4
	sll	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%i0+3], %o4
	or	%o4, %o3, %o3
	stwa	%o3, [%i1]%asi
	ldub	[%i0+4], %o4
	sll	%o4, 24, %o3
	ldub	[%i0+5], %o4
	sll	%o4, 16, %o4
	or	%o4, %o3, %o3
	ldub	[%i0+6], %o4
	sll	%o4, 8, %o4
	or	%o4, %o3, %o3
	ldub	[%i0+7], %o4
	or	%o4, %o3, %o3
	stwa	%o3, [%i1+4]%asi
	add	%i0, 8, %i0
	add	%i1, 8, %i1
	sub	%i2, 8, %i2
.co_unalnfin:
	cmp	%i2, 4
	blt,pt	%ncc, .co_unalnz
	tst	%i2
	ldub	[%i0], %o3		! read byte
	subcc	%i2, 4, %i2		! reduce count by 4
	sll	%o3, 24, %o3		! position
	ldub	[%i0+1], %o4
	sll	%o4, 16, %o4		! position
	or	%o4, %o3, %o3		! merge
	ldub	[%i0+2], %o4
	sll	%o4, 8, %o4		! position
	or	%o4, %o3, %o3		! merge
	add	%i1, 4, %i1		! advance dst by 4
	ldub	[%i0+3], %o4
	add	%i0, 4, %i0		! advance src by 4
	or	%o4, %o3, %o4		! merge
	bnz,pt	%ncc, .co_unaln3x
	stwa	%o4, [%i1-4]%asi
	ba	.co_exit
	nop
.co_unalnz:
	bz,pt	%ncc, .co_exit
	wr	%l5, %g0, %gsr		! restore %gsr
.co_unaln3x:				! Exactly 1, 2, or 3 bytes remain
	subcc	%i2, 1, %i2		! reduce count for cc test
	ldub	[%i0], %o4		! load one byte
	bz,pt	%ncc, .co_exit
	stba	%o4, [%i1]%asi		! store one byte
	ldub	[%i0+1], %o4		! load second byte
	subcc	%i2, 1, %i2
	bz,pt	%ncc, .co_exit
	stba	%o4, [%i1+1]%asi	! store second byte
	ldub	[%i0+2], %o4		! load third byte
	stba	%o4, [%i1+2]%asi	! store third byte
.co_exit:
	brnz	%g1, .co_fp_restore
	nop
	FZERO
	wr	%g1, %g0, %fprs
	ba,pt	%ncc, .co_ex2
	membar	#Sync
.co_fp_restore:
	BLD_FP_FROMSTACK(%o4)
.co_ex2:
	andn	SAVED_LOFAULT, FPUSED_FLAG, SAVED_LOFAULT
	stn	SAVED_LOFAULT, [THREAD_REG + T_LOFAULT] ! restore old t_lofault
	ret
	restore %g0, 0, %o0

.copyout_err:
	ldn	[THREAD_REG + T_COPYOPS], %o4
	brz	%o4, 2f
	nop
	ldn	[%o4 + CP_COPYOUT], %g2
	jmp	%g2
	nop
2:
	retl
	mov	-1, %o0

#else	/* NIAGARA_IMPL */
.do_copyout:
	!
	! Check the length and bail if zero.
	!
	tst	%o2
	bnz,pt	%ncc, 1f
	nop
	retl
	clr	%o0
1:
	sethi	%hi(copyio_fault), %o4
	or	%o4, %lo(copyio_fault), %o4
	sethi	%hi(copyio_fault_nowindow), %o3
	ldn	[THREAD_REG + T_LOFAULT], SAVED_LOFAULT
	or	%o3, %lo(copyio_fault_nowindow), %o3
	membar	#Sync
	stn	%o3, [THREAD_REG + T_LOFAULT]

	mov	%o0, SAVE_SRC
	mov	%o1, SAVE_DST
	mov	%o2, SAVE_COUNT

	!
	! Check to see if we're more than SMALL_LIMIT (7 bytes).
	! Run in leaf mode, using the %o regs as our input regs.
	!
	subcc	%o2, SMALL_LIMIT, %o3
	bgu,a,pt %ncc, .dco_ns
	or	%o0, %o1, %o3
	!
	! What was previously ".small_copyout"
	! Do full differenced copy.
	!
.dcobcp:
	sub	%g0, %o2, %o3		! negate count
	add	%o0, %o2, %o0		! make %o0 point at the end
	add	%o1, %o2, %o1		! make %o1 point at the end
	ba,pt	%ncc, .dcocl
	ldub	[%o0 + %o3], %o4	! load first byte
	!
	! %o0 and %o2 point at the end and remain pointing at the end
	! of their buffers. We pull things out by adding %o3 (which is
	! the negation of the length) to the buffer end which gives us
	! the curent location in the buffers. By incrementing %o3 we walk
	! through both buffers without having to bump each buffer's
	! pointer. A very fast 4 instruction loop.
	!
	.align 16
.dcocl:
	stba	%o4, [%o1 + %o3]ASI_USER
	inccc	%o3
	bl,a,pt	%ncc, .dcocl
	ldub	[%o0 + %o3], %o4
	!
	! We're done. Go home.
	!
	membar	#Sync
	stn	SAVED_LOFAULT, [THREAD_REG + T_LOFAULT]
	retl
	clr	%o0
	!
	! Try aligned copies from here.
	!
.dco_ns:
	! %o0 = kernel addr (to be copied from)
	! %o1 = user addr (to be copied to)
	! %o2 = length
	! %o3 = %o1 | %o2 (used for alignment checking)
	! %o4 is alternate lo_fault
	! %o5 is original lo_fault
	!
	! See if we're single byte aligned. If we are, check the
	! limit for single byte copies. If we're smaller or equal,
	! bounce to the byte for byte copy loop. Otherwise do it in
	! HW (if enabled).
	!
	btst	1, %o3
	bz,pt	%icc, .dcoh8
	btst	7, %o3
	!
	! Single byte aligned. Do we do it via HW or via
	! byte for byte? Do a quick no memory reference
	! check to pick up small copies.
	!
	sethi	%hi(hw_copy_limit_1), %o3
	!
	! Big enough that we need to check the HW limit for
	! this size copy.
	!
	ld	[%o3 + %lo(hw_copy_limit_1)], %o3
	!
	! Is HW copy on? If not, do everything byte for byte.
	!
	tst	%o3
	bz,pn	%icc, .dcobcp
	subcc	%o3, %o2, %o3
	!
	! If we're less than or equal to the single byte copy limit,
	! bop to the copy loop.
	!
	bge,pt	%ncc, .dcobcp
	nop
	!
	! We're big enough and copy is on. Do it with HW.
	!
	ba,pt	%ncc, .big_copyout
	nop
.dcoh8:
	!
	! 8 byte aligned?
	!
	bnz,a	%ncc, .dcoh4
	btst	3, %o3
	!
	! See if we're in the "small range".
	! If so, go off and do the copy.
	! If not, load the hard limit. %o3 is
	! available for reuse.
	!
	sethi	%hi(hw_copy_limit_8), %o3
	ld	[%o3 + %lo(hw_copy_limit_8)], %o3
	!
	! If it's zero, there's no HW bcopy.
	! Bop off to the aligned copy.
	!
	tst	%o3
	bz,pn	%icc, .dcos8
	subcc	%o3, %o2, %o3
	!
	! We're negative if our size is larger than hw_copy_limit_8.
	!
	bge,pt	%ncc, .dcos8
	nop
	!
	! HW assist is on and we're large enough. Do it.
	!
	ba,pt	%ncc, .big_copyout
	nop
.dcos8:
	!
	! Housekeeping for copy loops. Uses same idea as in the byte for
	! byte copy loop above.
	!
	add	%o0, %o2, %o0
	add	%o1, %o2, %o1
	sub	%g0, %o2, %o3
	ba,pt	%ncc, .dodebc
	srl	%o2, 3, %o2		! Number of 8 byte chunks to copy
	!
	! 4 byte aligned?
	!
.dcoh4:
	bnz,pn	%ncc, .dcoh2
	!
	! See if we're in the "small range".
	! If so, go off an do the copy.
	! If not, load the hard limit. %o3 is
	! available for reuse.
	!
	sethi	%hi(hw_copy_limit_4), %o3
	ld	[%o3 + %lo(hw_copy_limit_4)], %o3
	!
	! If it's zero, there's no HW bcopy.
	! Bop off to the aligned copy.
	!
	tst	%o3
	bz,pn	%icc, .dcos4
	subcc	%o3, %o2, %o3
	!
	! We're negative if our size is larger than hw_copy_limit_4.
	!
	bge,pt	%ncc, .dcos4
	nop
	!
	! HW assist is on and we're large enough. Do it.
	!
	ba,pt	%ncc, .big_copyout
	nop
.dcos4:
	add	%o0, %o2, %o0
	add	%o1, %o2, %o1
	sub	%g0, %o2, %o3
	ba,pt	%ncc, .dodfbc
	srl	%o2, 2, %o2		! Number of 4 byte chunks to copy
	!
	! We must be 2 byte aligned. Off we go.
	! The check for small copies was done in the
	! delay at .dcoh4
	!
.dcoh2:
	ble	%ncc, .dcos2
	sethi	%hi(hw_copy_limit_2), %o3
	ld	[%o3 + %lo(hw_copy_limit_2)], %o3
	tst	%o3
	bz,pn	%icc, .dcos2
	subcc	%o3, %o2, %o3
	bge,pt	%ncc, .dcos2
	nop
	!
	! HW is on and we're big enough. Do it.
	!
	ba,pt	%ncc, .big_copyout
	nop
.dcos2:
	add	%o0, %o2, %o0
	add	%o1, %o2, %o1
	sub	%g0, %o2, %o3
	ba,pt	%ncc, .dodtbc
	srl	%o2, 1, %o2		! Number of 2 byte chunks to copy
.small_copyout:
	!
	! Why are we doing this AGAIN? There are certain conditions in
	! big_copyout that will cause us to forego the HW assisted copies
	! and bounce back to a non-HW assisted copy. This dispatches those
	! copies. Note that we branch around this in the main line code.
	!
	! We make no check for limits or HW enablement here. We've
	! already been told that we're a poster child so just go off
	! and do it.
	!
	or	%o0, %o1, %o3
	btst	1, %o3
	bnz	%icc, .dcobcp		! Most likely
	btst	7, %o3
	bz	%icc, .dcos8
	btst	3, %o3
	bz	%icc, .dcos4
	nop
	ba,pt	%ncc, .dcos2
	nop
	.align 32
.dodebc:
	ldx	[%o0 + %o3], %o4
	deccc	%o2
	stxa	%o4, [%o1 + %o3]ASI_USER
	bg,pt	%ncc, .dodebc
	addcc	%o3, 8, %o3
	!
	! End of copy loop. Check to see if we're done. Most
	! eight byte aligned copies end here.
	!
	bz,pt	%ncc, .dcofh
	nop
	!
	! Something is left - do it byte for byte.
	! 
	ba,pt	%ncc, .dcocl
	ldub	[%o0 + %o3], %o4	! load next byte
	!
	! Four byte copy loop. %o2 is the number of 4 byte chunks to copy.
	!
	.align 32
.dodfbc:
	lduw	[%o0 + %o3], %o4
	deccc	%o2
	sta	%o4, [%o1 + %o3]ASI_USER
	bg,pt	%ncc, .dodfbc
	addcc	%o3, 4, %o3
	!
	! End of copy loop. Check to see if we're done. Most
	! four byte aligned copies end here.
	!
	bz,pt	%ncc, .dcofh
	nop
	!
	! Something is left. Do it byte for byte.
	!
	ba,pt	%ncc, .dcocl
	ldub	[%o0 + %o3], %o4	! load next byte
	!
	! two byte aligned copy loop. %o2 is the number of 2 byte chunks to
	! copy.
	!
	.align 32
.dodtbc:
	lduh	[%o0 + %o3], %o4
	deccc	%o2
	stha	%o4, [%o1 + %o3]ASI_USER
	bg,pt	%ncc, .dodtbc
	addcc	%o3, 2, %o3
	!
	! End of copy loop. Anything left?
	!
	bz,pt	%ncc, .dcofh
	nop
	!
	! Deal with the last byte
	!
	ldub	[%o0 + %o3], %o4
	stba	%o4, [%o1 + %o3]ASI_USER
.dcofh:
	membar	#Sync
	stn	SAVED_LOFAULT, [THREAD_REG + T_LOFAULT]	! restore old t_lofault
	retl
	clr	%o0

.big_copyout:
	! We're going to go off and do a block copy.
	! Switch fault handlers and grab a window. We
	! don't do a membar #Sync since we've done only
	! kernel data to this point.
	stn	%o4, [THREAD_REG + T_LOFAULT]

	! Copy out that reach here are larger than 256 bytes. The
	! hw_copy_limit_1 is set to 256. Never set this limit less
	! 128 bytes.
	save	%sp, -SA(MINFRAME), %sp
.do_block_copyout:

	! Swap src/dst since the code below is memcpy code
	! and memcpy/bcopy have different calling sequences
	mov	%i1, %i5
	mov	%i0, %i1
	mov	%i5, %i0

	! Block (64 bytes) align the destination.
	andcc	%i0, 0x3f, %i3		! is dst block aligned
	bz	%ncc, copyout_blalign	! dst already block aligned
	sub	%i3, 0x40, %i3
	neg	%i3			! bytes till dst 64 bytes aligned
	sub	%i2, %i3, %i2		! update i2 with new count

	! Based on source and destination alignment do
	! either 8 bytes, 4 bytes, 2 bytes or byte copy.

	! Is dst & src 8B aligned
	or	%i0, %i1, %o2
	andcc	%o2, 0x7, %g0
	bz	%ncc, .co_alewdcp
	nop

	! Is dst & src 4B aligned
	andcc	%o2, 0x3, %g0
	bz	%ncc, .co_alwdcp
	nop

	! Is dst & src 2B aligned
	andcc	%o2, 0x1, %g0
	bz	%ncc, .co_alhlfwdcp
	nop

	! 1B aligned
1:	ldub	[%i1], %o2
	stba	%o2, [%i0]ASI_USER
	inc	%i1
	deccc	%i3
	bgu,pt	%ncc, 1b
	inc	%i0

	ba	copyout_blalign
	nop

	! dst & src 4B aligned
.co_alwdcp:
	ld	[%i1], %o2
	sta	%o2, [%i0]ASI_USER
	add	%i1, 0x4, %i1
	subcc	%i3, 0x4, %i3
	bgu,pt	%ncc, .co_alwdcp
	add	%i0, 0x4, %i0

	ba	copyout_blalign
	nop

	! dst & src 2B aligned
.co_alhlfwdcp:
	lduh	[%i1], %o2
	stuha	%o2, [%i0]ASI_USER
	add	%i1, 0x2, %i1
	subcc	%i3, 0x2, %i3
	bgu,pt	%ncc, .co_alhlfwdcp
	add	%i0, 0x2, %i0

	ba	copyout_blalign
	nop

	! dst & src 8B aligned
.co_alewdcp:
	ldx	[%i1], %o2
	stxa	%o2, [%i0]ASI_USER
	add	%i1, 0x8, %i1
	subcc	%i3, 0x8, %i3
	bgu,pt	%ncc, .co_alewdcp
	add	%i0, 0x8, %i0

	! Now Destination is block (64 bytes) aligned
copyout_blalign:
	andn	%i2, 0x3f, %i3		! %i3 count is multiple of block size
	sub	%i2, %i3, %i2		! Residue bytes in %i2

	mov	ASI_BLK_INIT_QUAD_LDD_AIUS, %asi

	andcc	%i1, 0xf, %o2		! is src quadword aligned
	bz,pn	%xcc, .co_blkcpy	! src offset in %o2 (last 4-bits)
	nop
	cmp	%o2, 0x8
	bg	.co_upper_double
	nop
	bl	.co_lower_double
	nop

	! Falls through when source offset is equal to 8 i.e.
	! source is double word aligned.
	! In this case no shift/merge of data is required

	sub	%i1, %o2, %i1		! align the src at 16 bytes.
	andn	%i1, 0x3f, %l0		! %l0 has block aligned source
	prefetch [%l0+0x0], #one_read
	ldda	[%i1]ASI_BLK_INIT_ST_QUAD_LDD_P, %l2
.co_loop0:
	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_ST_QUAD_LDD_P, %l4
	prefetch [%l0+0x40], #one_read

	stxa	%l3, [%i0+0x0]%asi
	stxa	%l4, [%i0+0x8]%asi

	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_ST_QUAD_LDD_P, %l2

	stxa	%l5, [%i0+0x10]%asi
	stxa	%l2, [%i0+0x18]%asi

	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_ST_QUAD_LDD_P, %l4

	stxa	%l3, [%i0+0x20]%asi
	stxa	%l4, [%i0+0x28]%asi

	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_ST_QUAD_LDD_P, %l2

	stxa	%l5, [%i0+0x30]%asi
	stxa	%l2, [%i0+0x38]%asi

	add	%l0, 0x40, %l0
	subcc	%i3, 0x40, %i3
	bgu,pt	%xcc, .co_loop0
	add	%i0, 0x40, %i0
	ba	.co_blkdone
	add	%i1, %o2, %i1		! increment the source by src offset
					! the src offset was stored in %o2

.co_lower_double:

	sub	%i1, %o2, %i1		! align the src at 16 bytes.
	sll	%o2, 3, %o0		! %o0 left shift
	mov	0x40, %o1
	sub	%o1, %o0, %o1		! %o1 right shift = (64 - left shift)
	andn	%i1, 0x3f, %l0		! %l0 has block aligned source
	prefetch [%l0+0x0], #one_read
	ldda	[%i1]ASI_BLK_INIT_ST_QUAD_LDD_P, %l2	! partial data in %l2 and %l3 has
					! complete data
.co_loop1:
	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_ST_QUAD_LDD_P, %l4	! %l4 has partial data
							! for this read.
	ALIGN_DATA(%l2, %l3, %l4, %o0, %o1, %l6)	! merge %l2, %l3 and %l4
							! into %l2 and %l3
	prefetch [%l0+0x40], #one_read

	stxa	%l2, [%i0+0x0]%asi
	stxa	%l3, [%i0+0x8]%asi

	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_ST_QUAD_LDD_P, %l2
	ALIGN_DATA(%l4, %l5, %l2, %o0, %o1, %l6)	! merge %l2 with %l5 and
							! %l4 from previous read
							! into %l4 and %l5
	stxa	%l4, [%i0+0x10]%asi
	stxa	%l5, [%i0+0x18]%asi

	! Repeat the same for next 32 bytes.

	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_ST_QUAD_LDD_P, %l4
	ALIGN_DATA(%l2, %l3, %l4, %o0, %o1, %l6)

	stxa	%l2, [%i0+0x20]%asi
	stxa	%l3, [%i0+0x28]%asi

	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_ST_QUAD_LDD_P, %l2
	ALIGN_DATA(%l4, %l5, %l2, %o0, %o1, %l6)

	stxa	%l4, [%i0+0x30]%asi
	stxa	%l5, [%i0+0x38]%asi

	add	%l0, 0x40, %l0
	subcc	%i3, 0x40, %i3
	bgu,pt	%xcc, .co_loop1
	add	%i0, 0x40, %i0
	ba	.co_blkdone
	add	%i1, %o2, %i1		! increment the source by src offset
					! the src offset was stored in %o2

.co_upper_double:

	sub	%i1, %o2, %i1		! align the src at 16 bytes.
	sub	%o2, 0x8, %o0
	sll	%o0, 3, %o0		! %o0 left shift
	mov	0x40, %o1
	sub	%o1, %o0, %o1		! %o1 right shift = (64 - left shift)
	andn	%i1, 0x3f, %l0		! %l0 has block aligned source
	prefetch [%l0+0x0], #one_read
	ldda	[%i1]ASI_BLK_INIT_ST_QUAD_LDD_P, %l2	! partial data in %l3
							! for this read and
							! no data in %l2
.co_loop2:
	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_ST_QUAD_LDD_P, %l4	! %l4 has complete data
							! and %l5 has partial
	ALIGN_DATA(%l3, %l4, %l5, %o0, %o1, %l6)	! merge %l3, %l4 and %l5
							! into %l3 and %l4
	prefetch [%l0+0x40], #one_read

	stxa	%l3, [%i0+0x0]%asi
	stxa	%l4, [%i0+0x8]%asi

	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_ST_QUAD_LDD_P, %l2
	ALIGN_DATA(%l5, %l2, %l3, %o0, %o1, %l6)	! merge %l2 and %l3 with
							! %l5 from previous read
							! into %l5 and %l2

	stxa	%l5, [%i0+0x10]%asi
	stxa	%l2, [%i0+0x18]%asi

	! Repeat the same for next 32 bytes.

	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_ST_QUAD_LDD_P, %l4
	ALIGN_DATA(%l3, %l4, %l5, %o0, %o1, %l6)

	stxa	%l3, [%i0+0x20]%asi
	stxa	%l4, [%i0+0x28]%asi

	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_ST_QUAD_LDD_P, %l2
	ALIGN_DATA(%l5, %l2, %l3, %o0, %o1, %l6)

	stxa	%l5, [%i0+0x30]%asi
	stxa	%l2, [%i0+0x38]%asi

	add	%l0, 0x40, %l0
	subcc	%i3, 0x40, %i3
	bgu,pt	%xcc, .co_loop2
	add	%i0, 0x40, %i0
	ba	.co_blkdone
	add	%i1, %o2, %i1		! increment the source by src offset
					! the src offset was stored in %o2


	! Do fast copy using ASI_BLK_INIT_ST_QUAD_LDD_P
.co_blkcpy:

	andn	%i1, 0x3f, %o0		! %o0 has block aligned source
	prefetch [%o0+0x0], #one_read
1:
	ldda	[%i1]ASI_BLK_INIT_ST_QUAD_LDD_P, %l0
	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_ST_QUAD_LDD_P, %l2
	add	%i1, 0x10, %i1

	prefetch [%o0+0x40], #one_read

	stxa	%l0, [%i0+0x0]%asi

	ldda	[%i1]ASI_BLK_INIT_ST_QUAD_LDD_P, %l4
	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_ST_QUAD_LDD_P, %l6
	add	%i1, 0x10, %i1

	stxa	%l1, [%i0+0x8]%asi
	stxa	%l2, [%i0+0x10]%asi
	stxa	%l3, [%i0+0x18]%asi
	stxa	%l4, [%i0+0x20]%asi
	stxa	%l5, [%i0+0x28]%asi
	stxa	%l6, [%i0+0x30]%asi
	stxa	%l7, [%i0+0x38]%asi

	add	%o0, 0x40, %o0
	subcc	%i3, 0x40, %i3
	bgu,pt	%xcc, 1b
	add	%i0, 0x40, %i0

.co_blkdone:
	membar	#Sync

	brz,pt	%i2, .copyout_exit
	nop

	! Handle trailing bytes
	cmp	%i2, 0x8
	blu,pt	%ncc, .co_residue
	nop

	! Can we do some 8B ops
	or	%i1, %i0, %o2
	andcc	%o2, 0x7, %g0
	bnz	%ncc, .co_last4
	nop

	! Do 8byte ops as long as possible
.co_last8:
	ldx	[%i1], %o2
	stxa	%o2, [%i0]ASI_USER
	add	%i1, 0x8, %i1
	sub	%i2, 0x8, %i2
	cmp	%i2, 0x8
	bgu,pt	%ncc, .co_last8
	add	%i0, 0x8, %i0

	brz,pt	%i2, .copyout_exit
	nop

	ba	.co_residue
	nop

.co_last4:
	! Can we do 4B ops
	andcc	%o2, 0x3, %g0
	bnz	%ncc, .co_last2
	nop
1:
	ld	[%i1], %o2
	sta	%o2, [%i0]ASI_USER
	add	%i1, 0x4, %i1
	sub	%i2, 0x4, %i2
	cmp	%i2, 0x4
	bgu,pt	%ncc, 1b
	add	%i0, 0x4, %i0

	brz,pt	%i2, .copyout_exit
	nop

	ba	.co_residue
	nop

.co_last2:
	! Can we do 2B ops
	andcc	%o2, 0x1, %g0
	bnz	%ncc, .co_residue
	nop

1:
	lduh	[%i1], %o2
	stuha	%o2, [%i0]ASI_USER
	add	%i1, 0x2, %i1
	sub	%i2, 0x2, %i2
	cmp	%i2, 0x2
	bgu,pt	%ncc, 1b
	add	%i0, 0x2, %i0

	brz,pt	%i2, .copyout_exit
	nop

	! Copy the residue as byte copy
.co_residue:
	ldub	[%i1], %i4
	stba	%i4, [%i0]ASI_USER
	inc	%i1
	deccc	%i2
	bgu,pt	%xcc, .co_residue
	inc	%i0

.copyout_exit:
	membar	#Sync
	stn	SAVED_LOFAULT, [THREAD_REG + T_LOFAULT]	! restore old t_lofault
	ret
	restore	%g0, 0, %o0

.copyout_err:
	ldn	[THREAD_REG + T_COPYOPS], %o4
	brz	%o4, 2f
	nop
	ldn	[%o4 + CP_COPYOUT], %g2
	jmp	%g2
	nop
2:
	retl
	mov	-1, %o0
#endif	/* NIAGARA_IMPL */
	SET_SIZE(copyout)

#endif	/* lint */


#ifdef	lint

/*ARGSUSED*/
int
xcopyout(const void *kaddr, void *uaddr, size_t count)
{ return (0); }

#else	/* lint */

	ENTRY(xcopyout)
	sethi	%hi(.xcopyout_err), REAL_LOFAULT
	b	.do_copyout
	or	REAL_LOFAULT, %lo(.xcopyout_err), REAL_LOFAULT
.xcopyout_err:
	ldn	[THREAD_REG + T_COPYOPS], %o4
	brz	%o4, 2f
	nop
	ldn	[%o4 + CP_XCOPYOUT], %g2
	jmp	%g2
	nop
2:
	retl
	mov	%g1, %o0
	SET_SIZE(xcopyout)

#endif	/* lint */
	
#ifdef	lint

/*ARGSUSED*/
int
xcopyout_little(const void *kaddr, void *uaddr, size_t count)
{ return (0); }

#else	/* lint */

	ENTRY(xcopyout_little)
	sethi	%hi(.little_err), %o4
	ldn	[THREAD_REG + T_LOFAULT], %o5
	or	%o4, %lo(.little_err), %o4
	membar	#Sync			! sync error barrier
	stn	%o4, [THREAD_REG + T_LOFAULT]

	subcc	%g0, %o2, %o3
	add	%o0, %o2, %o0
	bz,pn	%ncc, 2f		! check for zero bytes
	sub	%o2, 1, %o4
	add	%o0, %o4, %o0		! start w/last byte
	add	%o1, %o2, %o1
	ldub	[%o0+%o3], %o4

1:	stba	%o4, [%o1+%o3]ASI_AIUSL
	inccc	%o3
	sub	%o0, 2, %o0		! get next byte
	bcc,a,pt %ncc, 1b
	ldub	[%o0+%o3], %o4

2:	membar	#Sync			! sync error barrier
	stn	%o5, [THREAD_REG + T_LOFAULT]	! restore old t_lofault
	retl
	mov	%g0, %o0		! return (0)
	SET_SIZE(xcopyout_little)

#endif	/* lint */

/*
 * Copy user data to kernel space (copyin/xcopyin/xcopyin_little)
 */

#if defined(lint)

/*ARGSUSED*/
int
copyin(const void *uaddr, void *kaddr, size_t count)
{ return (0); }

#else	/* lint */

	ENTRY(copyin)
	sethi	%hi(.copyin_err), REAL_LOFAULT
	or	REAL_LOFAULT, %lo(.copyin_err), REAL_LOFAULT

#if !defined(NIAGARA_IMPL)
.do_copyin:
	tst	%o2			! check for zero count;  quick exit
	bz,pt	%ncc, .ci_smallqx
	mov	%o0, SAVE_SRC
	mov	%o1, SAVE_DST
	mov	%o2, SAVE_COUNT
	cmp	%o2, FP_COPY		! check for small copy/leaf case
	bgt,pt	%ncc, .ci_copy_more
	ldn	[THREAD_REG + T_LOFAULT], SAVED_LOFAULT
/*
 * Small copy in code
 * 
 */
	sethi	%hi(copyio_fault_nowindow), %o3
	or	%o3, %lo(copyio_fault_nowindow), %o3
	membar	#Sync
	stn	%o3, [THREAD_REG + T_LOFAULT]

	mov	ASI_USER, %asi
	cmp	%o2, SHORTCOPY		! make sure there is enough to align
	ble,pt	%ncc, .ci_smallest
	andcc	%o1, 0x7, %o3		! is dest long word aligned
	bnz,pn	%ncc, .ci_align
	andcc	%o1, 1, %o3		! is dest byte aligned

! Destination is long word aligned
.ci_al_src:
	andcc	%o0, 7, %o3
	brnz,pt	%o3, .ci_src_dst_unal8
	nop
/*
 * Special case for handling when src and dest are both long word aligned
 * and total data to move is less than FP_COPY bytes
 * Also handles finish up for large block moves, so may be less than 32 bytes
 */
.ci_medlong:
	subcc	%o2, 31, %o2		! adjust length to allow cc test
	ble,pt	%ncc, .ci_medl31
	nop
.ci_medl32:
	ldxa	[%o0]%asi, %o4		! move 32 bytes
	subcc	%o2, 32, %o2		! decrement length count by 32
	stx	%o4, [%o1]
	ldxa	[%o0+8]%asi, %o4
	stx	%o4, [%o1+8]
	ldxa	[%o0+16]%asi, %o4
	add	%o0, 32, %o0		! increase src ptr by 32
	stx	%o4, [%o1+16]
	ldxa	[%o0-8]%asi, %o4
	add	%o1, 32, %o1		! increase dst ptr by 32
	bgu,pt	%ncc, .ci_medl32	! repeat if at least 32 bytes left
	stx	%o4, [%o1-8]
.ci_medl31:
	addcc	%o2, 24, %o2		! adjust count to be off by 7
	ble,pt	%ncc, .ci_medl7		! skip if 7 or fewer bytes left
	nop
.ci_medl8:
	ldxa	[%o0]%asi, %o4		! move 8 bytes
	add	%o0, 8, %o0		! increase src ptr by 8
	subcc	%o2, 8, %o2		! decrease count by 8
	add	%o1, 8, %o1		! increase dst ptr by 8
	bgu,pt	%ncc, .ci_medl8
	stx	%o4, [%o1-8]
.ci_medl7:
	addcc	%o2, 7, %o2		! finish adjustment of remaining count
	bnz,pt	%ncc, .ci_small4	! do final bytes if not finished
	nop
.ci_smallx:				! finish up and exit
	membar	#Sync
	stn	SAVED_LOFAULT, [THREAD_REG + T_LOFAULT]
.ci_smallqx:
	retl
	mov	%g0, %o0

.ci_small4:
	cmp	%o2, 4
	blt,pt	%ncc, .ci_small3x	! skip if less than 4 bytes left
	nop				!
	lda	[%o0]%asi, %o4		! move 4 bytes
	add	%o0, 4, %o0		! increase src ptr by 4
	add	%o1, 4, %o1		! increase dst ptr by 4
	subcc	%o2, 4, %o2		! decrease count by 4
	bz	%ncc, .ci_smallx
	stw	%o4, [%o1-4]

.ci_small3x:				! Exactly 1, 2, or 3 bytes remain
	subcc	%o2, 1, %o2		! reduce count for cc test
	lduba	[%o0]%asi, %o4		! load one byte
	bz,pt	%ncc, .ci_smallx
	stb	%o4, [%o1]		! store one byte
	lduba	[%o0+1]%asi, %o4	! load second byte
	subcc	%o2, 1, %o2
	bz,pt	%ncc, .ci_smallx
	stb	%o4, [%o1+1]		! store second byte
	lduba	[%o0+2]%asi, %o4	! load third byte
	ba	.ci_smallx
	stb	%o4, [%o1+2]		! store third byte

.ci_smallest:				! 7 or fewer bytes remain
	cmp	%o2, 4
	blt,pt	%ncc, .ci_small3x
	nop
	lduba	[%o0]%asi, %o4		! read byte
	subcc	%o2, 4, %o2		! reduce count by 4
	stb	%o4, [%o1]		! write byte
	lduba	[%o0+1]%asi, %o4	! repeat for total of 4 bytes
	add	%o0, 4, %o0		! advance src by 4
	stb	%o4, [%o1+1]
	lduba	[%o0-2]%asi, %o4
	add	%o1, 4, %o1		! advance dst by 4
	stb	%o4, [%o1-2]
	lduba	[%o0-1]%asi, %o4
	bnz,pt	%ncc, .ci_small3x
	stb	%o4, [%o1-1]
	membar	#Sync
	stn	SAVED_LOFAULT, [THREAD_REG + T_LOFAULT]
	retl
	mov	%g0, %o0

.ci_align:
	bnz,pt	%ncc, .ci_al_d1
.ci_al_d1f:				! dest is now half word aligned
	andcc	%o1, 2, %o3		! is dest word aligned
	bnz,pt	%ncc, .ci_al_d2
.ci_al_d2f:				! dest is now word aligned
	andcc	%o1, 4, %o3		! is dest longword aligned?
	bz,pt	%ncc, .ci_al_src
	nop
.ci_al_d4:				! dest is word aligned;  src is unknown
	lduba	[%o0]%asi, %o4		! move a word (src align unknown)
	lduba	[%o0+1]%asi, %o3
	sll	%o4, 24, %o4		! position
	sll	%o3, 16, %o3		! position
	or	%o4, %o3, %o3		! merge
	lduba	[%o0+2]%asi, %o4
	sll	%o4, 8, %o4		! position
	or	%o4, %o3, %o3		! merge
	lduba	[%o0+3]%asi, %o4
	or	%o4, %o3, %o4		! merge
	stw	%o4,[%o1]		! store four bytes
	add	%o0, 4, %o0		! adjust src by 4
	add	%o1, 4, %o1		! adjust dest by 4
	sub	%o2, 4, %o2		! adjust count by 4
	andcc	%o0, 7, %o3		! check for src long word alignment
	brz,pt	%o3, .ci_medlong
.ci_src_dst_unal8:
	! dst is 8-byte aligned, src is not
	! Size is less than FP_COPY
	! Following code is to select for alignment
	andcc	%o0, 0x3, %o3		! test word alignment
	bz,pt	%ncc, .ci_medword
	nop
	andcc	%o0, 0x1, %o3		! test halfword alignment
	bnz,pt	%ncc, .ci_med_byte	! go to byte move if not halfword
	andcc	%o0, 0x2, %o3		! test which byte alignment
	ba	.ci_medhalf
	nop
.ci_al_d1:				! align dest to half word
	lduba	[%o0]%asi, %o4		! move a byte
	add	%o0, 1, %o0
	stb	%o4, [%o1]
	add	%o1, 1, %o1
	andcc	%o1, 2, %o3		! is dest word aligned
	bz,pt	%ncc, .ci_al_d2f
	sub	%o2, 1, %o2
.ci_al_d2:				! align dest to word
	lduba	[%o0]%asi, %o4		! move a half-word (src align unknown)
	lduba	[%o0+1]%asi, %o3
	sll	%o4, 8, %o4		! position
	or	%o4, %o3, %o4		! merge
	sth	%o4, [%o1]
	add	%o0, 2, %o0
	add	%o1, 2, %o1
	andcc	%o1, 4, %o3		! is dest longword aligned?
	bz,pt	%ncc, .ci_al_src
	sub	%o2, 2, %o2
	ba	.ci_al_d4
	nop
/*
 * Handle all cases where src and dest are aligned on word
 * boundaries. Use unrolled loops for better performance.
 * This option wins over standard large data move when 
 * source and destination is in cache for medium
 * to short data moves.
 */
.ci_medword:
	subcc	%o2, 31, %o2		! adjust length to allow cc test
	ble,pt	%ncc, .ci_medw31
	nop
.ci_medw32:
	lda	[%o0]%asi, %o4		! move a block of 32 bytes
	stw	%o4, [%o1]
	lda	[%o0+4]%asi, %o4
	stw	%o4, [%o1+4]
	lda	[%o0+8]%asi, %o4
	stw	%o4, [%o1+8]
	lda	[%o0+12]%asi, %o4
	stw	%o4, [%o1+12]
	lda	[%o0+16]%asi, %o4
	stw	%o4, [%o1+16]
	lda	[%o0+20]%asi, %o4
	subcc	%o2, 32, %o2		! decrement length count
	stw	%o4, [%o1+20]
	lda	[%o0+24]%asi, %o4
	add	%o0, 32, %o0		! increase src ptr by 32
	stw	%o4, [%o1+24]
	lda	[%o0-4]%asi, %o4
	add	%o1, 32, %o1		! increase dst ptr by 32
	bgu,pt	%ncc, .ci_medw32	! repeat if at least 32 bytes left
	stw	%o4, [%o1-4]
.ci_medw31:
	addcc	%o2, 24, %o2		! adjust count to be off by 7
	ble,pt	%ncc, .ci_medw7		! skip if 7 or fewer bytes left
	nop				!
.ci_medw15:
	lda	[%o0]%asi, %o4		! move a block of 8 bytes
	subcc	%o2, 8, %o2		! decrement length count
	stw	%o4, [%o1]
	add	%o0, 8, %o0		! increase src ptr by 8
	lda	[%o0-4]%asi, %o4
	add	%o1, 8, %o1		! increase dst ptr by 8
	bgu,pt	%ncc, .ci_medw15
	stw	%o4, [%o1-4]
.ci_medw7:
	addcc	%o2, 7, %o2		! finish adjustment of remaining count
	bz,pt	%ncc, .ci_smallx	! exit if finished
	cmp	%o2, 4
	blt,pt	%ncc, .ci_small3x	! skip if less than 4 bytes left
	nop				!
	lda	[%o0]%asi, %o4		! move 4 bytes
	add	%o0, 4, %o0		! increase src ptr by 4
	add	%o1, 4, %o1		! increase dst ptr by 4
	subcc	%o2, 4, %o2		! decrease count by 4
	bnz	.ci_small3x
	stw	%o4, [%o1-4]
	membar	#Sync
	stn	SAVED_LOFAULT, [THREAD_REG + T_LOFAULT]
	retl
	mov	%g0, %o0

.ci_medhalf:
	subcc	%o2, 31, %o2		! adjust length to allow cc test
	ble,pt	%ncc, .ci_medh31
	nop
.ci_medh32:				! load and store block of 32 bytes
	subcc	%o2, 32, %o2		! decrement length count

	lduha	[%o0]%asi, %o4		! move 32 bytes
	lduwa	[%o0+2]%asi, %o3
	sllx	%o4, 48, %o4
	sllx	%o3, 16, %o3
	or	%o4, %o3, %o3
	lduha	[%o0+6]%asi, %o4
	or	%o4, %o3, %o4
	stx	%o4, [%o1]

	lduha	[%o0+8]%asi, %o4
	lduwa	[%o0+10]%asi, %o3
	sllx	%o4, 48, %o4
	sllx	%o3, 16, %o3
	or	%o4, %o3, %o3
	lduha	[%o0+14]%asi, %o4
	or	%o4, %o3, %o4
	stx	%o4, [%o1+8]

	lduha	[%o0+16]%asi, %o4
	lduwa	[%o0+18]%asi, %o3
	sllx	%o4, 48, %o4
	sllx	%o3, 16, %o3
	or	%o4, %o3, %o3
	lduha	[%o0+22]%asi, %o4
	or	%o4, %o3, %o4
	stx	%o4, [%o1+16]

	add	%o0, 32, %o0		! increase src ptr by 32
	add	%o1, 32, %o1		! increase dst ptr by 32

	lduha	[%o0-8]%asi, %o4
	lduwa	[%o0-6]%asi, %o3
	sllx	%o4, 48, %o4
	sllx	%o3, 16, %o3
	or	%o4, %o3, %o3
	lduha	[%o0-2]%asi, %o4
	or	%o3, %o4, %o4
	bgu,pt	%ncc, .ci_medh32	! repeat if at least 32 bytes left
	stx	%o4, [%o1-8]

.ci_medh31:
	addcc	%o2, 24, %o2		! adjust count to be off by 7
	ble,pt	%ncc, .ci_medh7		! skip if 7 or fewer bytes left
	nop				!
.ci_medh15:
	lduha	[%o0]%asi, %o4		! move 16 bytes
	subcc	%o2, 8, %o2		! decrement length count
	lduwa	[%o0+2]%asi, %o3
	sllx	%o4, 48, %o4
	sllx	%o3, 16, %o3
	or	%o4, %o3, %o3
	add	%o1, 8, %o1		! increase dst ptr by 8
	lduha	[%o0+6]%asi, %o4
	add	%o0, 8, %o0		! increase src ptr by 8
	or	%o4, %o3, %o4
	bgu,pt	%ncc, .ci_medh15
	stx	%o4, [%o1-8]
.ci_medh7:
	addcc	%o2, 7, %o2		! finish adjustment of remaining count
	bz,pt	%ncc, .ci_smallx	! exit if finished
	cmp	%o2, 4
	blt,pt	%ncc, .ci_small3x	! skip if less than 4 bytes left
	nop				!
	lduha	[%o0]%asi, %o4
	sll	%o4, 16, %o4
	lduha	[%o0+2]%asi, %o3
	or	%o3, %o4, %o4
	subcc	%o2, 4, %o2
	add	%o0, 4, %o0
	add	%o1, 4, %o1
	bnz	.ci_small3x
	stw	%o4, [%o1-4]
	membar	#Sync
	stn	SAVED_LOFAULT, [THREAD_REG + T_LOFAULT]
	retl
	mov	%g0, %o0

	.align 16
.ci_med_byte:
	bnz,pt	%ncc, .ci_medbh32a	! go to correct byte move
	subcc	%o2, 31, %o2		! adjust length to allow cc test
	ble,pt	%ncc, .ci_medb31
	nop
.ci_medb32:				! Alignment 1 or 5
	subcc	%o2, 32, %o2		! decrement length count

	lduba	[%o0]%asi, %o4		! load and store a block of 32 bytes
	sllx	%o4, 56, %o3
	lduha	[%o0+1]%asi, %o4
	sllx	%o4, 40, %o4
	or	%o4, %o3, %o3
	lduwa	[%o0+3]%asi, %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	lduba	[%o0+7]%asi, %o4
	or	%o4, %o3, %o4
	stx	%o4, [%o1]

	lduba	[%o0+8]%asi, %o4
	sllx	%o4, 56, %o3
	lduha	[%o0+9]%asi, %o4
	sllx	%o4, 40, %o4
	or	%o4, %o3, %o3
	lduwa	[%o0+11]%asi, %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	lduba	[%o0+15]%asi, %o4
	or	%o4, %o3, %o4
	stx	%o4, [%o1+8]

	lduba	[%o0+16]%asi, %o4
	sllx	%o4, 56, %o3
	lduha	[%o0+17]%asi, %o4
	sllx	%o4, 40, %o4
	or	%o4, %o3, %o3
	lduwa	[%o0+19]%asi, %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	lduba	[%o0+23]%asi, %o4
	or	%o4, %o3, %o4
	stx	%o4, [%o1+16]

	add	%o0, 32, %o0		! increase src ptr by 32
	add	%o1, 32, %o1		! increase dst ptr by 32

	lduba	[%o0-8]%asi, %o4
	sllx	%o4, 56, %o3
	lduha	[%o0-7]%asi, %o4
	sllx	%o4, 40, %o4
	or	%o4, %o3, %o3
	lduwa	[%o0-5]%asi, %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	lduba	[%o0-1]%asi, %o4
	or	%o4, %o3, %o4
	bgu,pt	%ncc, .ci_medb32	! repeat if at least 32 bytes left
	stx	%o4, [%o1-8]

.ci_medb31:				! 31 or fewer bytes remaining
	addcc	%o2, 24, %o2		! adjust count to be off by 7
	ble,pt	%ncc, .ci_medb7		! skip if 7 or fewer bytes left
	nop				!
.ci_medb15:

	lduba	[%o0]%asi, %o4		! load and store a block of 8 bytes
	subcc	%o2, 8, %o2		! decrement length count
	sllx	%o4, 56, %o3
	lduha	[%o0+1]%asi, %o4
	sllx	%o4, 40, %o4
	or	%o4, %o3, %o3
	lduwa	[%o0+3]%asi, %o4
	add	%o1, 8, %o1		! increase dst ptr by 16
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	lduba	[%o0+7]%asi, %o4
	add	%o0, 8, %o0		! increase src ptr by 16
	or	%o4, %o3, %o4
	bgu,pt	%ncc, .ci_medb15
	stx	%o4, [%o1-8]
.ci_medb7:
	addcc	%o2, 7, %o2		! finish adjustment of remaining count
	bz,pt	%ncc, .ci_smallx	! exit if finished
	cmp	%o2, 4
	blt,pt	%ncc, .ci_small3x	! skip if less than 4 bytes left
	nop				!
	lduba	[%o0]%asi, %o4		! move 4 bytes
	sll	%o4, 24, %o3
	lduha	[%o0+1]%asi, %o4
	sll	%o4, 8, %o4
	or	%o4, %o3, %o3
	lduba	[%o0+3]%asi, %o4
	or	%o4, %o3, %o4
	subcc	%o2, 4, %o2
	add	%o0, 4, %o0
	add	%o1, 4, %o1
	bnz	.ci_small3x
	stw	%o4, [%o1-4]
	membar	#Sync
	stn	SAVED_LOFAULT, [THREAD_REG + T_LOFAULT]
	retl
	mov	%g0, %o0

	.align 16
.ci_medbh32a:				! Alignment 3 or 7
	ble,pt	%ncc, .ci_medbh31
	nop
.ci_medbh32:				! Alignment 3 or 7
	subcc	%o2, 32, %o2		! decrement length count

	lduba	[%o0]%asi, %o4		! load and store a block of 32 bytes
	sllx	%o4, 56, %o3
	lduwa	[%o0+1]%asi, %o4
	sllx	%o4, 24, %o4
	or	%o4, %o3, %o3
	lduha	[%o0+5]%asi, %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	lduba	[%o0+7]%asi, %o4
	or	%o4, %o3, %o4
	stx	%o4, [%o1]

	lduba	[%o0+8]%asi, %o4
	sllx	%o4, 56, %o3
	lduwa	[%o0+9]%asi, %o4
	sllx	%o4, 24, %o4
	or	%o4, %o3, %o3
	lduha	[%o0+13]%asi, %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	lduba	[%o0+15]%asi, %o4
	or	%o4, %o3, %o4
	stx	%o4, [%o1+8]

	lduba	[%o0+16]%asi, %o4
	sllx	%o4, 56, %o3
	lduwa	[%o0+17]%asi, %o4
	sllx	%o4, 24, %o4
	or	%o4, %o3, %o3
	lduha	[%o0+21]%asi, %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	lduba	[%o0+23]%asi, %o4
	or	%o4, %o3, %o4
	stx	%o4, [%o1+16]

	add	%o0, 32, %o0		! increase src ptr by 32
	add	%o1, 32, %o1		! increase dst ptr by 32

	lduba	[%o0-8]%asi, %o4
	sllx	%o4, 56, %o3
	lduwa	[%o0-7]%asi, %o4
	sllx	%o4, 24, %o4
	or	%o4, %o3, %o3
	lduha	[%o0-3]%asi, %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	lduba	[%o0-1]%asi, %o4
	or	%o4, %o3, %o4
	bgu,pt	%ncc, .ci_medbh32	! repeat if at least 32 bytes left
	stx	%o4, [%o1-8]

.ci_medbh31:
	addcc	%o2, 24, %o2		! adjust count to be off by 7
	ble,pt	%ncc, .ci_medb7		! skip if 7 or fewer bytes left
	nop				!
.ci_medbh15:
	lduba	[%o0]%asi, %o4		! load and store a block of 8 bytes
	sllx	%o4, 56, %o3
	lduwa	[%o0+1]%asi, %o4
	sllx	%o4, 24, %o4
	or	%o4, %o3, %o3
	lduha	[%o0+5]%asi, %o4
	sllx	%o4, 8, %o4
	or	%o4, %o3, %o3
	lduba	[%o0+7]%asi, %o4
	or	%o4, %o3, %o4
	stx	%o4, [%o1]
	subcc	%o2, 8, %o2		! decrement length count
	add	%o1, 8, %o1		! increase dst ptr by 8
	add	%o0, 8, %o0		! increase src ptr by 8
	bgu,pt	%ncc, .ci_medbh15
	stx	%o4, [%o1-8]
	ba	.ci_medb7
	nop

/*
 * End of small copy in code (no window)
 * 
 */

/*
 * Long copy in code (using register window and fp regs)
 * 
 */

.ci_copy_more:
	sethi	%hi(copyio_fault), %o3
	or	%o3, %lo(copyio_fault), %o3
	membar	#Sync
	stn	%o3, [THREAD_REG + T_LOFAULT]
/*
 * Following code is for large copies. We know there is at
 * least FP_COPY bytes available. FP regs are used, so
 *  we save registers and fp regs before starting
 */
	save	%sp, -SA(MINFRAME + HWCOPYFRAMESIZE), %sp
	or	SAVED_LOFAULT, FPUSED_FLAG, SAVED_LOFAULT
	rd	%fprs, %g1		! check for unused fp
	! if fprs.fef == 0, set it.
	! Setting it when already set costs more than checking
	andcc	%g1, FPRS_FEF, %g1	! test FEF, fprs.du = fprs.dl = 0
	bz,pt	%ncc, .ci_fp_unused
	mov	ASI_USER, %asi
	BST_FP_TOSTACK(%o3)
	ba	.ci_fp_ready
.ci_fp_unused:
	prefetcha [%i0 + (1 * CACHE_LINE)]%asi, #one_read
	wr	%g0, FPRS_FEF, %fprs	! fprs.fef = 1
.ci_fp_ready:
	rd	%gsr, %l5		! save %gsr value
	andcc	%i1, 1, %o3		! is dest byte aligned
	bnz,pt	%ncc, .ci_big_d1
.ci_big_d1f:				! dest is now half word aligned
	andcc	%i1, 2, %o3
	bnz,pt	%ncc, .ci_big_d2
.ci_big_d2f:				! dest is now word aligned
	andcc	%i1, 4, %o3
	bnz,pt	%ncc, .ci_big_d4
.ci_big_d4f:				! dest is long word aligned
	andcc	%i0, 7, %o3		! is src long word aligned
	brnz,pt	%o3, .ci_big_unal8
	prefetcha [%i0 + (2 * CACHE_LINE)]%asi, #one_read
	! Src and dst are long word aligned
	! align dst to 64 byte boundary
	andcc	%i1, 0x3f, %o3		! %o3 == 0 means dst is 64 byte aligned
	brz,pn	%o3, .ci_al_to_64
	nop
	sub	%o3, 64, %o3		! %o3 has negative bytes to move
	add	%i2, %o3, %i2		! adjust remaining count
	andcc	%o3, 8, %o4		! odd long words to move?
	brz,pt	%o4, .ci_al_to_16
	nop
	add	%o3, 8, %o3
	ldxa	[%i0]%asi, %o4
	add	%i0, 8, %i0		! increment src ptr
	add	%i1, 8, %i1		! increment dst ptr
	stx	%o4, [%i1-8]
! Dest is aligned on 16 bytes, src 8 byte aligned
.ci_al_to_16:
	andcc	%o3, 0x30, %o4		! pair of long words to move?
	brz,pt	%o4, .ci_al_to_64
	nop
.ci_al_mv_16:
	add	%o3, 16, %o3
	ldxa	[%i0]%asi, %o4
	stx	%o4, [%i1]
	add	%i0, 16, %i0		! increment src ptr
	ldxa	[%i0-8]%asi, %o4
	stx	%o4, [%i1+8]
	andcc	%o3, 0x30, %o4
	brnz,pt	%o4, .ci_al_mv_16
	add	%i1, 16, %i1		! increment dst ptr
! Dest is aligned on 64 bytes, src 8 byte aligned
.ci_al_to_64:
	! Determine source alignment
	! to correct 8 byte offset
	andcc	%i0, 32, %o3
	brnz,pn	%o3, .ci_aln_1
	andcc	%i0, 16, %o3
	brnz,pn	%o3, .ci_aln_01
	andcc	%i0, 8, %o3
	brz,pn	%o3, .ci_aln_000
	prefetcha [%i0 + (3 * CACHE_LINE)]%asi, #one_read
	ba	.ci_aln_001
	prefetcha [%i0 + (4 * CACHE_LINE)]%asi, #one_read
.ci_aln_01:
	brnz,pn	%o3, .ci_aln_011
	prefetcha [%i0 + (3 * CACHE_LINE)]%asi, #one_read
	ba	.ci_aln_010
	prefetcha [%i0 + (4 * CACHE_LINE)]%asi, #one_read
.ci_aln_1:
	andcc	%i0, 16, %o3
	brnz,pn	%o3, .ci_aln_11
	andcc	%i0, 8, %o3
	brnz,pn	%o3, .ci_aln_101
	prefetcha [%i0 + (3 * CACHE_LINE)]%asi, #one_read
	ba	.ci_aln_100
	prefetcha [%i0 + (4 * CACHE_LINE)]%asi, #one_read
.ci_aln_11:
	brz,pn	%o3, .ci_aln_110
	prefetcha [%i0 + (3 * CACHE_LINE)]%asi, #one_read

.ci_aln_111:
! Alignment off by 8 bytes
	prefetcha [%i0 + (4 * CACHE_LINE)]%asi, #one_read
	ldda	[%i0]%asi, %d0
	add	%i0, 8, %i0
	sub	%i2, 8, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.ci_aln_111_loop:
	ldda	[%i0]ASI_BLK_AIUS,%d16		! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d2
	fmovd	%d18, %d4
	fmovd	%d20, %d6
	fmovd	%d22, %d8
	fmovd	%d24, %d10
	fmovd	%d26, %d12
	fmovd	%d28, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_P	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_P
	add	%i0, 64, %i0
	fmovd	%d30, %d0
	bgt,pt	%ncc, .ci_aln_111_loop
	prefetcha [%i0 + (4 * CACHE_LINE)]%asi, #one_read
	add	%i1, %i0, %i1

	std	%d0, [%i1]
	ba	.ci_remain_stuff
	add	%i1, 8, %i1
	! END OF aln_111

.ci_aln_110:
! Alignment off by 16 bytes
	prefetcha [%i0 + (4 * CACHE_LINE)]%asi, #one_read
	ldda	[%i0]%asi, %d0
	ldda	[%i0+8]%asi, %d2
	add	%i0, 16, %i0
	sub	%i2, 16, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.ci_aln_110_loop:
	ldda	[%i0]ASI_BLK_AIUS,%d16		! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d4
	fmovd	%d18, %d6
	fmovd	%d20, %d8
	fmovd	%d22, %d10
	fmovd	%d24, %d12
	fmovd	%d26, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_P	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_P
	add	%i0, 64, %i0
	fmovd	%d28, %d0
	fmovd	%d30, %d2
	bgt,pt	%ncc, .ci_aln_110_loop
	prefetcha [%i0 + (4 * CACHE_LINE)]%asi, #one_read
	add	%i1, %i0, %i1

	std	%d0, [%i1]
	std	%d2, [%i1+8]
	ba	.ci_remain_stuff
	add	%i1, 16, %i1
	! END OF aln_110

.ci_aln_101:
! Alignment off by 24 bytes
	prefetcha [%i0 + (4 * CACHE_LINE)]%asi, #one_read
	ldda	[%i0]%asi, %d0
	ldda	[%i0+8]%asi, %d2
	ldda	[%i0+16]%asi, %d4
	add	%i0, 24, %i0
	sub	%i2, 24, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.ci_aln_101_loop:
	ldda	[%i0]ASI_BLK_AIUS,%d16	! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d6
	fmovd	%d18, %d8
	fmovd	%d20, %d10
	fmovd	%d22, %d12
	fmovd	%d24, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_P	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_P
	add	%i0, 64, %i0
	fmovd	%d26, %d0
	fmovd	%d28, %d2
	fmovd	%d30, %d4
	bgt,pt	%ncc, .ci_aln_101_loop
	prefetcha [%i0 + (4 * CACHE_LINE)]%asi, #one_read
	add	%i1, %i0, %i1

	std	%d0, [%i1]
	std	%d2, [%i1+8]
	std	%d4, [%i1+16]
	ba	.ci_remain_stuff
	add	%i1, 24, %i1
	! END OF aln_101

.ci_aln_100:
! Alignment off by 32 bytes
	ldda	[%i0]%asi, %d0
	ldda	[%i0+8]%asi, %d2
	ldda	[%i0+16]%asi,%d4
	ldda	[%i0+24]%asi,%d6
	add	%i0, 32, %i0
	sub	%i2, 32, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.ci_aln_100_loop:
	ldda	[%i0]ASI_BLK_AIUS,%d16	! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d8
	fmovd	%d18, %d10
	fmovd	%d20, %d12
	fmovd	%d22, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_P	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_P
	add	%i0, 64, %i0
	fmovd	%d24, %d0
	fmovd	%d26, %d2
	fmovd	%d28, %d4
	fmovd	%d30, %d6
	bgt,pt	%ncc, .ci_aln_100_loop
	prefetcha [%i0 + (4 * CACHE_LINE)]%asi, #one_read
	add	%i1, %i0, %i1

	std	%d0, [%i1]
	std	%d2, [%i1+8]
	std	%d4, [%i1+16]
	std	%d6, [%i1+24]
	ba	.ci_remain_stuff
	add	%i1, 32, %i1
	! END OF aln_100

.ci_aln_011:
! Alignment off by 40 bytes
	prefetcha [%i0 + (4 * CACHE_LINE)]%asi, #one_read
	ldda	[%i0]%asi, %d0
	ldda	[%i0+8]%asi, %d2
	ldda	[%i0+16]%asi, %d4
	ldda	[%i0+24]%asi, %d6
	ldda	[%i0+32]%asi, %d8
	add	%i0, 40, %i0
	sub	%i2, 40, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.ci_aln_011_loop:
	ldda	[%i0]ASI_BLK_AIUS,%d16	! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d10
	fmovd	%d18, %d12
	fmovd	%d20, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_P	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_P
	add	%i0, 64, %i0
	fmovd	%d22, %d0
	fmovd	%d24, %d2
	fmovd	%d26, %d4
	fmovd	%d28, %d6
	fmovd	%d30, %d8
	bgt,pt	%ncc, .ci_aln_011_loop
	prefetcha [%i0 + (4 * CACHE_LINE)]%asi, #one_read
	add	%i1, %i0, %i1

	std	%d0, [%i1]
	std	%d2, [%i1+8]
	std	%d4, [%i1+16]
	std	%d6, [%i1+24]
	std	%d8, [%i1+32]
	ba	.ci_remain_stuff
	add	%i1, 40, %i1
	! END OF aln_011

.ci_aln_010:
! Alignment off by 48 bytes
	ldda	[%i0]%asi, %d0
	ldda	[%i0+8]%asi, %d2
	ldda	[%i0+16]%asi, %d4
	ldda	[%i0+24]%asi, %d6
	ldda	[%i0+32]%asi, %d8
	ldda	[%i0+40]%asi, %d10
	add	%i0, 48, %i0
	sub	%i2, 48, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.ci_aln_010_loop:
	ldda	[%i0]ASI_BLK_AIUS,%d16	! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d12
	fmovd	%d18, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_P	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_P
	add	%i0, 64, %i0
	fmovd	%d20, %d0
	fmovd	%d22, %d2
	fmovd	%d24, %d4
	fmovd	%d26, %d6
	fmovd	%d28, %d8
	fmovd	%d30, %d10
	bgt,pt	%ncc, .ci_aln_010_loop
	prefetcha [%i0 + (4 * CACHE_LINE)]%asi, #one_read
	add	%i1, %i0, %i1

	std	%d0, [%i1]
	std	%d2, [%i1+8]
	std	%d4, [%i1+16]
	std	%d6, [%i1+24]
	std	%d8, [%i1+32]
	std	%d10, [%i1+40]
	ba	.ci_remain_stuff
	add	%i1, 48, %i1
	! END OF aln_010

.ci_aln_001:
! Alignment off by 56 bytes
	ldda	[%i0]%asi, %d0
	ldda	[%i0+8]%asi, %d2
	ldda	[%i0+16]%asi, %d4
	ldda	[%i0+24]%asi, %d6
	ldda	[%i0+32]%asi, %d8
	ldda	[%i0+40]%asi, %d10
	ldda	[%i0+48]%asi, %d12
	add	%i0, 56, %i0
	sub	%i2, 56, %i2
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.ci_aln_001_loop:
	ldda	[%i0]ASI_BLK_AIUS,%d16	! block load
	subcc	%o3, 64, %o3
	fmovd	%d16, %d14
	stxa	%g0,[%i0+%i1]ASI_STBI_P	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_P
	add	%i0, 64, %i0
	fmovd	%d18, %d0
	fmovd	%d20, %d2
	fmovd	%d22, %d4
	fmovd	%d24, %d6
	fmovd	%d26, %d8
	fmovd	%d28, %d10
	fmovd	%d30, %d12
	bgt,pt	%ncc, .ci_aln_001_loop
	prefetcha [%i0 + (4 * CACHE_LINE)]%asi, #one_read
	add	%i1, %i0, %i1

	std	%d0, [%i1]
	std	%d2, [%i1+8]
	std	%d4, [%i1+16]
	std	%d6, [%i1+24]
	std	%d8, [%i1+32]
	std	%d10, [%i1+40]
	std	%d12, [%i1+48]
	ba	.ci_remain_stuff
	add	%i1, 56, %i1
	! END OF aln_001

.ci_aln_000:
	prefetcha [%i0 + (4 * CACHE_LINE)]%asi, #one_read
	andn	%i2, 0x7f, %o3		! %o3 is multiple of 2*block size
	and	%i2, 0x7f, %i2		! residue bytes in %i2
	sub	%i1, %i0, %i1
.ci_aln_000_loop:
	ldda	[%i0]ASI_BLK_AIUS,%d0
	subcc	%o3, 64, %o3
	stxa	%g0,[%i0+%i1]ASI_STBI_P	! block initializing store
	stda	%d0,[%i0+%i1]ASI_BLK_P
	add	%i0, 64, %i0
	bgt,pt	%ncc, .ci_aln_000_loop
	prefetcha [%i0 + (4 * CACHE_LINE)]%asi, #one_read
	add	%i1, %i0, %i1

	! END OF aln_000

.ci_remain_stuff:
	subcc	%i2, 31, %i2		! adjust length to allow cc test
	ble,pt	%ncc, .ci_aln_31
	nop
.ci_aln_32:
	ldxa	[%i0]%asi, %o4		! move 32 bytes
	subcc	%i2, 32, %i2		! decrement length count by 32
	stx	%o4, [%i1]
	ldxa	[%i0+8]%asi, %o4
	stx	%o4, [%i1+8]
	ldxa	[%i0+16]%asi, %o4
	add	%i0, 32, %i0		! increase src ptr by 32
	stx	%o4, [%i1+16]
	ldxa	[%i0-8]%asi, %o4
	add	%i1, 32, %i1		! increase dst ptr by 32
	bgu,pt	%ncc, .ci_aln_32	! repeat if at least 32 bytes left
	stx	%o4, [%i1-8]
.ci_aln_31:
	addcc	%i2, 24, %i2		! adjust count to be off by 7
	ble,pt	%ncc, .ci_aln_7		! skip if 7 or fewer bytes left
	nop				!
.ci_aln_15:
	ldxa	[%i0]%asi, %o4		! move 8 bytes
	add	%i0, 8, %i0		! increase src ptr by 8
	subcc	%i2, 8, %i2		! decrease count by 8
	add	%i1, 8, %i1		! increase dst ptr by 8
	bgu,pt	%ncc, .ci_aln_15
	stx	%o4, [%i1-8]		!
.ci_aln_7:
	addcc	%i2, 7, %i2		! finish adjustment of remaining count
	bz,pt	%ncc, .ci_exit		! exit if finished
	cmp	%i2, 4
	blt,pt	%ncc, .ci_unaln3x	! skip if less than 4 bytes left
	nop				!
	lda	[%i0]%asi, %o4		! move 4 bytes
	add	%i0, 4, %i0		! increase src ptr by 4
	add	%i1, 4, %i1		! increase dst ptr by 4
	subcc	%i2, 4, %i2		! decrease count by 4
	bnz	.ci_unaln3x
	stw	%o4, [%i1-4]
	ba	.ci_exit
	nop

	! destination alignment code
.ci_big_d1:
	lduba	[%i0]%asi, %o4		! move a byte
	add	%i0, 1, %i0
	stb	%o4, [%i1]
	add	%i1, 1, %i1
	andcc	%i1, 2, %o3
	bz,pt	%ncc, .ci_big_d2f
	sub	%i2, 1, %i2
.ci_big_d2:				! dest is now at least half word aligned
	lduba	[%i0]%asi, %o4		! move a half-word (src align unknown)
	lduba	[%i0+1]%asi, %o3
	add	%i0, 2, %i0
	sll	%o4, 8, %o4		! position
	or	%o4, %o3, %o4		! merge
	sth	%o4, [%i1]
	add	%i1, 2, %i1
	andcc	%i1, 4, %o3
	bz,pt	%ncc, .ci_big_d4f
	sub	%i2, 2, %i2
.ci_big_d4:				! dest is at least word aligned
	nop
	lduba	[%i0]%asi, %o4		! move a word (src align unknown)
	lduba	[%i0+1]%asi, %o3
	sll	%o4, 24, %o4		! position
	sll	%o3, 16, %o3		! position
	or	%o4, %o3, %o3		! merge
	lduba	[%i0+2]%asi, %o4
	sll	%o4, 8, %o4		! position
	or	%o4, %o3, %o3		! merge
	lduba	[%i0+3]%asi, %o4
	or	%o4, %o3, %o4		! merge
	stw	%o4,[%i1]		! store four bytes
	add	%i0, 4, %i0		! adjust src by 4
	add	%i1, 4, %i1		! adjust dest by 4
	ba	.ci_big_d4f
	sub	%i2, 4, %i2		! adjust count by 4


	! Dst is on 8 byte boundary; src is not;
.ci_big_unal8:
	andcc	%i1, 0x3f, %o3		! is dst 64-byte block aligned?
	bz	%ncc, .ci_unalnsrc
	sub	%o3, 64, %o3		! %o3 will be multiple of 8
	neg	%o3			! bytes until dest is 64 byte aligned
	sub	%i2, %o3, %i2		! update cnt with bytes to be moved
	! Move bytes according to source alignment
	andcc	%i0, 0x1, %o4
	bnz	%ncc, .ci_unalnbyte	! check for byte alignment
	nop
	andcc	%i0, 2, %o4		! check for half word alignment
	bnz	%ncc, .ci_unalnhalf
	nop
	! Src is word aligned, move bytes until dest 64 byte aligned
.ci_unalnword:
	lda	[%i0]%asi, %o4		! load 4 bytes
	stw	%o4, [%i1]		! and store 4 bytes
	lda	[%i0+4]%asi, %o4	! load 4 bytes
	add	%i0, 8, %i0		! increase src ptr by 8
	stw	%o4, [%i1+4]		! and store 4 bytes
	subcc	%o3, 8, %o3		! decrease count by 8
	bnz	%ncc, .ci_unalnword
	add	%i1, 8, %i1		! increase dst ptr by 8
	ba	.ci_unalnsrc
	nop

	! Src is half-word aligned, move bytes until dest 64 byte aligned
.ci_unalnhalf:
	lduha	[%i0]%asi, %o4		! load 2 bytes
	sllx	%o4, 32, %i3		! shift left
	lduwa	[%i0+2]%asi, %o4
	or	%o4, %i3, %i3
	sllx	%i3, 16, %i3
	lduha	[%i0+6]%asi, %o4
	or	%o4, %i3, %i3
	stx	%i3, [%i1]
	add	%i0, 8, %i0
	subcc	%o3, 8, %o3
	bnz	%ncc, .ci_unalnhalf
	add	%i1, 8, %i1
	ba	.ci_unalnsrc
	nop

	! Src is Byte aligned, move bytes until dest 64 byte aligned
.ci_unalnbyte:
	sub	%i1, %i0, %i1		! share pointer advance
.ci_unalnbyte_loop:
	lduba	[%i0]%asi, %o4
	sllx	%o4, 56, %i3
	lduha	[%i0+1]%asi, %o4
	sllx	%o4, 40, %o4
	or	%o4, %i3, %i3
	lduha	[%i0+3]%asi, %o4
	sllx	%o4, 24, %o4
	or	%o4, %i3, %i3
	lduha	[%i0+5]%asi, %o4
	sllx	%o4, 8, %o4
	or	%o4, %i3, %i3
	lduba	[%i0+7]%asi, %o4
	or	%o4, %i3, %i3
	stx	%i3, [%i1+%i0]
	subcc	%o3, 8, %o3
	bnz	%ncc, .ci_unalnbyte_loop
	add	%i0, 8, %i0
	add	%i1,%i0, %i1		! restore pointer

	! Destination is now block (64 byte aligned), src is not 8 byte aligned
.ci_unalnsrc:
	andn	%i2, 0x3f, %i3		! %i3 is multiple of block size
	and	%i2, 0x3f, %i2		! residue bytes in %i2
	add	%i2, 64, %i2		! Insure we don't load beyond
	sub	%i3, 64, %i3		! end of source buffer

	andn	%i0, 0x3f, %o4		! %o4 has block aligned src address
	prefetcha [%o4 + (3 * CACHE_LINE)]%asi, #one_read
	alignaddr %i0, %g0, %g0		! generate %gsr
	add	%i0, %i3, %i0		! advance %i0 to after blocks
	!
	! Determine source alignment to correct 8 byte offset
	andcc	%i0, 0x20, %o3
	brnz,pn	%o3, .ci_unaln_1
	andcc	%i0, 0x10, %o3
	brnz,pn	%o3, .ci_unaln_01
	andcc	%i0, 0x08, %o3
	brz,a	%o3, .ci_unaln_000
	prefetcha [%o4 + (4 * CACHE_LINE)]%asi, #one_read
	ba	.ci_unaln_001
	nop
.ci_unaln_01:
	brnz,a	%o3, .ci_unaln_011
	prefetcha [%o4 + (4 * CACHE_LINE)]%asi, #one_read
	ba	.ci_unaln_010
	nop
.ci_unaln_1:
	brnz,pn	%o3, .ci_unaln_11
	andcc	%i0, 0x08, %o3
	brnz,a	%o3, .ci_unaln_101
	prefetcha [%o4 + (4 * CACHE_LINE)]%asi, #one_read
	ba	.ci_unaln_100
	nop
.ci_unaln_11:
	brz,pn	%o3, .ci_unaln_110
	prefetcha [%i0 + (4 * CACHE_LINE)]%asi, #one_read

.ci_unaln_111:
	ldda	[%o4+56]%asi, %d14
.ci_unaln_111_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_AIUS, %d16
	faligndata %d14, %d16, %d48
	faligndata %d16, %d18, %d50
	faligndata %d18, %d20, %d52
	faligndata %d20, %d22, %d54
	faligndata %d22, %d24, %d56
	faligndata %d24, %d26, %d58
	faligndata %d26, %d28, %d60
	faligndata %d28, %d30, %d62
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_P
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .ci_unaln_111_loop
	prefetcha [%o4 + (4 * CACHE_LINE)]%asi, #one_read
	ba	.ci_unaln_done
	nop

.ci_unaln_110:
	ldda	[%o4+48]%asi, %d12
	ldda	[%o4+56]%asi, %d14
.ci_unaln_110_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_AIUS, %d16
	faligndata %d12, %d14, %d48
	faligndata %d14, %d16, %d50
	faligndata %d16, %d18, %d52
	faligndata %d18, %d20, %d54
	faligndata %d20, %d22, %d56
	faligndata %d22, %d24, %d58
	faligndata %d24, %d26, %d60
	faligndata %d26, %d28, %d62
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_P
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .ci_unaln_110_loop
	prefetcha [%o4 + (4 * CACHE_LINE)]%asi, #one_read
	ba	.ci_unaln_done
	nop

.ci_unaln_101:
	ldda	[%o4+40]%asi, %d10
	ldda	[%o4+48]%asi, %d12
	ldda	[%o4+56]%asi, %d14
.ci_unaln_101_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_AIUS, %d16
	faligndata %d10, %d12, %d48
	faligndata %d12, %d14, %d50
	faligndata %d14, %d16, %d52
	faligndata %d16, %d18, %d54
	faligndata %d18, %d20, %d56
	faligndata %d20, %d22, %d58
	faligndata %d22, %d24, %d60
	faligndata %d24, %d26, %d62
	fmovd	%d26, %d10
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_P
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .ci_unaln_101_loop
	prefetcha [%o4 + (4 * CACHE_LINE)]%asi, #one_read
	ba	.ci_unaln_done
	nop

.ci_unaln_100:
	ldda	[%o4+32]%asi, %d8
	ldda	[%o4+40]%asi, %d10
	ldda	[%o4+48]%asi, %d12
	ldda	[%o4+56]%asi, %d14
.ci_unaln_100_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_AIUS, %d16
	faligndata %d8, %d10, %d48
	faligndata %d10, %d12, %d50
	faligndata %d12, %d14, %d52
	faligndata %d14, %d16, %d54
	faligndata %d16, %d18, %d56
	faligndata %d18, %d20, %d58
	faligndata %d20, %d22, %d60
	faligndata %d22, %d24, %d62
	fmovd	%d24, %d8
	fmovd	%d26, %d10
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_P
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .ci_unaln_100_loop
	prefetcha [%o4 + (4 * CACHE_LINE)]%asi, #one_read
	ba	.ci_unaln_done
	nop

.ci_unaln_011:
	ldda	[%o4+24]%asi, %d6
	ldda	[%o4+32]%asi, %d8
	ldda	[%o4+40]%asi, %d10
	ldda	[%o4+48]%asi, %d12
	ldda	[%o4+56]%asi, %d14
.ci_unaln_011_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_AIUS, %d16
	faligndata %d6, %d8, %d48
	faligndata %d8, %d10, %d50
	faligndata %d10, %d12, %d52
	faligndata %d12, %d14, %d54
	faligndata %d14, %d16, %d56
	faligndata %d16, %d18, %d58
	faligndata %d18, %d20, %d60
	faligndata %d20, %d22, %d62
	fmovd	%d22, %d6
	fmovd	%d24, %d8
	fmovd	%d26, %d10
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_P
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .ci_unaln_011_loop
	prefetcha [%o4 + (4 * CACHE_LINE)]%asi, #one_read
	ba	.ci_unaln_done
	nop

.ci_unaln_010:
	ldda	[%o4+16]%asi, %d4
	ldda	[%o4+24]%asi, %d6
	ldda	[%o4+32]%asi, %d8
	ldda	[%o4+40]%asi, %d10
	ldda	[%o4+48]%asi, %d12
	ldda	[%o4+56]%asi, %d14
.ci_unaln_010_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_AIUS, %d16
	faligndata %d4, %d6, %d48
	faligndata %d6, %d8, %d50
	faligndata %d8, %d10, %d52
	faligndata %d10, %d12, %d54
	faligndata %d12, %d14, %d56
	faligndata %d14, %d16, %d58
	faligndata %d16, %d18, %d60
	faligndata %d18, %d20, %d62
	fmovd	%d20, %d4
	fmovd	%d22, %d6
	fmovd	%d24, %d8
	fmovd	%d26, %d10
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_P
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .ci_unaln_010_loop
	prefetcha [%o4 + (4 * CACHE_LINE)]%asi, #one_read
	ba	.ci_unaln_done
	nop

.ci_unaln_001:
	ldda	[%o4+8]%asi, %d2
	ldda	[%o4+16]%asi, %d4
	ldda	[%o4+24]%asi, %d6
	ldda	[%o4+32]%asi, %d8
	ldda	[%o4+40]%asi, %d10
	ldda	[%o4+48]%asi, %d12
	ldda	[%o4+56]%asi, %d14
.ci_unaln_001_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_AIUS, %d16
	faligndata %d2, %d4, %d48
	faligndata %d4, %d6, %d50
	faligndata %d6, %d8, %d52
	faligndata %d8, %d10, %d54
	faligndata %d10, %d12, %d56
	faligndata %d12, %d14, %d58
	faligndata %d14, %d16, %d60
	faligndata %d16, %d18, %d62
	fmovd	%d18, %d2
	fmovd	%d20, %d4
	fmovd	%d22, %d6
	fmovd	%d24, %d8
	fmovd	%d26, %d10
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_P
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .ci_unaln_001_loop
	prefetcha [%o4 + (4 * CACHE_LINE)]%asi, #one_read
	ba	.ci_unaln_done
	nop

.ci_unaln_000:
	ldda	[%o4]ASI_BLK_AIUS, %d0
.ci_unaln_000_loop:
	add	%o4, 64, %o4
	ldda	[%o4]ASI_BLK_AIUS, %d16
	faligndata %d0, %d2, %d48
	faligndata %d2, %d4, %d50
	faligndata %d4, %d6, %d52
	faligndata %d6, %d8, %d54
	faligndata %d8, %d10, %d56
	faligndata %d10, %d12, %d58
	faligndata %d12, %d14, %d60
	faligndata %d14, %d16, %d62
	fmovd	%d16, %d0
	fmovd	%d18, %d2
	fmovd	%d20, %d4
	fmovd	%d22, %d6
	fmovd	%d24, %d8
	fmovd	%d26, %d10
	fmovd	%d28, %d12
	fmovd	%d30, %d14
	stda	%d48, [%i1]ASI_BLK_P
	subcc	%i3, 64, %i3
	add	%i1, 64, %i1
	bgu,pt	%ncc, .ci_unaln_000_loop
	prefetcha [%o4 + (4 * CACHE_LINE)]%asi, #one_read

.ci_unaln_done:
	! Handle trailing bytes, 64 to 127
	! Dest long word aligned, Src not long word aligned
	cmp	%i2, 15
	bleu	%ncc, .ci_unaln_short

	andn	%i2, 0x7, %i3		! %i3 is multiple of 8
	and	%i2, 0x7, %i2		! residue bytes in %i2
	add	%i2, 8, %i2
	sub	%i3, 8, %i3		! insure we don't load past end of src
	andn	%i0, 0x7, %o4		! %o4 has long word aligned src address
	add	%i0, %i3, %i0		! advance %i0 to after multiple of 8
	ldda	[%o4]%asi, %d0		! fetch partial word
.ci_unaln_by8:
	ldda	[%o4+8]%asi, %d2
	add	%o4, 8, %o4
	faligndata %d0, %d2, %d16
	subcc	%i3, 8, %i3
	std	%d16, [%i1]
	fmovd	%d2, %d0
	bgu,pt	%ncc, .ci_unaln_by8
	add	%i1, 8, %i1

.ci_unaln_short:
	cmp	%i2, 8
	blt,pt	%ncc, .ci_unalnfin
	nop
	lduba	[%i0]%asi, %o4
	sll	%o4, 24, %o3
	lduba	[%i0+1]%asi, %o4
	sll	%o4, 16, %o4
	or	%o4, %o3, %o3
	lduba	[%i0+2]%asi, %o4
	sll	%o4, 8, %o4
	or	%o4, %o3, %o3
	lduba	[%i0+3]%asi, %o4
	or	%o4, %o3, %o3
	stw	%o3, [%i1]
	lduba	[%i0+4]%asi, %o4
	sll	%o4, 24, %o3
	lduba	[%i0+5]%asi, %o4
	sll	%o4, 16, %o4
	or	%o4, %o3, %o3
	lduba	[%i0+6]%asi, %o4
	sll	%o4, 8, %o4
	or	%o4, %o3, %o3
	lduba	[%i0+7]%asi, %o4
	or	%o4, %o3, %o3
	stw	%o3, [%i1+4]
	add	%i0, 8, %i0
	add	%i1, 8, %i1
	sub	%i2, 8, %i2
.ci_unalnfin:
	cmp	%i2, 4
	blt,pt	%ncc, .ci_unalnz
	tst	%i2
	lduba	[%i0]%asi, %o3		! read byte
	subcc	%i2, 4, %i2		! reduce count by 4
	sll	%o3, 24, %o3		! position
	lduba	[%i0+1]%asi, %o4
	sll	%o4, 16, %o4		! position
	or	%o4, %o3, %o3		! merge
	lduba	[%i0+2]%asi, %o4
	sll	%o4, 8, %o4		! position
	or	%o4, %o3, %o3		! merge
	add	%i1, 4, %i1		! advance dst by 4
	lduba	[%i0+3]%asi, %o4
	add	%i0, 4, %i0		! advance src by 4
	or	%o4, %o3, %o4		! merge
	bnz,pt	%ncc, .ci_unaln3x
	stw	%o4, [%i1-4]
	ba	.ci_exit
	nop
.ci_unalnz:
	bz,pt	%ncc, .ci_exit
	wr	%l5, %g0, %gsr		! restore %gsr
.ci_unaln3x:				! Exactly 1, 2, or 3 bytes remain
	subcc	%i2, 1, %i2		! reduce count for cc test
	lduba	[%i0]%asi, %o4		! load one byte
	bz,pt	%ncc, .ci_exit
	stb	%o4, [%i1]		! store one byte
	lduba	[%i0+1]%asi, %o4	! load second byte
	subcc	%i2, 1, %i2
	bz,pt	%ncc, .ci_exit
	stb	%o4, [%i1+1]		! store second byte
	lduba	[%i0+2]%asi, %o4	! load third byte
	stb	%o4, [%i1+2]		! store third byte
.ci_exit:
	brnz	%g1, .ci_fp_restore
	nop
	FZERO
	wr	%g1, %g0, %fprs
	ba,pt	%ncc, .ci_ex2
	membar	#Sync
.ci_fp_restore:
	BLD_FP_FROMSTACK(%o4)
.ci_ex2:
	andn	SAVED_LOFAULT, FPUSED_FLAG, SAVED_LOFAULT
	stn	SAVED_LOFAULT, [THREAD_REG + T_LOFAULT] ! restore old t_lofault
	ret
	restore %g0, 0, %o0

.copyin_err:
	ldn	[THREAD_REG + T_COPYOPS], %o4
	brz	%o4, 2f
	nop
	ldn	[%o4 + CP_COPYIN], %g2
	jmp	%g2
	nop
2:
	retl
	mov	-1, %o0

#else	/* NIAGARA_IMPL */
.do_copyin:
	!
	! Check the length and bail if zero.
	!
	tst	%o2
	bnz,pt	%ncc, 1f
	nop
	retl
	clr	%o0
1:
	sethi	%hi(copyio_fault), %o4
	or	%o4, %lo(copyio_fault), %o4
	sethi	%hi(copyio_fault_nowindow), %o3
	ldn	[THREAD_REG + T_LOFAULT], SAVED_LOFAULT
	or	%o3, %lo(copyio_fault_nowindow), %o3
	membar	#Sync
	stn	%o3, [THREAD_REG + T_LOFAULT]

	mov	%o0, SAVE_SRC
	mov	%o1, SAVE_DST
	mov	%o2, SAVE_COUNT

	!
	! Check to see if we're more than SMALL_LIMIT.
	!
	subcc	%o2, SMALL_LIMIT, %o3
	bgu,a,pt %ncc, .dci_ns
	or	%o0, %o1, %o3
	!
	! What was previously ".small_copyin"
	!
.dcibcp:
	sub	%g0, %o2, %o3		! setup for copy loop
	add	%o0, %o2, %o0
	add	%o1, %o2, %o1
	ba,pt	%ncc, .dcicl
	lduba	[%o0 + %o3]ASI_USER, %o4
	!
	! %o0 and %o1 point at the end and remain pointing at the end
	! of their buffers. We pull things out by adding %o3 (which is
	! the negation of the length) to the buffer end which gives us
	! the curent location in the buffers. By incrementing %o3 we walk
	! through both buffers without having to bump each buffer's
	! pointer. A very fast 4 instruction loop.
	!
	.align 16
.dcicl:
	stb	%o4, [%o1 + %o3]
	inccc	%o3
	bl,a,pt %ncc, .dcicl
	lduba	[%o0 + %o3]ASI_USER, %o4
	!
	! We're done. Go home.
	!	
	membar	#Sync
	stn	SAVED_LOFAULT, [THREAD_REG + T_LOFAULT]
	retl
	clr	%o0
	!
	! Try aligned copies from here.
	!
.dci_ns:
	!
	! See if we're single byte aligned. If we are, check the
	! limit for single byte copies. If we're smaller, or equal,
	! bounce to the byte for byte copy loop. Otherwise do it in
	! HW (if enabled).
	!
	btst	1, %o3
	bz,a,pt	%icc, .dcih8
	btst	7, %o3
	!
	! We're single byte aligned.
	!
	sethi	%hi(hw_copy_limit_1), %o3
	ld	[%o3 + %lo(hw_copy_limit_1)], %o3
	!
	! Is HW copy on? If not do everything byte for byte.
	!
	tst	%o3
	bz,pn	%icc, .dcibcp
	subcc	%o3, %o2, %o3
	!
	! Are we bigger than the HW limit? If not
	! go to byte for byte.
	!
	bge,pt	%ncc, .dcibcp
	nop
	!
	! We're big enough and copy is on. Do it with HW.
	!
	ba,pt	%ncc, .big_copyin
	nop
.dcih8:
	!
	! 8 byte aligned?
	!
	bnz,a	%ncc, .dcih4
	btst	3, %o3
	!
	! We're eight byte aligned.
	!
	sethi	%hi(hw_copy_limit_8), %o3
	ld	[%o3 + %lo(hw_copy_limit_8)], %o3
	!
	! Is HW assist on? If not, do it with the aligned copy.
	!
	tst	%o3
	bz,pn	%icc, .dcis8
	subcc	%o3, %o2, %o3
	bge	%ncc, .dcis8
	nop
	ba,pt	%ncc, .big_copyin
	nop
.dcis8:
	!
	! Housekeeping for copy loops. Uses same idea as in the byte for
	! byte copy loop above.
	!
	add	%o0, %o2, %o0
	add	%o1, %o2, %o1
	sub	%g0, %o2, %o3
	ba,pt	%ncc, .didebc
	srl	%o2, 3, %o2		! Number of 8 byte chunks to copy
	!
	! 4 byte aligned?
	!
.dcih4:
	bnz	%ncc, .dcih2
	sethi	%hi(hw_copy_limit_4), %o3
	ld	[%o3 + %lo(hw_copy_limit_4)], %o3
	!
	! Is HW assist on? If not, do it with the aligned copy.
	!
	tst	%o3
	bz,pn	%icc, .dcis4
	subcc	%o3, %o2, %o3
	!
	! We're negative if our size is less than or equal to hw_copy_limit_4.
	!
	bge	%ncc, .dcis4
	nop
	ba,pt	%ncc, .big_copyin
	nop
.dcis4:
	!
	! Housekeeping for copy loops. Uses same idea as in the byte
	! for byte copy loop above.
	!
	add	%o0, %o2, %o0
	add	%o1, %o2, %o1
	sub	%g0, %o2, %o3
	ba,pt	%ncc, .didfbc
	srl	%o2, 2, %o2		! Number of 4 byte chunks to copy
.dcih2:
	!
	! We're two byte aligned. Check for "smallness"
	! done in delay at .dcih4
	!
	bleu,pt	%ncc, .dcis2
	sethi	%hi(hw_copy_limit_2), %o3
	ld	[%o3 + %lo(hw_copy_limit_2)], %o3
	!
	! Is HW assist on? If not, do it with the aligned copy.
	!
	tst	%o3
	bz,pn	%icc, .dcis2
	subcc	%o3, %o2, %o3
	!
	! Are we larger than the HW limit?
	!
	bge	%ncc, .dcis2
	nop
	!
	! HW assist is on and we're large enough to use it.
	!
	ba,pt	%ncc, .big_copyin
	nop
	!
	! Housekeeping for copy loops. Uses same idea as in the byte
	! for byte copy loop above.
	!
.dcis2:
	add	%o0, %o2, %o0
	add	%o1, %o2, %o1
	sub	%g0, %o2, %o3
	ba,pt	%ncc, .didtbc
	srl	%o2, 1, %o2		! Number of 2 byte chunks to copy
	!
.small_copyin:
	!
	! Why are we doing this AGAIN? There are certain conditions in
	! big copyin that will cause us to forgo the HW assisted copys
	! and bounce back to a non-hw assisted copy. This dispatches
	! those copies. Note that we branch around this in the main line
	! code.
	!
	! We make no check for limits or HW enablement here. We've
	! already been told that we're a poster child so just go off
	! and do it.
	!
	or	%o0, %o1, %o3
	btst	1, %o3
	bnz	%icc, .dcibcp		! Most likely
	btst	7, %o3
	bz	%icc, .dcis8
	btst	3, %o3
	bz	%icc, .dcis4
	nop
	ba,pt	%ncc, .dcis2
	nop
	!
	! Eight byte aligned copies. A steal from the original .small_copyin
	! with modifications. %o2 is number of 8 byte chunks to copy. When
	! done, we examine %o3. If this is < 0, we have 1 - 7 bytes more
	! to copy.
	!
	.align 32
.didebc:
	ldxa	[%o0 + %o3]ASI_USER, %o4
	deccc	%o2
	stx	%o4, [%o1 + %o3]
	bg,pt	%ncc, .didebc
	addcc	%o3, 8, %o3
	!
	! End of copy loop. Most 8 byte aligned copies end here.
	!
	bz,pt	%ncc, .dcifh
	nop
	!
	! Something is left. Do it byte for byte.
	!
	ba,pt	%ncc, .dcicl
	lduba	[%o0 + %o3]ASI_USER, %o4
	!
	! 4 byte copy loop. %o2 is number of 4 byte chunks to copy.
	!
	.align 32
.didfbc:
	lduwa	[%o0 + %o3]ASI_USER, %o4
	deccc	%o2
	st	%o4, [%o1 + %o3]
	bg,pt	%ncc, .didfbc
	addcc	%o3, 4, %o3
	!
	! End of copy loop. Most 4 byte aligned copies end here.
	!
	bz,pt	%ncc, .dcifh
	nop
	!
	! Something is left. Do it byte for byte.
	!
	ba,pt	%ncc, .dcicl
	lduba	[%o0 + %o3]ASI_USER, %o4
	!
	! 2 byte aligned copy loop. %o2 is number of 2 byte chunks to
	! copy.
	!
	.align 32
.didtbc:
	lduha	[%o0 + %o3]ASI_USER, %o4
	deccc	%o2
	sth	%o4, [%o1 + %o3]
	bg,pt	%ncc, .didtbc
	addcc	%o3, 2, %o3
	!
	! End of copy loop. Most 2 byte aligned copies end here.
	!
	bz,pt	%ncc, .dcifh
	nop
	!
	! Deal with the last byte
	!
	lduba	[%o0 + %o3]ASI_USER, %o4
	stb	%o4, [%o1 + %o3]
.dcifh:
	membar	#Sync
	stn	SAVED_LOFAULT, [THREAD_REG + T_LOFAULT] ! restore old t_lofault
	retl
	clr	%o0

.big_copyin:
	! We're going off to do a block copy.
	! Switch fault hendlers and grab a window. We
	! don't do a membar #Sync since we've done only
	! kernel data to this point.
	stn	%o4, [THREAD_REG + T_LOFAULT]

	! Copy in that reach here are larger than 256 bytes. The
	! hw_copy_limit_1 is set to 256. Never set this limit less
	! 128 bytes.
	save	%sp, -SA(MINFRAME), %sp
.do_blockcopyin:

	! Swap src/dst since the code below is memcpy code
	! and memcpy/bcopy have different calling sequences
	mov	%i1, %i5
	mov	%i0, %i1
	mov	%i5, %i0

	! Block (64 bytes) align the destination.
	andcc	%i0, 0x3f, %i3		! is dst block aligned
	bz	%ncc, copyin_blalign	! dst already block aligned
	sub	%i3, 0x40, %i3
	neg	%i3			! bytes till dst 64 bytes aligned
	sub	%i2, %i3, %i2		! update i2 with new count

	! Based on source and destination alignment do
	! either 8 bytes, 4 bytes, 2 bytes or byte copy.

	! Is dst & src 8B aligned
	or	%i0, %i1, %o2
	andcc	%o2, 0x7, %g0
	bz	%ncc, .ci_alewdcp
	nop

	! Is dst & src 4B aligned
	andcc	%o2, 0x3, %g0
	bz	%ncc, .ci_alwdcp
	nop

	! Is dst & src 2B aligned
	andcc	%o2, 0x1, %g0
	bz	%ncc, .ci_alhlfwdcp
	nop

	! 1B aligned
1:	lduba	[%i1]ASI_USER, %o2
	stb	%o2, [%i0]
	inc	%i1
	deccc	%i3
	bgu,pt	%ncc, 1b
	inc	%i0

	ba	copyin_blalign
	nop

	! dst & src 4B aligned
.ci_alwdcp:
	lda	[%i1]ASI_USER, %o2
	st	%o2, [%i0]
	add	%i1, 0x4, %i1
	subcc	%i3, 0x4, %i3
	bgu,pt	%ncc, .ci_alwdcp
	add	%i0, 0x4, %i0

	ba	copyin_blalign
	nop

	! dst & src 2B aligned
.ci_alhlfwdcp:
	lduha	[%i1]ASI_USER, %o2
	stuh	%o2, [%i0]
	add	%i1, 0x2, %i1
	subcc	%i3, 0x2, %i3
	bgu,pt	%ncc, .ci_alhlfwdcp
	add	%i0, 0x2, %i0

	ba	copyin_blalign
	nop

	! dst & src 8B aligned
.ci_alewdcp:
	ldxa	[%i1]ASI_USER, %o2
	stx	%o2, [%i0]
	add	%i1, 0x8, %i1
	subcc	%i3, 0x8, %i3
	bgu,pt	%ncc, .ci_alewdcp
	add	%i0, 0x8, %i0

copyin_blalign:
	andn	%i2, 0x3f, %i3		! %i3 count is multiple of block size
	sub	%i2, %i3, %i2		! Residue bytes in %i2

	mov	ASI_BLK_INIT_ST_QUAD_LDD_P, %asi

	andcc	%i1, 0xf, %o2		! is src quadword aligned
	bz,pn	%xcc, .ci_blkcpy	! src offset in %o2 (last 4-bits)
	nop
	cmp	%o2, 0x8
	bg	.ci_upper_double
	nop
	bl	.ci_lower_double
	nop

	! Falls through when source offset is equal to 8 i.e.
	! source is double word aligned.
	! In this case no shift/merge of data is required

	sub	%i1, %o2, %i1		! align the src at 16 bytes.
	andn	%i1, 0x3f, %l0		! %l0 has block aligned source
	prefetcha [%l0]ASI_USER, #one_read
	ldda	[%i1]ASI_BLK_INIT_QUAD_LDD_AIUS, %l2
	add	%l0, 0x40, %l0
.ci_loop0:
	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_QUAD_LDD_AIUS, %l4

	prefetcha [%l0]ASI_USER, #one_read

	stxa	%l3, [%i0+0x0]%asi
	stxa	%l4, [%i0+0x8]%asi

	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_QUAD_LDD_AIUS, %l2

	stxa	%l5, [%i0+0x10]%asi
	stxa	%l2, [%i0+0x18]%asi

	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_QUAD_LDD_AIUS, %l4

	stxa	%l3, [%i0+0x20]%asi
	stxa	%l4, [%i0+0x28]%asi

	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_QUAD_LDD_AIUS, %l2

	stxa	%l5, [%i0+0x30]%asi
	stxa	%l2, [%i0+0x38]%asi

	add	%l0, 0x40, %l0
	subcc	%i3, 0x40, %i3
	bgu,pt	%xcc, .ci_loop0
	add	%i0, 0x40, %i0
	ba	.ci_blkdone
	add	%i1, %o2, %i1		! increment the source by src offset
					! the src offset was stored in %o2

.ci_lower_double:

	sub	%i1, %o2, %i1		! align the src at 16 bytes.
	sll	%o2, 3, %o0		! %o0 left shift
	mov	0x40, %o1
	sub	%o1, %o0, %o1		! %o1 right shift = (64 - left shift)
	andn	%i1, 0x3f, %l0		! %l0 has block aligned source
	prefetcha [%l0]ASI_USER, #one_read
	ldda	[%i1]ASI_BLK_INIT_QUAD_LDD_AIUS, %l2	! partial data in %l2
							! and %l3 has complete
							! data
	add	%l0, 0x40, %l0
.ci_loop1:
	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_QUAD_LDD_AIUS, %l4	! %l4 has partial data
							! for this read.
	ALIGN_DATA(%l2, %l3, %l4, %o0, %o1, %l6)	! merge %l2, %l3 and %l4
							! into %l2 and %l3

	prefetcha [%l0]ASI_USER, #one_read

	stxa	%l2, [%i0+0x0]%asi
	stxa	%l3, [%i0+0x8]%asi

	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_QUAD_LDD_AIUS, %l2
	ALIGN_DATA(%l4, %l5, %l2, %o0, %o1, %l6)	! merge %l2 with %l5 and
							! %l4 from previous read
							! into %l4 and %l5
	stxa	%l4, [%i0+0x10]%asi
	stxa	%l5, [%i0+0x18]%asi

	! Repeat the same for next 32 bytes.

	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_QUAD_LDD_AIUS, %l4
	ALIGN_DATA(%l2, %l3, %l4, %o0, %o1, %l6)

	stxa	%l2, [%i0+0x20]%asi
	stxa	%l3, [%i0+0x28]%asi

	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_QUAD_LDD_AIUS, %l2
	ALIGN_DATA(%l4, %l5, %l2, %o0, %o1, %l6)

	stxa	%l4, [%i0+0x30]%asi
	stxa	%l5, [%i0+0x38]%asi

	add	%l0, 0x40, %l0
	subcc	%i3, 0x40, %i3
	bgu,pt	%xcc, .ci_loop1
	add	%i0, 0x40, %i0
	ba	.ci_blkdone
	add	%i1, %o2, %i1		! increment the source by src offset
					! the src offset was stored in %o2

.ci_upper_double:

	sub	%i1, %o2, %i1		! align the src at 16 bytes.
	sub	%o2, 0x8, %o0
	sll	%o0, 3, %o0		! %o0 left shift
	mov	0x40, %o1
	sub	%o1, %o0, %o1		! %o1 right shift = (64 - left shift)
	andn	%i1, 0x3f, %l0		! %l0 has block aligned source
	prefetcha [%l0]ASI_USER, #one_read
	ldda	[%i1]ASI_BLK_INIT_QUAD_LDD_AIUS, %l2	! partial data in %l3
							! for this read and
							! no data in %l2
	add	%l0, 0x40, %l0
.ci_loop2:
	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_QUAD_LDD_AIUS, %l4	! %l4 has complete data
							! and %l5 has partial
	ALIGN_DATA(%l3, %l4, %l5, %o0, %o1, %l6)	! merge %l3, %l4 and %l5
							! into %l3 and %l4
	prefetcha [%l0]ASI_USER, #one_read

	stxa	%l3, [%i0+0x0]%asi
	stxa	%l4, [%i0+0x8]%asi

	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_QUAD_LDD_AIUS, %l2
	ALIGN_DATA(%l5, %l2, %l3, %o0, %o1, %l6)	! merge %l2 and %l3 with
							! %l5 from previous read
							! into %l5 and %l2

	stxa	%l5, [%i0+0x10]%asi
	stxa	%l2, [%i0+0x18]%asi

	! Repeat the same for next 32 bytes.

	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_QUAD_LDD_AIUS, %l4
	ALIGN_DATA(%l3, %l4, %l5, %o0, %o1, %l6)

	stxa	%l3, [%i0+0x20]%asi
	stxa	%l4, [%i0+0x28]%asi

	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_QUAD_LDD_AIUS, %l2
	ALIGN_DATA(%l5, %l2, %l3, %o0, %o1, %l6)

	stxa	%l5, [%i0+0x30]%asi
	stxa	%l2, [%i0+0x38]%asi

	add	%l0, 0x40, %l0
	subcc	%i3, 0x40, %i3
	bgu,pt	%xcc, .ci_loop2
	add	%i0, 0x40, %i0
	ba	.ci_blkdone
	add	%i1, %o2, %i1		! increment the source by src offset
					! the src offset was stored in %o2


	! Do fast copy using ASI_BLK_INIT_ST_QUAD_LDD_P
.ci_blkcpy:

	andn	%i1, 0x3f, %o0		! %o0 has block aligned source
	prefetcha [%o0]ASI_USER, #one_read
	add	%o0, 0x40, %o0
1:
	ldda	[%i1]ASI_BLK_INIT_QUAD_LDD_AIUS, %l0
	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_QUAD_LDD_AIUS, %l2
	add	%i1, 0x10, %i1

	prefetcha [%o0]ASI_USER, #one_read

	stxa	%l0, [%i0+0x0]%asi

	ldda	[%i1]ASI_BLK_INIT_QUAD_LDD_AIUS, %l4
	add	%i1, 0x10, %i1
	ldda	[%i1]ASI_BLK_INIT_QUAD_LDD_AIUS, %l6
	add	%i1, 0x10, %i1

	stxa	%l1, [%i0+0x8]%asi
	stxa	%l2, [%i0+0x10]%asi
	stxa	%l3, [%i0+0x18]%asi
	stxa	%l4, [%i0+0x20]%asi
	stxa	%l5, [%i0+0x28]%asi
	stxa	%l6, [%i0+0x30]%asi
	stxa	%l7, [%i0+0x38]%asi

	add	%o0, 0x40, %o0
	subcc	%i3, 0x40, %i3
	bgu,pt	%xcc, 1b
	add	%i0, 0x40, %i0

.ci_blkdone:
	membar	#Sync

	brz,pt	%i2, .copyin_exit
	nop

	! Handle trailing bytes
	cmp	%i2, 0x8
	blu,pt	%ncc, .ci_residue
	nop

	! Can we do some 8B ops
	or	%i1, %i0, %o2
	andcc	%o2, 0x7, %g0
	bnz	%ncc, .ci_last4
	nop

	! Do 8byte ops as long as possible
.ci_last8:
	ldxa	[%i1]ASI_USER, %o2
	stx	%o2, [%i0]
	add	%i1, 0x8, %i1
	sub	%i2, 0x8, %i2
	cmp	%i2, 0x8
	bgu,pt	%ncc, .ci_last8
	add	%i0, 0x8, %i0

	brz,pt	%i2, .copyin_exit
	nop

	ba	.ci_residue
	nop

.ci_last4:
	! Can we do 4B ops
	andcc	%o2, 0x3, %g0
	bnz	%ncc, .ci_last2
	nop
1:
	lda	[%i1]ASI_USER, %o2
	st	%o2, [%i0]
	add	%i1, 0x4, %i1
	sub	%i2, 0x4, %i2
	cmp	%i2, 0x4
	bgu,pt	%ncc, 1b
	add	%i0, 0x4, %i0

	brz,pt	%i2, .copyin_exit
	nop

	ba	.ci_residue
	nop

.ci_last2:
	! Can we do 2B ops
	andcc	%o2, 0x1, %g0
	bnz	%ncc, .ci_residue
	nop

1:
	lduha	[%i1]ASI_USER, %o2
	stuh	%o2, [%i0]
	add	%i1, 0x2, %i1
	sub	%i2, 0x2, %i2
	cmp	%i2, 0x2
	bgu,pt	%ncc, 1b
	add	%i0, 0x2, %i0

	brz,pt	%i2, .copyin_exit
	nop

	! Copy the residue as byte copy
.ci_residue:
	lduba	[%i1]ASI_USER, %i4
	stb	%i4, [%i0]
	inc	%i1
	deccc	%i2
	bgu,pt	%xcc, .ci_residue
	inc	%i0

.copyin_exit:
	membar	#Sync
	stn	SAVED_LOFAULT, [THREAD_REG + T_LOFAULT]	! restore old t_lofault
	ret
	restore	%g0, 0, %o0
.copyin_err:
	ldn	[THREAD_REG + T_COPYOPS], %o4
	brz	%o4, 2f
	nop
	ldn	[%o4 + CP_COPYIN], %g2
	jmp	%g2
	nop
2:
	retl
	mov	-1, %o0
#endif	/* NIAGARA_IMPL */
	SET_SIZE(copyin)

#endif	/* lint */

#ifdef	lint

/*ARGSUSED*/
int
xcopyin(const void *uaddr, void *kaddr, size_t count)
{ return (0); }

#else	/* lint */

	ENTRY(xcopyin)
	sethi	%hi(.xcopyin_err), REAL_LOFAULT
	b	.do_copyin
	or	REAL_LOFAULT, %lo(.xcopyin_err), REAL_LOFAULT
.xcopyin_err:
	ldn	[THREAD_REG + T_COPYOPS], %o4
	brz	%o4, 2f
	nop
	ldn	[%o4 + CP_XCOPYIN], %g2
	jmp	%g2
	nop
2:
	retl
	mov	%g1, %o0
	SET_SIZE(xcopyin)

#endif	/* lint */

#ifdef	lint

/*ARGSUSED*/
int
xcopyin_little(const void *uaddr, void *kaddr, size_t count)
{ return (0); }

#else	/* lint */

	ENTRY(xcopyin_little)
	sethi	%hi(.little_err), %o4
	ldn	[THREAD_REG + T_LOFAULT], %o5
	or	%o4, %lo(.little_err), %o4
	membar	#Sync				! sync error barrier
	stn	%o4, [THREAD_REG + T_LOFAULT]	

	subcc	%g0, %o2, %o3
	add	%o0, %o2, %o0
	bz,pn	%ncc, 2f		! check for zero bytes
	sub	%o2, 1, %o4
	add	%o0, %o4, %o0		! start w/last byte	
	add	%o1, %o2, %o1
	lduba	[%o0+%o3]ASI_AIUSL, %o4

1:	stb	%o4, [%o1+%o3]
	inccc	%o3
	sub	%o0, 2, %o0		! get next byte
	bcc,a,pt %ncc, 1b
	lduba	[%o0+%o3]ASI_AIUSL, %o4

2:	membar	#Sync				! sync error barrier
	stn	%o5, [THREAD_REG + T_LOFAULT]	! restore old t_lofault
	retl
	mov	%g0, %o0		! return (0)

.little_err:
	membar	#Sync				! sync error barrier
	stn	%o5, [THREAD_REG + T_LOFAULT]	! restore old t_lofault
	retl
	mov	%g1, %o0
	SET_SIZE(xcopyin_little)

#endif	/* lint */


/*
 * Copy a block of storage - must not overlap (from + len <= to).
 * No fault handler installed (to be called under on_fault())
 */
#if defined(lint)

/* ARGSUSED */
void
copyin_noerr(const void *ufrom, void *kto, size_t count)
{}

#else	/* lint */

	ENTRY(copyin_noerr)
	sethi	%hi(.copyio_noerr), REAL_LOFAULT
	b	.do_copyin
	or	REAL_LOFAULT, %lo(.copyio_noerr), REAL_LOFAULT
.copyio_noerr:
	jmp	SAVED_LOFAULT
	nop
	SET_SIZE(copyin_noerr)

#endif /* lint */

/*
 * Copy a block of storage - must not overlap (from + len <= to).
 * No fault handler installed (to be called under on_fault())
 */

#if defined(lint)

/* ARGSUSED */
void
copyout_noerr(const void *kfrom, void *uto, size_t count)
{}

#else	/* lint */

	ENTRY(copyout_noerr)
	sethi	%hi(.copyio_noerr), REAL_LOFAULT
	b	.do_copyout
	or	REAL_LOFAULT, %lo(.copyio_noerr), REAL_LOFAULT
	SET_SIZE(copyout_noerr)

#endif /* lint */

#if defined(lint)

int use_hw_bcopy = 1;
int use_hw_bzero = 1;
uint_t hw_copy_limit_1 = 0x100;
uint_t hw_copy_limit_2 = 0x200;
uint_t hw_copy_limit_4 = 0x400;
uint_t hw_copy_limit_8 = 0x400;

#else /* !lint */

	.align	4
	DGDEF(use_hw_bcopy)
	.word	1
	DGDEF(use_hw_bzero)
	.word	1
	DGDEF(hw_copy_limit_1)
	.word	0x100
	DGDEF(hw_copy_limit_2)
	.word	0x200
	DGDEF(hw_copy_limit_4)
	.word	0x400
	DGDEF(hw_copy_limit_8)
	.word	0x400

	.align	64
	.section ".text"
#endif /* !lint */

/*
 * hwblkclr - clears block-aligned, block-multiple-sized regions that are
 * longer than 256 bytes in length using Niagara's block stores/quad store.
 * If the criteria for using this routine are not met then it calls bzero
 * and returns 1.  Otherwise 0 is returned indicating success.
 * Caller is responsible for ensuring use_hw_bzero is true and that
 * kpreempt_disable() has been called.
 */
#ifdef lint
/*ARGSUSED*/
int
hwblkclr(void *addr, size_t len)
{ 
	return(0);
}
#else /* lint */
	! %i0 - start address
	! %i1 - length of region (multiple of 64)

	ENTRY(hwblkclr)
	save	%sp, -SA(MINFRAME), %sp

	! Must be block-aligned
	andcc	%i0, 0x3f, %g0
	bnz,pn	%ncc, 1f
	nop

	! ... and must be 256 bytes or more
	cmp	%i1, 0x100
	blu,pn	%ncc, 1f
	nop

	! ... and length must be a multiple of 64
	andcc	%i1, 0x3f, %g0
	bz,pn	%ncc, .pz_doblock
	mov	ASI_BLK_INIT_ST_QUAD_LDD_P, %asi

1:	! punt, call bzero but notify the caller that bzero was used
	mov	%i0, %o0
	call	bzero
	mov	%i1, %o1
	ret
	restore	%g0, 1, %o0	! return (1) - did not use block operations

	! Already verified that there are at least 256 bytes to set
.pz_doblock:
	stxa	%g0, [%i0+0x0]%asi
	stxa	%g0, [%i0+0x40]%asi
	stxa	%g0, [%i0+0x80]%asi
	stxa	%g0, [%i0+0xc0]%asi

	stxa	%g0, [%i0+0x8]%asi
	stxa	%g0, [%i0+0x10]%asi
	stxa	%g0, [%i0+0x18]%asi
	stxa	%g0, [%i0+0x20]%asi
	stxa	%g0, [%i0+0x28]%asi
	stxa	%g0, [%i0+0x30]%asi
	stxa	%g0, [%i0+0x38]%asi

	stxa	%g0, [%i0+0x48]%asi
	stxa	%g0, [%i0+0x50]%asi
	stxa	%g0, [%i0+0x58]%asi
	stxa	%g0, [%i0+0x60]%asi
	stxa	%g0, [%i0+0x68]%asi
	stxa	%g0, [%i0+0x70]%asi
	stxa	%g0, [%i0+0x78]%asi

	stxa	%g0, [%i0+0x88]%asi
	stxa	%g0, [%i0+0x90]%asi
	stxa	%g0, [%i0+0x98]%asi
	stxa	%g0, [%i0+0xa0]%asi
	stxa	%g0, [%i0+0xa8]%asi
	stxa	%g0, [%i0+0xb0]%asi
	stxa	%g0, [%i0+0xb8]%asi

	stxa	%g0, [%i0+0xc8]%asi
	stxa	%g0, [%i0+0xd0]%asi
	stxa	%g0, [%i0+0xd8]%asi
	stxa	%g0, [%i0+0xe0]%asi
	stxa	%g0, [%i0+0xe8]%asi
	stxa	%g0, [%i0+0xf0]%asi
	stxa	%g0, [%i0+0xf8]%asi

	sub	%i1, 0x100, %i1
	cmp	%i1, 0x100
	bgu,pt	%ncc, .pz_doblock
	add	%i0, 0x100, %i0

2:
	! Check if more than 64 bytes to set
	cmp	%i1,0x40
	blu	%ncc, .pz_finish
	nop

3:
	stxa	%g0, [%i0+0x0]%asi
	stxa	%g0, [%i0+0x8]%asi
	stxa	%g0, [%i0+0x10]%asi
	stxa	%g0, [%i0+0x18]%asi
	stxa	%g0, [%i0+0x20]%asi
	stxa	%g0, [%i0+0x28]%asi
	stxa	%g0, [%i0+0x30]%asi
	stxa	%g0, [%i0+0x38]%asi

	subcc	%i1, 0x40, %i1
	bgu,pt	%ncc, 3b
	add	%i0, 0x40, %i0

.pz_finish:
	membar	#Sync
	ret
	restore	%g0, 0, %o0		! return (bzero or not)
	SET_SIZE(hwblkclr)
#endif	/* lint */

#ifdef	lint
/* Copy 32 bytes of data from src to dst using physical addresses */
/*ARGSUSED*/
void
hw_pa_bcopy32(uint64_t src, uint64_t dst)
{}
#else	/*!lint */

	/*
	 * Copy 32 bytes of data from src (%o0) to dst (%o1)
	 * using physical addresses.
	 */
	ENTRY_NP(hw_pa_bcopy32)
	rdpr	%pstate, %g1
	andn	%g1, PSTATE_IE, %g2
	wrpr	%g0, %g2, %pstate

	ldxa	[%o0]ASI_MEM, %o2
	add	%o0, 8, %o0
	ldxa	[%o0]ASI_MEM, %o3
	add	%o0, 8, %o0
	ldxa	[%o0]ASI_MEM, %o4
	add	%o0, 8, %o0
	ldxa	[%o0]ASI_MEM, %o5
	stxa	%o2, [%o1]ASI_MEM
	add	%o1, 8, %o1
	stxa	%o3, [%o1]ASI_MEM
	add	%o1, 8, %o1
	stxa	%o4, [%o1]ASI_MEM
	add	%o1, 8, %o1
	stxa	%o5, [%o1]ASI_MEM

	membar	#Sync
	retl
	wrpr	%g0, %g1, %pstate
	SET_SIZE(hw_pa_bcopy32)
#endif /* lint */

/*
 * Zero a block of storage.
 *
 * uzero is used by the kernel to zero a block in user address space.
 */

/*
 * Control flow of the bzero/kzero/uzero routine.
 *
 *	For fewer than 7 bytes stores, bytes will be zeroed.
 *
 *	For less than 15 bytes stores, align the address on 4 byte boundary.
 *	Then store as many 4-byte chunks, followed by trailing bytes.
 *
 *	For sizes greater than 15 bytes, align the address on 8 byte boundary.
 *	if (count > 128) {
 *		store as many 8-bytes chunks to block align the address
 *		store using ASI_BLK_INIT_ST_QUAD_LDD_P (bzero/kzero) OR
 *		store using ASI_BLK_INIT_QUAD_LDD_AIUS (uzero)
 *	}
 *	Store as many 8-byte chunks, followed by trailing bytes.
 */

#if defined(lint)

/* ARGSUSED */
int
kzero(void *addr, size_t count)
{ return(0); }

/* ARGSUSED */
void
uzero(void *addr, size_t count)
{}

#else	/* lint */

	ENTRY(uzero)
	!
	! Set a new lo_fault handler only if we came in with one
	! already specified.
	!
	wr	%g0, ASI_USER, %asi
	ldn	[THREAD_REG + T_LOFAULT], %o5
	tst	%o5
	bz,pt	%ncc, .do_zero
	sethi	%hi(.zeroerr), %o2
	or	%o2, %lo(.zeroerr), %o2
	membar	#Sync
	ba,pt	%ncc, .do_zero
	stn	%o2, [THREAD_REG + T_LOFAULT]

	ENTRY(kzero)
	!
	! Always set a lo_fault handler
	!
	wr	%g0, ASI_P, %asi
	ldn	[THREAD_REG + T_LOFAULT], %o5
	sethi	%hi(.zeroerr), %o2
	or	%o5, LOFAULT_SET, %o5
	or	%o2, %lo(.zeroerr), %o2
	membar	#Sync
	ba,pt	%ncc, .do_zero
	stn	%o2, [THREAD_REG + T_LOFAULT]

/*
 * We got here because of a fault during kzero or if
 * uzero or bzero was called with t_lofault non-zero.
 * Otherwise we've already run screaming from the room.
 * Errno value is in %g1. Note that we're here iff
 * we did set t_lofault.
 */
.zeroerr:
	!
	! Undo asi register setting. Just set it to be the
	! kernel default without checking.
	!
	wr	%g0, ASI_P, %asi

	!
	! We did set t_lofault. It may well have been zero coming in.
	!
1:
	tst	%o5
	membar #Sync
	bne,pn	%ncc, 3f		
	andncc	%o5, LOFAULT_SET, %o5
2:
	!
	! Old handler was zero. Just return the error.
	!
	retl				! return
	mov	%g1, %o0		! error code from %g1
3:
	!
	! We're here because %o5 was non-zero. It was non-zero
	! because either LOFAULT_SET was present, a previous fault
	! handler was present or both. In all cases we need to reset
	! T_LOFAULT to the value of %o5 after clearing LOFAULT_SET
	! before we either simply return the error or we invoke the
	! previously specified handler.
	!
	be	%ncc, 2b
	stn	%o5, [THREAD_REG + T_LOFAULT]
	jmp	%o5			! goto real handler
	nop
	SET_SIZE(kzero)
	SET_SIZE(uzero)

#endif	/* lint */

/*
 * Zero a block of storage.
 */

#if defined(lint)

/* ARGSUSED */
void
bzero(void *addr, size_t count)
{}

#else	/* lint */

	ENTRY(bzero)
	wr	%g0, ASI_P, %asi

	ldn	[THREAD_REG + T_LOFAULT], %o5	! save old vector
	tst	%o5
	bz,pt	%ncc, .do_zero
	sethi	%hi(.zeroerr), %o2
	or	%o2, %lo(.zeroerr), %o2
	membar	#Sync				! sync error barrier
	stn	%o2, [THREAD_REG + T_LOFAULT]	! install new vector

.do_zero:
	cmp	%o1, 7
	blu,pn	%ncc, .byteclr
	nop

	cmp	%o1, 15
	blu,pn	%ncc, .wdalign
	nop

	andcc	%o0, 7, %o3		! is add aligned on a 8 byte bound
	bz,pt	%ncc, .blkalign		! already double aligned
	sub	%o3, 8, %o3		! -(bytes till double aligned)
	add	%o1, %o3, %o1		! update o1 with new count

1:
	stba	%g0, [%o0]%asi
	inccc	%o3
	bl,pt	%ncc, 1b
	inc	%o0

	! Now address is double aligned
.blkalign:
	cmp	%o1, 0x80		! check if there are 128 bytes to set
	blu,pn	%ncc, .bzero_small
	mov	%o1, %o3

	sethi	%hi(use_hw_bzero), %o2
	ld	[%o2 + %lo(use_hw_bzero)], %o2
	tst	%o2
	bz	%ncc, .bzero_small
	mov	%o1, %o3

	rd	%asi, %o3
	wr	%g0, ASI_BLK_INIT_ST_QUAD_LDD_P, %asi
	cmp	%o3, ASI_P
	bne,a	%ncc, .algnblk
	wr	%g0, ASI_BLK_INIT_QUAD_LDD_AIUS, %asi

.algnblk:
	andcc	%o0, 0x3f, %o3		! is block aligned?
	bz,pt	%ncc, .bzero_blk
	sub	%o3, 0x40, %o3		! -(bytes till block aligned)
	add	%o1, %o3, %o1		! o1 is the remainder
	
	! Clear -(%o3) bytes till block aligned
1:
	stxa	%g0, [%o0]%asi
	addcc	%o3, 8, %o3
	bl,pt	%ncc, 1b
	add	%o0, 8, %o0

.bzero_blk:
	and	%o1, 0x3f, %o3		! calc bytes left after blk clear
	andn	%o1, 0x3f, %o4		! calc size of blocks in bytes

	cmp	%o4, 0x100		! 256 bytes or more
	blu,pn	%ncc, 3f
	nop

2:
	stxa	%g0, [%o0+0x0]%asi
	stxa	%g0, [%o0+0x40]%asi
	stxa	%g0, [%o0+0x80]%asi
	stxa	%g0, [%o0+0xc0]%asi

	stxa	%g0, [%o0+0x8]%asi
	stxa	%g0, [%o0+0x10]%asi
	stxa	%g0, [%o0+0x18]%asi
	stxa	%g0, [%o0+0x20]%asi
	stxa	%g0, [%o0+0x28]%asi
	stxa	%g0, [%o0+0x30]%asi
	stxa	%g0, [%o0+0x38]%asi

	stxa	%g0, [%o0+0x48]%asi
	stxa	%g0, [%o0+0x50]%asi
	stxa	%g0, [%o0+0x58]%asi
	stxa	%g0, [%o0+0x60]%asi
	stxa	%g0, [%o0+0x68]%asi
	stxa	%g0, [%o0+0x70]%asi
	stxa	%g0, [%o0+0x78]%asi

	stxa	%g0, [%o0+0x88]%asi
	stxa	%g0, [%o0+0x90]%asi
	stxa	%g0, [%o0+0x98]%asi
	stxa	%g0, [%o0+0xa0]%asi
	stxa	%g0, [%o0+0xa8]%asi
	stxa	%g0, [%o0+0xb0]%asi
	stxa	%g0, [%o0+0xb8]%asi

	stxa	%g0, [%o0+0xc8]%asi
	stxa	%g0, [%o0+0xd0]%asi
	stxa	%g0, [%o0+0xd8]%asi
	stxa	%g0, [%o0+0xe0]%asi
	stxa	%g0, [%o0+0xe8]%asi
	stxa	%g0, [%o0+0xf0]%asi
	stxa	%g0, [%o0+0xf8]%asi

	sub	%o4, 0x100, %o4
	cmp	%o4, 0x100
	bgu,pt	%ncc, 2b
	add	%o0, 0x100, %o0

3:
	! ... check if 64 bytes to set
	cmp	%o4, 0x40
	blu	%ncc, .bzero_blk_done
	nop

4:
	stxa	%g0, [%o0+0x0]%asi
	stxa	%g0, [%o0+0x8]%asi
	stxa	%g0, [%o0+0x10]%asi
	stxa	%g0, [%o0+0x18]%asi
	stxa	%g0, [%o0+0x20]%asi
	stxa	%g0, [%o0+0x28]%asi
	stxa	%g0, [%o0+0x30]%asi
	stxa	%g0, [%o0+0x38]%asi

	subcc	%o4, 0x40, %o4
	bgu,pt	%ncc, 3b
	add	%o0, 0x40, %o0

.bzero_blk_done:
	membar	#Sync
	!
	! Undo asi register setting.
	!
	rd	%asi, %o4
	wr	%g0, ASI_P, %asi
	cmp	%o4, ASI_BLK_INIT_ST_QUAD_LDD_P
	bne,a	%ncc, .bzero_small
	wr	%g0, ASI_USER, %asi

.bzero_small:
	! Set the remaining doubles
	subcc	%o3, 8, %o3		! Can we store any doubles?
	blu,pn	%ncc, .byteclr
	and	%o1, 7, %o1		! calc bytes left after doubles

.dbclr:
	stxa	%g0, [%o0]%asi		! Clear the doubles
	subcc	%o3, 8, %o3
	bgeu,pt	%ncc, .dbclr
	add	%o0, 8, %o0

	ba	.byteclr
	nop

.wdalign:			
	andcc	%o0, 3, %o3		! is add aligned on a word boundary
	bz,pn	%ncc, .wdclr
	andn	%o1, 3, %o3		! create word sized count in %o3

	dec	%o1			! decrement count
	stba	%g0, [%o0]%asi		! clear a byte
	ba	.wdalign
	inc	%o0			! next byte

.wdclr:
	sta	%g0, [%o0]%asi		! 4-byte clearing loop
	subcc	%o3, 4, %o3
	bnz,pt	%ncc, .wdclr
	inc	4, %o0

	and	%o1, 3, %o1		! leftover count, if any

.byteclr:
	! Set the leftover bytes
	brz	%o1, .bzero_exit
	nop

7:
	deccc	%o1			! byte clearing loop
	stba	%g0, [%o0]%asi
	bgu,pt	%ncc, 7b
	inc	%o0

.bzero_exit:
	!
	! We're just concerned with whether t_lofault was set
	! when we came in. We end up here from either kzero()
	! or bzero(). kzero() *always* sets a lofault handler.
	! It ors LOFAULT_SET into %o5 to indicate it has done
	! this even if the value of %o5 is otherwise zero.
	! bzero() sets a lofault handler *only* if one was
	! previously set. Accordingly we need to examine
	! %o5 and if it is non-zero be sure to clear LOFAULT_SET
	! before resetting the error handler.
	!
	tst	%o5
	bz	%ncc, 1f
	andn	%o5, LOFAULT_SET, %o5
	membar	#Sync				! sync error barrier
	stn	%o5, [THREAD_REG + T_LOFAULT]	! restore old t_lofault
1:
	retl
	clr	%o0			! return (0)

	SET_SIZE(bzero)
#endif	/* lint */
