My signature
Principia Lenguaje Ensamblador para Intel 808x/80x86

II.- EL LENGUAJE ENSAMBLADOR

 

 

 

1.- UN EJEMPLO CLASICO.

 

                                       En esta parte se describe lo que es el lenguaje ensamblador, no al ensamblador o al proceso de ensamblado. Aquí se tratará todo lo concerniente con el lenguaje ensamblador y el conjunto de directivas del Microsoft Macro Assembler v4.0. Si bien esto puede resultar bastante extenso y complejo, aquí sólo se describirán las instrucciones y directivas básicas.

 

       Para comenzar veamos un pequeño ejemplo que ilustra el formato del programa fuente. Este ejemplo está completamente desarrollado en lenguaje ensamblador que usa servicios o funciones de MS-DOS (system calls)  para imprimir el mensaje Hola mundo!!  en pantalla.

 

; HOLA.ASM

; Programa clasico de ejemplo. Despliega una leyenda en pantalla.

STACK     SEGMENT STACK                ; Segmento de pila

                    DW     64 DUP (?)             ; Define espacio en la pila

STACK     ENDS

 

DATA      SEGMENT                             ; Segmento de datos

SALUDO      DB    "Hola mundo!!",13,10,"$" ; Cadena

DATA      ENDS

 

CODE      SEGMENT                            ; Segmento de Codigo

          ASSUME CS:CODE, DS:DATA, SS:STACK

 

INICIO:                                                  ; Punto de entrada al programa

          MOV  AX,DATA                         ; Pone direccion en AX

          MOV  DS,AX                              ; Pone la direccion en los registros

          MOV  DX,OFFSET SALUDO      ; Obtiene direccion del mensaje

          MOV  AH,09H                            ; Funcion: Visualizar cadena

          INT     21H                                  ; Servicio: Funciones alto nivel DOS

          MOV  AH,4CH                            ; Funcion: Terminar

          INT     21H

CODE      ENDS

          END  INICIO                               ; Marca fin y define INICIO

 

 

   La descripción del programa es como sigue:

 

1.- Las declaraciones SEGMENT y ENDS definen los segmentos a usar.

 

2.- La variable SALUDO en el segmento DATA, define la cadena a ser desplegada. El signo de dólares al final de la cadena (denominado centinela) es requerido por la función de visualización de la cadena de MS-DOS. La cadena incluye los códigos para carriage-return y line-feed.

 

3.- La etiqueta START  en el segmento de código marca el inicio de las instrucciones del programa.

 

4.- La declaración DW en el segmento de pila define el espacio para ser usado por el stack del programa.

 

5.-  La declaración ASSUME indica que registros de segmento se asociarán con las etiquetas declaradas en las definiciones de segmentos.

 

6.- Las primeras dos instrucciones cargan la dirección del segmento de datos en el registro DS.  Estas instrucciones no son necesarias para los segmentos de código y stack puesto que la dirección del segmento de código siempre es cargado en el registro CS y la dirección de la declaración del stack segment es automáticamente cargada en el registro SS.

 

7.- Las últimas dos instrucciones del segmento CODE  usa la función 4CH de MS-DOS para regresar el control al sistema operativo.  Existen muchas otras formas de hacer esto, pero ésta es la más recomendada.

 

8.- La directiva END indica el final del código fuente y especifica a START como punto de arranque.

 

 

 

