簡體   English   中英

大會-NASM析因問題

[英]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!時會發生什么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

這里已經有兩個問題:

  1. 您期望從C調用時此代碼可以正常工作,這意味着您需要遵循通常的調用約定(對於具有gcc的Linux,這表示“ cdecl”-請參閱http://en.wikipedia.org/wiki/X86_calling_conventions ) 。 因此,您需要保留esiediebpebx 但是此代碼將覆蓋esi 當從C調用函數時,這將導致無法預測的行為,因為C編譯器生成的代碼將假定在調用factorial之前esi中的任何內容在返回時仍然存在。 只有先將它們的值保存在某個位置(並在返回之前將其還原),才能使用這些寄存器。

  2. 返回值以eax傳遞,但是您在此處未將任何內容放入eax 您想要1!的答案1! 1 ,而不是“什么亂垃圾恰好是eax的時刻”!

2)遞歸的遞歸情況

...
.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
...
  1. esi一樣, edi是需要保留的寄存器,如上所述。

  2. mov edi, esi ;copies n - 1 into edi是錯誤的。 您不想將n - 1放入edi您正在嘗試計算(n-1)!*n ,所以您想放入(n-1)! 進入edi ,即由遞歸調用計算出的答案。 正如@lloydm指出的那樣,該結果以eax返回。 (我被原始答案中的評論誤導了,並認為您確實是在嘗試將edi n - 1放入edi 。這行不通,因為esicall factorial之后不再包含n - 1 ,因為您沒有請遵守通話慣例。)

  3. mov esi,[ebp+4] ;gets the original value back (n)是錯誤的(正如我最初指出的那樣); [ebp+4]包含返回地址; 這應該是[ebp-4]

3)奇怪的大價值

4! = 13803416593125867520 4! = 13803416593125867520是一個奇怪的答案,比它最初出現的要大:對於32位值而言,它太大了。 (以十六進制表示: 0xbf8f964200000000 ,所以它是一個64位值,在前32位中有一個大數字,在后32位中為零。)

考慮到其他錯誤,您可能希望得到一個完全隨機的值作為答案,但是factorial返回32位隨機值。 那么,為什么在這里打印64位值? (如果您不是故意這樣做的話,我想它可能與C代碼做了一些奇怪的事情有關,因為您的代碼並未保留esiedi 。)

調試提示

不要從試圖弄清​​為什么factorial(5)不起作用的開始。 使用factorial(1)盡可能簡單地開始。 然后處理factorial(2)等。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM