|
|
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.
| Ultima actualización: . |