Ассамблея - NASM Факторные вопросы - PullRequest
0 голосов
/ 10 декабря 2011

Привет, я пишу факториальную функцию в ассемблере, используя NASM. Я должен использовать русское умножение вместо мул для моего задания. Я использую 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 для моего финала. Любая помощь приветствуется! Спасибо!

1 Ответ

1 голос
/ 10 декабря 2011

(Примечание: я переписал этот ответ, после того, как снова посмотрел на него, пока проснулся и прочитал комментарий @ 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

Здесь уже есть две проблемы:

  1. Вы ожидаете, что этот код будет работать правильно при вызове из C, что означает, что вам нужно следовать обычному соглашению о вызовах (которое дляLinux с gcc означает «cdecl» - см. http://en.wikipedia.org/wiki/X86_calling_conventions). Так что вам нужно сохранить esi, edi, ebp и ebx. Но этот код перезаписывает все, что было в esi. Это будетприводят к непредсказуемому поведению, когда функция вызывается из C, потому что код, сгенерированный компилятором C, будет предполагать, что все, что было в esi до вызова factorial, все еще остается там, когда оно возвращается.Вы должны сначала сохранить их значения где-нибудь (и восстановить их перед возвратом).

  2. Возвращаемое значение передается в eax, но вы ничего не помещаете в eax здесь.Вы хотите, чтобы ответ для 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. edi, как и esi, - это регистр, который необходимо сохранить, как описано выше.

  2. Линия mov edi, esi ;copies n - 1 into edi неверна.Вы не хотите помещать n - 1 в edi - вы пытаетесь вычислить (n-1)!*n здесь, поэтому вы хотите поместить (n-1)! в edi, то есть ответ, вычисленный рекурсивным вызовом.Который, как указывает @lloydm, возвращается в eax.(Я был введен в заблуждение комментарием в моем первоначальном ответе и подумал, что вы действительно пытаетесь вставить n - 1 в edi. Это тоже не сработает, потому что esi больше не содержит n - 1 после call factorialпотому что вы не соблюдаете соглашения о вызовах.)

  3. mov esi,[ebp+4] ;gets the original value back (n) неправильно (как я отмечал изначально);[ebp+4] содержит обратный адрес;это должно быть [ebp-4].

3) Странно большое значение

4! = 13803416593125867520 - более странный ответ, чем кажется на первый взгляд: оно слишком велико для 32-битное значение.(В шестнадцатеричном виде: 0xbf8f964200000000, так что это 64-битное значение с большим числом в старших 32 битах и ​​нулем в нижних 32 битах.)

Вы можете ожидать получить совершенно случайное значение какответ, учитывая другие ошибки, но factorial возвращает 32-битное случайное значение.Так почему вы печатаете 64-битное значение здесь?(Если вы не делаете это намеренно, я полагаю, это может быть связано с тем, что код C делает что-то странное, потому что esi и edi не были сохранены вашим кодом.)

Совет отладки

Не начинайте с попытки понять, почему factorial(5) не работает.Начните как можно проще с factorial(1).Тогда работай до factorial(2) и т. Д.

...