[英]Assembly - NASM Factorial issues
嗨,我正在使用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
我相信我的代码是正确的,但是我不知道该怎么做。 我需要获得与rpmult函数一起使用的阶乘函数,以便最终完成。 任何帮助表示赞赏! 谢谢!
(注意:在我更加清醒的时候再次查看它并阅读了@lloydm的评论后,我已经重写了此答案。)
存在三个问题区域:
在调试递归函数时,先检查基本情况总是明智的。
那么计算1!
时会发生什么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调用时此代码可以正常工作,这意味着您需要遵循通常的调用约定(对于具有gcc的Linux,这表示“ cdecl”-请参阅http://en.wikipedia.org/wiki/X86_calling_conventions ) 。 因此,您需要保留esi
, edi
, ebp
和ebx
。 但是此代码将覆盖esi
。 当从C调用函数时,这将导致无法预测的行为,因为C编译器生成的代码将假定在调用factorial
之前esi
中的任何内容在返回时仍然存在。 只有先将它们的值保存在某个位置(并在返回之前将其还原),才能使用这些寄存器。
返回值以eax
传递,但是您在此处未将任何内容放入eax
。 您想要1!
的答案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
...
像esi
一样, edi
是需要保留的寄存器,如上所述。
mov edi, esi ;copies n - 1 into edi
是错误的。 您不想将n - 1
放入edi
您正在尝试计算(n-1)!*n
,所以您想放入(n-1)!
进入edi
,即由递归调用计算出的答案。 正如@lloydm指出的那样,该结果以eax
返回。 (我被原始答案中的评论误导了,并认为您确实是在尝试将edi
n - 1
放入edi
。这行不通,因为esi
在call factorial
之后不再包含n - 1
,因为您没有请遵守通话惯例。)
mov esi,[ebp+4] ;gets the original value back (n)
是错误的(正如我最初指出的那样); [ebp+4]
包含返回地址; 这应该是[ebp-4]
。
4! = 13803416593125867520
4! = 13803416593125867520
是一个奇怪的答案,比它最初出现的要大:对于32位值而言,它太大了。 (以十六进制表示: 0xbf8f964200000000
,所以它是一个64位值,在前32位中有一个大数字,在后32位中为零。)
考虑到其他错误,您可能希望得到一个完全随机的值作为答案,但是factorial
返回32位随机值。 那么,为什么在这里打印64位值? (如果您不是故意这样做的话,我想它可能与C代码做了一些奇怪的事情有关,因为您的代码并未保留esi
和edi
。)
不要从试图弄清为什么factorial(5)
不起作用的开始。 使用factorial(1)
尽可能简单地开始。 然后处理factorial(2)
等。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.