Cómo hacer Inline Assembly en Lenguaje C (Dev-C++)


gcc

Para entender un poco este artículo va a comenzar desde lo más básico hasta finalmente hacer una calculadora con funciones básicas hechas en ASM o Assambler.

Muchas veces queremos aprender a programar en ASM pero es bastante dificil encontrar los emuladores, tutoriales y los recursos necesarios si no se sabe donde buscar, ese fue mi caso hace algún tiempo en el que quería aprender a programar en ASM pero no sabía ni siquiera que buscar debido a que hay arquitecturas, registros, entre otros conceptos que no tenía ni idea de que eran. Más adelante en la universidad una profesora que estimo mucho nos enseñó los conceptos básicos del lenguaje ensamblador y como funciona en relación a un computador. Luego de esto recordé que en el 2010 cuando estaba aprendiendo a programar en Lenguaje C o simplemente C vi un concepto que me llamó muchísimo la atención que era combinar ASM y C para hacer aplicaciones más robustas, rápidas y capaces de hacer casi lo inimaginable.

Antes que nada hay que tener una idea clara de como funciona el Inline Assembly en Lenguaje C es hacer de cuenta que se coge una jeringa y se inyecta código dentro de C para que el compilador (en este caso GCC) entienda como si fuera uno solo.

Cuando se va a hacer Inline Assembly primero hay que mirar el entorno de desarrollo de C, en este caso es Dev-C++ que es un IDE (Integrated Development Environment) que está construido en Delphi 6 y que usa Mingw port de GCC (GNU Compiler Collection) como compilador de código C y C++.

Sabiendo que hay sintaxis Intel y AT&T en el lenguaje ensamblador y conociendo la naturaleza de Dev-C++ se debe escoger la sintaxis AT&T debido a que esta se usa en entornos UNIX y la Intel en Windows, Hasta este punto creo que logré confundir a muchos lectores… ¿estamos en Windows usando Dev-C++ y voy a usar sintaxis para UNIX?, si así es, recapitulando la naturaleza de Dev-C++, el compilador está basado en GCC que está construido especialmente para sistemas GNU y es totalmente de código abierto o libre, si deseas más información haz click aquí.

Después de conocer que es lo que se va a buscar y como comenzar a trabajar es mucho más sencillo el asunto, el problema es que encontrar comandos para la sintaxis AT&T es mucho más complicado y dificil de entender, así que en esta página nos enseñan los diferentes tipos de Inline Assembly que se pueden hacer, pero para resumir, ellos nos dan una pequeña tabla comparativa donde nos muestran los comandos Intel y AT&T:

+------------------------------+------------------------------------+
|       Intel Code             |      AT&T Code                     |
+------------------------------+------------------------------------+
| mov     eax,1                |  movl    $1,%eax                   |   
| mov     ebx,0ffh             |  movl    $0xff,%ebx                |   
| int     80h                  |  int     $0x80                     |   
| mov     ebx, eax             |  movl    %eax, %ebx                |
| mov     eax,[ecx]            |  movl    (%ecx),%eax               |
| mov     eax,[ebx+3]          |  movl    3(%ebx),%eax              | 
| mov     eax,[ebx+20h]        |  movl    0x20(%ebx),%eax           |
| add     eax,[ebx+ecx*2h]     |  addl    (%ebx,%ecx,0x2),%eax      |
| lea     eax,[ebx+ecx]        |  leal    (%ebx,%ecx),%eax          |
| sub     eax,[ebx+ecx*4h-20h] |  subl    -0x20(%ebx,%ecx,0x4),%eax |
+------------------------------+------------------------------------+

Si eres nuevo en este tema te dará lo mismo la tabla comparativa así que a continuación unos conceptos básicos de registros de uso general:

  • AX: El registro acumulador, dividido en AH y AL (8 bits cada uno). Interviene en las operaciones aritméticas y lógicas.
  • BX: Registro base, dividido en BH y BL. Se utiliza en transferencias de datos entre la memoria y el procesador.
  • CX: Registro contador, dividido en CH y CL. Se utiliza como contador en bucles (LOOP), en operaciones con cadenas (REP), y en desplazamientos(CL).
  • DX: Registro de datos, dividido en DH y DL.- Se utiliza en operaciones de multiplicación y división junto con Ax y en operaciones de entrada y salida de puertos, su mitad inferior DL contiene el número de puertos.

