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