1 | /* copy no more than N bytes from SRC to DEST, returning the address of |
2 | the terminating '\0' in DEST. |
3 | For Intel 80x86, x>=3. |
4 | Copyright (C) 1994-2024 Free Software Foundation, Inc. |
5 | This file is part of the GNU C Library. |
6 | |
7 | The GNU C Library is free software; you can redistribute it and/or |
8 | modify it under the terms of the GNU Lesser General Public |
9 | License as published by the Free Software Foundation; either |
10 | version 2.1 of the License, or (at your option) any later version. |
11 | |
12 | The GNU C Library is distributed in the hope that it will be useful, |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | Lesser General Public License for more details. |
16 | |
17 | You should have received a copy of the GNU Lesser General Public |
18 | License along with the GNU C Library; if not, see |
19 | <https://www.gnu.org/licenses/>. */ |
20 | |
21 | #include <sysdep.h> |
22 | #include "asm-syntax.h" |
23 | |
24 | #define PARMS 4+4 /* space for 1 saved reg */ |
25 | #define RTN PARMS |
26 | #define DEST RTN |
27 | #define SRC DEST+4 |
28 | #define LEN SRC+4 |
29 | |
30 | .text |
31 | ENTRY (__stpncpy) |
32 | |
33 | pushl %esi |
34 | cfi_adjust_cfa_offset (4) |
35 | |
36 | movl DEST(%esp), %eax |
37 | movl SRC(%esp), %esi |
38 | cfi_rel_offset (esi, 0) |
39 | movl LEN(%esp), %ecx |
40 | |
41 | subl %eax, %esi /* magic: reduce number of loop variants |
42 | to one using addressing mode */ |
43 | jmp L(1) /* jump to loop "head" */ |
44 | |
45 | ALIGN(4) |
46 | |
47 | /* Four times unfolded loop with two loop counters. We get the |
48 | third value (the source address) by using the index+base |
49 | addressing mode. */ |
50 | L(2): movb (%eax,%esi), %dl /* load current char */ |
51 | movb %dl, (%eax) /* and store it */ |
52 | testb %dl, %dl /* was it NUL? */ |
53 | jz L(7) /* yes, then exit */ |
54 | |
55 | movb 1(%eax,%esi), %dl /* load current char */ |
56 | movb %dl, 1(%eax) /* and store it */ |
57 | testb %dl, %dl /* was it NUL? */ |
58 | jz L(6) /* yes, then exit */ |
59 | |
60 | movb 2(%eax,%esi), %dl /* load current char */ |
61 | movb %dl, 2(%eax) /* and store it */ |
62 | testb %dl, %dl /* was it NUL? */ |
63 | jz L(5) /* yes, then exit */ |
64 | |
65 | movb 3(%eax,%esi), %dl /* load current char */ |
66 | movb %dl, 3(%eax) /* and store it */ |
67 | testb %dl, %dl /* was it NUL? */ |
68 | jz L(4) /* yes, then exit */ |
69 | |
70 | addl $4, %eax /* increment loop counter for full round */ |
71 | |
72 | L(1): subl $4, %ecx /* still more than 4 bytes allowed? */ |
73 | jae L(2) /* yes, then go to start of loop */ |
74 | |
75 | /* The maximal remaining 15 bytes are not processed in a loop. */ |
76 | |
77 | addl $4, %ecx /* correct above subtraction */ |
78 | jz L(9) /* maximal allowed char reached => go to end */ |
79 | |
80 | movb (%eax,%esi), %dl /* load current char */ |
81 | movb %dl, (%eax) /* and store it */ |
82 | testb %dl, %dl /* was it NUL? */ |
83 | jz L(3) /* yes, then exit */ |
84 | |
85 | incl %eax /* increment pointer */ |
86 | decl %ecx /* decrement length counter */ |
87 | jz L(9) /* no more allowed => exit */ |
88 | |
89 | movb (%eax,%esi), %dl /* load current char */ |
90 | movb %dl, (%eax) /* and store it */ |
91 | testb %dl, %dl /* was it NUL? */ |
92 | jz L(3) /* yes, then exit */ |
93 | |
94 | incl %eax /* increment pointer */ |
95 | decl %ecx /* decrement length counter */ |
96 | jz L(9) /* no more allowed => exit */ |
97 | |
98 | movb (%eax,%esi), %dl /* load current char */ |
99 | movb %dl, (%eax) /* and store it */ |
100 | testb %dl, %dl /* was it NUL? */ |
101 | jz L(3) /* yes, then exit */ |
102 | |
103 | incl %eax /* increment pointer */ |
104 | jmp L(9) /* we don't have to test for counter underflow |
105 | because we know we had a most 3 bytes |
106 | remaining => exit */ |
107 | |
108 | /* When coming from the main loop we have to adjust the pointer. */ |
109 | L(4): decl %ecx /* decrement counter */ |
110 | incl %eax /* increment pointer */ |
111 | |
112 | L(5): decl %ecx /* increment pointer */ |
113 | incl %eax /* increment pointer */ |
114 | |
115 | L(6): decl %ecx /* increment pointer */ |
116 | incl %eax /* increment pointer */ |
117 | L(7): |
118 | |
119 | addl $3, %ecx /* correct pre-decrementation of counter |
120 | at the beginning of the loop; but why 3 |
121 | and not 4? Very simple, we have to count |
122 | the NUL char we already wrote. */ |
123 | jz L(9) /* counter is also 0 => exit */ |
124 | |
125 | /* We now have to fill the rest of the buffer with NUL. This |
126 | is done in a tricky way. Please note that the addressing mode |
127 | used below is not the same we used above. Here we use the |
128 | %ecx register. */ |
129 | L(8): |
130 | movb $0, (%ecx,%eax) /* store NUL char */ |
131 | L(3): decl %ecx /* all bytes written? */ |
132 | jnz L(8) /* no, then again */ |
133 | |
134 | L(9): popl %esi /* restore saved register content */ |
135 | cfi_adjust_cfa_offset (-4) |
136 | cfi_restore (esi) |
137 | |
138 | ret |
139 | END (__stpncpy) |
140 | |
141 | libc_hidden_def (__stpncpy) |
142 | weak_alias (__stpncpy, stpncpy) |
143 | |