Tarifas
Contacto

Cursos y Tutoriales

CURSOS   DE   PROGRAMACION

LENGUAJE   ENSAMBLADOR

Parte Cuarta

Parte 1 Parte 2 Parte 3 Parte 4

Estructura de un programa en Ensamblador:

Para entrar directamente en materia, veamos un listado en len­guaje ensamblador de un programa corriente: se trata de un progra­ma que muestra la cadena "Primer Programa" en el monitor y retorna al Sistema Operativo.

PILA SEGMENT STACK 'STACK'             ; Abre el segmento de PILA.

DW 100h DUP (?)                                                                             ; Reserva 100 palabras para la PILA.

PILA ENDS                                             ; Cierra el segmento de PILA.

DATOS SEGMENT 'DATA'                                  ; Abre el segmento de DATOS.

mensaje DB "Primer Programa", '$'       ; Mensaje a escribir.

DATOS ENDS                                        ; Cierra el segmento de DATOS.

CODIGO SEGMENT 'CODE'                    ; Abre el segmento de CODIGO.

ASSUME CS:CODIGO, DS:DATOS, SS:PILA

ENTRADA:

MOV ax, DATOS                                 ; Valor de segmento para DATOS.

MOV ds, ax                             ; Para acceder a "mensaje".

MOV dx, OFFSET mensaje                  ; Para la interrupción 21h, función 09.

MOV ah, 09      ; Especifica el servicio o función 09.

INT 21h          ; Invoca el servicio 09: Imprimir Cadena.

MOV ax, 4C00h            ; Servicio (Función) 4Ch, con valor de retorno 0.

INT 21h          ; Invoca servicio 4Ch: Retorno al DOS.

CODIGO ENDS               ; Cierra el segmento de CODIGO.

END ENTRADA               ; Final del modulo fuente y primera instrucción

; desde donde debe empezarse a ejecutar el programa.

Veamos primero el formato de SEGMENT:

nombre SEGMENT [alineamiento] [READONLY] [combinación] ['clase']

...........

nombre ENDS

Sirve para indicarle al ensamblador que la información que venga a continuación, y hasta que no encuentre una directiva de fin de segmento (ENDS), corresponde al mismo segmento.

  -   nombre: Indica el nombre del segmento que vamos a utilizar. El "nombre" indicado en SEGMENT y el "nombre" indicado en ENDS deben ser el mismo.

  -   alineamiento: En "alineamiento" vamos a situar una informa­ción que le dirá al ensamblador las características que debe tener la dirección de memoria que elija para realizar la car­ga de ese segmento. Los valores que le podemos dar son:

   BYTE: Puede colocar el segmento en cualquier dirección.

   WORD: Debe colocar el segmento en una dirección múltiplo de 2.

   DWORD: Debe colocar el segmento en una dirección múltiplo de 4.

   PARA: Debe colocar el segmento en una dirección múltiplo de 16. Es el alineamiento por defecto.

  -   READONLY:  Informa al ensamblador de un error producido cuan­do alguna instrucción del segmento que contiene la sentencia READONLY es modificada.

  -   combinación: La combinación nos dirá una serie de informacio­nes para el montador (LINKER). Le podemos situar las posibi­lidades:

      PRIVATE: No combina el segmento con segmentos de otros módulos, aún cuando tengan el mismo nombre.

      PUBLIC: Le dice al montador (LINKER) que este segmento y todos los que tengan el mismo nombre en 'clase' se conca­tenarán en un mismo segmento.

      STACK: Indica que estamos tratando con un segmento de pi­la. Esta identificación es obligatoria en el caso de que el segmento contenga la pila. Al menos dabe haber un seg­mento de pila para crear un módulo ejecutable con el mon­tador (LINKER).

      AT expresión: Sirve para indicarle al montador en que di­rección de memoria deberá situar la información correspon­diente a éste segmento. La dirección de memoria viene es­pecificada en el parámetro "expresión".

      COMMON: Indica que este segmento y todos los del mismo nombre ("clase") que procese el montador empezarán en la misma dirección, solapándose entre sí. La longitud asigna­da por el montador es la longitud máxima de todos los seg­mentos COMMON procesados.

      MEMORY: EL segmento se ubicará en una dirección de memoria superior a la de otros que aparecerán durante el montaje (LINK) del programa. Se puede aplicar, por ejemplo, para utilizar la memoria más allá de los límites del programa. Sólo puede haber un segmento de este tipo. Si existieran varios, solo se procesará el primero como tal, y el resto se procesará como COMMON.

 

  -   use (sólo 80386/486): Determina el tamaño del segmento. USE16 indica que el desplazamiento es de 16 bits de ancho, y USE32 indica que el desplazamiento es de 32 bits de ancho.

  -   'clase': La 'clase' es un nombre que sirve para que el monta­dor pueda unificar todos los segmentos que tengan asociados dicho nombre, si es que le damos la posibilidad de ello.

