Thread: strlen() isn't easy to re-write in assembly

  1. #1
    Supermassive black hole cboard_member's Avatar
    Join Date
    Jul 2005
    Posts
    1,709

    strlen() isn't easy to re-write in assembly

    ... at least not for me. I'm still a beginner at this assembly lark (well, x86 assembly at least).

    Basically I'm trying to write my own strlen (for nothing more than educational purposes) but I, well, can't. I'm going over array access here:

    http://www.geocities.com/SiliconVall...230/index.html

    (Have to put up with his terrible english).

    So I want to load the first character of the argument stri into a register, is this the right way to go about it?:

    Code:
    _declspec(naked) int StringLen(char* stri)
    {
        int l;
    
        _asm
        {
            push    ebp
            mov     ebp, esp
    
            mov     [l], 0
            mov     eax, [stri]
            mov     bl, byte ptr [eax]
    
            mov     esp, ebp
            pop     ebp
            lea     eax, [l]
            ret
        }
    }
    I've stepped through it and bl does indeed get loaded with 'm' (oh, I pass the string "my name is LEEEEEEEEE") but I want to iterate it just to make sure. So I need to inc bl each time to access the next element, right?
    Last edited by cboard_member; 05-06-2006 at 10:23 AM.
    Good class architecture is not like a Swiss Army Knife; it should be more like a well balanced throwing knife.

    - Mike McShaffry

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > So I need to inc bl each time to access the next element, right?
    No, you compare it with 0
    eax you increment to step through the array.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Supermassive black hole cboard_member's Avatar
    Join Date
    Jul 2005
    Posts
    1,709
    Ok gimmie a sec.
    Good class architecture is not like a Swiss Army Knife; it should be more like a well balanced throwing knife.

    - Mike McShaffry

  4. #4
    Supermassive black hole cboard_member's Avatar
    Join Date
    Jul 2005
    Posts
    1,709
    Bloody hell this is frustrating. Can someone spell it out a bit more?
    Pretty please
    Good class architecture is not like a Swiss Army Knife; it should be more like a well balanced throwing knife.

    - Mike McShaffry

  5. #5
    Supermassive black hole cboard_member's Avatar
    Join Date
    Jul 2005
    Posts
    1,709
    Here's what I've got:

    Code:
    _declspec(naked) int StringLen(char* stri)
    {
        int l;
    
        _asm
        {
            push    ebp
            mov     ebp, esp
    
            mov     [l], 0
            mov     ebx, 0
    
    _proc:
            mov     eax, stri [ebx]
            cmp     eax, 0
            je      _out
            inc     ebx
            jmp     _proc
    
    _out:
            mov     esp, ebp
            pop     ebp
            mov     eax, [l]
            ret
        }
    }
    Except I'm having trouble returning the value I want (l). I thought what I've got there was right but it's not, apparently.
    Good class architecture is not like a Swiss Army Knife; it should be more like a well balanced throwing knife.

    - Mike McShaffry

  6. #6
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > mov eax, [l]
    Do this before messing with restoring ebp,esp
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  7. #7
    Supermassive black hole cboard_member's Avatar
    Join Date
    Jul 2005
    Posts
    1,709
    Hmm, "DAMAGE: After normal block (#72) at <location>".
    Think it's time to post the whole thing:

    Code:
    #include "stdafx.h"
    
    _declspec(naked) void FastCopy16(char* src, char* dst, int c)
    {
        _asm
        {
            push    ebp
            mov     ebp, esp
    
            mov     esi, [esp + 08h]
            mov     edi, [esp + 0Ch]
            cld
            mov     ecx, [esp + 10h]
            shr     ecx, 1
            rep     movsw
            lea     eax, [edi]
            mov     byte ptr [eax], 00h
    
            mov     esp, ebp
            pop     ebp
            ret
        }
    }
    
    _declspec(naked) void ZeroString(char* s, int c)
    {
        _asm
        {
            push    ebp
            mov     ebp, esp
    
            mov     edi, [esp + 08h]
            mov     ecx, [esp + 0Ch]
            shr     ecx, 1
            xor     eax, eax
            rep     stosw
    
            mov     esp, ebp
            pop     ebp
            ret
        }
    }
    
    _declspec(naked) int StringLen(char* stri)
    {
        int l;
    
        _asm
        {
            push    ebp
            mov     ebp, esp
    
            mov     [l], 0
            mov     ebx, 0
    
    _proc:
            mov     eax, stri [ebx]
            cmp     eax, 0
            je      _out
            inc     ebx
            inc     l
            jmp     _proc
    
    _out:
            mov     eax, [l]
            mov     esp, ebp
            pop     ebp
            ret
        }
    }
    
    int main(int argc, char **argv)
    {
        using namespace std;
    
        char* src = "my name is LEEEEEEEEEE";
        char* dst = new char[120];
        int len = StringLen(src);
    
        ZeroString(dst, 120);
        FastCopy16(src, dst, len);
        cout << "src: " << src << "\ndst: " << dst << endl;
    
        delete[] dst;
        return 0;
    }
    Hmm, hmm indeed.
    EDIT: The funny thing is my output window shows the correct output:

    Code:
    src: my name is LEEEEEEEEEE
    dst: my name is LEEEEEEEEEE
    Press any key to continue
    Good class architecture is not like a Swiss Army Knife; it should be more like a well balanced throwing knife.

    - Mike McShaffry

  8. #8
    Registered User
    Join Date
    Aug 2005
    Location
    Austria
    Posts
    1,990
    This is just some guess but I think that the compiler should have created a stackframe for you because you declare a local variable l. In that case
    Code:
        push    ebp
        mov     ebp, esp
    would destroy it.
    Kurt

  9. #9
    Supermassive black hole cboard_member's Avatar
    Join Date
    Jul 2005
    Posts
    1,709
    Ah I see - I was about to ask why mov'ing it into eax first would make a difference.
    Good class architecture is not like a Swiss Army Knife; it should be more like a well balanced throwing knife.

    - Mike McShaffry

  10. #10
    Registered User
    Join Date
    May 2006
    Posts
    34
    Here's my go at it...although certainly not optimized, but I think I'd be using the SCASB instruction (scan string for byte) if I were trying to do a better version of this.

    Code:
    int StringLen(char* stri)
    {
        _asm
        {
    		mov edi, [stri]         // get address of parameter
    		mov ebx, 0              // register to hold character
    		mov eax, 0;             // register to hold character count
    L1:
    		inc eax                 // increment character count
    		mov bl, byte ptr [edi]  // get character at address of edi
    		inc edi                 // increment pointer to next character
    		cmp bl, 0               // is the character a NULL?
    		jnz L1                  // if it's not NULL, loop again
            // at this point, whatever's in eax will be returned by the function
        }
    }
    I removed the stack stuff, and the naked declaration since I don't think they're needed.

    Edit: I just realized the code above is off by one, so maybe throw in a "dec eax" at the end there
    Last edited by veecee; 05-06-2006 at 03:04 PM.

  11. #11
    Supermassive black hole cboard_member's Avatar
    Join Date
    Jul 2005
    Posts
    1,709
    Thanks veecee that worked wonders. I've learnt my one thing for today
    It looks like I was over-thinking it.
    Good class architecture is not like a Swiss Army Knife; it should be more like a well balanced throwing knife.

    - Mike McShaffry

  12. #12
    Supermassive black hole cboard_member's Avatar
    Join Date
    Jul 2005
    Posts
    1,709
    Just a quick question while it's on my mind: Is there an equivalent of GCC's -S switch for MSVC 7.1?
    Good class architecture is not like a Swiss Army Knife; it should be more like a well balanced throwing knife.

    - Mike McShaffry

  13. #13
    Registered User
    Join Date
    May 2006
    Posts
    34
    Hmmm, did a quick google for gcc's -S option...seems like it enables outputing assembly code I guess. I don't use the command line to compile with msvc, but it looks like the "/FA" switch or "/FAs" is what you want. In the IDE, it's under "C/C++>Output Files" for your project settings.

  14. #14
    Registered User
    Join Date
    Jun 2005
    Posts
    32
    the FA option gives you a crazy amount of output.

    ex.

    Code:
    int strlen(const char* text)
    {
    	const char* s = text;
    	for ( ; *s; ++s )
    		;
    	return (int)(s - text);
    }
    becomes

    Code:
    ?strlen@@YAHPBD@Z PROC					; strlen, COMDAT
    ; Line 3
    	push	ebp
    	mov	ebp, esp
    	sub	esp, 204				; 000000ccH
    	push	ebx
    	push	esi
    	push	edi
    	lea	edi, DWORD PTR [ebp-204]
    	mov	ecx, 51					; 00000033H
    	mov	eax, -858993460				; ccccccccH
    	rep stosd
    ; Line 4
    	mov	eax, DWORD PTR _text$[ebp]
    	mov	DWORD PTR _s$[ebp], eax
    	jmp	SHORT $LN3@strlen
    $LN2@strlen:
    ; Line 5
    	mov	eax, DWORD PTR _s$[ebp]
    	add	eax, 1
    	mov	DWORD PTR _s$[ebp], eax
    $LN3@strlen:
    	mov	eax, DWORD PTR _s$[ebp]
    	movsx	ecx, BYTE PTR [eax]
    	test	ecx, ecx
    	je	SHORT $LN1@strlen
    ; Line 6
    	jmp	SHORT $LN2@strlen
    $LN1@strlen:
    ; Line 7
    	mov	eax, DWORD PTR _s$[ebp]
    	sub	eax, DWORD PTR _text$[ebp]
    ; Line 8
    	pop	edi
    	pop	esi
    	pop	ebx
    	mov	esp, ebp
    	pop	ebp
    	ret	0
    ?strlen@@YAHPBD@Z ENDP					; strlen
    Although given it might help you <shrug>

    I would suggest you keep it as simple as possible, don't worry about optimizations until you have it working.

  15. #15
    Registered User
    Join Date
    May 2006
    Posts
    34
    Maybe you were using debug mode, so you got a long list of unoptimized code....I got this nice listing of your code here in release mode:

    Code:
    ?mystrlen@@YAHPBD@Z PROC				; mystrlen, COMDAT
    
    ; 11   : 	const char* s = text;
    
    	mov	eax, OFFSET ??_C@_0BH@GBNNBFPP@How?5long?5is?5this?5text?$DP?$AA@
    $LL3@mystrlen:
    
    ; 12   : 	for ( ; *s; ++s )
    
    	add	eax, 1
    	cmp	BYTE PTR [eax], 0
    	jne	SHORT $LL3@mystrlen
    
    ; 13   : 		;
    ; 14   : 	return (int)(s - text);
    
    	sub	eax, OFFSET ??_C@_0BH@GBNNBFPP@How?5long?5is?5this?5text?$DP?$AA@
    
    ; 15   : }
    
    	ret	0
    ?mystrlen@@YAHPBD@Z ENDP
    Heh, I just realized my crappy attempt at the strlen function in asm was off by one Oh well.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. read write lock in C#
    By George2 in forum C# Programming
    Replies: 0
    Last Post: 04-16-2008, 08:49 AM
  2. Replies: 13
    Last Post: 01-13-2008, 09:38 PM
  3. Game Programming FAQ
    By TechWins in forum Game Programming
    Replies: 5
    Last Post: 09-29-2004, 02:00 AM
  4. DJGPP assembly syntax ills...
    By VirtualAce in forum A Brief History of Cprogramming.com
    Replies: 5
    Last Post: 11-11-2001, 02:54 AM