proc string.length uses ebx, _str
    mov     eax, 0
    mov     ebx, [_str]
  @@:
    cmp     [ebx], byte 0
    je	    @f
    inc     eax
    inc     ebx
    jmp     @b
  @@:
    ret
 endp

 proc string.copy uses eax ebx ecx, _src, _dst
    mov     eax, [_src]
    mov     ebx, [_dst]
  @@:
    mov     cl, [eax]
    mov     [ebx], cl
    cmp     cl, 0
    je	    @f
    inc     eax
    inc     ebx
    jmp     @b
  @@:
    ret
 endp

 proc string.concatenate uses eax, _src, _dst
    stdcall string.length, [_dst]
    add     eax, [_dst]
    stdcall string.copy, [_src], eax
    ret
 endp

 proc string.compare uses ebx ecx edx, _str1, _str2
    mov     eax, 0
    mov     ebx, [_str1]
    mov     ecx, [_str2]
  @@:
    mov     dl, [ebx]
    cmp     dl, [ecx]
    jne     .not_eq
    cmp     dl, 0
    je	    @f
    cmp     [ecx], byte 0
    je	    @f
    inc     ebx
    inc     ecx
    jmp     @b
 .not_eq:
    mov     eax, -1
  @@:
    inc     eax
    ret
 endp

 proc string.to_lower_case uses eax, _str
    mov     eax, [_str]
  @@:
    cmp     [eax], byte 0
    je	    @f
    cmp     [eax], byte 65
    jl	    .next
    cmp     [eax], byte 90
    jg	    .next
    add     [eax], byte 97 - 65
 .next:
    inc     eax
    jmp     @b
  @@:
    ret
 endp

 proc string.to_upper_case uses eax, _str
    mov     eax, [_str]
  @@:
    cmp     [eax], byte 0
    je	    @f
    cmp     [eax], byte 97
    jl	    .next
    cmp     [eax], byte 122
    jg	    .next
    sub     [eax], byte 97 - 65
 .next:
    inc     eax
    jmp     @b
  @@:
    ret
 endp

 proc string.match uses ebx ecx edx, _str1, _str2
    mov     ebx, [_str1]
    mov     ecx, [_str2]
  @@:
    cmp     [ebx], byte 0
    je	    @f
    cmp     [ecx], byte 0
    je	    @f

    mov     dl, [ebx]
    cmp     [ecx], byte '?'
    je	    .next
    cmp     [ecx], byte '*'
    je	    .next_ebx
    cmp     [ecx], dl
    je	    .next

    cmp     [ecx - 1], byte '*'
    je	    .next_ecx

    jmp     @f

 .next_ecx:
    dec     ecx
    jmp     .next
 .next_ebx:
    dec     ebx
 .next:
    inc     ebx
    inc     ecx
    jmp     @b
  @@:

  @@:
    cmp     [ecx], byte 0
    je	    @f
    cmp     [ecx], byte '*'
    jne     @f
    inc     ecx
    jmp     @b
  @@:

    cmp     [ecx], byte 0
    je	    @f
    mov     eax, 0
    ret
  @@:
    mov     eax, 1
    ret
 endp

 proc string.trim_last uses eax, _str
    stdcall string.length, [_str]
    add     eax, [_str]
    dec     eax
  @@:
    cmp     [eax], byte ' '
    jne     @f
    mov     [eax], byte 0
    dec     eax
    jmp     @b
  @@:
    ret
 endp

 proc string.trim_first, _str
    mov     eax, [_str]
  @@:
    cmp     [eax], byte ' '
    jne     @f
    inc     eax
    jmp     @b
  @@:
    ret
 endp

 proc string.index_of uses ebx ecx, _str, _char, _num
    mov     ebx, [_char]
    mov     ecx, [_str]
    mov     eax, 0
  @@:
    cmp     [ecx], byte 0
    je	    @f
    cmp     [ecx], bl
    jne     .after_check
    dec     [_num]
    jz	    .finded
 .after_check:
    inc     ecx
    inc     eax
    jmp     @b
  @@:
    mov     eax, -1
 .finded:
    ret
 endp

 proc string.last_index_of uses ebx ecx, _str, _char, _num
    stdcall string.length, [_str]
    mov     ecx, [_str]
    add     ecx, eax
    mov     ebx, [_char]
  @@:
    cmp     eax, 0
    je	    @f
    cmp     [ecx], bl
    jne     .after_check
    dec     [_num]
    jz	    .finded
 .after_check:
    dec     ecx
    dec     eax
    jmp     @b
  @@:
    mov     eax, -2
 .finded:
    inc     eax
    ret
 endp