Volvamos al programa.

 PILA SEGMENT STACK 'STACK'.

Esta línea lleva la directiva SEGMENT, que hace que el ensambla­dor incluya una entrada en el fichero objeto para este segmento.

PILA es el nombre con el que nos referimos a éste segmento den­tro del listado fuente, mientras que 'STACK' incluida entre comi­llas simples es utilizada por el enlazador (LINKER). La palabra STACK sin comillas indica tanto al ensamblador como al enlazador (LINKER) que este segmento se utilizará como espacio de pila en ejecución.

Cada línea SEGMENT debe tener su una correspondiente ENDS (Fin de segmento).

DW 100h DUP (?)         ; esta línea lleva la directiva DW (definir palabra).

 Esta directiva permite fijar directamente los valores incluidos en el módulo al tamaño, en este caso, de una palabra, el uso de DUP nos permite fijar un número determinado de palabras (100h = 256 en este caso) a un mismo valor.

El valor se indica entre paréntesis; en esta línea es un inte­rrogante, con lo que indicamos al ensamblador que no son importan­tes los contenidos iniciales de esta 256 palabras. El enlazador puede intentar reducir el tamaño del ejecutable gracias a que no nos interesan lon contenidos iniciales de esta posiciones de memo­ria.

La instrucción DUP se utiliza para definir arrays (vectores).

El formato sería:

contador DUP (valor_inicial [valor_inicial]...)

El valor de "contador" nos dice el número de veces que se repite lo que hay en "valor_inicial". El "valor_inicial" debe definirse siempre entre parétesis. El "valor_inicial" suele se o un "?" que nos indica que no nos interesa el contenido inicial del vector o un número, que nos indicará el contenido de cada posición del vec­tor.

Ejemplos:

baray BYTE 5 DUP (1)        ; En este cado tenemos un vector de 5 posiciones, y cada posición ocupa un BYTE y tiene un valor inicial de 1 (cada posición).

array DWORD 10 DUP (1)   ; En este caso tenemos un vector de 10 posiciones, y cada posición ocupa una Doble Palabra (DWORD) y tiene un valor inicial de 1.

buffer BYTE 256 DUP (?)    ; Vector de 256 posiciones, y cada posición ocupa un BYTE y tiene un valor inicial que no nos interesa.

También se puede definir un "array" de la siguiente forma:

warray WORD 2, 4, 6, 8, 10

La forma de acceder a alguno de estos elementos dentro del códi­go de segmento sería:

DATOS SEGMENT 'DATA'

btabla BYTE 12 DUP (?)

warray WORD 2, 4, 6, 8, 10

DATOS ENDS

CODIGO SEGMENT 'CODE'

ASSUME CS:CODIGO, DS:DATOS

UNO:

MOV ax, DATOS

MOV ds, ax

XOR al, al  ; Ponemos lo que vamos a meter en "btabla" a 0

MOV si,0               ; colocamos el puntero de "btabla" (SI) a 0 (el primer elemento de la matriz o el array empieza en la posición cero)

MOV cx, 12                       ; contador a 12.

DOS:

MOV btabla[si], al  ; Movemos un valor de un BYTE (AL), (ya que hemos definido "btabla" de tipo BYTE) a "btabla". También podemos poner esta ins­trucción como:  MOV btabla + SI, AL

INC si                    ; Al tratarse de elementos de un BYTE el puntero sólo se debe incrementar en una unidad.

LOOP DOS                                                                                                                                                          XOR ax, ax

MOV si; 0              ; Ponemos el índice del array a cero

MOV xc, 5

TRES:

MOV ax, warray[si] ; Movemos el valor que indica el puntero (SI) dentro de "warray” a AX (Ya que hemos definido "warray" de tipo PALABRA).

; También podemos poner esta instrucción

; como:  MOV AX, warray + SI

ADD si, 2               ; Incrementamos SI en 2 unidades debido a que "warray" esta definido con PALABRAS.          

