繁体   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