En este punto se debe conocer que si vamos a usar por ejemplo este comando

movl $0xffh, %ebx

El registro ebx almacena lo que hay en la entrada que es 0ffh, esto es muy importante que lo conozcamos porque ese valor hexadecimal que en el ejemplo di, puede ser una variable entera, un color, un caracter, etc.

Ahora volviendo a Dev-C++ se deben conocer las diferentes funciones para hacer Inline Assembly en Lenguaje C, estas son:

  • asm();
  • __asm__();
  • __asm();
  • asm volatile ();
  • asm __volatile__();
  • __asm__ __volatile__();

Estas dependen del sistema operativo donde se vayan a usar, compilador, arquitectura(x86, x64), librerías, entre otras variables, para nuestro caso vamos a usar __asm();

Ahora si, manos a la obra.

Aquí lo importante es el código inyectado de ASM así que no me demoraré mucho explicando código de C, para crear la calculadora que me propuse a hacer voy a encapsular cada función para que haga solo una tarea en específico y no quede un código incomprensible. Para poder usar la función __asm(); y se van a manejar variables toca que sean globales al programa.

entonces:


__asm("movl _numA, %ebx ");
__asm("movl _numB, %eax");
__asm("addl %ebx,%eax");
__asm("movl %eax, _numA");

Donde por comodidad cargo lo que hay en la variable numA en el registro b (ebx) y numB en el registro a (eax) pero para acceder a la variable global toca anteponer el caracter ‘_’ si no no funciona. En la tercera línea sumo y guardo en el registro a el resultado y en la última línea vuelvo a almacenar el resultado de la suma en la variable numA.

El código anterior era de la suma, el siguiente es el de la resta, el que es muy parecido:


__asm("movl _numA, %eax");
__asm("movl _numB, %ebx");
__asm("sub %ebx, %eax");
__asm("movl %eax, _numA");

El siguiente es el de la multiplicación pero este tiene algo muy en particular y es que en la operación ya no aparece dos registros sino uno, aquí cabe aclarar que imull almacena en el registro a el resultado, por lo que no nos tenemos que preocupar de almacenar el resultado, lo que si hay que tener es cuidado al tener alguna información guardada y perderla.

__asm("movl _numA, %eax");
__asm("movl _numB, %ebx");
__asm("imull %ebx");
__asm("movl %eax, _numA");

El de la división es muy parecido:


__asm("movl _numA, %eax");
__asm("movl _numB, %ebx");
__asm("cltd");
__asm("idivl %ebx");
__asm("movl %eax, _numA");

Finalmente la operación más compleja en esta calculadora es la potenciación, en la que es capaz de elevar un número por cualquier potencia, esta operación es un poco diferente por lo que el algoritmo de elevar a una potencia es multiplicar el mismo número n veces repetidas, por ejemplo .

Si se estuviera haciendo el programa en su totalidad de Lenguaje C solo basta con hacer un for hasta n, aquí toca hacer un ciclo y cargar un contador a memoria y sigue iterando hasta que el contador llega a 0, por lo que queda el código así:


decr = numB--;
__asm("movl _numA, %eax);
__asm("movl _numA, %ebx);
__asm("movl _decr, %ecx);
__asm("CICLO_PARA_ELEVAR:");
__asm("imull %ebx, %eax");
__asm("loop CICLO_PARA_ELEVAR");
__asm("movl %eax, _numA");

Lo importante es recalcar que la instrucción imull recibe un segundo parámetro.

Con esas funciones basta con hacer una calculadora en lenguaje C.

Si deseas el código completo puedes descargarlo haciendo click aquí. o entrando a la dirección https://github.com/thEpisode/CalculatorASM

5 comentarios en “Cómo hacer Inline Assembly en Lenguaje C (Dev-C++)

  1. Al intentar ver el código me aparece lo siguiente:
    El vínculo ya no funciona
    Ponte en contacto con el propietario de este elemento para obtener un nuevo vínculo.
    ¿Podrías subirlo a otro lado?

Los comentarios están cerrados.