LOOP TRES                                                                                                                                               CODIGO ENDS

END UNO

 El algoritmo para acceder a un elemento dentro de un array, a partir de la dirección de comienzo (vector), puede ser (sería el valor que habría que moverle al puntero dentro del array para ac­ceder al elemento a buscar):

(ELEMENTO - 1) * TIPO

Donde: ELEMENTO es el elemento a buscar y TIPO es el tamaño con el que hemos definido el array (1 - BYTE, 2 - WORD (2 bytes), 4 - DWORD (4 bytes), etc).

En el caso de matrices, ésta nos presenta un almacemaniento que ocupa posiciones consecutivas y que lo debemos tratar como un vec­tor. Para tratar dicho vector se necesita conocer el número de filas y el número de columnas. El algoritmo para acceder a un ele­mento, a partir de la dirección de comienzo, sería:

(FILA - 1) * Nº COL * TIPO + (COL - 1) * TIPO

Donde: FILA es la posición de la fila a buscar, Nº COL es el núme­ro de columnas que tiene cada fila. TIPO es lo mismo que en los vectores. COL es la posición de la columna a buscar.

Por ejemplo:    En una matriz de 4 filas por 3 columnas tipo BYTE, queremos ver el contenido de la posición (3, 2).  (3 - 1) * 3 * 1 + (2 - 1) * 1

PILA ENDS

Cierra el segmento de pila. Esta directiva (ENDS) cierra el segmento abierto pos la directiva PILA SEGMENT. El identificador que antecede a ENDS debe ser el mismo que el que antecede a la directiva SEGMENT correspondiente, recordando que no son importan­tes las mayúsculas o minúsculas.

DATOS SEGMENT 'DATA'

Abre el segmento de datos. Esta directiva abre otro segmento. Este segmento lo utilizaremos para los datos del programa.

Mensaje DB "Primer Programa", '$'

Mensaje a escribir. (También lo podemos ver definido como: 'Pri­mer Programa$,' o como: "Primer Programa","$", etc.).

Esta directiva sirve para reservar memoria para la variable mensaje. DB quiere decir "definir byte". En éste caso, se reservan 16 bytes (15 del mensaje y uno del $). El carácter $ se utiliza como delimitador del texto a escribir. Es decir, cuando vayamos a escribir mensaje por pantalla se escribirán todos los caracteres hasta encontrar el carácter $.

También podemos encontrar, por ejemplo, el mensaje: 

"Pimer Programa", 13, 10, "$"

que nos indicaría que, después de escribir el texto, ejecutaría el carácter 13 (retorno de carro) y luego el carácter 10 (salto de línea).

En cuanto al tamaño de los datos, éste puede ser:

TAMAÑO                                                                 RANGO

BYTE, DB                    Valor de 0 a 225 (1 Byte).

SBYTE                        Valor entre: -128 y +127 (1 Byte).

WORD, DW                                         Valor de 0 a 65.535 (2 Bytes).

SWORD                                              Valor entre: -32.768 t +32.767 (2 Byte).

DWORD, DD               Valor de 0 a 4 Megabytes (4.294.967.259) (4 bytes).

SDWORD                                            Valor entre: -2.147.438.648 y +2.147.438.647 (4 bytes)

FWORD, DF                            Tamaño de 6 Bytes. Utilizando sólo como variable de pun­tero para los procesadores 38­6/486.

QWORD, DQ               Tamaño de 8 Bytes.

TBYTE, DT       Tamaño de 10 Bytes.

En cuanto a número reales en "coma flotante":

TIPO DE DATO     BITS      DIGITOS SIGNIF.    RANGO APROXIMADO

REAL4                           32                  6 - 7                             De: ±1.18x10-38  a ±3.40x1038

 REAL8                     64                        15 - 16              De: ±2.23x10-308  a ±1.79x10308

REAL10                      80                     19                                De: ±3.37x10-4932  a ±1.18x104932

Para expresar un número real se utiliza este formato:

[+/-] entero. [fracción] [E] [[+/-] exponente]

Ejemplos:

corto       REAL4  25.23                  ; 25,23

doble    REAL8  2.523E1                 ; 2.523x101 = 25,23

diezbytes REAL10 -2523.0E-2         ; = -2523x10-2 = -25,23

Seguimos con el programa.

DATOS ENDS

Cierra el segmento de datos.

CODIGO SEGMENT 'CODE' 