2.- EL FORMATO DEL ENSAMBLADOR.

 

                                       De acuerdo a las convenciones y notación seguidas en el manual del Microsoft Macro Assembler, y que usaremos nosotros también, tenemos:

 

            Notación                                                          Significado                                                 

            Negritas                                   Comandos, símbolos y parámetros a ser usados como se

                                                            muestra.

            Itálicas                                      Todo aquello que debe ser  reemplazado por el usuario

               [ ]                                         Indican un parámetro opcional

               ,,,                                          Denota un parámetros que puede repetirse varias veces

                ¦                                           Separa dos valores mutuamente excluyentes

             letra chica                                              Usada para ejemplos. Código y lo que aparece en pantalla.

 

 

   Cada programa en lenguaje ensamblador es creado a partir de un archivo fuente de código ensamblador. Estos son archivos de texto que contienen todas las declaraciones de datos e instrucciones que componen al programa y  que se agrupan en áreas o secciones, cada una con un propósito especial. Las sentencias en ensamblador tienen la siguiente sintaxis:

 

                                     [nombre]  mnemónico  [operandos] [;comentarios]

 

 

   En cuanto a la estructura, todos los archivos fuente tienen la misma forma: cero  o más segmentos de programa seguidos por una directiva END. No  hay una regla sobre la estructura  u orden que deben seguir las diversas secciones o áreas en la creación del código fuente de un programa en ensamblador. Sin embargo la mayoría de los programas tiene un segmento de datos, un segmento de código y un segmento  de stack, los cuales pueden ser puestos en cualquier lugar.

 

   Para la definición de datos y declaración de instrucciones y  operandos el  MASM reconoce el conjunto de caracteres formado por letras mayúsculas, letras minúsculas (excluyendo caracteres acentuados, ñ, Ñ), números, y los símbolos: ? @ _ $ : . [ ] ( ) ‹ › { } + - / * & % ! ´ ~ ¦  \  = #  ˆ ; , " ‘

 

    La declaración de números requiere tener presente ciertas consideraciones. En el MASM un entero  se refiere a un número entero: combinación de dígitos hexadecimales, octales, decimales o binarios,  más una raíz opcional. La raíz se especifica con B,  Q u O, D, o H. El ensamblador usará siempre la raíz decimal por defecto, si se omite la especificación de la raíz (la cual se puede cambiar con la directiva .RADIX).  Así nosotros podemos especificar un entero de la siguiente manera: dígitos, dígitosB, dígitosQ  o dígitosO, dígitosD, dígitosH. Si una D o B aparecen al final de un número, éstas siempre se considerarán un indicador de raíz, e.g. 11B será tratado como 112 (210), mientras que si se trata del número 11B16 debe introducirse como 11Bh.

 

  Para los números reales tenemos al designador R, que sólo puede ser usado con  números hexadecimales de 8, 16, ó 20 dígitos de la forma dígitosR. También puede usarse una de las directivas DD, DQ, y DT con el formato [+¦-]dígitos.dígitos[E[+¦-]igitos].

 

   Las cadenas de carácter y constantes alfanuméricas son formadas como ´caracteres´  o "caracteres" .  Para referencias simbólicas se utilizan cadenas especiales denominadas nombres. Los nombres son cadenas de caracteres que no se entrecomillan  y que deben comenzar con una A..Z ¦ a..z ¦ _ ¦ $ ¦ @  los caracteres restantes pueden ser cualquiera de los permitidos,  y solamente los 31 primeros caracteres son reconocidos.

 

 

3.- DIRECTIVAS.

 

                                       El MASM posee un conjunto de instrucciones que no  pertenecen  al lenguaje ensamblador propiamente sino que son instrucciones que únicamente son reconocidas por el ensamblador y que han sido agregadas para facilitar la tarea de ensamblado, tanto para el programador como para el programa que lo lleva a cabo. Dichas instrucciones son denominadas directivas. En general, las directivas son usadas para especificar  la organización de memoria,  realizar ensamblado condicional, definir macros,  entrada, salida, control de archivos,  listados, cross-reference, direcciones e información acerca de la estructura de un  programa y  las declaraciones de datos. El apéndice D proporciona una lista completa de estas directivas.

 

 * Conjunto de instrucciones.-  Dentro de las directivas más importantes, tenemos las que establecen el conjunto de instrucciones a soportar para un microprocesador en especial:

 

.8086(defecto).- Activa las instrucciones para el 8086 y 8088 e inhibe las del 80186 y 80286.

