CAPITULO V. DIRECTIVAS Y MÁS DIRECTIVAS.

Conocer las directivas que acepta nuestro ensamblador simplifica mucho el trabajo, pero a fin de no hacer esto soporífero (aunque creo que ya es demasiado tarde) sólo incluiré las típicas. También hay que tener en cuenta que esta parte puede cambiar bastante de un ensamblador a otro. NASM presenta una sintaxis semejante (aunque con importantes diferencias) a TASM o MASM, ensambladores de Borland y Microsoft respectivamente, para MSDOS/Windows. GASM (GNU Assembler, también con un nutrido grupo de adeptos), por otra parte, emplea una notación bien distinta (y en muchos casos odiada por los que no la usamos), conocida como AT&T. Con esto digo que aunque las instrucciones sean las mismas, puede cambiar mucho la manera de escribirlas.

Y quizá sea esta la parte más ingrata del ensamblador, pero bueno, la ventaja es que muy fácil, y no hace falta conocer todas las directivas para realizar un código eficiente.. Simplemente nos harán la vida más fácil. Empecemos.

Cada línea de ensamblador tiene (salvando las excepciones de las macros y cosas similares) la misma pinta:

etiqueta: instruccion operandos ;comentario

Todos los campos son optativos (menos, evidentemente, cuando la instrucción requiera operandos); así pues es posible definir etiquetas en cualquier sitio, o simplemente colocar un comentario. Si la línea fuese demasiado larga y quisiéramos partirla en varias (cosa un tanto rara, pero que viene bien saber), se puede usar el carácter '\'. Si la línea termina en \, se colocará la siguiente a continuación.

El ensamblador admite operadores de multiplicación, suma, resta, paréntesis.. Cuando toca ensamblar, él solito opera y sustituye. Por ejemplo:

MOV AX,2*(2+4)

es exactamente lo mismo que

