[英]Assembly - NASM Factorial issues
Hi I am writing a factorial function in Assembly using NASM. 嗨,我正在使用NASM在Assembly中编写阶乘函数。 I have to use russian multiplication in place of the mul for my assignment.
我必须用俄语乘法代替mul来分配作业。 I am using 32 bit linux
我正在使用32位Linux
Here is my Factorial code 这是我的阶乘代码
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
Here is my rpmult code: 这是我的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
When I use the function through a C program, say the factorial of 4! 当我通过C程序使用该功能时,请说4阶乘! I get
我懂了
4! = 13803416593125867520
I believe my code is right, but I have no idea what to do. 我相信我的代码是正确的,但是我不知道该怎么做。 I need to get the factorial function working with the rpmult function for my final.
我需要获得与rpmult函数一起使用的阶乘函数,以便最终完成。 Any help is appreciated!
任何帮助表示赞赏! Thanks!
谢谢!
(Note: I've rewritten this answer, after looking at it again while I'm more awake, and having read @lloydm's comment.) (注意:在我更加清醒的时候再次查看它并阅读了@lloydm的评论后,我已经重写了此答案。)
There are three problem areas: 存在三个问题区域:
When debugging recursive functions, it's always sensible to check the base case first. 在调试递归函数时,先检查基本情况总是明智的。
So what happens when calculating 1!
那么计算
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
There are two issues here already: 这里已经有两个问题:
You're expecting this code work correctly when called from C, which means that you need to follow the usual calling convention (which for Linux with gcc means "cdecl" - see http://en.wikipedia.org/wiki/X86_calling_conventions ). 您期望从C调用时此代码可以正常工作,这意味着您需要遵循通常的调用约定(对于具有gcc的Linux,这表示“ cdecl”-请参阅http://en.wikipedia.org/wiki/X86_calling_conventions ) 。 So you need to preserve
esi
, edi
, ebp
and ebx
. 因此,您需要保留
esi
, edi
, ebp
和ebx
。 But this code is overwriting whatever was in esi
. 但是此代码将覆盖
esi
。 This will lead to unpredictable behaviour when the function is called from C, because the code generated by the C compiler will assume that whatever was in esi
before factorial
was called is still there when it returns. 当从C调用函数时,这将导致无法预测的行为,因为C编译器生成的代码将假定在调用
factorial
之前esi
中的任何内容在返回时仍然存在。 You can only use these registers if you save their values somewhere first (and restore them before returning). 只有先将它们的值保存在某个位置(并在返回之前将其还原),才能使用这些寄存器。
The return value is passed out in eax
, but you're not putting anything into eax
here. 返回值以
eax
传递,但是您在此处未将任何内容放入eax
。 You want the answer for 1!
您想要
1!
的答案1!
to be 1
, not "whatever random junk happens to be in eax
at the moment"! 是
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
, like esi
, is a register which needs to be preserved, as described above. 像
esi
一样, edi
是需要保留的寄存器,如上所述。
The line mov edi, esi ;copies n - 1 into edi
is wrong. mov edi, esi ;copies n - 1 into edi
是错误的。 You don't want to put n - 1
into edi
- you're trying to calculate (n-1)!*n
here, so you want to put (n-1)!
您不想将
n - 1
放入edi
您正在尝试计算(n-1)!*n
,所以您想放入(n-1)!
into edi
, ie the answer calculated by the recursive call. 进入
edi
,即由递归调用计算出的答案。 Which, as @lloydm points out, is returned in eax
. 正如@lloydm指出的那样,该结果以
eax
返回。 (I was misled by the comment in my original answer, and thought that you really were trying to put n - 1
into edi
. That wouldn't work either because esi
no longer contains n - 1
after the call factorial
, because you don't follow the calling conventions.) (我被原始答案中的评论误导了,并认为您确实是在尝试将
edi
n - 1
放入edi
。这行不通,因为esi
在call factorial
之后不再包含n - 1
,因为您没有请遵守通话惯例。)
mov esi,[ebp+4] ;gets the original value back (n)
is wrong (as I noted originally); mov esi,[ebp+4] ;gets the original value back (n)
是错误的(正如我最初指出的那样); [ebp+4]
contains the return address; [ebp+4]
包含返回地址; this should be [ebp-4]
. 这应该是
[ebp-4]
。
4! = 13803416593125867520
4! = 13803416593125867520
is a stranger answer than it first appears: it's far too large for a 32-bit value. 4! = 13803416593125867520
是一个奇怪的答案,比它最初出现的要大:对于32位值而言,它太大了。 (In hex: 0xbf8f964200000000
, so it's a 64-bit value with a big number in the top 32 bits and zero in the bottom 32 bits.) (以十六进制表示:
0xbf8f964200000000
,所以它是一个64位值,在前32位中有一个大数字,在后32位中为零。)
You might expect to get a completely random value as an answer, given the other bugs, but factorial
returns a 32-bit random value. 考虑到其他错误,您可能希望得到一个完全随机的值作为答案,但是
factorial
返回32位随机值。 So why are you printing a 64-bit value here? 那么,为什么在这里打印64位值? (If you're not doing it deliberately, I suppose it could possibly be related to the C code doing something strange because
esi
and edi
have not been preserved by your code.) (如果您不是故意这样做的话,我想它可能与C代码做了一些奇怪的事情有关,因为您的代码并未保留
esi
和edi
。)
Don't start by trying to work out why factorial(5)
doesn't work. 不要从试图弄清为什么
factorial(5)
不起作用的开始。 Start as simply as possible, with factorial(1)
. 使用
factorial(1)
尽可能简单地开始。 Then work up to factorial(2)
, etc. 然后处理
factorial(2)
等。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.