2013-10-07 2 views
1

Изучение сборки NASM на 32-битном Ubuntu. Теперь я пытаюсь узнать о рекурсивных функциях, начиная с factorial (обратите внимание: здесь я предполагаю, что параметр всегда будет неотрицательным).Понимание рекурсивной факторной функции в сборке NASM

Если предположить, что у меня есть

push 3 
call factorial 

Я хочу, чтобы в конечном итоге с 6 в EAX.

Вот моя попытка:

SECTION .text 
global main 
main: 
; ----------------------------------------------------------- 
; Main 
; ----------------------------------------------------------- 
push 3 
call factorial 

; ----------------------------------------------------------- 
; Exit 
; ----------------------------------------------------------- 
mov EAX,1 
int 0x80 

; ----------------------------------------------------------- 
; Recursive Factorial: n! = n * (n - 1)! 
; ----------------------------------------------------------- 
factorial: 

push EBP   ; Retrieve parameter and put it 
mov  EBP,ESP  ; into EBX register 
add  EBP,8  ; 
mov  EBX,[EBP] ; EBX = Param 

cmp  EBX,0  ; Check for base case 
je  base  ; It is base if (n == 0) 

dec  EBX   ; Decrement EBX to put it in the stack 
push EBX   ; Put (EBX - 1) in stack 
inc  EBX   ; Restore EBX 
call factorial ; Calculate factorial for (EBX - 1) 
mul  EBX   ; EAX = (EAX * EBX) = (EBX - 1)! * EBX 
pop  EBX   ; Retrieve EBX from the stack 

jmp end 
base:    ; Base case 
mov  EAX,1  ; The result would be 1 

end: 

pop  EBP   ; Release EBP 
ret 

По крайней мере, это работает для базового случая, ха ... Но для любого другого значения я толкнуть, она всегда возвращает 0. У меня было подозрение, что, может быть, с EAX будет 0, MUL всегда будет приводить к 0, объясняя это. Чтобы проверить, я решил дать EAX значение 2, ожидая некоторого ненулевого значения, но оно продолжалось в результате 0.

Можете ли вы посоветовать мне, как сделать рекурсивную факторную функцию, которая принимает свой параметр из стека? Я верю, что видел некоторые примеры, но либо они не были рекурсивными, либо принимали свои параметры из других мест, либо использовали кучу переменных (когда я думаю, что это можно сделать только с помощью регистров).

+0

Подсказка: записать функцию в C, скомпилировать с 'GCC -Wall -O3 -m32 -S ...' затем использовать полученный компилятор генерируется источником ассемблерного в качестве шаблона для вашей собственной функции (или даже для общего руководства по управлению стеком, передачи параметры и результаты функции и т. д.). –

+0

@PaulR: Спасибо за совет. Кто-то однажды рассказал мне о сайте, который перевел C++ на Assembly (http://assembly.ynh.io/), как вы думаете, это хорошо для этой цели? – Voldemort

+0

Поскольку вы используете Linux, у вас уже есть все необходимые инструменты (например, gcc), но у вас никогда не будет слишком много инструментов, и если сайт может сгенерировать asm в формате Intel, подходящем для NASM, тогда вам может быть более полезно в этом частный случай. Попробуйте оба в любом случае - это хороший способ изучить материал. –

ответ

3

Обратите внимание, что factorial(n-1) перепишет factorial(n)'s значение EBX первое, что он делает, таким образом, оказание inc EBX после push бессмысленно. После того, как вы достигли базового варианта вы будете иметь ситуацию, когда EBX равен 0, когда вы делаете mul, и, конечно, ничего * 0 == 0.

Самое простое решение было бы изменить пролог:

push EBP   ; Retrieve parameter and put it 
push EBX   ; save previous param 
mov  EBP,ESP  ; into EBX register 
add  EBP,12  ; 
mov  EBX,[EBP] ; EBX = Param 

И эпилог:

pop  EBX   ; restore previous param 
pop  EBP   ; Release EBP 
ret 
Смежные вопросы