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