Chapter 1 – String Length

by G. Adam Stanislav
Whiz Kid Technomagic

The Unix programmer often works with C-style text strings, which consist of the text followed by a NUL, i.e., a byte equal to 0.

The C library provides a strlen() function to find the length of the string. The function is usually written in C and, therefore, does not take advantage of the string manipulation routines built into the Intel microprocessor since its lowly 8086/8088 origins.

In assembly language finding the length of a C-style string is a snap. The x86 family of microprocessors come with with the scasb instruction which searches for the first occurence of a byte whose value is equal to that of the AL register. The address of the start of the string itself has to be in the EDI register. Technically, it is supposed to be in the extra segment, but we do not need to worry about that in the flat 32-bit memory mode anymore. When used along with the repne prefix, the scasb instruction goes up (or down, depending on the direction flag) the memory, looking for the match.

As it scans the bytes, it decreases the value of the ECX register and increases the value of EDI. It quits when it either finds a match, or ECX becomes equal to 0.

To find the length of a string, we need to initialize ECX to the highest value possible, which is 4,294,967,295 in the 32-bit mode. When viewed as a signed value, it is the same as -1. The easiest way to achieve that is by first setting ECX to 0, then reversing all its bits, e.g.:

	sub	ecx, ecx	; ECX = 0
	not	ecx		; ECX = -1, or 4,294,967,295

We need to initialize AL to NUL, or 0:

	sub	al, al

Assuming the start of our string is pointed at by EDI, we can now search for NUL at the end of the string:

	cld
repne	scasb

Now that we have found it, we can figure out the length of the string. We could take several approaches:

The most obvious would be by subtraction of the position of the NUL from the start of the string. This is how the typical C library does it.

But in assembly language we have a faster way. Note that while we initialized ECX to 4,294,967,295, it was the same as -1. But the microprocessor decreased its value with every scan, including when it found the NUL. Thus, ECX = - strlen - 2. We could negate it, so we would get ECX = strlen + 2. We could then subtract the 2:

	neg	ecx
	dec	ecx
	dec	ecx

But this is still not the most efficient way. We know from digital electronics that reversing all bits of a negative number results in its absolute value - 1. We can, therefore, replace the first two lines of the above with not ecx:

	not	ecx
	dec	ecx

ECX now contains the length of the string, not counting the terminating NUL.

To put it all together, to find the length of the string whose starting address is in EDI, we only need a few lines of assembly language code:

	sub	ecx, ecx
	sub	al, al
	not	ecx
	cld
repne	scasb
	not	ecx
	dec	ecx
TIP: In assembly language you are not limited to NUL-terminated strings. You can find the length of a string ending with any terminator by simply placing it in AL in the above code.

1.1. Improving the C Library

If we were to write a C library, naturally, we would write the strlen() function in assembly language for greater speed.

The typical C compiler expects any function to preserve the value of EDI. It expects to find the return value in EAX. And it does not care what we do with ECX. It also passes its arguments on the stack.

Here, then, is our improved version of the C strlen() function:

global	strlen
	; int strlen(const char *string);
strlen:
	push	edi
	sub	ecx, ecx
	mov	edi, [esp+8]
	not	ecx
	sub	al, al
	cld
repne	scasb
	not	ecx
	pop	edi
	lea	eax, [ecx-1]
	ret
N.B.: While on the ELF-based systems, the C function names are used unchanged in assembly language, on some systems they are prepended with an underline. On such systems, substitute _strlen for strlen. Of course, you could use both:
global	strlen, _strlen
_strlen:
strlen:
	push	edi
	[etc...]

Copyright © 2000 G. Adam Stanislav.
All rights reserved.

[ Home | FreeBSD Assembly Language ]