[英]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.