Initial revision

This commit is contained in:
Ben Gras
2005-04-21 14:53:53 +00:00
commit 9865aeaa79
2264 changed files with 411685 additions and 0 deletions

112
lib/i386/string/Makefile Executable file
View File

@@ -0,0 +1,112 @@
# Makefile for lib/i386/string.
CC1 = $(CC) -Was-ack -c
LIBRARY = ../../libc.a
all: $(LIBRARY)
OBJECTS = \
$(LIBRARY)(_memmove.o) \
$(LIBRARY)(_strncat.o) \
$(LIBRARY)(_strncmp.o) \
$(LIBRARY)(_strncpy.o) \
$(LIBRARY)(_strnlen.o) \
$(LIBRARY)(bcmp.o) \
$(LIBRARY)(bcopy.o) \
$(LIBRARY)(bzero.o) \
$(LIBRARY)(index.o) \
$(LIBRARY)(memchr.o) \
$(LIBRARY)(memcmp.o) \
$(LIBRARY)(memcpy.o) \
$(LIBRARY)(memmove.o) \
$(LIBRARY)(memset.o) \
$(LIBRARY)(rindex.o) \
$(LIBRARY)(strcat.o) \
$(LIBRARY)(strchr.o) \
$(LIBRARY)(strcmp.o) \
$(LIBRARY)(strcpy.o) \
$(LIBRARY)(strlen.o) \
$(LIBRARY)(strncat.o) \
$(LIBRARY)(strncmp.o) \
$(LIBRARY)(strncpy.o) \
$(LIBRARY)(strnlen.o) \
$(LIBRARY)(strrchr.o) \
$(LIBRARY): $(OBJECTS)
aal cr $@ *.o
rm *.o
$(LIBRARY)(_memmove.o): _memmove.s
$(CC1) _memmove.s
$(LIBRARY)(_strncat.o): _strncat.s
$(CC1) _strncat.s
$(LIBRARY)(_strncmp.o): _strncmp.s
$(CC1) _strncmp.s
$(LIBRARY)(_strncpy.o): _strncpy.s
$(CC1) _strncpy.s
$(LIBRARY)(_strnlen.o): _strnlen.s
$(CC1) _strnlen.s
$(LIBRARY)(bcmp.o): bcmp.s
$(CC1) bcmp.s
$(LIBRARY)(bcopy.o): bcopy.s
$(CC1) bcopy.s
$(LIBRARY)(bzero.o): bzero.s
$(CC1) bzero.s
$(LIBRARY)(index.o): index.s
$(CC1) index.s
$(LIBRARY)(memchr.o): memchr.s
$(CC1) memchr.s
$(LIBRARY)(memcmp.o): memcmp.s
$(CC1) memcmp.s
$(LIBRARY)(memcpy.o): memcpy.s
$(CC1) memcpy.s
$(LIBRARY)(memmove.o): memmove.s
$(CC1) memmove.s
$(LIBRARY)(memset.o): memset.s
$(CC1) memset.s
$(LIBRARY)(rindex.o): rindex.s
$(CC1) rindex.s
$(LIBRARY)(strcat.o): strcat.s
$(CC1) strcat.s
$(LIBRARY)(strchr.o): strchr.s
$(CC1) strchr.s
$(LIBRARY)(strcmp.o): strcmp.s
$(CC1) strcmp.s
$(LIBRARY)(strcpy.o): strcpy.s
$(CC1) strcpy.s
$(LIBRARY)(strlen.o): strlen.s
$(CC1) strlen.s
$(LIBRARY)(strncat.o): strncat.s
$(CC1) strncat.s
$(LIBRARY)(strncmp.o): strncmp.s
$(CC1) strncmp.s
$(LIBRARY)(strncpy.o): strncpy.s
$(CC1) strncpy.s
$(LIBRARY)(strnlen.o): strnlen.s
$(CC1) strnlen.s
$(LIBRARY)(strrchr.o): strrchr.s
$(CC1) strrchr.s

52
lib/i386/string/README Executable file
View File