.8087(defecto).- Activa instrucciones para el 8087 y desactiva las del 80287.

.186.- Activa las instrucciones del 80186.

.286c.- Activa instrucciones del 80286 en modo no protegido.

.286p.- Activa instrucciones del 80286 en modo protegido y no protegido.

.287.- Activa las instrucciones para el 80287.

 

*  Declaración de segmentos.-   En lo que respecta a la estructura del programa tenemos las directivas SEGMENT  y  ENDS que marcan el inicio y final de un segmento del programa. Un segmento de programa es una colección de instrucciones y/o datos cuyas direcciones son todas relativas para el mismo registro de segmento. Su  sintaxis es:

 

                                        nombre SEGMENT [alineación] [combinación] [´clase´]

                                        nombre ENDS

 

   El nombre del segmento es dado por nombre, y debe ser único. Segmentos con el mismo nombre se tratan como un mismo segmento.  Las opciones alineación, combinación, y clase proporcionan información al LINK sobre cómo ajustar los segmentos. Para alineación tenemos los siguientes valores: byte (usa cualquier byte de dirección), word (usa cualquier palabra de dirección, 2 bytes/word),  para (usa direcciones de párrafos, 16 bytes/párrafo, deafult), y page (usa direcciones de página, 256 bytes/page). combinación define cómo se combinarán los segmentos con el mismo nombre. Puede asumir valores de: public (concatena todos los segmentos en uno solo),  stack (igual al anterior,  pero con direcciones relativas al  registro SS, common (crea segmentos sobrepuestos colocando el inicio de todos en una misma dirección),  memory (indica al LINK tratar los segmentos igual que   MASM con public, at address (direccionamiento relativo a address).    clase  indica el tipo de segmento, señalados con cualquier nombre. Cabe señalar que en la definición está permitido el anidar segmentos, pero  no se permite de ninguna manera el sobreponerlos.

 

* Fin de código fuente.- Otra directiva importante es la que indica el final de un módulo. Al alcanzarla el ensamblador ignorará cualquier otra declaración que siga a ésta. Su sintaxis es:

 

                                                            END [expresión]

 

la opción expresión  permite definir la dirección en la cual el programa iniciará.

 

* Asignación de segmentos.- La directiva ASSUME permite indicar cuales serán los valores por defecto que asumirán los registros de segmento. Existen dos formas de hacer esto:

 

                                             ASSUME registrosegmento:nombre,,,

                                             ASSUME NOTHING

 

NOTHING cancela valores previos.

 

* Etiquetas.- Las etiquetas son declaradas

 

                                                           nombre:

 

donde nombre constituye una cadena de caracteres.

 

* Declaración de datos.-  Estos se declaran según el tipo, mediante la regla

 

                                                      [nombre] directiva  valor,,,

 

donde directiva puede ser DB (bytes), DW (palabras), DD (palabra doble),  DQ (palabra cuádruple), DT (diez bytes). También pueden usarse las directivas LABEL (crea etiquetas de instrucciones o datos), EQU (crea símbolos de igualdad) , y el símbolo = ( asigna absolutos) para declarar símbolos. Estos tienen la siguiente sintaxis:


                                                                                    nombre = expresión

                                                                                    nombre EQU expresión

                                                                                    nombre LABEL tipo

 

donde tipo puede ser BYTE,  WORD,  DWORD,  QWORD,  TBYTE,  NEAR,  FAR.

 

* Declaración de estructuras.- Para la declaración de estructuras de datos se emplea la directiva STRUC. Su sintaxis es:


                                                          nombre STRUC

                                                                                  campos

                                                                                  nombre ENDS

 

 

4.- CONJUNTO DE INSTRUCCIONES.

 

                                       El juego completo de instrucciones reconocidas por los procesadores Intel 8086 a 80286, junto con los coprocesadores 8087 y 80287, se enlistan en el  apéndice E.  Como puede verse en dicho apéndice,  la mayoría de las instrucciones requieren algunos operandos o expresiones para trabajar,  y lo cual es válido también para las directivas.  Los operandos representan valores, registros o localidades de memoria a ser accedidas de alguna manera. Las expresiones combinan operandos y operadores aritméticos y lógicos para calcular en valor o la dirección a acceder.

 

    Los operandos permitidos se enlistan a continuación:

 

Constantes.-  Pueden ser números, cadenas o expresiones que representan un valor fijo. Por ejemplo, para cargar un registro con valor constante usaríamos la instrucción MOV indicando el registro y el valor que cargaríamos dicho registro.

 

                                                                                                mov ax,9

                                                                                                mov al,´c´

                                                                                                mov bx,65535/3

                                                                                                mov cx,count

 

para el último ejemplo count sólo será válido si este fue declarado con la directiva EQU.

 

Directos.-  Aquí se debe especificar la dirección de memoria a acceder en la forma segmento:offset.

 

                                                                                                mov ax,ss:0031h

                                                                                                mov al,data:0

                                                                                                mov bx,DGROUP:block

 

Relocalizables.- Por medio de un símbolo  asociado a una dirección de memoria y que puede ser usado también para llamados.

 

                                                                                                mov ax, value

                                                                                                call main

                                                                                                mov al,OFFSET dgroup:tabla

                                                                                                mov bx, count

 

para el último ejemplo count sólo será válido si fue declarado con la directiva DW.

 

Contador de localización.- Usado para indicar la actual localización en el actual segmento durante el ensamblado. Representado con el símbolo $ y también conocido como centinela.

 

 

                                                                                                help DB ´OPCIONES´,13,10

                                                                                                F1    DB ´    F1                 salva pantalla´,13,10

                                                                                                                .

                                                                                                                .

                                                                                                                .

                                                                                                F10 DB  ´    F10                exit´,13,10,´$

                                                                                                DISTANCIA = $-help

 

Registros.- Cuando se hace referencia a cualquiera de los registros de propósito general, apuntadores, índices, o de segmento.

 

Basados.- Un operador basado representa una dirección de memoria relativa a uno de los registros de base (BP o BX). Su sintaxis es:

 

desplazamiento[BP]

desplazamiento[BX]

[desplazamiento][BP]

[BP+desplazamiento]

[BP].desplazamiento

[BP]+desplazamiento

 

en cada caso la dirección efectiva es la suma del desplazamiento y el contenido del registro.

 

                                                                                                mov ax,[BP]

                                                                                                mov al,[bx]

                                                                                                mov bx,12[bx]

                                                                                                mov bx,fred[bp]

 

Indexado.- Un operador indexado representa una dirección de memoria relativa a uno de los registros índice (SI o DI). Su sintaxis es:

 

desplazamiento[DI]

desplazamiento[SI]

[desplazamiento][DI]

[DI+desplazamiento]

[DI].desplazamiento

[DI]+desplazamiento

 

en cada caso la dirección efectiva es la suma del desplazamiento y el contenido del registro.

 

                                                                                                mov ax,[si]

                                                                                                mov al,[di]

                                                                                                mov bx,12[di]

                                                                                                mov bx,fred[si]

 

Base-indexados.- Un operador base-indexado representa una dirección de memoria relativa a la combinación de los registros de base e índice. Su sintaxis es:

 

desplazamiento[BP][SI]

desplazamiento[BX][DI]

desplazamiento[BX][SI]

desplazamiento[BP][DI]

[desplazamiento][BP][DI]

[BP+DI+desplazamiento]

[BP+DI].desplazamiento

[DI]+desplazamiento+[BP]

 

en cada caso la dirección efectiva es la suma del desplazamiento y el contenido del registro.

 

                                                                                                mov ax,[BP][si]

                                                                                                mov al,[bx+di]

                                                                                                mov bx,12[bp+di]

                                                                                                mov bx,fred[bx][si]

 

 

Estructuras.-  Su sintaxis es variable.campo.  variable es el nombre con que se declaró la estructura, y campo es el nombre del campo dentro de la estructura.

 

                                                                                date STRUC

                                                                                         mes    DW  ?

                                                                                         dia      DW  ?

                                                                                          aa       DW ?

                                                                                date ENDS

                                                   

                                                                                actual    date  ‹´ja´,´01´,´84´›

                                                   

                                                                                mov      ax,actual.dia

                                                                                mov      actual.aa, ´85´

 

Operadores y expresiones.- Se cuenta con los siguientes operadores:

 

 -aritméticos

                                       expresión1  * expresión2

                                       expresión1  / expresión2

                                       expresión1  MOD expresión2

                                       expresión1  + expresión2

                                       expresión1  - expresión2

                                       + expresión

                                       - expresión

 

-de corrimiento

                                       expresión1  SHR contador

                                       expresión1  SHL contador

 

-relacionales

                                       expresión1  EQ expresión2

                                       expresión1  NE expresión2

                                       expresión1  LT expresión2

                                       expresión1  LE expresión2

                                       expresión1  GT expresión2

                                       expresión1  GE expresión2

 

- de bit

                                       NOT expresión

                                       expresión1  AND expresión2

                                       expresión1  OR expresión2

                                       expresión1  XOR expresión2

 

-de índice

                                       [expresión1] [expresión2]

 

ejemplos:

                                                                                                mov     al, string[3]

                                                                                                mov     string[last],al

                                                                                                mov     cx,dgroup:[1]                  ; igual a mov cx,dgroup:1

 

-de apuntador

                                       tipo  PTR expresión

 

tipo puede ser BYTE ó 1, WORD ó 2, DWORD ó 4, QWORD ó 8,  TBYTE ó 10, NEAR ó 0FFFFh, FAR ó 0FFFEh. Ejemplos:

 

                                                                                                call    FAR PTR subrout3

                                                                                                mov   BYTE ptr [array], 1

                                                                                                add    al, BYTE ptr [full_word]

 

-de nombre de campo

                                       estructura.campo

 

ejemplos:

                                                                                                inc    month.day

                                                                                                mov   time.min,0

                                                                                                mov   [bx].dest

 

-de propósito especial.



OFFSET expresión.- Regresa el desplazamiento del operando


                                                                        mov   bx, OFFSET dgroup:array

                                                                                                mov   bx, offset subrout3

 

SHORT etiqueta.- Para un salto de menos de 128 bytes


                                                                                                jmp      SHORT  loop

 

LENGTH variable.- Regresa el número de elementos de variable según su tipo


                                                                                                mov   cx,length array

 

SIZE variable.- Regresa el tamaño en bytes alojados para variable


                                                                                                mov   cx,size array

 

SEG expresión.- Regresa el valor del segmento para expresión


                                                                                                mov   ax, SEG saludo

 

 

5.- MACROS Y PROCEDIMIENTOS.

 

                                       La manera más fácil de modularizar  un programa es dividirlo en dos o más partes. Para esto, es necesario que datos, símbolos, y demás valores de un módulo sean reconocidos por el otro u otros módulos. Para este tipo de declaraciones globales existen dos directivas:

 

PUBLIC nombre,,,     que  hace la variable, etiqueta o símbolo absoluto disponible para todos los programas.

 

EXTRN nombre:tipo,,,    que especifica una variable, etiqueta o símbolo externos identificados por nombre  y tipo (que puede ser BYTE, WORD, DWORD, QWORD, TBYTE, NEAR, FAR, o ABS, éste último para números absolutos).

 

   El siguiente ejemplo ilustra el uso de las directivas. El primer listado corresponde al módulo principal, mientras que el segundo al módulo que contiene una rutina. Ambos módulos son archivos que se editan por separado, se ensamblan por separado, pero se ligan juntos.

 

MODULO PRINCIPAL: MAIN.ASM

             NAME      main

             PUBLIC    exit

             EXTRN     print:near

 

stack    SEGMENT   word stack 'STACK'

             DW        64 DUP(?)

stack    ENDS

 

data      SEGMENT   word public 'DATA'

data      ENDS

 

code     SEGMENT byte public 'CODE'

             ASSUME  cs:code, ds:data

start:

             mov  ax,data             ; carga localizacion del segmento

             mov  ds,ax                ;   en el registro DS

             jmp   print                  ; va a PRINT en el otro modulo

exit:

            mov  ah,4ch

            int     21h

code    ENDS

            END  start

 

 

SUBMODULO: TASK.ASM

               NAME      task

               PUBLIC    print

               EXTRN     exit:near

 

data       SEGMENT   word public 'DATA'

entrada  DB        "Entrando a un submodulo....",13,10,"$"

salida     DB        ".......saliendo del submodulo.",01,07,13,10,"$"

data       ENDS

 

code      SEGMENT   byte public 'CODE'

              ASSUME    cs:code, ds:data

print:

              mov       ah,06h             ; Funcion para borrar pantalla

              mov       al,0                  ; todas las lineas

              mov       cx,0                 ; de 0,0

              mov       dh,24d

              mov       dl,79d

              mov       bh,0                 ; atributo en lineas vacias

              int          10h                  ; Servicio de e/s video

              mov       dx, OFFSET entrada

              mov       ah,09h

              int          21h

              mov       dx, OFFSET salida

              int          21h

              jmp        exit                 ; Regresa al otro modulo

code      ENDS

              END

 

 

   La declaración de macros se hace a través de  las directivas MACRO y ENDM. Su sintaxis es:

 

                                                  nombre MACRO [parámetros,,,]

                                                              declaraciones

                                                  ENDM

 

parámetros son los valores que se substituirán en la macro cada vez que se haga referencia a ésta.

 

   Para la definición de procedimientos se emplean las directivas PROC y ENDP. Su sintaxis es:

 

                                                   nombre PROC  [distancia]

                                                               sentencias

                                                   nombre ENDP

 

distancia, que puede ser  NEAR (defecto) o FAR permiten indicar el tipo de acciones a realizar en brincos y llamados a subrutinas. nombre  se puede usar como dirección en llamados o brincos.

 

 

6.- INTERRUPCIONES.

 

                                       Como se mencionó anteriormente la PC esta constituida lógicamente por su BIOS y sistema operativo.  La mayoría de las rutinas que controlan al computador están grabadas en el  ROM del BIOS, aunque muchas rutinas son establecidas por el sistema operativo y se cargan en RAM al momento de encender al computador.  Estas rutinas son denominadas interrupciones y son activadas mediante la instrucción: INT número.  Una interrupción es una operación que invoca la ejecución de una rutina específica que suspende la ejecución del programa que la llamó, de tal manera que el sistema toma control del computador colocando en el stack el contenido de los registros CS e IP. El programa suspendido vuelve a activarse cuando termina la ejecución de la interrupción y son restablecidos los registros salvados.  Existen dos razones para ejecutar una interrupción: (1) intencionalmente como petición para la entrada o salida de datos de un dispositivo, y (2) un error serio y no intencional, como sobreflujo o división por cero.

 

   El operando de una interrupción  indica cuál es la rutina a activar. La dirección de la rutina es localizada por medio de una tabla que el sistema mantiene a partir de la dirección 0000:0000h. Existen 256 entradas de 4 bytes de longitud, y cada interrupción proporciona varias funciones. Las interrupciones de 00h a 1Fh corresponden al BIOS y de 20h a FFh son del DOS y BASIC. El apéndice F proporciona  una lista de las interrupciones para equipo XT.



Este texto puede ser copiado y reproducido libremente mientras su contenido no sea alterado, se cite la fuente y referencias. Las marcas registradas son responsabilidad de sus dueños y el autor de este texto no tiene relación alguna con estos. M. en C. Eduardo René Rodríguez Avila. © Todos los derechos reservados.
Ultima actualización: .