Abre el segmento de código. Abre el segmento de código, donde incluímos el código del programa. Ni el nombre CODIGO, ni la clase 'CODE' son tratados de forma especial por el ensamblador o el en­lazador.

Es en la última línea del programa fuente donde se indica el punto de entrada al programa, fijando tanto el valor de segmento inicial para CS como el desplazamiento inicial dentro del segmen­to.

ASSUME CS:CODIGO, DS:DATOS, SS:PILA      

Esta línea informa al ensamblador de los segmentos a los que apuntarán durante la ejecución los diferentes registros de segmen­to.

De este modo, si intentamos acceder a la etiqueta 'mensaje' definida en el segmento "DATOS", el ensamblador sabrá que puede acceder a ella por medio del registro DS. El registro CS se asocia con el segmento de CODIGO y el registro SS con el segmentos de PILA.

MOV AX, DATOS

Valor de segmento para "DATOS" (AX=dirección del segmento de DATOS). Almacena la componente segmento del segmento DATOS sobre el registro AX. Como el registro DS apunta al comienzo del PSP (Prefijo de Segmento de Programa; antes de que el procesador de co­mandos (COMAND.COM) del DOS pase el control al programa, cons­truye un bloque de 256=100h bytes a partir de la primera posición de memo­ria disponible. El PSP contine campos como la dirección de retorno al DOS cuando acabe de ejecutarse el programa, la dirección del có­digo si se pulsa Ctrl-Break, la dirección de la rutina del trata­miento de errores críticos, la cantidad de memoria disponible para el programa y los parámetros, etc.), es necesario cambiarlo para que se pueda acceder a los datos (en el segmento de datos) median­te el registro DS. La instrucción MOV no permite mover directamen­te a DS, por lo que se utiliza el registro AX como registro inter­medio.

MOV DS, AX

Para acceder a 'mensaje'. Esta líneas inicializan el registro DS para que apunte al segmento donde reside el mensaje "mensaje". Como se ve, se puede utilizar el nombre de un segmento (DATOS en este caso) como valor inmediato, en cuyo caso el ensamblador, el enlazador y el cargador del MS-DOS harán que el dato final cargado sea el valor de segmento adecuado (hemos direccionado el segmento DATOS mediante DS).

Al segmento de código no hace falta direccionarlo con CS, pues lo hace el sistema operativo MS-DOS.

Tampoco hace falta direccionar el registro SS para acceder a la pila, pues el DOS lo inicializa también.

En éste programa no existe segmento extra.

MOV DX, offset “mensaje”

Esta instrucción carga en el registro DX el desplazamiento de la etiqueta "mensaje", dentro del segmento donde ha sido definida. Podemos utilizar la expresión OFFSET etiqueta para obtener el des­plazamiento dentro de un segmento de una etiqueta cualquiera (DS:DX = dirección del texto).

MOV AH, 09 

Especifica la Función 09.

INT 21h      

Invoca el servicio (interrupción) 21h. imprimir cadena.

Esta dos instrucciones invocan la función 09 de la interrupción 21h. correspondiente al MS-DOS. Este servicio envía a la salida estándar del programa (habitualmente la pantalla) la cadena de caracteres apuntada por DS:DX.

El MS-DOS reconoce como carácter de fin de cadena el código ASCII del carácter $ (dólar), que se puede encontrar después de la cadena mensaje. Si se olvida incluir el carácter $  el MS-DOS se­guirá imprimiendo lo que encuentre hasta encontrar un $.

MOV AX, 4C00h           

Servicio 4Ch; valor de retorno 0.

INT 21h   

Invoca el servicio 4Ch de la interrupción 21h, que retorna al sistema operativo con el ERRORLEVEL, indicando en el registro AL, en éste cado cero.

CODIGO ENDS            

Cierra el segmento "CODIGO".

END ENTRADA

Fin de programa; indica punto de entrada al programa.

La directiva END marca el ensamblador el final del código fuen­te. El símbolo a continuación de END indica al ensamblador en qué punto debe comenzar la ejecución del programa. El ensamblador pa­rará la información al enlazador, que incluirá esta información en la cabecera del EXE.

Directivas Simplificadas de Segmento:

Si se quieren utilizar esta directivas, es necesario primero incluir una línea con la directiva .MODEL.

El formato de esta directiva es el siguiente:

.MODEL modelo de memoria [,opciones]

