您好我正在使用NASM在Assembly中编写阶乘函数。为了我的任务,我必须使用俄语乘法代替mul。我使用的是32位linux
这是我的因子代码
section .text
global factorial
extern rpmult
factorial:
push ebp
mov ebp, esp
sub esp, 4 ;creates memory for local variable at ebp-4
mov esi, [ebp+8] ; put n in esi
cmp esi, 1 ; n <= 1
jbe .done ; if so jump to done
.try:
mov [ebp-4],esi ;adds n temporarily into ebp-4
dec esi ; n - 1
push esi ; push arugment
call factorial ;call factorial again stores result in esi
add esp, 4 ;gets rid of the argument
mov edi, esi ;copies n - 1 into edi
mov esi,[ebp+4] ;gets the original value back (n)
call rpmult ;multiply
jmp .done ;once it reaches here, finished the function
.done:
mov esp, ebp ;restores esp
pop ebp
ret ;return the value
这是我的rpmult代码:
section .text
global rpmult
rpmult:
push ebp
mov ebp, esp
sub esp, 4 ;allocate m
mov dword [ebp-4], 0 ; m = 0;
.while:
test edi, edi ; x == 0?
je .done
test esi, esi ; y == 0?
je .done
test edi, 0x01 ; x is odd?
jz .shifts
add [ebp-4], esi ; m += y;
.shifts:
shr edi, 1 ; x >>= 1;
shl esi, 1 ; y <<= 1;
jmp .while
.done:
mov eax, [ebp-4]
;mov esp, ebp
;pop ebp
leave
ret
当我通过C程序使用该功能时,请说4的阶乘!我得到了
4! = 13803416593125867520
我相信我的代码是正确的,但我不知道该怎么做。我需要让factorial函数与我的final的rpmult函数一起使用。任何帮助表示赞赏! 谢谢!
答案 0 :(得分:1)
(注意:在我清醒过来之后再次查看这个答案并阅读了@ lloydm的评论后,我重写了这个答案。)
有三个问题领域:
调试递归函数时,首先检查基本情况总是明智的。
那么在计算1!
时会发生什么?
factorial:
push ebp
mov ebp, esp
sub esp, 4 ;creates memory for local variable at ebp-4
mov esi, [ebp+8] ; put n in esi
cmp esi, 1 ; n <= 1
jbe .done ; if so jump to done
...
.done:
mov esp, ebp ;restores esp
pop ebp
ret ;return the value
这里有两个问题:
从C调用时,您希望此代码正常工作,这意味着您需要遵循通常的调用约定(对于Linux,使用gcc意味着“cdecl” - 请参阅http://en.wikipedia.org/wiki/X86_calling_conventions)。因此,您需要保留esi
,edi
,ebp
和ebx
。但是这段代码会覆盖esi
中的任何内容。当从C调用函数时,这将导致不可预测的行为,因为C编译器生成的代码将假定在调用esi
之前factorial
中的任何内容在返回时仍然存在。如果先将值保存在某处(并在返回前将其恢复),则只能使用这些寄存器。
返回值在eax
中传递,但您没有在eax
中添加任何内容。您希望1!
的答案为1
,而不是“目前eax
中的任何随机垃圾”!
...
.try:
mov [ebp-4],esi ;adds n temporarily into ebp-4
dec esi ; n - 1
push esi ; push arugment
call factorial ;call factorial again stores result in esi
add esp, 4 ;gets rid of the argument
mov edi, esi ;copies n - 1 into edi
mov esi,[ebp+4] ;gets the original value back (n)
call rpmult ;multiply
jmp .done ;once it reaches here, finished the function
...
edi
与esi
一样,是一个需要保留的注册表,如上所述。
第mov edi, esi ;copies n - 1 into edi
行是错误的。您不想将n - 1
放入edi
- 您在此处尝试计算(n-1)!*n
,因此您希望将(n-1)!
放入edi
,即递归调用计算的答案。正如@lloydm指出的那样,在eax
中返回。 (我在原始答案中被评论误导了,并且认为你真的试图将n - 1
放入edi
。这不会因为esi
不再包含{{}而无效1}}在n - 1
之后,因为你没有遵循调用约定。)
call factorial
错了(正如我最初指出的那样); mov esi,[ebp+4] ;gets the original value back (n)
包含返回地址;这应该是[ebp+4]
。
[ebp-4]
是一个比第一次出现的更陌生的答案:它对于32位值来说太大了。 (在十六进制中:4! = 13803416593125867520
,所以它是一个64位值,前32位有一个大数,后32位有零。)
考虑到其他错误,您可能希望得到一个完全随机的值作为答案,但0xbf8f964200000000
会返回一个32位的随机值。那么为什么要在这里打印64位值呢? (如果你没有故意这样做,我想它可能与C代码做了一些奇怪的事情有关,因为代码没有保留factorial
和esi
。)
首先要弄清楚为什么edi
不起作用。使用factorial(5)
尽可能简单地开始。然后一直工作到factorial(1)
等。