Page 1 of 1

Реализация strlen на ассемблере

Posted: Sun Dec 01, 2013 12:51 pm
by Insolor
EDIT by yogev_ezra: Выделил "Реализация strlen на ассемблере" в отдельную тему.

Искал реализацию strlen на ассемблере, наткнулся на этот сайт:
http://www.int80h.org/ - Unix Assembly Language Programming
В примерах используется intel синтаксис.

Re: Ссылки на сайты

Posted: Sun Dec 01, 2013 1:06 pm
by Heavyiron
Insolor wrote:Искал реализацию strlen на ассемблере, наткнулся на этот сайт:
http://www.int80h.org/ - Unix Assembly Language Programming
В примерах используется intel синтаксис.
http://www.manhunter.ru/assembler/403_n ... blere.html - там 3 варианта strlen в зависимости от того, что требуется производительность или малый размер кода.

Re: Ссылки на сайты

Posted: Sun Dec 01, 2013 4:30 pm
by Insolor
Heavyiron wrote:
Insolor wrote:Искал реализацию strlen на ассемблере, наткнулся на этот сайт:
http://www.int80h.org/ - Unix Assembly Language Programming
В примерах используется intel синтаксис.
http://www.manhunter.ru/assembler/403_n ... blere.html - там 3 варианта strlen в зависимости от того, что требуется производительность или малый размер кода.
Эту статью вроде просматривал, но уже забыл про нее, поэтому полез в поиск. Для моих целей достаточно будет и первого варианта.

Re: Ссылки на сайты

Posted: Sun Dec 01, 2013 4:33 pm
by 0CodErr
Heavyiron wrote:http://www.manhunter.ru/assembler/403_n ... blere.html - там 3 варианта strlen в зависимости от того, что требуется производительность или малый размер кода.
Во втором варианте можно дальше пойти:
Spoiler:

Code: Select all

cmp     byte [eax+3], 0
; ..............................
cmp     byte [eax+255], 0
Если точно известно, сколько памяти выделено под строку(например, выделено 4K), то можно смело читать dword за один раз. Примерно так делается, например, в libxds, которая идёт вместе с XDS Modula-2/Oberon-2 compiler.

Первый вариант — наверное, первое, что приходит в голову :)
Я примерно так же сделал в kernel32, которая используется в PELoad.
lstrlenA, lstrlenW:
Spoiler:

Code: Select all

;**********************************************************************************
lstrlenA: ;////////////////////////////////////////////////////////////////////////
;**********************************************************************************
%define lpString              [esp +  4 +1*4] ; null-terminated string to be checked
        push   edi

        mov    edi, lpString
        xor    ecx, ecx
        dec    ecx
        xor    al, al
        repne scasb
        not    ecx
        dec    ecx
        mov    eax, ecx

        pop    edi
        ret    4
%undef lpString
align 16
;**********************************************************************************
lstrlenW: ;////////////////////////////////////////////////////////////////////////
;**********************************************************************************
%define lpString              [esp +  4 +1*4] ; null-terminated string to be checked
        push   edi

        mov    edi, lpString
        xor    ecx, ecx
        dec    ecx
        xor    ax, ax
        repne scasw
        not    ecx
        dec    ecx
        mov    eax, ecx

        pop    edi
        ret    4
%undef lpString

Я удивился, когда потом обнаружил почти то же самое в исходниках HX DOS Extender.
Но там не всё так хорошо, как могло быть.
Например lstrcatA.
У меня:
Spoiler:

Code: Select all

;**********************************************************************************
lstrcatA: ;////////////////////////////////////////////////////////////////////////
;**********************************************************************************
%define lpString1             [esp +  4 +2*4] ; first null-terminated string
%define lpString2             [esp +  8 +2*4] ; null-terminated string to be added
        push   esi
        push   edi

        mov    esi, lpString2
        xor    ecx, ecx
        xor    eax, eax
        mov    edi, esi
        dec    ecx
        repne scasb
        mov    edx, ecx
        mov    edi, lpString1
        repne scasb
        dec    edi
        not    edx
        mov    eax, lpString1
        mov    ecx, edx
        shr    ecx, 2
        rep movsd
        mov    ecx, edx
        and    ecx, 3
        rep movsb

        pop    edi
        pop    esi
        ret    8