Los modelos de memoria pueden ser:

   TINY: Tipo de puntero del Segmento de Código: NEAR, tipo de puntero del Segmento de datos: NEAR; permite combunar el Segmento de Código y el Segmento de Datos.

   SMALL: Tipo de puntero del Segmento de Código: NEAR, tipo de puntero del Segmento de datos: NEAR.

   MEDIUM: Tipo de puntero del Segmento de Código: FAR, tipo de puntero del Segmento de datos: NEAR.

   COMPACT: Tipo de puntero del Segmento de Código: NEAR, tipo de puntero del Segmento de datos: FAR.

   LARGE: Tipo de puntero del Segmento de Código: FAR, tipo de puntero del Segmento de datos: FAR.

   HUGE: Tipo de puntero del Segmento de Código: FAR, tipo de puntero del Segmento de datos: FAR.

   FLAT: Tipo de puntero del Segmento de Código: NEAR, tipo de puntero del Segmento de datos: NEAR; permite combinar el Segmento de Código y el segmento de Datos, pero se utiliza, exclusivamente, con el sistema operativo OS/2 2.x.

Las opciones pueden ser:

- Lenguaje:                La opción "lenguaje" facilita la compatibili­dad del ensamblador con lenguajes de alto ni­vel ( interfaz de lenguaje ensamblador con lenguaje de alto nivel), por determinar la codificación interna para nombre simbólicos públicos y externos. Puede tomar los siguien­tes valores: C, BASIC, FORTRAN, PASCAL, ¿TPASCAL?, SYSCAL­L y STDCALL.

- Sistema Operativos: Pueden tomar los valores: OS_OS2 o OS_DOS.

- Tamaño de la Pila: Pueden tomar los valores: NEARSTACK (Cuan­do el Segmento de Pila y el Segmento de Datos comparten el mismo segmento físico (SS=DS)) y FARSTACK (Cuando el Segmento de Pila y el Segmento de datos no comparten el mismo segmento físico (SS­DS)).

Por  ahora utilizaremos el modelo SMALL para la generación de programas EXE que utilicen un sólo segmento para código y otro para datos, y el modelo TINY para programa de tipo COM.

Cuando el ensamblador procesa la directiva .MODEL, prepara la definición de algunos segmentos por defecto par el código, los datos y la pila. Los nombres, alineamiento, combinación y clase de estos segmentos vienen fijados apropiadamente por el modelo espe­cificado, por lo que no necesitamos preocuparnos de ellos.

El modelo SMALL soporta un Segmento de Código y un Segmento de Datos.

El modelo MEDIUM soporta varios Segmentos de Código y un Segmen­to de Datos.

El modelo COMPACT soporta un Segmento de Código y varios Segmen­to de Datos.

Los modelos LARGE y HUGE son equivalentes, soportan varios Seg­mentos de Códigos y Varios Segmentos de Datos.

Para activar el tipo de procesador con el que vamos a trabajar y las instrucciones disponibles para ese tipo de procesador, se utilizan las sentencias:

.186, .286., .386 y .486

correspondientes al tipo de procesador del ordenador con el que estemos trabajando.           

Después de esto, el ensamblador reconoce algunas directivas especiales para abrir segmentos:         

.CODE para abrir el Segmento de Código.

.DATA para abrir el Segmento de Datos.

.STACK para fijar el tamaño del Segmento de Pila.

Estas directivas, además de abrir el segmento asociado, cierran el último segmento abierto, por lo que no es necesaria la directi­va ENDS.

La directiva .CODE, además de abrir el Segmento de Código, gene­ra el ASSUME adecuado para el modelo especificado.

La directiva .STACK lleva un parámetro opcional que fija el tamaño de la pila en bytes. En su defecto, el ensamblador asume un tamaño de 1024 bytes. El ensamblador y el enlazador tratan siempre de forma especial el segmento de pila, de manera que los datos del segmento de pila no aparecen en el fichero ejecutable, reduciendo por tanto el tamaño de éste y haciendo el tamaño del EXE indepen­diente del tamaño de la pila.

La directiva END, además de las funcione vistas, cierra el últi­mo segmento abierto. Es probable que se necesite inicializar re­gistros de segmento del mismo modo que en la forma de codificación normal, como MOV ax, datos  pero la directiva simplificada de se­gemto no permite ver el nombre de los segmentos generados.

Para solucionar este problema, el ensamblador le permite usar los símbolos : @CODE y @DATA en lugar del nombre de los segmento de Código y Datos, respectivamente.

