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

You can leave links to different KoOS-related sites and articles here
  • Insolor wrote:Искал реализацию strlen на ассемблере, наткнулся на этот сайт:
    http://www.int80h.org/ - Unix Assembly Language Programming
    В примерах используется intel синтаксис.
    http://www.manhunter.ru/assembler/403_n ... blere.html - там 3 варианта strlen в зависимости от того, что требуется производительность или малый размер кода.
  • 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 в зависимости от того, что требуется производительность или малый размер кода.
    Эту статью вроде просматривал, но уже забыл про нее, поэтому полез в поиск. Для моих целей достаточно будет и первого варианта.
  • 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, может быть, там уже что-то изменилось.
  • Я у 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
    На нем и остановился для своих целей.
  • 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
    ; ------------------------------------
  • Последний крут (позволяет избавиться от 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
    
    Красота!
  • Выделил "Реализация 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
  • Who is online

    Users browsing this forum: No registered users and 1 guest