%undef lpString1
%undef lpString2
У Japheth:
Spoiler:

Code: Select all

lstrcatA proc uses esi edi string1:ptr byte,string2:ptr byte

       xor     eax,eax
       mov     ecx,-1
       mov     edi,string2
       repne   scasb
       push    ecx
       mov     edi,string1
       repne   scasb
       dec     edi
       pop     ecx
       not     ecx
       mov     esi,string2
       shr     ecx,1
       rep     movsw
       adc     ecx,ecx
       rep     movsb
       mov     eax,string1
       ret
lstrcatA endp
Первый вариант с rep movsd, конечно будет быстрее, чем второй с rep movsw. Я привёл часть исходника из v2.16, stable. Сейчас также доступна v2.17, release candidate, может быть, там уже что-то изменилось.

Re: Ссылки на сайты

Posted: Sun Dec 01, 2013 5:02 pm
by Heavyiron
Я у mike.dld в тинипаде наткнулся на 3-й вариант и он оказался на 4 байта короче и совсем чуть-чуть быстрее, чем первый:

Code: Select all

proc _lstrlen lpStr:DWORD
        push    ebx
        mov     ebx,[lpStr]
        xor     eax,eax
    @@: cmp     byte[ebx+eax],0
        je      @f
        inc     eax
        jmp     @b
    @@: pop     ebx
        ret
endp
На нем и остановился для своих целей.

Re: Ссылки на сайты

Posted: Sun Dec 01, 2013 5:34 pm
by 0CodErr
Heavyiron, ещё 3 подобных варианта:
Spoiler:

Code: Select all

; ------------------------------------
strlen1:
        mov     ecx, [lpStr]
        mov     eax, ecx
.next:        
        cmp     [eax], byte 0
        je      .exit
        inc     eax
        jmp     .next
.exit:        
        sub     eax, ecx
        ret     4
; ------------------------------------
strlen2:
        mov     eax, [lpStr]
.next:        
        cmp     [eax], byte 0
        je      .exit
        inc     eax
        jmp     .next
.exit:        
        sub     eax, [lpStr]
        ret     4
; ------------------------------------
strlen3:
        mov     eax, [lpStr]
        dec     eax
.next:  
        inc     eax
        cmp     [eax], byte 0
        jne     .next
        sub     eax, [lpStr]
        ret     4
; ------------------------------------

Re: Ссылки на сайты

Posted: Sun Dec 01, 2013 5:58 pm
by Heavyiron
Последний крут (позволяет избавиться от push/pop, потому чуть быстрее и по размеру еще на 3 байта короче). А если заведомо известно, что длина строки ненулевая, можно убрать dec eax - еще минус 1 байт.

Code: Select all

proc _lstrlen lpStr:DWORD
        mov     eax, [lpStr]
@@:  
        inc     eax
        cmp     byte [eax], 0
        jne     @b
        sub     eax, [lpStr]
        ret
endp
Красота!

Re: Реализация strlen на ассемблере

Posted: Thu Apr 10, 2014 8:46 pm
by yogev_ezra
Выделил "Реализация strlen на ассемблере" в отдельную тему.

Ещё 1 вариант от 0CodErr:

Code: Select all

strlen:
        mov     eax, [src]
.next: 
        cmp    [eax], byte 1 ; IF byte[eax] = 0 THEN carry = 1
        inc    eax           ; after "inc" carry not affected
        jnc    .next         ; IF carry = 0 THEN next
        sbb    eax, [src]    ; eax = eax - [src] - carry ; we here IF carry = 1