Así quedaría, por tanto, el programa que codificamos anterior­mente en versión EXE usando las directivas simplificadas de seg­mentos:

.MODEL SMALL  ; Modelo de memoria SMALL: Usamos un Segmento de Código y un de Datos.

.STACK 200h    ; Tamaño de la pila 200h=512 bytes. En el otro programa eran: DW 100h, es decir, 256 palabras, es decir, 512 bytes.

.DATA                 ; Abre el Segmento de Datos

mensaje DB "Primer Programa","$"       ; Mensaje a imprimir

.CODE                ; Cierra el Segmento de Datos, abre el de Código y genera el

; ASSUME

ENTRADA:

MOV ax, @DATA          ; Valor del segmento para ".DATA".

MOV ds, ax      ; Para acceder a "mensaje".

MOV dx, OFFSET mensaje       ; Todo lo de abajo es igual.

MOV ah, 09

INT 21h

MOV ax, 4C00h

INT 21h

END ENTRADA   ; Fin del programa. Cierra el segmento de Código e indica el punto de entrada al programa.

MACROS.

Una macro consiste en una serie de lineas a la que se asocia un nombre y que se puede repetir en cualquier punto del listado sin más que dar su nombre.

Toda macro debe tener dos partes:

1)   La cabezera de la macro: Aquí se especifica el nombre de identificación de la misma, es decir, al grupo de instrucio­nes que engloba esa macro :

Nombre-Macro MACRO parámetro [, parámetro, ...]

2)   Texto o cuerpo de la macro: Aquí se situan las instrucciones (sentencias).

3)   Fin de macro (ENDM).

El formato a utilizar sería:

Nombre_Macro MACRO parámetro [, parámetro,...]

sentencias

ENDM

La cabecera y el fin de la MACRO, siempre vienen dados por pseu­doinstrucciones o directivas,.

El cuerpo de la MACRO, viene dado por instrucciones normales de ensamblador.

 Cuando el ensamblador se encuentra con una cabecera de MACRO, lo que hace es almacenarla con su cuerpo correspondiente en una tabla. Posteriormente, cuando en el programa se utilice la cabece­ra o el nombre de esa MACRO, el ensamblador accederá a la tabla mencionada y sustituirá, en el programa, ese nombre de MACRO por el cuerpo de la misma.

A la operación de búsqueda en la tabla de macros se le conoce como "Llamada a la MACRO", y a la sustitución del nombre de la MACRO por el cuerpo de la misma se denomina "Expansión de la MA­CRO".

Según lo comentado, si nos fijamos en un programa codificado en ensamblador, tenemos dos versiones de programa. Una de ellas es­crita directamente y otra mediante macros, con lo que nos podíamos encontrar que después de la expansión de las macros las dos ver­siones de  programa son iguales.

El empleo de macros no debemos confundirlo con el empleo de procedimientos, ya que una llamada a una macro sustituye su nombre por el cuerpo de la misma, en cambio, una llamada a procedimientos lo único que realiza es un enlace con otro programa. Las macros se suelen incluir todas dentro de ficheros específicos. En cada uno de los ficheros situaremos las macros que tengan alguna relación entre sí.

Nos podemos encontrar con macros que en la realización de una tarea puedan utilizar datos distintos.

Para no tener que definir una macro para cada dato distinto que tengamos que utilizar, se debe permitir la utilización de "Paráme­tro Formales", entendiéndose que estos datos serían los nombres que utilizaremos en el cuerpo de la MACRO cuando la definiésemos por primera vez.

Estos parámetros van situados en la cabecera de la definición de la macro.

En la llamada a la macro sería donde situaríamos los "Parámetros Reales", con los que va a trabajar la macro. Cuando se hace una expansión de la macro los parámetros formales se sustituyen por los parámetros reales.

INCLUDE.  Esta directiva nos proporciona la posibilidad de in­troducir, dentro de nuestro programa un conjunto de instrucciones que están situadas en otro fichero.

Estas instrucciones se sustituyen justamente en la posición en la que se encuentra la directiva. Dicha directiva se usa dentro de cualquiera de los segmentos, pero su utilización corriente es den­tro del Segmento de Código.

Se suele utilizar para poder incluir dentro de un programa los ficheros de MACROS.

Su formato es:               INCLUDE Nombre_Fichero

