Cómo invertir una cadena en ensamblador x86: 8086, 8088...

Cómo invertir una cadena en ensamblador x86: 8086, 8088...

Este pequeño código en lenguaje ensamblador está hecho para las arquitecturas x86 (procesadores Intel y AMD 32 bits) y usa la sintaxis de NASM, un ensamblador libre, gratuito y que puede ser utilizado en diferentes plataformas como Linux o Windows.

¿Cuáles son las funciones y nociones utilizadas?

Las funciones externas utilizadas provienen de la biblioteca C estándar. Por tanto, no tendrás problemas con tu equipo al utilizar este código, ya que no depende del sistema operativo utilizado. Únicamente depende de la arquitectura x86. Para saber cómo utilizar NASM a fin de probar este código, consulta el siguiente artículo: Cómo compilar un programa ensamblador con NASM.

¿Qué es una cadena de caracteres?

Un carácter ASCII estándar está codificado en un octeto. Por lo tanto, una cadena de caracteres es una serie de caracteres de un octeto y no de cuatro como los enteros. Una cadena termina con el carácter 0 (el valor 0 y no el carácter '0'). Además, únicamente se pueden apilar elementos de dos o cuatro octetos en la pila (a menos que se trate de la constante de un octeto).

Por otro lado, 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 y luego c, las recuperarás en el orden inverso: primero c, luego b y luego a.

¿Cuál es el código en C?

El objetivo es crear una función en ensamblador que sea capaz de invertir una cadena de caracteres. La función deberá aceptar como parámetro de entrada un puntero hacia una cadena de caracteres e invertir esta cadena en el mismo puntero. El código en C sería:

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", que es el resultado del string invertido.*/ 

¿Cuál es el código en ensamblador x86?

  • A continuación, el código:
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
  • Deberás insertar el código aquí:
extern printf
section .data
 cadena db '¡Inviérteme! ¡Yo te diré qué programador eres!', 0x0 section .text
 global main, invertir_cadena
invertir_cadena:
  ;Pon el código aquí
main:
  mov eax, cadena ;Dirección de cadena en eax
  push eax
                ;Llamada a 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

¿Cuál es el algoritmo utilizado?

Como ha sido sugerido anteriormente, lo ideal es utilizar la pila. Se apilan los caracteres de la cadena y al acceder a ellos se obtienen en el orden inverso. Esto es lo que ocurre:

  1. Primero apilamos 0, que será obtenido al final para indicar el fin de la cadena.
  2. Luego, apilamos uno a uno cada carácter de la cadena pasando por bl (los 8 caracteres de menor peso de ebx) que es suficiente para un carácter. Al apilar, tomamos bx (16 bits de menor peso de ebx) que contiene a su vez bl, pues estamos obligados a apilar al menos 2 octetos. Durante estos sucesivos apilamientos, tendremos cuidado de no apilar el carácter de fin.
  3. En la siguiente etapa, volvemos a cargar el puntero con la dirección del inicio de la cadena. Luego, vamos obteniendo sucesivamente cada carácter en la cadena borrando los valores antiguos. Cuando hayamos llegado al 0 de fin de cadena, también lo insertamos en la cadena y terminamos.

Lenguajes