@@ -0,0 +1,52 @@
Notes on i80386 string assembly routines. Author: Kees J. Bot
2 Jan 1994
Remarks.
All routines set up proper stack frames, so that stack traces can be
derived from core dumps. String routines are often the ones that
get the bad pointer.
Flags are often not right in boundary cases (zero string length) on
repeated string scanning or comparing instructions. This has been
handled in sometimes nonobvious ways.
Only the eax, edx, and ecx registers are not preserved, all other
registers are. This is what GCC expects. (ACK sees ebx as scratch
too.) The direction byte is assumed to be wrong, and left clear on
exit.
Assumptions.
The average string is short, so short strings should not suffer from
smart tricks to copy, compare, or search large strings fast. This
means that the routines are fast on average, but not optimal for
long strings.
It doesn't pay to use word or longword operations on strings, the
setup time hurts the average case.
Memory blocks are probably large and on word or longword boundaries.
No unaligned word moves are done. Again the setup time may hurt the
average case. Furthermore, the author likes to enable the alignment
check on a 486.
String routines.
They have been implemented using byte string instructions. The
length of a string it usually determined first, followed by the
actual operation.
Strcmp.
This is the only string routine that uses a loop, and not
instructions with a repeat prefix. Problem is that we don't know
how long the string is. Scanning for the end costs if the strings
are unequal in the first few bytes.
Strchr.
The character we look for is often not there, or at some distance
from the start. The string is scanned twice, for the terminating
zero and the character searched, in chunks of increasing length.
Memory routines.
Memmove, memcpy, and memset use word or longword instructions if the
address(es) are at word or longword boundaries. No tricks to get
alignment after doing a few bytes. No unaligned operations.

58
lib/i386/string/_memmove.s Executable file
View File

@@ -0,0 +1,58 @@
! _memmove() Author: Kees J. Bot
! 2 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! void *_memmove(void *s1, const void *s2, size_t n)
! Copy a chunk of memory. Handle overlap.
!
.sect .text
.define __memmove, __memcpy
.align 16
__memmove:
push ebp
mov ebp, esp
push esi
push edi
mov edi, 8(ebp) ! String s1
mov esi, 12(ebp) ! String s2
mov ecx, 16(ebp) ! Length
mov eax, edi
sub eax, esi
cmp eax, ecx
jb downwards ! if (s2 - s1) < n then copy downwards
__memcpy:
cld ! Clear direction bit: upwards
cmp ecx, 16
jb upbyte ! Don't bother being smart with short arrays
mov eax, esi
or eax, edi
testb al, 1
jnz upbyte ! Bit 0 set, use byte copy
testb al, 2
jnz upword ! Bit 1 set, use word copy
uplword:shrd eax, ecx, 2 ! Save low 2 bits of ecx in eax
shr ecx, 2
rep
movs ! Copy longwords.
shld ecx, eax, 2 ! Restore excess count
upword: shr ecx, 1
rep
o16 movs ! Copy words
adc ecx, ecx ! One more byte?
upbyte: rep
movsb ! Copy bytes
done: mov eax, 8(ebp) ! Absolutely noone cares about this value
pop edi
pop esi
pop ebp
ret
! Handle bad overlap by copying downwards, don't bother to do word copies.
downwards:
std ! Set direction bit: downwards
lea esi, -1(esi)(ecx*1)
lea edi, -1(edi)(ecx*1)
rep
movsb ! Copy bytes
cld
jmp done

41
lib/i386/string/_strncat.s Executable file
View File

@@ -0,0 +1,41 @@
! _strncat() Author: Kees J. Bot
! 1 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! char *_strncat(char *s1, const char *s2, size_t edx)
! Append string s2 to s1.
!
.sect .text
.define __strncat
.align 16
__strncat:
push ebp
mov ebp, esp
push esi
push edi
mov edi, 8(ebp) ! String s1
mov ecx, -1
xorb al, al ! Null byte
cld
repne
scasb ! Look for the zero byte in s1
dec edi ! Back one up (and clear 'Z' flag)
push edi ! Save end of s1
mov edi, 12(ebp) ! edi = string s2
mov ecx, edx ! Maximum count
repne
scasb ! Look for the end of s2
jne no0
inc ecx ! Exclude null byte
no0: sub edx, ecx ! Number of bytes in s2
mov ecx, edx
mov esi, 12(ebp) ! esi = string s2
pop edi ! edi = end of string s1
rep
movsb ! Copy bytes
stosb ! Add a terminating null
mov eax, 8(ebp) ! Return s1
pop edi
pop esi
pop ebp
ret