Si el Fichero a incluir en el programa (Nombre_Fichero), no se encuentra en el mismo directorio del programa o en la misma uni­dad, habrá que indicarle en la instrucción, el camino de acceso para encontrar ese Fichero.

Ejemplo:  En un disco tenemos un fichero con una serie de MACROS y que se llama: MACROS.ASM:

Listado de MACROS.ASM

retorno MACRO

           MOV ah, 4ch

            INT 21h

ENDM

display  MACRO cadena

MOV dx, OFFSET cadena

MOV ah, 09h

INT 21h

ENDM

leer_teclado MACRO

MOV ah, 08h

INT 21h

ENDM

En nuestro ordenador estamos realizando nuestro programa:

PILA SEGMENT STACK 'STACK'

dw 100h dup (?)

PILA ENDS

DATOS SEGMENT 'DATA'

paso db "MIOOO"

control db 5 dup (?)

men1 db "Clave de acceso:".13,10,"$"

men2 db "Bienvenido","$"

DATOS ENDS

CODIGO SEGMENT 'CODE'

ASSUME CS:  CODIGO, DS: DATOS, SS: PILA

EMPEZAR:

MOV ax, DATOS

MOV ds, ax

INCLUDE A:\ MACROS,ASM    ; Aquí le decimos al programa que incluya las ma­cros del porgrama  que se encuentra en el fichero MACROS:ASM que se encuentra en un disco en la unidad A de nuestro ordenador.

DOS:

MOV si, 0

display men1 ; "display" es una MACRO que tiene parámetro.

MOV cx, 5

UNO:

leer teclado         ; Otra MACRO. Esta macro no tiene parámetros.

MOV control[SI], al

INC si

LOOP  UNO

MOV si, 0

MOV cx, 5

TRES:

MOV al, control[SI]

CMP al, paso[SI]

JNE DOS

INC SI

LOOP TRES

display men2       ; Otra MACRO

retorno                ; Otra MACRO sin parámetros

CODIGO ENDS

ENDS EMPEZAR

Con las directivas simplificadas las macros funcionan igual.

LOCAL:   Directiva que le indica al ensamblador que dentro de la macro van a existir una serie de etiquetas que deben de ser identificadas de una forma especial al realizar la expansión de la macro y siempre considerando la relación que existe con la tabla de símbolos. Con ello se evita las definiciones múltiples de estas etiquetas.

El formato es el siguiente:

LOCAL etiquetas (separadas por comas)

Esta directiva debe situarse, en el caso de que exista después de la cabecera de la macro.

Ejemplo:

esperar MACRO numero

  LOCAL SEGUIR

  MOV cx, numero

seguir:

  LOOP seguir

ENDM

Otra cosa aconsejable que se puede hacer en referencia a las macros es guardar previamente en la pila el valor de los registros que vayamos a usar dentro de la macro y después, cuando acaben las instruciones de la macro, volver a retornárselos.

Ejemplo:

display MACRO cadena

 PUSH dx

 PUSH ax

 MOV dx, offset cadena

 MOV ax, 09h

 INT 21h

 POP ax

 POP dx

ENDM

Ejercicios:

1.- Disponemos de una matriz almacenada en una serie de bytes en memoria. Dicha matriz está estructurada mediante 4 filas y 3 co­lumnas. Realizar la suma de los ele­mentos de esta matriz reco­rriéndola fila a fila.

2.- Disponemos de una matriz de 4 filas y 3 columnas ocupando cada elemento de la matriz un byte. Calcular la suma de los elementos de esta matriz columna a columna,

3.- Disponemos de una matriz almacenada en palabras de memoria cuya estructura de 4 filas y 4 columnas. Se pide realizar la suma de sus dos diagonales.

4.- Disponemos de una matriz almacenada en palabras de memoria que posee una estructura de 4 filas y 3 columnas. Sumar los elementos que ocupen posiciones impares, a continuación, sumar los que ocu­pen posiciones pares, y la suma de elementos impares es mayor que la de los pares, efectuar la resta "impares - pares", y si no es así, efectuar la resta "pares - impares".

5.- Se trata de teclear un máximo de 40 caracteres que irán apare­ciendo en pantalla. Si durante el tecleo no deseamos seguir con el mismo, podemos romper la secuencia  pulsando el ENTER. Una vez tecleados los 40 caracteres, aparecerá en pantalla un literal que nos indicará que introduzcamos una palabra a localizar dentro de la secuencia inicial tecleada. Una vez localizada la palabra se nos deberá indicar cuantas veces aparece esta palabra dentro del texto inicial tecleado.

 PROCEDIMIENTOS.