MOV AX,12

  • Definición de datos
  • Las directivas básicas para definir datos son las siguientes:

  • DB Define Byte, 1 byte
  • DW Define Word, 2 bytes
  • DD Define Double Word (4 bytes)
  • DQ Define Quad Word (8 bytes)
  • DT Define Ten Bytes (10 bytes; aunque parezca muy raro, cuando veas el coprocesador no podrás vivir sin ella)


    Ejemplos:
    db 'Soy una cadena' ;es cierto, es una cadena
    dw 12,1 ;0C00h 0100h (ordenación Intel, no se te olvide)
    dt 3.14159265359 ;representación en coma flotante de 10 bytes PI

    Lo de definir números reales se puede hacer con DD (precisión simple, 32 bits), DQ (precisión doble, 64 bits) y DT (a veces llamado real temporal, una precisión de la leche, 80 bits; uno para el signo, 64 para la mantisa y 15 para el exponente o_O'). Los caracteres ascii van entrecomillados con ''; cuando el ensamblador encuentre un caracter entre comillas simples lo sustituye por el valor numérico del código ascii correspondiente.

    Para reservar zonas amplias de memoria se emplean RESB y familia, que lo que hacen es dejar hueco para N bytes, palabras (RESW), palabras dobles (RESD), palabras cuádruples (RESQ) o bloques de 10 bytes (REST).

    resb 10 ;deja un hueco de 10 bytes sin inicializar
    resq 4*4 DUP (1) ;espacio para una matriz 4x4 (16 reales de precisión doble)

    Una directiva muy peculiar, de uso muy esporádico pero muy interesante, es INCBIN; lo que hace es incluir dentro del código el contenido binario de un archivo dado. Puede ser útil, por ejemplo, para incrustar pequeñas imágenes o logotipos dentro del ejecutable:

    incbin "datos.dat" ;ensambla introduciendo datos.dat en este punto
    incbin "datos.dat",100 ;incluye datos.dat, excepto los primeros 100 bytes
    incbin "datos.dat",100,200 ;incluye los 200 bytes siguientes a los 100 primeros de datos.dat

  • ORG
  • ORG es una directiva con la que hay que tener especial cuidado, pues en otros ensambladores puede tener un efecto distinto. En nasm solamente influye en los offsets de las etiquetas e instrucciones que se refieran a direcciones. Es decir, colocar un org 100h no genera un hueco de 256 bytes donde empieza a colocarnos nuestro programa; simplemente hace que las referencias a offsets se realicen según este desplazamiento inicial. Si ensamblamos el programa "COM" del tema anterior, vemos que no ocupa 256 bytes + el programa propiamente dicho; simplemente el programa está preparado para que cuando se copie en memoria a partir de un offset de 256 bytes las direcciones sean correctas.

    El uso es tan simple como org numero_de_bytes

  • EQU
  • Equivalencia. El ensamblador sustituye en todo el fuente la palabra indicada por la expresión que sigue a equ. Muy apropiada cuando tenemos que usar una constante en varios puntos del código, pues nos ahorra buscar las referencias por todo el fuente cada vez que cambiemos de idea y la asignemos otro valor.

    Siglo equ 21
    MOV AX,Siglo ;AX ahora vale 21

  • Procesadores/Coprocesadores: 'CPU'
  • La directiva CPU determina el tipo de procesador que vamos a emplear. En general no será importante pues todos comparten el juego de instrucciones básico, pero si quisiéramos programar en un procesador más limitado (pongamos un 286), y por un casual, intentásemos usar una instrucción mínimamente moderna (digamos alguna MMX), el ensamblador nos daría un error. Algunas CPU's (se puede especificar más):

    cpu 8086
    cpu 186
    cpu 286
    cpu 386
    cpu 486
    cpu 586
    cpu 686
    cpu IA64

    Si no se selecciona ninguna, por defecto se admiten todas las instrucciones.

  • %include archivo
  • Sustituye la directiva por el contenido del archivo (a la manera del #include de c)

  • %define
  • Definición de una macro de una sola línea. Cuando el ensamblador encuentra una referencia a una macro, la sustituye por su definción, incluyendo los argumentos si los hubiera. Por ejemplo

    %define h2(a,b) ((a*a)+(b*b))
    mov ax,h2(2,3)

    es equivalente a

    mov ax,(2*2)+(3*3)

    es decir

    mov ax,13

  • %macro
  • Definición de una macro. El funcionamiento es similar, sólo que para fragmentos de código:

    %macro nom_macro num_arg
    ......
    %endmacro

    En los ejemplos del tema anterior vimos las llamadas al sistema para mostrar una cadena en pantalla. Si tuviéramos que repetir esa llamada varias veces, sería más cómodo y legible usar una macro como la siguiente:

    %macro imprime 2
            mov edx,%1
            mov ecx,%2
            mov ebx,1
            mov eax,4
            int 80h
    %endmacro

    con lo que cada vez que cada vez que quisiéramos imprimir una cadena sólo tendríamos que escribir

    imprime longitud,cadena

    Aunque es tentador hacerlo, no hay que abusar de las macros porque el código engorda que da gusto. Las macros son más rápidas que las subrutinas (pues ahorran dos saltos, uno de ida y uno de vuelta, más los tejemanejes de la pila), pero a menudo no compensa por muy bonitas que parezcan: el que la pantalla de bienvenida de un programa cargue un par de milisegundos antes o después no se nota. Claro que ahora la memoria es cada vez menos problema... En cualquier caso, su uso es deseable siempre que se favorezca la legibilidad del código.

    Una aplicación que da idea de la potencia de las macros es la de emplear macros con el nombre de instrucciones. Es así posible definir una macro llamada push que reciba dos argumentos, por ejemplo, y que ejecute las instrucciones push %1 y push %2. Cuando se escriba push eax, al recibir un número de argumentos distintos a los de la definición de la macro, sabrá que se refiere a una instrucción (aunque dará un warning por ello), mientras que al escribir push eax,ebx desarrollará la macro como push eax seguido de push ebx.

    Para especificar que una etiqueta dentro de la macro es local (a fin de que no se duplique la etiqueta al usar dos veces la misma macro), hay que poner %% justo delante de la etiqueta.

    %macro macroboba 0
            %%EtiquetaInutil:
    %endmacro

    Si llamamos a esta macro varias veces, cada vez que se la llame usará un nombre distinto para la etiqueta (pero igual en toda la macro, claro).

    ...y eso es todo en cuanto a directivas. Hay muchas más. Existen deficiones de condicionales para que ensamble unas partes de código u otras en función de variables que definamos con EQU, comparadores, operadores lógicos.. Son de uso muy sencillo todas ellas, por lo que basta con echar un ojo de vez en cuando a la documentación del ensamblador para tenerlas presentes.

    Regresar al índice