如何计算 i386 中字符串中 char 的出现次数?

How do I count the occurence of a char in a string in i386?

我是 80386 汇编语言的新手。目前正在努力完成一项要求用汇编语言编写将在 c 程序中调用的函数的学校作业。

extern int count(char *string, char c);

我想我知道应该怎么做,但仍在努力选择正确的指令(指令以 'b'、'w' 或 'l' 结尾),也许“正确的”寄存器,我知道有一些是为特定目的保留的。

.text
.global count

count:
    pushl   %ebp        # set up stack frame
    movl    %esp,%ebp   # save %esp in %ebp
    subl    , %esp   # automatic variables
    movl    [=10=], %eax    # initialize %eax to 0
    movl    8(%ebp), %esi   # pointer to s
    movb    12(%ebp), %bh   # pointer to c

check:
    movb    (%esi), %bl # move the first char in s to %bl
    cmp     0, %bl      # if the char is [=10=] 
    je      done        # job is done

    cmp     %bh, %bl    # else compare the char to %bh
    je      found1      # if match increase the counter
    incb    %bl         # else move to next char
    jmp     check

found1:
    addl    , %eax    # found a match
    incb    %bl
    jmp     check       # go back to the beginning of check
    
done:
    movl    %ebp, %esp  # restore %esp from %ebp
    popl    %ebp        # restore %ebp
    ret

.end

我对这个程序的理解是它应该将两个值(string和char)的地址存储到两个寄存器中。然后通过char访问字符串char,并将其与存储在另一个寄存器中的char进行比较。如果找到匹配项,则增加 %eax 中的 return 值,否则转到字符串中的下一个字符,直到到达结尾 [=12=]

我的程序似乎陷入了循环,因为它既没有崩溃也没有输出结果。

我们将不胜感激。

我不会用汇编程序编程所以我让 gcc 帮我编译它:

int count(const char *str, const char ch)
{
    int count = 0;
    while(*str) 
    {
        if(*str == ch) count++;
        str++;
    }
    return count;
}
count:
        pushl   %ebx
        movl    8(%esp), %edx
        movb    12(%esp), %cl
        movb    (%edx), %al
        xorl    %ebx, %ebx
        testb   %al, %al
        je      .L1
.L4:
        cmpb    %cl, %al
        jne     .L3
        incl    %ebx
.L3:
        incl    %edx
        movb    (%edx), %al
        testb   %al, %al
        jne     .L4
.L1:
        movl    %ebx, %eax
        popl    %ebx
        ret

我认为没有真正的理由将 %esp 保存到 %ebp,或从 %esp 中减去。您确实需要保存 %esi。我认为a,b,c,d寄存器可以安全丢失,但如果不能(我已经有一段时间没用汇编了),你需要保存%ebx还有。
(更新:正如@NateEldredge 指出的那样,必须保留 %ebx - 我忘记更新堆栈指针。是的,已经太久了)。

count:
    pushl   %esi             # save %esi as we use it
    pushl   %ebx
    # "In assembly language, all the labels and numeric constants used 
    #  as immediate operands (i.e. not in an address calculation like 
    #  3(%eax,%ebx,8)) are always prefixed by a dollar sign."
    #  https://flint.cs.yale.edu/cs421/papers/x86-asm/asm.html
    movl    12(%esp), %esi   # pointer to s
    movb    16(%esp), %bh    # char
    # I think it's more common "xor %eax, %eax"
    movl    [=10=], %eax         # initialize %eax to 0

check:
    movb    (%esi), %bl      # move the current char in s to %bl
    cmp     [=10=], %bl          # if the char is [=10=] 
    je      done             # job is done

    cmp     %bh, %bl         # else compare the char to %bh
    je      found1           # if match increase the counter
    # We must increase the pointer to the character, not %bl
    incl    %esi             # else move to next char
    jmp     check
found1:
    addl    , %eax         # found a match
    # incb    %bl
    incl    %esi             # move to next char
    jmp     check            # go back to the beginning of check
done:
    popl    %ebx
    popl    %esi             # restore %esi
    ret

.end

您也可以反转测试以保存一些指令:

    cmp     %bh, %bl         # else compare the char to %bh
    jne     notfound         # if not match, skip incrementing
    addl    , %eax         # found a match
notfound:
    incl    %esi             # move to next char
    jmp     check