Introducción
Este pequeño ejercicio de lenguaje ensamblador es para la arquitectura x86 (Procesadores Intel y Amd 32 bits) y utiliza la sintaxis de
Nasm, un ensamblador libre, gratuito y que puede ser utilizado en diferentes plataformas como Windows o Linux.
Además, las funciones externas utilizadas provienen de la biblioteca de C estándar.
Por lo tanto, no tendrás problemas con tu equipo para realizar este ejercicio: éste no depende del sistema operativo utilizado. Únicamente depende de la arquitectura x86.
Nota: Para utilizar nasm con el propósito de probar este ejercicio, puedes consultar el tutorial de uso e instalación de nasm para Windows y Linux haciendo clic
aquí
Nociones tratadas en este ejercicio
- Las funciones con parámetro de entrada
- Los saltos
- La pila
- Las cadenas de caracteres
Enunciado
El objetivo es el de escribir una función en ensamblador capaz de invertir una cadena de caracteres.
Esta función deberá aceptar como parámetro de entrada un puntero hacia una cadena de caracteres e invertir esta cadena en el mismo puntero.
Esto es lo que daría esta función en C:
void invertir_cadena(char *str); //El modelo de esta función
//Ejemplo de uso:
char string[] = "Esta es la cadena a invertir";
invertir_cadena(string);
printf(string); //Mostrará "ritrevni a anedac al se atsE"
Deberás insertar este código aquí:
extern printf
section .data
cadena db 'Invierteme! Yo te dire que programador eres!', 0x0
section .text
global main, invertir_cadena
invertir_cadena:
;Pon el código aquí
main:
mov eax, cadena ;Direccion de cadena en eax
push eax
;Llamado de invertir_cadena con la dirección de la cadena a invertir
call invertir_cadena
;Las dos líneas siguientes son opcionales ya que la dirección
De la cadena (ahora invertida) siempre está en la pila.
mov eax, cadena; Dirección de la cadena en eax
push eax
;Visualización de la cadena con printf
call printf
add esp, 4 ;Salimos de la función main
mov eax, 0
Intenta resolver este ejercicio por ti mismo sin mirar las siguientes secciones. Si tienes alguna dificultad, consulta la sección que sigue (Índices), luego vuelve a intentarlo.
Para recordar
- Un carácter ASCII estándar está codificado en un octeto. Por lo tanto, una cadena de caracteres es una serie de caracteres de 1 octeto y no de 4 como los enteros.
- Una cadena termina con el carácter 0 (el valor 0 y no el carácter '0').
- Únicamente se pueden apilar elementos de 2 ó 4 octetos en la pila (a menos que se trate de una constante de un octeto).
Propiedad
Probablemente sepas que la pila es una memoria cuyo acceso es del tipo
Last
In
First
Out. Lo que significa que si pones en la pila tres letras una a continuación de la otra, por ejemplo primero a luego b luego c, las recuperarás en el orden inverso: primero c luego b luego a.
Solución
Aquí una solución:
invertir_cadena:
;Inicio de la función
push ebp
mov ebp, esp
;Cargamos el puntero pasado como parámetro en eax
mov eax, [ebp+8]
;Introducimos el carácter de fin de cadena en la pila
push word 0
cadena_en_pila:
;Vamos a apilar cada carácter de la cadena
;Recuperación del carácter actual
mov bl, byte [eax]
;¿Es el fin de la cadena? (bl = 0 ?)
test bl, bl
;Si es sí pasamos a la etapa siguiente
jz fin_cadena_en_pila
;Si no apilamos el siguiente carácter
push bx
;Incrementamos el puntero en 1 para procesar el siguiente carácter
inc eax
;Pasamos al siguiente carácter
jmp cadena_en_pila
fin_cadena_en_pila:
;Volvemos a cargar el puntero de la cadena para desapilar uno a uno cada carácter
mov eax, [ebp + 8]
invertir:
;Desapilamos el carácter actual
pop bx
;Lo cargamos en el puntero de cadena
mov byte [eax], bl
;Incrementamos la dirección
inc eax
;¿Era el fin de la cadena? (¿el 0 que hemos apilado al inicio?)
test bl, bl
;No entonces continuamos
jnz invertir
;Es el fin de la cadena, indicamos el fin de la función
leave
ret
Explicación
Como ha sido sugerido en la sección “índices”, lo ideal es utilizar la pila. Se apilan los caracteres de la cadena y al ser desapilados se obtienen en el orden inverso.
Esto es lo que ocurre: primero apilamos 0 que será desapilado al final para indicar el fin de la cadena.
Luego, apilamos uno a uno cada carácter de la cadena pasando por bl (los 8 caracteres de peso débil de ebx) que es suficiente para un carácter. Al apilar, tomamos bx (16 bits de peso debil de ebx) que contiene a su vez bl ya que estamos obligados a apilar al menos 2 octetos.
Durante estos sucesivos apilamientos, tendremos cuidado de no apilar el carácter de fin.
En la siguiente etapa, volvemos a cargar el puntero con la dirección del inicio de la cadena. Luego, desapilamos sucesivamente cada carácter en la cadena borrando los valores antiguos. Cuando hayamos desapilado el 0 de fin de cadena, también lo insertamos en la cadena luego terminamos. Y listo!
Véase también
Comunidad de asistencia y consejos.
El artículo original fue escrito por
kilian. Traducido por
Carlos-vialfa.