35
lib/i386/string/_strncmp.s Executable file
View File

@@ -0,0 +1,35 @@
! strncmp() Author: Kees J. Bot
! 1 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! int strncmp(const char *s1, const char *s2, size_t ecx)
! Compare two strings.
!
.sect .text
.define __strncmp
.align 16
__strncmp:
push ebp
mov ebp, esp
push esi
push edi
test ecx, ecx ! Max length is zero?
je done
mov esi, 8(ebp) ! esi = string s1
mov edi, 12(ebp) ! edi = string s2
cld
compare:
cmpsb ! Compare two bytes
jne done
cmpb -1(esi), 0 ! End of string?
je done
dec ecx ! Length limit reached?
jne compare
done: seta al ! al = (s1 > s2)
setb ah ! ah = (s1 < s2)
subb al, ah
movsxb eax, al ! eax = (s1 > s2) - (s1 < s2), i.e. -1, 0, 1
pop edi
pop esi
pop ebp
ret

24
lib/i386/string/_strncpy.s Executable file
View File

@@ -0,0 +1,24 @@
! _strncpy() Author: Kees J. Bot
! 1 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! char *_strncpy(char *s1, const char *s2, size_t ecx)
! Copy string s2 to s1.
!
.sect .text
.define __strncpy
.align 16
__strncpy:
mov edi, 12(ebp) ! edi = string s2
xorb al, al ! Look for a zero byte
mov edx, ecx ! Save maximum count
cld
repne
scasb ! Look for end of s2
sub edx, ecx ! Number of bytes in s2 including null
xchg ecx, edx
mov esi, 12(ebp) ! esi = string s2
mov edi, 8(ebp) ! edi = string s1
rep
movsb ! Copy bytes
ret

28
lib/i386/string/_strnlen.s Executable file
View File

@@ -0,0 +1,28 @@
! _strnlen() Author: Kees J. Bot
! 1 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! size_t _strnlen(const char *s, size_t ecx)
! Return the length of a string.
!
.sect .text
.define __strnlen
.align 16
__strnlen:
push ebp
mov ebp, esp
push edi
mov edi, 8(ebp) ! edi = string
xorb al, al ! Look for a zero byte
mov edx, ecx ! Save maximum count
cmpb cl, 1 ! 'Z' bit must be clear if ecx = 0
cld
repne
scasb ! Look for zero
jne no0
inc ecx ! Don't count zero byte
no0: mov eax, edx
sub eax, ecx ! Compute bytes scanned
pop edi
pop ebp
ret

28
lib/i386/string/bcmp.s Executable file
View File

