汇编乘法循环返回错误的高数

Assembly multiplication loop returning wrong high number

我正在尝试编写一个 for 循环,它通过将一个数字 (var a) 乘以另一个数字 (var b) 次来进行乘法运算。

.globl times

times:
movl [=10=], %ecx        # i = 0

cmpl %ecx, %esi       #if b-i
jge end              # if >= 0, jump to end


 loop:
 addl (%edi, %eax), %eax  #sum += a
 incl %ecx               # i++
 cmpl %esi, %ecx         # compare (i-b)
 jl loop                 # < 0? loop b times total

 end:
 ret

我哪里错了?我已经 运行 通过逻辑,我无法弄清楚问题是什么。

这是伪代码,请牢记。

mov X,ebx <- put into EBX your counter, your B
mov Y,edx <- put into EDX your value, your A
mov 0,eax <- Result

loop:
add eax,edx
dec ebx
jnz loop <- While EBX is not zero

上面的实现应该会导致你的值进入 EAX。您的代码似乎缺少 eax 初始化。

TL:DR:您没有将 EAX 归零,并且您的 ADD 指令正在使用内存操作数。


你应该用过调试器。您很容易看出 EAX 一开始就不是零。有关使用 gdb 调试 asm 的提示,请参阅 标记 wiki 的底部。

我猜你正在使用 x86-64 系统 V ABI,所以你的参数(a 和 b)在 %edi 和 %esi 中。

在一个函数的开头,除了保存你的参数的寄存器之外,其他的寄存器应该被假定为包含垃圾。即使是 保存参数的寄存器的高位部分也可能包含垃圾。 (此规则的例外情况:


两个 arg 都不是指针,因此您不应该取消对它们的引用。 add (%edi, %eax), %eax 计算一个 32 位地址为 EDI+EAX,然后从那里加载 32 位。它将那个双字添加到 EAX(目标操作数)。

令我震惊的是您的程序没有出现段错误,因为您将整数 arg 用作指针。

对于许多 x86 指令(如 ADD),目标操作数不是只写的。 add %edi, %eax 确实 EAX += EDI。我认为您混淆了 3 操作数 RISC 语法,其中您可能有像 add %src1, %src2, %dst.

这样的指令

x86 有一些这样的指令,作为最近的扩展添加,比如 BMI2 bzhi, but the usual instructions are all 2-operand with destructive destinations. (except for LEA, where instead of loading from the address, it stores the address in the destination. So lea (%edi, %eax), %eax would work. You could even put the result in a different register. LEA is great for saving MOV instructions by doing shift+add and a mov all in one instruction, using the addressing mode syntax and machine-code encoding.


您有一条评论 ie eax = sum + (a x 4bits)。不知道你在说什么。 a 是 4 个字节(不是位),并且你没有将 a (%edi) 乘以任何东西。


只是为了好玩,下面是我编写函数的方式(如果我不得不避免 imul %edi, %esi / mov %esi, %eax)。为了简单起见,我假设两个参数都是非负的。如果您的参数是有符号整数,并且如果 b 为负则您必须循环 -b 次,那么您需要一些额外的代码。

# args: int a(%edi), int b(%esi)    # comments are important for documenting inputs/outputs to blocks of code
# return value: product in %eax
# assumptions: b is non-negative.
times:

    xor   %eax, %eax       # zero eax

    test  %esi, %esi       # set flags from b
    jz    loop_end         # early-out if it's zero

loop:                      # do{
    add   %edi, %eax       #  sum += a, 
    dec   %esi             #   b--  (setting flags based on the result, except for CF so don't use ja or jb after it)
    jge   loop             # }while(b>=0)

loop_end:
    ret

注意缩进样式,这样很容易找到分支目标。有些人喜欢为循环内的指令额外缩进。

你的方法工作正常(如果你做对了),但我的方法说明在 asm 中倒计时更容易(不需要额外的寄存器或立即数来保持上限)。此外,避免冗余比较。但是在您编写出至少可以正常工作的代码之前,不要担心优化。