xref: /minix3/common/lib/libc/arch/arm/string/strcpy_arm.S (revision 84d9c625bfea59e274550651111ae9edfdc40fbd)
1*84d9c625SLionel Sambuc/*-
2*84d9c625SLionel Sambuc * Copyright (c) 2013 The NetBSD Foundation, Inc.
3*84d9c625SLionel Sambuc * All rights reserved.
4*84d9c625SLionel Sambuc *
5*84d9c625SLionel Sambuc * This code is derived from software contributed to The NetBSD Foundation
6*84d9c625SLionel Sambuc * by Matt Thomas of 3am Software Foundry.
7*84d9c625SLionel Sambuc *
8*84d9c625SLionel Sambuc * Redistribution and use in source and binary forms, with or without
9*84d9c625SLionel Sambuc * modification, are permitted provided that the following conditions
10*84d9c625SLionel Sambuc * are met:
11*84d9c625SLionel Sambuc * 1. Redistributions of source code must retain the above copyright
12*84d9c625SLionel Sambuc *    notice, this list of conditions and the following disclaimer.
13*84d9c625SLionel Sambuc * 2. Redistributions in binary form must reproduce the above copyright
14*84d9c625SLionel Sambuc *    notice, this list of conditions and the following disclaimer in the
15*84d9c625SLionel Sambuc *    documentation and/or other materials provided with the distribution.
16*84d9c625SLionel Sambuc *
17*84d9c625SLionel Sambuc * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18*84d9c625SLionel Sambuc * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19*84d9c625SLionel Sambuc * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20*84d9c625SLionel Sambuc * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21*84d9c625SLionel Sambuc * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22*84d9c625SLionel Sambuc * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23*84d9c625SLionel Sambuc * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24*84d9c625SLionel Sambuc * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25*84d9c625SLionel Sambuc * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26*84d9c625SLionel Sambuc * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27*84d9c625SLionel Sambuc * POSSIBILITY OF SUCH DAMAGE.
28*84d9c625SLionel Sambuc */
29*84d9c625SLionel Sambuc
30*84d9c625SLionel Sambuc#include <machine/asm.h>
31*84d9c625SLionel Sambuc
32*84d9c625SLionel SambucRCSID("$NetBSD: strcpy_arm.S,v 1.3 2013/08/11 04:56:32 matt Exp $")
33*84d9c625SLionel Sambuc
34*84d9c625SLionel Sambuc#ifdef STRLCPY
35*84d9c625SLionel Sambuc#ifdef _LIBC
36*84d9c625SLionel SambucWEAK_ALIAS(strlcpy, _strlcpy)
37*84d9c625SLionel Sambuc#endif
38*84d9c625SLionel Sambuc#define	FUNCNAME	strlcpy
39*84d9c625SLionel Sambuc#elif defined(STRNCPY)
40*84d9c625SLionel Sambuc#define	FUNCNAME	strncpy
41*84d9c625SLionel Sambuc#else
42*84d9c625SLionel Sambuc#define	FUNCNAME	strcpy
43*84d9c625SLionel Sambuc#endif
44*84d9c625SLionel Sambuc
45*84d9c625SLionel Sambuc#ifdef _LIBC
46*84d9c625SLionel Sambuc#include "namespace.h"
47*84d9c625SLionel Sambuc#endif
48*84d9c625SLionel Sambuc
49*84d9c625SLionel Sambuc#ifdef __ARMEL__
50*84d9c625SLionel Sambuc#define	lslo	lsr		/* shift to lower address */
51*84d9c625SLionel Sambuc#define	lshi	lsl		/* shift to higher address */
52*84d9c625SLionel Sambuc#define	BYTE0	0x000000ff
53*84d9c625SLionel Sambuc#define	BYTE1	0x0000ff00
54*84d9c625SLionel Sambuc#define	BYTE2	0x00ff0000
55*84d9c625SLionel Sambuc#define	BYTE3	0xff000000
56*84d9c625SLionel Sambuc#else
57*84d9c625SLionel Sambuc#define	lslo	lsl		/* shift to lower address */
58*84d9c625SLionel Sambuc#define	lshi	lsr		/* shift to higher address */
59*84d9c625SLionel Sambuc#define	BYTE0	0xff000000
60*84d9c625SLionel Sambuc#define	BYTE1	0x00ff0000
61*84d9c625SLionel Sambuc#define	BYTE2	0x0000ff00
62*84d9c625SLionel Sambuc#define	BYTE3	0x000000ff
63*84d9c625SLionel Sambuc#endif
64*84d9c625SLionel Sambuc
65*84d9c625SLionel Sambuc/*
66*84d9c625SLionel Sambuc * On armv6 and later, to quickly determine if a word contains a NUL (0) byte,
67*84d9c625SLionel Sambuc * we add 254 to each byte using the UQADD8 (unsigned saturating add 8)
68*84d9c625SLionel Sambuc * instruction.  For every non-NUL byte, the result for that byte will become
69*84d9c625SLionel Sambuc * 255.  For NUL, it will be 254.  When we complement the result of all 4 adds,
70*84d9c625SLionel Sambuc * if the result is non-0 then we must have encountered a NUL.
71*84d9c625SLionel Sambuc *
72*84d9c625SLionel Sambuc * For earlier architecture, we just use tst on all 4 bytes.  There are other
73*84d9c625SLionel Sambuc * algorithms to detect NULs but they take longer and use more instructions.
74*84d9c625SLionel Sambuc */
75*84d9c625SLionel Sambuc
76*84d9c625SLionel Sambuc/*
77*84d9c625SLionel Sambuc * char *strcpy(char *dst, const char *src);
78*84d9c625SLionel Sambuc * char *strncpy(char *dst, const char *src, size_t len);
79*84d9c625SLionel Sambuc * size_t strlcpy(char *dst, const char *src, size_t len);
80*84d9c625SLionel Sambuc */
81*84d9c625SLionel Sambuc
82*84d9c625SLionel Sambuc	.text
83*84d9c625SLionel SambucENTRY(FUNCNAME)
84*84d9c625SLionel Sambuc#if defined(STRLCPY)
85*84d9c625SLionel Sambuc	cmp	r2, #1			/* is length 1 or less? */
86*84d9c625SLionel Sambuc	bhi	1f			/*   no, do normal */
87*84d9c625SLionel Sambuc	moveq	r3, #0			/*   = 1? load NUL */
88*84d9c625SLionel Sambuc	strbeq	r3, [r0]		/*   = 1? write NUL to dst */
89*84d9c625SLionel Sambuc	mov	r0, r1			/* move src to r0 */
90*84d9c625SLionel Sambuc	b	PLT_SYM(_C_LABEL(strlen)) /* and tailcall strlen */
91*84d9c625SLionel Sambuc1:
92*84d9c625SLionel Sambuc	sub	r2, r2, #1		/* leave one byte for NUL */
93*84d9c625SLionel Sambuc#endif
94*84d9c625SLionel Sambuc#if defined(STRNCPY)
95*84d9c625SLionel Sambuc	cmp	r2, #0			/* 0 length? */
96*84d9c625SLionel Sambuc	RETc(eq)			/*   yes, just return */
97*84d9c625SLionel Sambuc#endif
98*84d9c625SLionel Sambuc	push	{r4-r9}			/* save some registers */
99*84d9c625SLionel Sambuc#ifdef _ARM_ARCH_6
100*84d9c625SLionel Sambuc#ifdef _ARM_ARCH_7
101*84d9c625SLionel Sambuc	movw	r7, #0xfefe		/* magic constant; 254 in each byte */
102*84d9c625SLionel Sambuc#else
103*84d9c625SLionel Sambuc	mov	r7, #0xfe		/* put 254 in low byte */
104*84d9c625SLionel Sambuc	orr	r7, r7, r7, lsl #8	/* move to next byte */
105*84d9c625SLionel Sambuc#endif
106*84d9c625SLionel Sambuc	orr	r7, r7, r7, lsl #16	/* move to next halfword */
107*84d9c625SLionel Sambuc#endif
108*84d9c625SLionel Sambuc
109*84d9c625SLionel Sambuc#if defined(STRLCPY)
110*84d9c625SLionel Sambuc	add	r6, r1, #1		/* save for return (deal with NUL) */
111*84d9c625SLionel Sambuc#else
112*84d9c625SLionel Sambuc	mov	r6, r0			/* save for return */
113*84d9c625SLionel Sambuc#endif
114*84d9c625SLionel Sambuc
115*84d9c625SLionel Sambuc.Ldst_align:
116*84d9c625SLionel Sambuc	tst	r0, #3			/* check for dst alignment */
117*84d9c625SLionel Sambuc	beq	.Ldst_aligned		/*   ok, proceed to next check */
118*84d9c625SLionel Sambuc	ldrb	r5, [r1], #1		/* load a byte */
119*84d9c625SLionel Sambuc#if defined(STRNCPY)
120*84d9c625SLionel Sambuc	subs	r2, r2, #1		/* subtract out from count */
121*84d9c625SLionel Sambuc	bmi	.Ldst_full		/*   zero? the dst has no more room */
122*84d9c625SLionel Sambuc#endif
123*84d9c625SLionel Sambuc	strb	r5, [r0], #1		/* store a byte */
124*84d9c625SLionel Sambuc	teq	r5, #0			/* was it a NUL? */
125*84d9c625SLionel Sambuc	beq	.Lend_of_string		/*   yes, we are done */
126*84d9c625SLionel Sambuc#if defined(STRLCPY)
127*84d9c625SLionel Sambuc	subs	r2, r2, #1		/* subtract one from count */
128*84d9c625SLionel Sambuc	strbeq	r2, [r0], #1		/*    zero? write trailing NUL */
129*84d9c625SLionel Sambuc	beq	.Ldst_full		/*    zero? the dst has no more room */
130*84d9c625SLionel Sambuc#endif
131*84d9c625SLionel Sambuc	b	.Ldst_align		/* loop around for next byte */
132*84d9c625SLionel Sambuc.Ldst_aligned:
133*84d9c625SLionel Sambuc	tst	r1, #3			/* get the misalignment of src */
134*84d9c625SLionel Sambuc	bne	.Lincongruent		/*  !=? incongruent (slower) */
135*84d9c625SLionel Sambuc
136*84d9c625SLionel Sambuc	/*   =?   congruent (faster) */
137*84d9c625SLionel Sambuc
138*84d9c625SLionel Sambuc.Lcongruent:
139*84d9c625SLionel Sambuc#if defined(STRLCPY)
140*84d9c625SLionel Sambuc	add	r6, r6, #3		/* compensate for word post-inc */
141*84d9c625SLionel Sambuc#endif
142*84d9c625SLionel Sambuc	b	.Lcongruent_mainloop_load
143*84d9c625SLionel Sambuc.Lcongruent_mainloop:
144*84d9c625SLionel Sambuc#if defined(STRLCPY) || defined(STRNCPY)
145*84d9c625SLionel Sambuc	subs	r2, r2, #4		/* subtract 4 from the count */
146*84d9c625SLionel Sambuc	bmi	.Lno_more_room
147*84d9c625SLionel Sambuc#endif
148*84d9c625SLionel Sambuc	str	r5, [r0], #4		/* store word into dst */
149*84d9c625SLionel Sambuc#if defined(STRLCPY)
150*84d9c625SLionel Sambuc	beq	.Lno_more_room		/*   count is 0? no room in dst */
151*84d9c625SLionel Sambuc#endif
152*84d9c625SLionel Sambuc#if defined(STRNCPY)
153*84d9c625SLionel Sambuc	beq	.Ldst_full_word_aligned	/*   count is 0? no room in dst */
154*84d9c625SLionel Sambuc#endif
155*84d9c625SLionel Sambuc.Lcongruent_mainloop_load:
156*84d9c625SLionel Sambuc	ldr	r5, [r1], #4		/* load word from source */
157*84d9c625SLionel Sambuc#if defined(_ARM_ARCH_6)
158*84d9c625SLionel Sambuc	uqadd8	r3, r5, r7		/* magic happens here */
159*84d9c625SLionel Sambuc	mvns	r3, r3			/* is the complemented result 0? */
160*84d9c625SLionel Sambuc	beq	.Lcongruent_mainloop	/*    yes, no NULs, do it again */
161*84d9c625SLionel Sambuc#else
162*84d9c625SLionel Sambuc	tst	r5, #BYTE0		/* does byte 0 contain a NUL? */
163*84d9c625SLionel Sambuc	tstne	r5, #BYTE1		/*   no, does byte 1 contain a NUL? */
164*84d9c625SLionel Sambuc	tstne	r5, #BYTE2		/*   no, does byte 2 contain a NUL? */
165*84d9c625SLionel Sambuc	tstne	r5, #BYTE3		/*   no, does byte 3 contain a NUL? */
166*84d9c625SLionel Sambuc	bne	.Lcongruent_mainloop	/*    yes, no NULs, do it again */
167*84d9c625SLionel Sambuc#endif
168*84d9c625SLionel Sambuc#if defined(STRLCPY) && 0
169*84d9c625SLionel Sambuc	sub	r1, r1, #3		/* back up src pointer */
170*84d9c625SLionel Sambuc#endif
171*84d9c625SLionel Sambuc#if defined(_ARM_ARCH_6)
172*84d9c625SLionel Sambuc#ifdef __ARMEL__
173*84d9c625SLionel Sambuc	rev	r3, r3			/* CLZ needs BE data */
174*84d9c625SLionel Sambuc#endif
175*84d9c625SLionel Sambuc	clz	r3, r3			/* count leading zeros */
176*84d9c625SLionel Sambuc#else
177*84d9c625SLionel Sambuc	mov	r3, #0			/* assume NUL is in byte 0 */
178*84d9c625SLionel Sambuc	tst	r5, #BYTE0		/* is NUL in byte 2? */
179*84d9c625SLionel Sambuc	beq	.Lcongruent_last_bytes	/*   yes, done searching. */
180*84d9c625SLionel Sambuc	mov	r3, #8			/* assume NUL is in byte 1 */
181*84d9c625SLionel Sambuc	tst	r5, #BYTE1		/* is NUL in byte 2? */
182*84d9c625SLionel Sambuc	beq	.Lcongruent_last_bytes	/*   yes, done searching. */
183*84d9c625SLionel Sambuc	mov	r3, #16			/* assume NUL is in byte 2 */
184*84d9c625SLionel Sambuc	tst	r5, #BYTE2		/* is NUL in byte 2? */
185*84d9c625SLionel Sambuc#if !defined(STRLCPY)
186*84d9c625SLionel Sambuc	beq	.Lcongruent_last_bytes	/*   yes, done searching. */
187*84d9c625SLionel Sambuc	mov	r3, #24			/* NUL must be in byte 3 */
188*84d9c625SLionel Sambuc#else
189*84d9c625SLionel Sambuc	movne	r3, #24			/*    no, then NUL is in byte 3 */
190*84d9c625SLionel Sambuc#endif
191*84d9c625SLionel Sambuc#endif /* _ARM_ARCH_6 */
192*84d9c625SLionel Sambuc#if defined(STRLCPY)
193*84d9c625SLionel Sambuc.Lcongruent_last_bytes:
194*84d9c625SLionel Sambuc#endif
195*84d9c625SLionel Sambuc#if defined(STRLCPY)
196*84d9c625SLionel Sambuc	add	r1, r1, r3, lsr #3	/* position to point at NUL + 4 */
197*84d9c625SLionel Sambuc#endif
198*84d9c625SLionel Sambuc	b	.Llast_bytes		/* store the last bytes */
199*84d9c625SLionel Sambuc
200*84d9c625SLionel Sambuc
201*84d9c625SLionel Sambuc.Lincongruent:
202*84d9c625SLionel Sambuc	/*
203*84d9c625SLionel Sambuc	 * At this point dst is word aligned by src is not.  Read bytes
204*84d9c625SLionel Sambuc	 * from src until it is read aligned.
205*84d9c625SLionel Sambuc	 */
206*84d9c625SLionel Sambuc	and	r3, r1, #3		/* extract misalignment */
207*84d9c625SLionel Sambuc	mov	r9, r3, lsl #3		/* calculate discard shift */
208*84d9c625SLionel Sambuc	rsb	r8, r9, #32		/* calculate insertion shift */
209*84d9c625SLionel Sambuc#if defined(STRLCPY)
210*84d9c625SLionel Sambuc	add	r6, r6, #3		/* compensate for word post-inc */
211*84d9c625SLionel Sambuc#endif
212*84d9c625SLionel Sambuc	bic	r1, r1, #3		/* word align src */
213*84d9c625SLionel Sambuc	ldr	r5, [r1], #4		/* load word frm src */
214*84d9c625SLionel Sambuc	mov	r4, r5, lslo r9		/* discard lo bytes from src */
215*84d9c625SLionel Sambuc	tst	r4, #BYTE0		/* does byte 0 contain a NUL? */
216*84d9c625SLionel Sambuc#if defined(STRNCPY)
217*84d9c625SLionel Sambuc	beq	.Lend_of_string		/*   yes, zero fill rest of string */
218*84d9c625SLionel Sambuc#else
219*84d9c625SLionel Sambuc	moveq	r3, r9			/*   yes, set offset */
220*84d9c625SLionel Sambuc	beq	.Lincongruent_end_of_string /*   yes, deal with the last bytes */
221*84d9c625SLionel Sambuc#endif
222*84d9c625SLionel Sambuc	/*
223*84d9c625SLionel Sambuc	 * To make our test for NULs below do not generate false positives,
224*84d9c625SLionel Sambuc	 * fill the bytes in the word we don't want to match with all 1s.
225*84d9c625SLionel Sambuc	 */
226*84d9c625SLionel Sambuc	mvn	r3, #0			/* create a mask */
227*84d9c625SLionel Sambuc	mov	r3, r3, lslo r8		/* zero out bytes being kept */
228*84d9c625SLionel Sambuc	orr	r5, r5, r3		/* merge src and mask */
229*84d9c625SLionel Sambuc#ifdef _ARM_ARCH_6
230*84d9c625SLionel Sambuc	uqadd8	r3, r5, r7		/* NUL detection magic happens */
231*84d9c625SLionel Sambuc	mvns	r3, r3			/* is the complemented result 0? */
232*84d9c625SLionel Sambuc	beq	.Lincongruent_mainloop_load /*   yes, no NUL encountered! */
233*84d9c625SLionel Sambuc#ifdef __ARMEL__
234*84d9c625SLionel Sambuc	rev	r3, r3			/* CLZ wants BE input */
235*84d9c625SLionel Sambuc#endif
236*84d9c625SLionel Sambuc	clz	r3, r3			/* count leading zeros */
237*84d9c625SLionel Sambuc#else
238*84d9c625SLionel Sambuc	/*
239*84d9c625SLionel Sambuc	 * We already tested for byte 0 above so we don't need to it again.
240*84d9c625SLionel Sambuc	 */
241*84d9c625SLionel Sambuc	mov	r3, #24			/* assume NUL is in byte 3 */
242*84d9c625SLionel Sambuc	tst	r5, #BYTE1		/* did we find a NUL in byte 1? */
243*84d9c625SLionel Sambuc	subeq	r3, r3, #8		/*   yes, decremnt byte position */
244*84d9c625SLionel Sambuc	tstne	r5, #BYTE2		/*   no, did we find a NUL in byte 2? */
245*84d9c625SLionel Sambuc	subeq	r3, r3, #8		/*   yes, decremnt byte position */
246*84d9c625SLionel Sambuc	tstne	r5, #BYTE3		/*   no, did we find a NUL in byte 3? */
247*84d9c625SLionel Sambuc	bne	.Lincongruent_mainloop_load /*   no, no NUL encountered! */
248*84d9c625SLionel Sambuc#endif
249*84d9c625SLionel Sambuc	mov	r5, r4			/* discard already dealt with bytes */
250*84d9c625SLionel Sambuc.Lincongruent_end_of_string:
251*84d9c625SLionel Sambuc#if defined(STRLCPY)
252*84d9c625SLionel Sambuc	add	r1, r1, r3, lsr #3	/* then add offset to NUL */
253*84d9c625SLionel Sambuc#endif
254*84d9c625SLionel Sambuc	sub	r3, r3, r9		/* adjust NUL offset */
255*84d9c625SLionel Sambuc	b	.Llast_bytes		/* NUL encountered! finish up */
256*84d9c625SLionel Sambuc
257*84d9c625SLionel Sambuc#if defined(STRLCPY) || defined(STRNCPY)
258*84d9c625SLionel Sambuc.Lincongruent_no_more_room:
259*84d9c625SLionel Sambuc	mov	r5, r4			/* move data to be stored to r5 */
260*84d9c625SLionel Sambuc	b	.Lno_more_room		/* fill remaining space */
261*84d9c625SLionel Sambuc#endif /* STRLCPY || STRNCPY */
262*84d9c625SLionel Sambuc
263*84d9c625SLionel Sambuc	/*
264*84d9c625SLionel Sambuc	 * At this point both dst and src are word aligned and r4 contains
265*84d9c625SLionel Sambuc	 * partial contents from src.
266*84d9c625SLionel Sambuc	 */
267*84d9c625SLionel Sambuc.Lincongruent_mainloop:
268*84d9c625SLionel Sambuc	orr	r4, r4, r5, lshi r8	/* put new src data into dst word */
269*84d9c625SLionel Sambuc#if defined(STRLCPY) || defined(STRNCPY)
270*84d9c625SLionel Sambuc	subs	r2, r2, #4		/* subtract 4 from count */
271*84d9c625SLionel Sambuc	bmi	.Lincongruent_no_more_room /*   count < 0? dst will be full */
272*84d9c625SLionel Sambuc#endif
273*84d9c625SLionel Sambuc	str	r4, [r0], #4		/* store word in dst */
274*84d9c625SLionel Sambuc#if defined(STRLCPY)
275*84d9c625SLionel Sambuc	beq	.Lno_more_room		/*   space left is 0? stop copy */
276*84d9c625SLionel Sambuc#endif
277*84d9c625SLionel Sambuc#if defined(STRNCPY)
278*84d9c625SLionel Sambuc	beq	.Ldst_full_word_aligned	/*   space left is 0? stop copy */
279*84d9c625SLionel Sambuc#endif
280*84d9c625SLionel Sambuc	mov	r4, r5, lslo r9		/* move rest of src into dst word */
281*84d9c625SLionel Sambuc.Lincongruent_mainloop_load:
282*84d9c625SLionel Sambuc	ldr	r5, [r1], #4		/* read src */
283*84d9c625SLionel Sambuc#ifdef _ARM_ARCH_6
284*84d9c625SLionel Sambuc	uqadd8	r3, r5, r7		/* magic happens here */
285*84d9c625SLionel Sambuc	mvns	r3, r3			/* is the complemented result 0? */
286*84d9c625SLionel Sambuc	beq	.Lincongruent_mainloop	/*   yes, no NUL encountered! */
287*84d9c625SLionel Sambuc	/*
288*84d9c625SLionel Sambuc	 * fall into this since we encountered a NULL.  At this point we have
289*84d9c625SLionel Sambuc	 * from 1-5 bytes (excluding trailing NUL) to write.
290*84d9c625SLionel Sambuc	 */
291*84d9c625SLionel Sambuc#ifdef __ARMEL__
292*84d9c625SLionel Sambuc	rev	r3, r3			/* CLZ works on BE data */
293*84d9c625SLionel Sambuc#endif
294*84d9c625SLionel Sambuc	clz	r3, r3			/* count leading zeroes */
295*84d9c625SLionel Sambuc#else
296*84d9c625SLionel Sambuc	tst	r5, #BYTE0		/* does byte 0 contain a NUL? */
297*84d9c625SLionel Sambuc	tstne	r5, #BYTE1		/*   no, does byte 1 contain a NUL? */
298*84d9c625SLionel Sambuc	tstne	r5, #BYTE2		/*   no, does byte 2 contain a NUL? */
299*84d9c625SLionel Sambuc	tstne	r5, #BYTE3		/*   no, does byte 3 contain a NUL? */
300*84d9c625SLionel Sambuc	bne	.Lincongruent_mainloop	/*   no, no NUL encountered! */
301*84d9c625SLionel Sambuc	/*
302*84d9c625SLionel Sambuc	 * fall into this since we encountered a NULL.  At this point we have
303*84d9c625SLionel Sambuc	 * from 1-5 bytes (excluding trailing NUL) to write.
304*84d9c625SLionel Sambuc	 */
305*84d9c625SLionel Sambuc	mov	r3, #0			/* assume a NUL is in byte 0 */
306*84d9c625SLionel Sambuc	tst	r5, #BYTE0		/* is there a NUL in byte 0? */
307*84d9c625SLionel Sambuc	beq	1f			/*   yes, found a NUL! */
308*84d9c625SLionel Sambuc	mov	r3, #8			/* assume a NUL is in byte 1 */
309*84d9c625SLionel Sambuc	tst	r5, #BYTE1		/* is there a NUL in byte 0? */
310*84d9c625SLionel Sambuc	beq	1f			/*   yes, found a NUL! */
311*84d9c625SLionel Sambuc	tst	r5, #BYTE2		/* is there a NUL in byte 2? */
312*84d9c625SLionel Sambuc	moveq	r3, #16			/*   yes, mark its position */
313*84d9c625SLionel Sambuc	movne	r3, #24			/*   no, it must be in byte 3 */
314*84d9c625SLionel Sambuc1:
315*84d9c625SLionel Sambuc#endif
316*84d9c625SLionel Sambuc	orr	r4, r4, r5, lshi r8	/* merge new and old src words */
317*84d9c625SLionel Sambuc#if defined(STRLCPY)
318*84d9c625SLionel Sambuc	add	r1, r1, r3, lsr #3	/* adjust src to point to NUL */
319*84d9c625SLionel Sambuc#endif
320*84d9c625SLionel Sambuc	add	r3, r3, r8		/* add remainder bytes worth */
321*84d9c625SLionel Sambuc	cmp	r3, #32			/* do we have at least one word to write? */
322*84d9c625SLionel Sambuc	movlt	r5, r4			/*   no, move source bytes to expected reg */
323*84d9c625SLionel Sambuc	blt	.Llast_bytes		/*   no, deal with them */
324*84d9c625SLionel Sambuc#if defined(STRLCPY)
325*84d9c625SLionel Sambuc	subs	r2, r2, #4		/* subtract 4 from count */
326*84d9c625SLionel Sambuc	bpl	1f			/*   we have space for at least 4 */
327*84d9c625SLionel Sambuc	/*
328*84d9c625SLionel Sambuc	 * Since the space just went minus, we don't have enough room to
329*84d9c625SLionel Sambuc	 * write all 4 bytes.  In fact, the most we can write is 3 so just
330*84d9c625SLionel Sambuc	 * just lie and say we have 3 bytes to write and discard the rest.
331*84d9c625SLionel Sambuc	 */
332*84d9c625SLionel Sambuc	add	r2, r2, #4		/* add 4 back */
333*84d9c625SLionel Sambuc	mov	r3, #24			/* say we have 3 bytes */
334*84d9c625SLionel Sambuc	mov	r5, r4			/* discard the bytes we can't store */
335*84d9c625SLionel Sambuc	b	.Llast_bytes		/* and treat this as our last word */
336*84d9c625SLionel Sambuc1:
337*84d9c625SLionel Sambuc#elif defined(STRNCPY)
338*84d9c625SLionel Sambuc	subs	r2, r2, #4		/* subtract 4 from count */
339*84d9c625SLionel Sambuc	bmi	.Lincongruent_no_more_room /*   count < 0? dst will be full */
340*84d9c625SLionel Sambuc#endif
341*84d9c625SLionel Sambuc	str	r4, [r0], #4		/* store dst word */
342*84d9c625SLionel Sambuc#if defined(STRNCPY)
343*84d9c625SLionel Sambuc	beq	.Ldst_full_word_aligned	/*   space left is 0? stop copy */
344*84d9c625SLionel Sambuc#endif
345*84d9c625SLionel Sambuc#if defined(STRLCPY)
346*84d9c625SLionel Sambuc	bne	1f			/* we still have space remaining */
347*84d9c625SLionel Sambuc	strb	r2, [r0]		/* write final NUL */
348*84d9c625SLionel Sambuc	b	.Lend_of_string		/* we are done */
349*84d9c625SLionel Sambuc1:
350*84d9c625SLionel Sambuc#endif
351*84d9c625SLionel Sambuc	/*
352*84d9c625SLionel Sambuc	 * Subtract the 32 bits just written from the number of bits left
353*84d9c625SLionel Sambuc	 * to write.  If 0 bits are left and not doing strncpy, just write
354*84d9c625SLionel Sambuc	 * the trailing NUL and be done.
355*84d9c625SLionel Sambuc	 */
356*84d9c625SLionel Sambuc	subs	r3, r3, #32		/* we wrote one word */
357*84d9c625SLionel Sambuc#if !defined(STRNCPY)
358*84d9c625SLionel Sambuc	bne	1f			/* no more data? */
359*84d9c625SLionel Sambuc	strb	r3, [r0]		/* write final NUL */
360*84d9c625SLionel Sambuc	b	.Lend_of_string		/* we are done */
361*84d9c625SLionel Sambuc1:
362*84d9c625SLionel Sambuc#endif
363*84d9c625SLionel Sambuc	/*
364*84d9c625SLionel Sambuc	 * At this point after writing 4 bytes, we have 0 or 1 bytes left to
365*84d9c625SLionel Sambuc	 * write (excluding the trailing NUL).
366*84d9c625SLionel Sambuc	 */
367*84d9c625SLionel Sambuc	mov	r5, r5, lslo r9		/* get remainder of src */
368*84d9c625SLionel Sambuc
369*84d9c625SLionel Sambuc	/* fall into .Llast_bytes */
370*84d9c625SLionel Sambuc
371*84d9c625SLionel Sambuc#if !defined(STRLCPY)
372*84d9c625SLionel Sambuc.Lcongruent_last_bytes:
373*84d9c625SLionel Sambuc#endif
374*84d9c625SLionel Sambuc.Llast_bytes:
375*84d9c625SLionel Sambuc	/*
376*84d9c625SLionel Sambuc	 * r5 contains the last word and is in host byte order.
377*84d9c625SLionel Sambuc	 * r3 contains number of bits left to copy (0..31).
378*84d9c625SLionel Sambuc	 * r1 should point to the NUL + 4.
379*84d9c625SLionel Sambuc	 */
380*84d9c625SLionel Sambuc	bics	ip, r3, #7		/* truncate bits, is result 0? */
381*84d9c625SLionel Sambuc#if !defined(STRNCPY)
382*84d9c625SLionel Sambuc	bne	1f			/*   no, have to write some bytes */
383*84d9c625SLionel Sambuc	strb	ip, [r0]		/*   yes, write trailing NUL */
384*84d9c625SLionel Sambuc	b	.Lend_of_string		/*   yes, and we are the end */
385*84d9c625SLionel Sambuc1:
386*84d9c625SLionel Sambuc#endif
387*84d9c625SLionel Sambuc#if defined(STRLCPY) || defined(STRNCPY)
388*84d9c625SLionel Sambuc	cmp	r2, ip, lsr #3		/* is there enough room? */
389*84d9c625SLionel Sambuc	movlt	ip, r2, lsl #3		/*   no, only fill remaining space */
390*84d9c625SLionel Sambuc#endif
391*84d9c625SLionel Sambuc	mvn	r3, #0			/* create a mask */
392*84d9c625SLionel Sambuc	mov	r3, r3, lshi ip		/* clear leading bytes */
393*84d9c625SLionel Sambuc	bic	r5, r5, r3		/* clear trailing bytes */
394*84d9c625SLionel Sambuc#if defined(STRNCPY)
395*84d9c625SLionel Sambuc	cmp	r2, #4			/* room for 4 bytes? */
396*84d9c625SLionel Sambuc	movge	ip, #32			/*   yes, we will write 4 bytes */
397*84d9c625SLionel Sambuc	bge	2f			/*   yes, and go do it */
398*84d9c625SLionel Sambuc	mvn	r3, #0			/* create a mask (again) */
399*84d9c625SLionel Sambuc	mov	ip, r2, lsl #3		/* remaining space bytes -> bits */
400*84d9c625SLionel Sambuc	mov	r3, r3, lshi ip		/* clear remaining bytes */
401*84d9c625SLionel Sambuc#elif defined(STRLCPY)
402*84d9c625SLionel Sambuc	cmp	r2, #3			/* do we have room for 3 bytes & NUL? */
403*84d9c625SLionel Sambuc	bge	2f			/*   yes, just clear out dst */
404*84d9c625SLionel Sambuc	mov	r3, r3, lshi #8		/* mask out trailing NUL */
405*84d9c625SLionel Sambuc#else
406*84d9c625SLionel Sambuc	cmp	ip, #24			/* are we writing 3 bytes & a NUL? */
407*84d9c625SLionel Sambuc	bge	2f			/*   yes, just overwrite dst */
408*84d9c625SLionel Sambuc	mov	r3, r3, lshi #8		/* mask out trailing NUL */
409*84d9c625SLionel Sambuc#endif /* !STRNCPY */
410*84d9c625SLionel Sambuc	ldr	r4, [r0]		/* fetch dst word */
411*84d9c625SLionel Sambuc	and	r4, r4, r3		/* preserve trailing bytes */
412*84d9c625SLionel Sambuc	orr	r5, r5, r4		/* merge dst with src */
413*84d9c625SLionel Sambuc2:	str	r5, [r0], #4		/* store last word */
414*84d9c625SLionel Sambuc#if defined(STRNCPY)
415*84d9c625SLionel Sambuc	subs	r2, r2, ip, lsr #3	/* subtract bytes cleared from count */
416*84d9c625SLionel Sambuc	beq	.Ldst_full_word_aligned
417*84d9c625SLionel Sambuc#endif
418*84d9c625SLionel Sambuc	b	.Lend_of_string
419*84d9c625SLionel Sambuc
420*84d9c625SLionel Sambuc#if defined(STRLCPY) || defined(STRNCPY)
421*84d9c625SLionel Sambuc.Lno_more_room:
422*84d9c625SLionel Sambuc#if defined(STRLCPY)
423*84d9c625SLionel Sambuc	cmp	r2, #-1			/* tried to write 3 bytes? */
424*84d9c625SLionel Sambuc	blt	1f			/*   less, partial word write */
425*84d9c625SLionel Sambuc	cmp	r2, #0			/* no space left? */
426*84d9c625SLionel Sambuc	strbeq	r2, [r0]		/* write the final NUL */
427*84d9c625SLionel Sambuc	bicne	r5, r5, #BYTE3		/* clear trailing NUL */
428*84d9c625SLionel Sambuc	strne	r5, [r0]		/* write last word */
429*84d9c625SLionel Sambuc	b	.Ldst_full_word_aligned	/* the dst buffer is full */
430*84d9c625SLionel Sambuc1:
431*84d9c625SLionel Sambuc#endif /* STRLCPY */
432*84d9c625SLionel Sambuc	add	r2, r2, #4		/* restore remaining space */
433*84d9c625SLionel Sambuc	ldr	r4, [r0]		/* load dst */
434*84d9c625SLionel Sambuc	mvn	r3, #0			/* create a mask */
435*84d9c625SLionel Sambuc	mov	r2, r2, lsl #3		/* bytes -> bits */
436*84d9c625SLionel Sambuc	mov	r3, r3, lshi r2		/* clear leading bytes */
437*84d9c625SLionel Sambuc	bic	r5, r5, r3		/* clear trailing bytes from src */
438*84d9c625SLionel Sambuc#if defined(STRLCPY)
439*84d9c625SLionel Sambuc	mov	r3, r3, lshi #8		/* mask out trailing NUL */
440*84d9c625SLionel Sambuc#endif /* STRLCPY */
441*84d9c625SLionel Sambuc	and	r4, r4, r3		/* preserve trailing bytes in dst */
442*84d9c625SLionel Sambuc	orr	r4, r4, r5		/* merge src with dst */
443*84d9c625SLionel Sambuc	str	r4, [r0], #4		/* write last word */
444*84d9c625SLionel Sambuc	b	.Ldst_full_word_aligned
445*84d9c625SLionel Sambuc#endif /* STRLCPY || STRNCPY */
446*84d9c625SLionel Sambuc
447*84d9c625SLionel Sambuc#if defined(STRLCPY)
448*84d9c625SLionel Sambuc	/*
449*84d9c625SLionel Sambuc	 * Destination was filled (and NUL terminated).
450*84d9c625SLionel Sambuc	 * All that's left is count the number of bytes left in src.
451*84d9c625SLionel Sambuc	 */
452*84d9c625SLionel Sambuc.Ldst_full:
453*84d9c625SLionel Sambuc1:	tst	r1, #3			/* dst word aligned? */
454*84d9c625SLionel Sambuc	beq	2f			/*   yes, so do it word by word */
455*84d9c625SLionel Sambuc	ldrb	r5, [r1], #1		/* load next byte */
456*84d9c625SLionel Sambuc	teq	r5, #0			/* is it a NUL? */
457*84d9c625SLionel Sambuc	bne	1b			/*   no, check alignment */
458*84d9c625SLionel Sambuc	b	.Lend_of_string		/* and return */
459*84d9c625SLionel Sambuc2:	add	r6, r6, #3		/* compensate for post-inc */
460*84d9c625SLionel Sambuc.Ldst_full_word_aligned:
461*84d9c625SLionel Sambuc3:	ldr	r5, [r1], #4		/* load word from src */
462*84d9c625SLionel Sambuc#ifdef _ARM_ARCH_6
463*84d9c625SLionel Sambuc	uqadd8	r5, r5, r7		/* perform NUL magic */
464*84d9c625SLionel Sambuc	mvns	r5, r5			/* complement all 0s? */
465*84d9c625SLionel Sambuc	beq	3b			/*   yes, no NUL so get next word */
466*84d9c625SLionel Sambuc#else
467*84d9c625SLionel Sambuc	tst	r5, #BYTE0		/* does byte 0 contain a NUL? */
468*84d9c625SLionel Sambuc	tstne	r5, #BYTE1		/*   no, does byte 1 contain a NUL? */
469*84d9c625SLionel Sambuc	tstne	r5, #BYTE2		/*   no, does byte 2 contain a NUL? */
470*84d9c625SLionel Sambuc	tstne	r5, #BYTE3		/*   no, does byte 3 contain a NUL? */
471*84d9c625SLionel Sambuc	bne	3b			/*   no, no NUL encountered! */
472*84d9c625SLionel Sambuc#endif
473*84d9c625SLionel Sambuc#ifdef _ARM_ARCH_6
474*84d9c625SLionel Sambuc#ifdef __ARMEL__
475*84d9c625SLionel Sambuc	rev	r5, r5			/* CLZ needs BE data */
476*84d9c625SLionel Sambuc#endif
477*84d9c625SLionel Sambuc	clz	r5, r5			/* count leading zeros */
478*84d9c625SLionel Sambuc	add	r1, r1, r5, lsr #3	/* add offset to NUL to src pointer */
479*84d9c625SLionel Sambuc#else
480*84d9c625SLionel Sambuc	tst	r5, #BYTE0		/* is there a NUL in byte 0? */
481*84d9c625SLionel Sambuc	beq	4f			/*   yes, don't check any further */
482*84d9c625SLionel Sambuc	add	r1, r1, #1		/*   no, advance src pointer by 1 */
483*84d9c625SLionel Sambuc	tst	r5, #BYTE1		/* is there a NUL in byte 1? */
484*84d9c625SLionel Sambuc	beq	4f			/*   yes, don't check any further */
485*84d9c625SLionel Sambuc	add	r1, r1, #1		/*   no, advance src pointer by 1 */
486*84d9c625SLionel Sambuc	tst	r5, #BYTE2		/* is there a NUL in byte 2? */
487*84d9c625SLionel Sambuc	addne	r1, r1, #1		/*   no, there must be in byte 3 */
488*84d9c625SLionel Sambuc4:
489*84d9c625SLionel Sambuc#endif /* _ARM_ARCH_6 */
490*84d9c625SLionel Sambuc.Lend_of_string:
491*84d9c625SLionel Sambuc	sub	r0, r1, r6		/* subtract start from finish */
492*84d9c625SLionel Sambuc	pop	{r4-r9}			/* restore registers */
493*84d9c625SLionel Sambuc	RET
494*84d9c625SLionel Sambuc#elif defined(STRNCPY)
495*84d9c625SLionel Sambuc.Lend_of_string:
496*84d9c625SLionel Sambuc	teq	r2, #0			/* any bytes left to zero? */
497*84d9c625SLionel Sambuc	beq	3f 			/*   no, just return. */
498*84d9c625SLionel Sambuc	mov	r1, #0			/*   yes, prepare to zero */
499*84d9c625SLionel Sambuc	cmp	r2, #16			/* some, but not a lot? */
500*84d9c625SLionel Sambuc	ble	1f
501*84d9c625SLionel Sambuc	mov	r4, lr			/* preserve lr */
502*84d9c625SLionel Sambuc	bl	PLT_SYM(_C_LABEL(memset)) /*   yes, and let memset do it */
503*84d9c625SLionel Sambuc	mov	lr, r4			/* restore lr */
504*84d9c625SLionel Sambuc	b	3f			/* return */
505*84d9c625SLionel Sambuc1:	add	ip, r0, r2		/* calculate stopping point */
506*84d9c625SLionel Sambuc2:	strb	r1, [r0], #1		/* clear a byte */
507*84d9c625SLionel Sambuc	cmp	r0, ip			/* done? */
508*84d9c625SLionel Sambuc	blt	2b			/*   no, clear next byte */
509*84d9c625SLionel Sambuc3:	mov	r0, r6			/* restore dst pointer */
510*84d9c625SLionel Sambuc	pop	{r4-r9}			/* restore registers */
511*84d9c625SLionel Sambuc	RET
512*84d9c625SLionel Sambuc.Ldst_full:
513*84d9c625SLionel Sambuc.Ldst_full_word_aligned:
514*84d9c625SLionel Sambuc	/*
515*84d9c625SLionel Sambuc	 * Destination was filled (but not NUL terminated).
516*84d9c625SLionel Sambuc	 * All that's left is return the start of dst
517*84d9c625SLionel Sambuc	 */
518*84d9c625SLionel Sambuc	mov	r0, r6			/* restore dst pointer */
519*84d9c625SLionel Sambuc	pop	{r4-r9}			/* restore registers */
520*84d9c625SLionel Sambuc	RET
521*84d9c625SLionel Sambuc#else
522*84d9c625SLionel Sambuc.Lend_of_string:
523*84d9c625SLionel Sambuc	mov	r0, r6			/* restore dst pointer */
524*84d9c625SLionel Sambuc	pop	{r4-r9}			/* restore registers */
525*84d9c625SLionel Sambuc	RET
526*84d9c625SLionel Sambuc#endif
527*84d9c625SLionel SambucEND(FUNCNAME)
528