@@ -0,0 +1,28 @@
! bcmp() Author: Kees J. Bot
! 2 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! int bcmp(const void *s1, const void *s2, size_t n)
! Compare two chunks of memory.
! This is a BSD routine that escaped from the kernel. Don't use.
! (Alas it is not without some use, it reports the number of bytes
! after the bytes that are equal. So it can't be simply replaced.)
!
.sect .text
.define _bcmp
.align 16
_bcmp:
push ebp
mov ebp, esp
push 16(ebp)
push 12(ebp)
push 8(ebp)
call _memcmp ! Let memcmp do the work
test eax, eax
jz equal
sub edx, 8(ebp) ! Memcmp was nice enough to leave "esi" in edx
dec edx ! Number of bytes that are equal
mov eax, 16(ebp)
sub eax, edx ! Number of bytes that are unequal
equal: leave
ret

16
lib/i386/string/bcopy.s Executable file
View File

@@ -0,0 +1,16 @@
! bcopy() Author: Kees J. Bot
! 2 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! void bcopy(const void *s1, void *s2, size_t n)
! Copy a chunk of memory. Handle overlap.
! This is a BSD routine that escaped from the kernel. Don't use.
!
.sect .text
.define _bcopy
.align 16
_bcopy:
mov eax, 4(esp) ! Exchange string arguments
xchg eax, 8(esp)
mov 4(esp), eax
jmp __memmove ! Call the proper routine

20
lib/i386/string/bzero.s Executable file
View File

@@ -0,0 +1,20 @@
! bzero() Author: Kees J. Bot
! 2 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! void bzero(void *s, size_t n)
! Set a chunk of memory to zero.
! This is a BSD routine that escaped from the kernel. Don't use.
!
.sect .text
.define _bzero
.align 16
_bzero:
push ebp
mov ebp, esp
push 12(ebp) ! Size
push 0 ! Zero
push 8(ebp) ! String
call _memset ! Call the proper routine
leave
ret

13
lib/i386/string/index.s Executable file
View File

@@ -0,0 +1,13 @@
! index() Author: Kees J. Bot
! 2 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! char *index(const char *s, int c)
! Look for a character in a string. Has suffered from a hostile
! takeover by strchr().
!
.sect .text
.define _index
.align 16
_index:
jmp _strchr

30
lib/i386/string/memchr.s Executable file
View File

@@ -0,0 +1,30 @@
! memchr() Author: Kees J. Bot
! 2 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! void *memchr(const void *s, int c, size_t n)
! Look for a character in a chunk of memory.
!
.sect .text
.define _memchr
.align 16
_memchr:
push ebp
mov ebp, esp
push edi
mov edi, 8(ebp) ! edi = string
movb al, 12(ebp) ! The character to look for
mov ecx, 16(ebp) ! Length
cmpb cl, 1 ! 'Z' bit must be clear if ecx = 0
cld
repne
scasb
jne failure
lea eax, -1(edi) ! Found
pop edi
pop ebp
ret
failure:xor eax, eax
pop edi
pop ebp
ret

56
lib/i386/string/memcmp.s Executable file
View File

@@ -0,0 +1,56 @@
! memcmp() Author: Kees J. Bot
! 2 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! int memcmp(const void *s1, const void *s2, size_t n)
! Compare two chunks of memory.
!
.sect .text
.define _memcmp
.align 16
_memcmp:
cld
push ebp
mov ebp, esp
push esi
push edi
mov esi, 8(ebp) ! String s1
mov edi, 12(ebp) ! String s2
mov ecx, 16(ebp) ! Length
cmp ecx, 16
jb cbyte ! Don't bother being smart with short arrays
mov eax, esi
or eax, edi
testb al, 1
jnz cbyte ! Bit 0 set, use byte compare
testb al, 2
jnz cword ! Bit 1 set, use word compare
clword: shrd eax, ecx, 2 ! Save low two bits of ecx in eax
shr ecx, 2
repe
cmps ! Compare longwords
sub esi, 4
sub edi, 4
inc ecx ! Recompare the last longword
shld ecx, eax, 2 ! And any excess bytes
jmp last
cword: shrd eax, ecx, 1 ! Save low bit of ecx in eax
shr ecx, 1
repe
o16 cmps ! Compare words
sub esi, 2
sub edi, 2
inc ecx ! Recompare the last word
shld ecx, eax, 1 ! And one more byte?
cbyte: test ecx, ecx ! Set 'Z' flag if ecx = 0
last: repe
cmpsb ! Look for the first differing byte
seta al ! al = (s1 > s2)
setb ah ! ah = (s1 < s2)
subb al, ah
movsxb eax, al ! eax = (s1 > s2) - (s1 < s2), i.e. -1, 0, 1
mov edx, esi ! For bcmp() to play with
pop edi
pop esi
pop ebp
ret

24
lib/i386/string/memcpy.s Executable file
View File

@@ -0,0 +1,24 @@
! memcpy() Author: Kees J. Bot
! 2 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! void *memcpy(void *s1, const void *s2, size_t n)
! Copy a chunk of memory.
! This routine need not handle overlap, so it does not handle overlap.
! One could simply call __memmove, the cost of the overlap check is
! negligible, but you are dealing with a programmer who believes that
! if anything can go wrong, it should go wrong.
!
.sect .text
.define _memcpy
.align 16
_memcpy:
push ebp
mov ebp, esp
push esi
push edi
mov edi, 8(ebp) ! String s1
mov esi, 12(ebp) ! String s2
mov ecx, 16(ebp) ! Length
! No overlap check here
jmp __memcpy ! Call the part of __memmove that copies up

12
lib/i386/string/memmove.s Executable file
View File

@@ -0,0 +1,12 @@
! memmove() Author: Kees J. Bot
! 2 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! void *memmove(void *s1, const void *s2, size_t n)
! Copy a chunk of memory. Handle overlap.
!
.sect .text
.define _memmove
.align 16
_memmove:
jmp __memmove ! Call common code

44
lib/i386/string/memset.s Executable file
View File

@@ -0,0 +1,44 @@
! memset() Author: Kees J. Bot
! 2 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! void *memset(void *s, int c, size_t n)
! Set a chunk of memory to the same byte value.
!
.sect .text
.define _memset
.align 16
_memset:
push ebp
mov ebp, esp
push edi
mov edi, 8(ebp) ! The string
movzxb eax, 12(ebp) ! The fill byte
mov ecx, 16(ebp) ! Length
cld
cmp ecx, 16
jb sbyte ! Don't bother being smart with short arrays
test edi, 1
jnz sbyte ! Bit 0 set, use byte store
test edi, 2
jnz sword ! Bit 1 set, use word store
slword: movb ah, al
mov edx, eax
sal edx, 16
or eax, edx ! One byte to four bytes
shrd edx, ecx, 2 ! Save low two bits of ecx in edx
shr ecx, 2
rep
stos ! Store longwords.
shld ecx, edx, 2 ! Restore low two bits
sword: movb ah, al ! One byte to two bytes
shr ecx, 1
rep
o16 stos ! Store words
adc ecx, ecx ! One more byte?
sbyte: rep
stosb ! Store bytes
done: mov eax, 8(ebp) ! Return some value you have no need for
pop edi
pop ebp
ret

13
lib/i386/string/rindex.s Executable file
View File

@@ -0,0 +1,13 @@
! rindex() Author: Kees J. Bot
! 2 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! char *rindex(const char *s, int c)
! Look for the last occurrence a character in a string. Has suffered
! from a hostile takeover by strrchr().
!
.sect .text
.define _rindex
.align 16
_rindex:
jmp _strrchr

13
lib/i386/string/strcat.s Executable file
View File

@@ -0,0 +1,13 @@
! strcat() Author: Kees J. Bot
! 1 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! char *strcat(char *s1, const char *s2)
! Append string s2 to s1.
!
.sect .text
.define _strcat
.align 16
_strcat:
mov edx, -1 ! Unlimited length
jmp __strncat ! Common code

41
lib/i386/string/strchr.s Executable file
View File

@@ -0,0 +1,41 @@
! strchr() Author: Kees J. Bot
! 1 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! char *strchr(const char *s, int c)
! Look for a character in a string.
!
.sect .text
.define _strchr
.align 16
_strchr:
push ebp
mov ebp, esp
push edi
cld
mov edi, 8(ebp) ! edi = string
mov edx, 16 ! Look at small chunks of the string
next: shl edx, 1 ! Chunks become bigger each time
mov ecx, edx
xorb al, al ! Look for the zero at the end
repne
scasb
pushf ! Remember the flags
sub ecx, edx
neg ecx ! Some or all of the chunk
sub edi, ecx ! Step back
movb al, 12(ebp) ! The character to look for
repne
scasb
je found
popf ! Did we find the end of string earlier?
jne next ! No, try again
xor eax, eax ! Return NULL
pop edi
pop ebp
ret
found: pop eax ! Get rid of those flags
lea eax, -1(edi) ! Address of byte found
pop edi
pop ebp
ret

13
lib/i386/string/strcmp.s Executable file
View File

@@ -0,0 +1,13 @@
! strcmp() Author: Kees J. Bot
! 1 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! int strcmp(const char *s1, const char *s2)
! Compare two strings.
!
.sect .text
.define _strcmp
.align 16
_strcmp:
mov ecx, -1 ! Unlimited length
jmp __strncmp ! Common code

22
lib/i386/string/strcpy.s Executable file
View File

@@ -0,0 +1,22 @@
! strcpy() Author: Kees J. Bot
! 1 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! char *strcpy(char *s1, const char *s2)
! Copy string s2 to s1.
!
.sect .text
.define _strcpy
.align 16
_strcpy:
push ebp
mov ebp, esp
push esi
push edi
mov ecx, -1 ! Unlimited length
call __strncpy ! Common code
mov eax, 8(ebp) ! Return s1
pop edi
pop esi
pop ebp
ret

13
lib/i386/string/strlen.s Executable file
View File

@@ -0,0 +1,13 @@
! strlen() Author: Kees J. Bot
! 1 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! size_t strlen(const char *s)
! Return the length of a string.
!
.sect .text
.define _strlen
.align 16
_strlen:
mov ecx, -1 ! Unlimited length
jmp __strnlen ! Common code

13
lib/i386/string/strncat.s Executable file
View File

@@ -0,0 +1,13 @@
! strncat() Author: Kees J. Bot
! 1 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! size_t strncat(char *s1, const char *s2, size_t n)
! Append string s2 to s1.
!
.sect .text
.define _strncat
.align 16
_strncat:
mov edx, 12(esp) ! Maximum length
jmp __strncat ! Common code

13
lib/i386/string/strncmp.s Executable file
View File

@@ -0,0 +1,13 @@
! strncmp() Author: Kees J. Bot
! 1 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! int strncmp(const char *s1, const char *s2, size_t n)
! Compare two strings.
!
.sect .text
.define _strncmp
.align 16
_strncmp:
mov ecx, 12(esp) ! Maximum length
jmp __strncmp ! Common code

25
lib/i386/string/strncpy.s Executable file
View File

@@ -0,0 +1,25 @@
! strncpy() Author: Kees J. Bot
! 1 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! char *strncpy(char *s1, const char *s2, size_t n)
! Copy string s2 to s1.
!
.sect .text
.define _strncpy
.align 16
_strncpy:
push ebp
mov ebp, esp
push esi
push edi
mov ecx, 16(ebp) ! Maximum length
call __strncpy ! Common code
mov ecx, edx ! Number of bytes not copied
rep
stosb ! strncpy always copies n bytes by null padding
mov eax, 8(ebp) ! Return s1
pop edi
pop esi
pop ebp
ret

13
lib/i386/string/strnlen.s Executable file
View File

@@ -0,0 +1,13 @@
! strnlen() Author: Kees J. Bot
! 1 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! size_t strnlen(const char *s, size_t n)
! Return the length of a string.
!
.sect .text
.define _strnlen
.align 16
_strnlen:
mov ecx, 8(esp) ! Maximum length
jmp __strnlen ! Common code

36
lib/i386/string/strrchr.s Executable file
View File

@@ -0,0 +1,36 @@
! strrchr() Author: Kees J. Bot
! 2 Jan 1994
.sect .text; .sect .rom; .sect .data; .sect .bss
! char *strrchr(const char *s, int c)
! Look for the last occurrence a character in a string.
!
.sect .text
.define _strrchr
.align 16
_strrchr:
push ebp
mov ebp, esp
push edi
mov edi, 8(ebp) ! edi = string
mov ecx, -1
xorb al, al
cld
repne
scasb ! Look for the end of the string
not ecx ! -1 - ecx = Length of the string + null
dec edi ! Put edi back on the zero byte
movb al, 12(ebp) ! The character to look for
std ! Downwards search
repne
scasb
cld ! Direction bit back to default
jne failure
lea eax, 1(edi) ! Found it
pop edi
pop ebp
ret
failure:xor eax, eax ! Not there
pop edi
pop ebp
ret