section .bss
input resb 10 ; Reservar 10 bytes para leer el número desde el teclado
result resb 12 ; Reservar 12 bytes para almacenar el resultado convertido a texto
section .data
prompt db "Ingrese un número: ", 0 ; Mensaje de entrada
prompt_len equ $ - prompt ; Longitud del mensaje
newline db 0xA, 0 ; Caracter de nueva línea ('\n') seguido de nulo
section .text
global _start ; Punto de entrada del programa
_start:
; --- Mostrar mensaje de entrada ---
mov eax, 4 ; syscall number 4 = write
mov ebx, 1 ; descriptor 1 = stdout
mov ecx, prompt ; dirección del mensaje
mov edx, prompt_len ; longitud del mensaje
int 0x80 ; invocar syscall
; --- Leer número desde teclado ---
mov eax, 3 ; syscall number 3 = read
mov ebx, 0 ; descriptor 0 = stdin
mov ecx, input ; buffer donde se guarda lo leído
mov edx, 10 ; leer hasta 10 caracteres
int 0x80 ; invocar syscall
; --- Convertir cadena ASCII a número entero ---
mov esi, input ; puntero al inicio de la cadena leída
call atoi ; resultado se devuelve en eax
; --- Calcular el factorial del número ---
push eax ; pasar el número como argumento a la pila
call factorial ; llamada a función factorial
add esp, 4 ; limpiar el argumento de la pila
; --- Convertir el resultado (eax) a texto ---
mov ebx, eax ; guardar el resultado en ebx
mov edi, result + 11 ; puntero al final del buffer (último byte disponible)
mov byte [edi], 0 ; agregar terminador nulo
dec edi ; retroceder al último carácter útil
call itoa ; convierte el número a texto, resultado comienza en edi
; --- Calcular longitud del número convertido ---
mov ecx, edi ; guardar puntero al inicio del texto convertido
mov esi, result ; inicio del buffer
add esi, 11 ; puntero al final del buffer
sub esi, ecx ; longitud = fin - inicio
mov edx, esi ; guardar longitud del texto en edx
; --- Mostrar el número en pantalla ---
mov eax, 4 ; syscall number 4 = write
mov ebx, 1 ; descriptor 1 = stdout
mov ecx, edi ; puntero al inicio del texto convertido
int 0x80 ; invocar syscall
; --- Mostrar salto de línea ---
mov eax, 4 ; syscall: write
mov ebx, 1 ; stdout
mov ecx, newline ; dirección del caracter '\n'
mov edx, 1 ; longitud = 1 byte
int 0x80 ; invocar syscall
; --- Salir del programa ---
mov eax, 1 ; syscall number 1 = exit
xor ebx, ebx ; código de salida = 0
int 0x80 ; invocar syscall
; ------------------------------------------------------------
; atoi: convierte cadena ASCII decimal a número entero
; Entrada: ESI apunta al inicio de la cadena
; Salida: EAX contiene el número
; ------------------------------------------------------------
atoi:
xor eax, eax ; inicializar acumulador a 0
.next_digit:
movzx ecx, byte [esi] ; cargar siguiente byte como entero sin signo
cmp ecx, 10 ; ¿es fin de línea (LF)?
je .done ; si sí, terminar
cmp ecx, '0' ; ¿es menor que '0'?
jl .done ; si sí, terminar
cmp ecx, '9' ; ¿es mayor que '9'?
jg .done ; si sí, terminar
sub ecx, '0' ; convertir carácter ASCII a número
imul eax, eax, 10 ; desplazar el acumulador decimal
add eax, ecx ; sumar el nuevo dígito
inc esi ; pasar al siguiente carácter
jmp .next_digit ; repetir
.done:
ret ; devolver número en eax
; ------------------------------------------------------------
; factorial: calcula factorial recursivamente
; Entrada: número en [esp + 4]
; Salida: resultado en eax
; ------------------------------------------------------------
factorial:
mov eax, [esp + 4] ; obtener argumento desde la pila
cmp eax, 1 ; si eax <= 1
jbe .base ; retornar 1 en ese caso
dec eax ; preparar n - 1
push eax ; empujar n - 1 a la pila
call factorial ; llamada recursiva
add esp, 4 ; limpiar la pila
mov ebx, [esp + 4] ; recuperar n original desde la pila
imul eax, ebx ; resultado = n * factorial(n - 1)
ret ; retornar resultado
.base:
mov eax, 1 ; retornar 1
ret
; ------------------------------------------------------------
; itoa: convierte entero a cadena ASCII decimal
; Entrada: EBX = número, EDI = fin del buffer
; Salida: EDI apunta al inicio del texto
; ------------------------------------------------------------
itoa:
xor edx, edx ; limpiar resto
mov eax, ebx ; mover número a dividir a eax
mov ecx, 10 ; base decimal
.convert:
xor edx, edx ; limpiar resto antes de div
div ecx ; dividir eax / 10, resto en edx
add dl, '0' ; convertir dígito a ASCII
mov [edi], dl ; guardar carácter en buffer
dec edi ; moverse hacia atrás en el buffer
test eax, eax ; ¿quedan más dígitos?
jnz .convert ; si sí, continuar
inc edi ; ajustar puntero al primer carácter válido
ret ; retornar puntero en edi