El uso de los procedimientos va a estar marcado por elementos principales:

CALL:  (Llamada a un procedimiento). Esta instrucción transfiere el control a otro punto de la memoria, cargado IP (cuando el procedimiento llamado está dentro del mismo segmento (NEAR)), y eventualmente CS (cuando el procedimiento lla­mado está en otro segmento (FAR)), con nuevos valores. La dirección actual (CS:IP o solamente IP, en función de si la instrucción altera CS o no) se introduce en la pila para permitir un posterior retorno. El destino del salto puede estar indicado tras el código de operación de la instrucción o puede obtenerse indirectamente de un regis­tro o posición de memoria especificada en la instrucción. Hay varios tipos de llamadas (a etiquetas, a variables, etc), nosotros vamos a tratar las llamadas a etiquetas.

 Formato:

CALL [tipo_salto] destino

"tipo_salto" indica si la llamada es a un procedimiento cercano (entoces su valor es NEAR) o lejano (su valor es FAR), y "destino" es el nombre del procedimiento al que vamos a llamar.

PROC: Indica el comienzo de un procedimiento (un procedimiento es un bloque de instrucciones que sirven para realizar una tarea determinada y que pueden invocarse desde varios puntos del programa. Puede considerarse como una subruti­na).

 Formato:

Nombre_Procedimiento PROC atributo

sentencias

RET

Nombre_Procedimiento ENDP

El "atributo" puede ser NEAR o FAR dependiendo de si el salto es dentro del mismo segmento (NEAR) o no (FAR).

Los valores de IP (NEAR) o CS:IP (FAR) se guardan en la pila cuando se produce una llamada a un procedimiento. Para que estos valores puedan ser restaurados después de la ejecución del proce­dimiento y elñ programa sigua con su ejecución normal se ejecuta la instricción RET (retornar valores).

Ejemplo:

PILA SEGMENT STACK 'STACK'

dw 100h dup (?)

PILA ENDS              

DATOS SEGMENT 'DATA'

mensaje db "Primer Programa:","$"

DATOS ENDS

CODIGO SEGMENT 'CODE'

ASSUME CS:  CODIGO, DS: DATOS, SS: PILA

INCLUDE MACROS.ASM

ENTRADA:

MOV ax, DATOS

MOV ds, ax

display mensaje

retorno

;

; **** Procedimientos ****

;

retorno PROC

PUSH ax

MOV ax, 4C00h

INT 21h

POP ax

RET

retorno ENDP

CODIGO ENDS

END ENTRADA

(Fichero MACROS.ASM)

display MACRO mem

 PUSH dx

 PUSH ax

 MOV dx, offset mem

 MOV ah, 09h

 INT 21h

 POP ax

 POP dx

ENDM

COMPILACION.

Para conseguir un programa ejecutable, el programa fuente escri­to en lenguaje ensamblador, entre otras tareas, habrá que ensam­blarlo y linkarlo (LINKER).

Para ello el MASM 6.0 nos suministra una instrucción que nos realiza estas dos funciones: ML.

ML es el ensamblador que nos suministra MASM 6.0 y se encuentra (normalmente) en el subdirectorio: \MASM\BIN. El ensamblaje de un programa fuente escrito en ensamblador se realiza de la siguiente forma:

ML [\opción] Nombre_Programa.ASM

Si el fichero no se encuentra en el mismo directorio que el ensamblador, hay que poner el PATH donde se encuentra el fichero fuente. Para visualizar las opciones con las que cuenta el ensam­blador basta con ejecutar: 

 ML /?

Algunas de estas opciones son:

- /AT: Habilita el modelo TINY (para ficheros .COM).

- /c: Ensambla sin Linkar (LINK).

- /Zi: Añade información simbólica para el BEBUG.

- /Zm: Habilita la compatibilidad con MASM 5.10.

Si se quiere ejecutar el MASM 6.0 desde un entorno gráfico, se puede ejecutar un programa que hay en el directorio \MASM\BIN y que se llama PWB. Si fuera necesario la ejecución de LINK este se encuentra en el directorio \MASM\BINB.

         Parte 1 Parte 2 Parte 3 Parte 4

Copyright © 1998-2012- DS Tecnologia® manager@dstecnologia.com.ar - Politica de Privacidad