Vous êtes sur la page 1sur 84

Manual de Lenguaje Ensamblador

TEMARIO

Introducción.
I.- Definiciones y conceptos preliminares.
1.- Conceptos básicos.
1.1.- El lenguaje de maquina y el lenguaje ensamblador.
1.2.- Interpretes, compiladores y ensambladores.
1.3.- El proceso de link, las rutinas de RUN-TIME y
los servicios del sistema operativo.
2.- La arquitectura de las computadoras personales IBM y
Compatibles.
3.- La arquitectura de los microprocesadores Intel.
4.- El sistema operativo MS-DOS.
5.- Ensambladores y Macroensambladores.
6. Sistemas numéricos
7. Operaciones con bytes
7.1. AND
7.2. OR
7.3. XOR
7.4. NOT
8. El juego de registros.
9.- ¿Qué es....la BIOS?
9.1. Configuración avanzada y del chipset
9.2. Periféricos integrados
9.3. Administración de energía
9.4. Configuración de PNP y slots PCI
9.5. Autoconfiguración de la BIOS
9.6. Otras utilidades
9.7. Salir de la BIOS
9.8. Actualizar la BIOS
9.9. La BIOS y la pila
9.10. Cómo saltarse la password de la BIOS
II.- El lenguaje ensamblador.
1.- Un ejemplo clásico.
2.- El formato del ensamblador.
3.- Directivas.
4.- Conjunto de instrucciones.
5.- Macros y procedimientos.
6.- Interrupciones.
III.- Creación y depuración de programas en lenguaje ensamblador
1.- Edición.
2.- Ensamblado.
3.- Link.
4.- Ejecución.
5.- Depuración.
5.1.- Uso de DEBUG de DOS I
5.2.- Uso de DEBUG II: Trazar un Programa
Manual de Lenguaje Ensamblador

5.3.- Uso de DEBUG III: Escribir un Programa


5.4.- Observaciones
6.- La utilería exe2bin y los archivos .exe y .com .
Resumen del DEBUG
Apéndice a.- Modelo de registros del 8086.
Apéndice b.- Registros expandidos del 8087.
Apéndice c.- Modelo de segmentación de memoria.
Apéndice d.- Directivas del MASM.
Apéndice e.- Juego de instrucciones del 8086, 8087, 80186, 80286 y 80287.
Apéndice f.- Interrupciones.
Apéndice g.- Ejemplos adicionales.

Bibliografía.
Manual de Lenguaje Ensamblador

INTRODUCCION.

El presente manual, es un documento que está orientado a la introducción a la programación en lenguaje


Ensamblador, y principalmente, a aquellas personas que nunca han trabajado con este lenguaje y probablemente
con ningún otro.

Tal y como lo indica su título, está enfocado al lenguaje ensamblador de los microprocesadores Intel,
particularmente el 8088, 8086, 80186, 80188 y 80286. Sin embargo, todos los programas generados para estos
procesadores correrán en un 80386 ó 80486. Adicionalmente se proporciona el juego de instrucciones de los
coprocesadores matemáticos 8087 y 80287, que es compatible con el de los microprocesadores ya citados, es
decir, el manual se enfoca completamente hacia las computadoras que operan con procesadores de la familia x86
de Intel y, considerando que el ensamblador basa su funcionamiento en los recursos internos del procesador, los
ejemplos descritos no son compatibles con ninguna otra arquitectura.

Este documento trata de abarcar, de la forma más general, todo aquello que involucra el conocimiento y
uso del lenguaje ensamblador. Así, ha sido organizado en tres partes. La primera describe los conocimientos
básicos que deben poseerse para una mejor comprensión e interpretación de lo que es el lenguaje ensamblador y
cómo debe ser usado. La segunda parte está dedicada por completo a lo que es el lenguaje ensamblador, las
interrupciones del sistema operativo MS-DOS y el conjunto de instrucciones 8086-80286. La última parte se ha
dedicado al proceso de ensamble, la depuración de programas en ensamblador, y algunas consideraciones sobre
la creación de programas y su ejecución.
Manual de Lenguaje Ensamblador

I.- DEFINICIONES Y CONCEPTOS PRELIMINARES.

1.- CONCEPTOS BASICOS.

1.1.- EL LENGUAJE DE MAQUINA Y EL LENGUAJE ENSAMBLADOR.

Todo procesador, grande o pequeño, desde el de una calculadora hasta el de un supercomputador, ya


sea de propósito general o específico, posee un lenguaje único que es capaz de reconocer y ejecutar. Por
razones que resultan obvias, este lenguaje ha sido denominado Lenguaje de Máquina y más que ser propio de un
computador pertenece a su microprocesador. El lenguaje de máquina está compuesto por una serie de
instrucciones, que son las únicas que pueden ser reconocidas y ejecutadas por el microprocesador. Este
lenguaje es un conjunto de números que representan las operaciones que realiza el microprocesador a través de
su circuitería interna. Estas instrucciones, por decirlo así, están grabadas o "alambradas" en el hardware y no
pueden ser cambiadas. El nivel más bajo al que podemos aspirar a llegar en el control de un microprocesador es
precisamente el del lenguaje de máquina.

Ahora bien, siendo el lenguaje de máquina un conjunto de números, ¿cómo es capaz el microprocesador
de saber cuándo un número representa una instrucción y cuándo un dato? El secreto de esto reside en la
dirección de inicio de un programa y en el estado del microprocesador. La dirección de inicio nos indica en qué
localidad de memoria comienza un programa, y en consecuencia que datos deberemos considerar como
instrucciones. El estado del microprocesador nos permite saber cuándo éste espera una instrucción y cuándo
éste espera un dato.

Obviamente, el lenguaje de máquina de un microprocesador no puede ser ejecutado por otro


microprocesador de arquitectura distinta, a menos que haya cierto tipo de compatibilidad prevista. Por ejemplo,
un 80486 es capaz de ejecutar lenguaje de máquina propio y soporta el código generado para microprocesadores
anteriores de la misma serie (desde un 8086 hasta un 80386). Por otra parte, un PowerPC es capaz de ejecutar
instrucciones de los microprocesadores Motorola 68xxx y de los Intel 80xx/80x86. En ambos casos, el diseño de
los microprocesadores se hizo tratando de mantener cierto nivel de compatibilidad con los desarrollados
anteriormente. En el segundo caso, este nivel de compatibilidad se extendió a los de otra marca. Sin embargo, un
8088 no puede ejecutar código de un 80186 o superiores, ya que los procesadores más avanzados poseen
juegos de instrucciones y registros nuevos no contenidos por un 8088. Un caso similar es la serie 68xxx, pero de
ninguna manera podemos esperar que un Intel ejecute código de un Motorola y viceversa. Y esto no tiene nada
que ver con la compañía, ya que Intel desarrolla otros tipos de microprocesadores como el 80860 y el iWARP, los
cuales no pueden compartir código ni entre ellos ni entre los 80xx/80xxx.

Ahora bien, mientras que con el lenguaje de máquina, nosotros obtenemos un control total del
microprocesador, la programación en este lenguaje resulta muy difícil y fácil para cometer errores. No tanto por el
hecho de que las instrucciones son sólo números, sino porque se debe calcular y trabajar con las direcciones de
memoria de los datos, los saltos y las direcciones de llamadas a subrutinas, además de que para poder hacer
ejecutable un programa, se deben enlazar las rutinas de run-time y servicios del sistema operativo. Este proceso
es al que se le denomina ensamblado de código. Para facilitar la elaboración de programas a este nivel, se
desarrollaron los Ensambladores y el Lenguaje Ensamblador.
Manual de Lenguaje Ensamblador

Existe una correspondencia 1 a 1 entre las instrucciones del lenguaje de máquina y las del lenguaje
ensamblador. Cada uno de los valores numéricos del lenguaje de máquina tiene una representación simbólica de
3 a 5 letras como instrucción del lenguaje ensamblador. Adicionalmente, este lenguaje proporciona un conjunto de
pseudo-operaciones (también conocidas como directivas del ensamblador) que sirven para definir datos, rutinas
y todo tipo de información para que el programa ejecutable sea creado de determinada forma y en determinado
lugar.

1.2- INTERPRETES, COMPILADORES Y ENSAMBLADORES.

Aún cuando el lenguaje ensamblador fue diseñado para hacer más fácil la programación de bajo nivel,
esta resulta todavía complicada y muy laboriosa. Por tal motivo se desarrollaron los lenguajes de alto nivel, para
facilitar la programación de los computadores, minimizando la cantidad de instrucciones a especificar. Sin
embargo, esto no quiere decir que el microprocesador ejecute dichos lenguajes. Cada una de las instrucciones de
un lenguaje de alto nivel o de un nivel intermedio, equivalen a varias de lenguaje máquina o lenguaje
ensamblador.

La traducción de las instrucciones de nivel superior a las de bajo nivel la realizan determinados
programas. Por una parte tenemos los intérpretes, como DBase, BASIC, APL, y Lisp. En estos, cada vez que se
encuentra una instrucción, se llama una determinada rutina de lenguaje de máquina que se encarga de realizar
las operaciones asociadas, pero en ningún momento se genera un código objeto y mucho menos un código
ejecutable. Por otra parte, tenemos los compiladores, como los desarrollados para Fortran, Clipper, COBOL,
Pascal o C, que en vez de llamar y ejecutar una rutina en lenguaje de máquina, éstos juntan esas rutinas para
formar el código objeto que, después de enlazar las rutinas de run-time y llamadas a otros programas y servicios
del sistema operativo, se transformará en el programa ejecutable.

Finalmente, tenemos los ensambladores— como los descritos en este trabajo —que son como una
versión reducida y elemental de un compilador (pero que de ninguna manera deben considerarse como tales), ya
que lo único que tienen que hacer es cambiar toda referencia simbólica por la dirección correspondiente, calcular
los saltos, resolver referencias y llamadas a otros programas, y realizar el proceso de enlace. Los ensambladores
son programas destinados a realizar el ensamblado de un determinado código.

1.3.- EL PROCESO DE LIGA, LAS RUTINAS DE RUN-TIME Y LOS SERVICIOS DEL SISTEMA
OPERATIVO.

Para crear un programa ejecutable a partir de un código objeto se requiere que se resuelvan las llamadas
a otros programas y a los servicios del sistema operativo, y agregar las rutinas o información de run-time para que
el programa pueda ser cargado a memoria y ejecutado. Este proceso es lo que se conoce como Link o proceso
de liga, y se realiza a través de un ligador o Linker que toma de entrada el código objeto y produce de salida el
código ejecutable.

Las rutinas de RUN-TIME son necesarias, puesto que el sistema operativo requiere tener control sobre el
programa en cualquier momento, además de que la asignación de recursos y su acceso deben hacerse
solamente a través del sistema operativo. Para los computadores personales, esto no es tan complejo como para
otros computadores y sistemas operativos, pero es requerido.
Manual de Lenguaje Ensamblador

2.- LA ARQUITECTURA DE LAS COMPUTADORAS PERSONALES IBM Y


COMPATIBLES.

Los computadores PC están compuestos físicamente por: monitor, teclado, CPU, floppy drives, hard disk
drives, periféricos y componentes adicionales. Lógicamente están compuestos por el BIOS (Basic Input-Output
System) y el sistema operativo MS-DOS (MicroSoft Disk Operating System). El teclado se encuentra conectado a
un puerto de entrada destinado específicamente para tal propósito. Este tiene asociado un área de memoria
(buffer) al cual llegan los códigos de las teclas presionadas en el teclado.

La CPU está compuesta por la memoria RAM, memoria ROM (donde reside el BIOS), los controladores
de disco, la tarjeta de video, los puertos de entrada-salida y el microprocesador. Los periféricos se encuentran
conectados y asociados por el sistema operativo con un puerto en particular (una vez que el puerto ha sido
abierto). Las tarjetas controladores de floppy y disco duro se encargan de intercambiar información entre
memoria, procesador y unidades de disco. Para tal propósito se reservan en memoria áreas específicas, para
servir de buffers en el intercambio de datos del computador con las unidades de disco.

El monitor y la tarjeta de video están muy relacionados entre sí, ya que si bien el monitor se encarga de
desplegar la información, no es éste quien la mantiene, sino la memoria designada a la tarjeta de video. Esta
memoria es como cualquier otra, direccionable y modificable, y es en donde se guarda la información que
aparece en pantalla. Debido al tipo de microprocesador empleado, la memoria de la PC se encuentra dividida en
una serie de blocks denominados segmentos, de 64KB cada uno. La memoria es accesada especificando el
segmento y el desplazamiento dentro del segmento (segmento: desplazamiento, para mayor detalle ver el
apéndice C). Para las PC´s la memoria se clasifica en tres tipos:

- Convencional: Es la memoria de tipo básico y que abarca las direcciones de 0 a 640KB. En ésta es
donde se cargan los programas de usuario y el sistema operativo, y es la que está disponible para equipo XT
(8088,8086, 80186 y 80188).

- Extendida: Esta memoria sólo está disponible para procesadores 80286 y mayores (equipo AT, 80386
y 80486). Muchos programas que usan la memoria convencional no pueden usar la memoria extendida porque
las direcciones en memoria extendida están más allá de las que el programa puede reconocer. Únicamente las
direcciones dentro de los 640KB pueden ser reconocidas por todos los programas. Para reconocer la memoria
extendida se requiere de un manejador de memoria extendida, como HIMEM.SYS que provee MS-DOS.

- Expandida: Esta es la memoria que se agrega al computador a través de una tarjeta de expansión, y
que debe ser administrada por un programa especial, como el EMM386.EXE. A diferencia de la memoria
convencional o extendida, la memoria expandida es dividida en bloques de 16K llamados páginas (pages).
Cuando un programa solicita información de memoria expandida el manejador copia la página correspondiente
en un área denominada page frame para poder ser accesada en la memoria extendida.

Como se puede observar, el 8088, 8086, 80188 y 80186 son capaces de direccionar hasta 1 MB de
memoria. Ya hemos indicado que la memoria convencional sólo abarca 640KB, así nos quedan 384KB libres.
Esta parte de la memoria es denominada parte alta, y como no está disponible para muchos programas
generalmente se usa para cargar drivers del sistema operativo, programas residentes y datos de hardware
(ROM y páginas de video).
Manual de Lenguaje Ensamblador

3.- LA ARQUITECTURA DE LOS MICROPROCESADORES INTEL.

Sin importar de qué microprocesador se trate, los microprocesadores del 8088 al 80486 usan el modelo
de registros del 8086. Los microprocesadores matemáticos 80287 al 80487 utilizan el modelo de registros
expandidos del 8087. Para mayor detalle ver los apéndices A y B.

Los microprocesadores matemáticos están diseñados exclusivamente para efectuar operaciones


aritméticas de una manera más rápida y precisa bajo el control de otro procesador razón por la cual se
denominan coprocesadores. Estos también poseen un juego de instrucciones de lenguaje de máquina propio. La
diferencia entre los diversos microprocesadores de uso general y los coprocesadores reside en el nuevo conjunto
de instrucciones, registros y señalizadores agregados con cada nueva liberación de un procesador superior.
Estas adiciones se hicieron con el fin de agregar un mayor poder de cómputo sin alterar la estructura básica,
para así mantener la compatibilidad con los desarrollos anteriores, tanto de software como de hardware.

La diferencia entre los 8086 y 8088 con los 80186 y 80188 no es muy grande, ésta radica en un grupo
de instrucciones que fueron agregadas al 80186 y al 80188. La diferencia entre el 8086 y el 8088, lo mismo que
entre el 80186 y el 80188, es el modelo de memoria que usan ambos procesadores. El 8088 y el 80188 están
diseñados como microprocesadores de 8 bits por lo que el modo de acceso a la memoria es ligeramente distinto
pero compatible con el 8086 y el 80186. Esto se verá con más detalle en un tema posterior.

4.- EL SISTEMA OPERATIVO MS-DOS.

Junto con todo lo visto anteriormente, y como se mencionó anteriormente, uno de los componentes que
caracterizan los computadores personales es su sistema operativo. Una PC puede correr varios sistemas
operativos: CP/M, CP/M-86, XENIX, Windows, PC-DOS, y MS-DOS. Lo que los define es la forma en que están
integrados sus servicios y la forma en que se accesa a ellos. Esto es precisamente lo que el linker debe enlazar y
resolver. Aquí nos enfocaremos exclusivamente en el sistema operativo MS-DOS, y lo que se mencione aquí será
valido para las versiones 3.0 y superiores. Este sistema operativo está organizado de la siguiente manera:

Comandos Internos (reconocidos y ejecutados por el COMMAND.COM)


Comandos Externos (.EXEs y .COMs)
Utilerías y drivers (programas de administración del sistema)
Shell (Interfaz amigable, sólo versiones 4.0 o mayores)
Servicios (Interrupciones)

Los servicios, más conocidos como interrupciones o vectores de interrupción, es parte medular de lo que
es MS-DOS, y no son más que rutinas definidas por MS-DOS y el BIOS, ubicadas a partir de una localidad de
memoria específica. La manera de acceder a estas rutinas y a los servicios que ofrecen es mediante una
instrucción que permite ejecutar una interrupción. MS-DOS proporciona dos módulos: IBMBIO.COM (provee una
interfaz de bajo nivel para el BIOS) e IBMDOS.COM (contiene un manejador de archivos y servicios para manejo
de registros). En equipos compatibles estos archivos tienen los nombres IO.SYS y MSDOS.SYS,
respectivamente. El acceso a los servicios del computador se realiza de la siguiente manera:

Programa DOS DOS ROM EXTERNO


de usuario Alto nivel Bajo nivel
Petición de ‹—› IBMDOS.COM ‹—› IBMBIO.COM ‹—› BIOS ‹—› Dispositivo I/O

5.- ENSAMBLADORES Y MACROENSAMBLADORES.


Manual de Lenguaje Ensamblador

Existen varios ensambladores disponibles para ambiente MS-DOS: el IBM Macro Assembler, el Turbo
Assembler de Borland, el Turbo Editassm de Speedware, por citar algunos. Una breve descripción de cada uno
se proporciona a continuación.

Macro Ensamblador IBM: Está integrado por un ensamblador y un macroensamblador. En gran medida
su funcionamiento y forma de invocarlo es sumamente similar al de Microsoft. Su forma de uso consiste en
generar un archivo fuente en código ASCII, se procede a generar un programa objeto que es ligado y se genera
un programa .EXE. Opcionalmente puede recubrirse a la utilería EXE2BIN de MS-DOS para transformarlo a
.COM. Es capaz de generar un listado con información del proceso de ensamble y referencias cruzadas.

Macro Ensamblador de Microsoft: Dependiendo de la versión, este ensamblador es capaz de soportar


el juego de instrucciones de distintos tipos de microprocesadores Intel de la serie 80xx/80x86. En su versión 4.0
este soporta desde el 8086 al 80286 y los coprocesadores 8087 y 80287. Requiere 128KB de memoria y sistema
operativo MS-DOS v2.0 o superior. Trabaja con un archivo de código fuente creado a partir de un editor y grabado
en formato ASCII. Este archivo es usado para el proceso de ensamble y generación de código objeto.
Posteriormente, y con un ligador, es creado el código ejecutable en formato .EXE.

Turbo Editassm: Este es desarrollado por Speddware, Inc., y consiste de un ambiente integrado que
incluye un editor y utilerías para el proceso de ensamble y depuración. Es capaz de realizar el ensamble línea a
línea, conforme se introducen los mnemónicos, y permite revisar listas de referencias cruzadas y contenido de los
registros. Este ensamblador trabaja con tablas en memoria, por lo que la generación del código ejecutable no
implica la invocación explícita del ligador por parte del programador. Adicionalmente permite la generación de
listados de mensajes e información de cada etapa del proceso y la capacidad de creación de archivos de código
objeto.

Turbo Assembler (De Borland Intl): es muy superior al Turbo Editassm. Trabaja de la misma forma,
pero proporciona una interfaz mucho más fácil de usar y un mayor conjunto de utilerías y servicios.

En lo que se refiere a las presentes notas, nos enfocaremos al Microsoft Macro Assembler v4.0. Los
programas ejemplo han sido desarrollados con éste y está garantizado su funcionamiento. Estos mismos
programas posiblemente funcionen con otros ensambladores sin cambios o con cambios mínimos cuando utilizan
directivas o pseudoinstrucciones. Realmente la diferencia entre los ensambladores radica en la forma de generar
el código y en las directivas con que cuente, aunque estas diferencias son mínimas. El código ensamblador no
cambia puesto que los microprocesadores con los que se va a trabajar son comunes. Así, todos los programas
que se creen con un ensamblador en particular podrán ser ensamblados en otro, cambiando las pseudo-
operaciones no reconocidas por el equivalente indicado en el manual de referencia del paquete empleado.

Los programas que componen el Macro Ensamblador Microsoft v4.0 son los siguientes:

Programa Descripción
MASM.EXE Microsoft Macro Assembler
SYMDEB.EXE Microsoft Symbolic Debuger Utility
LINK.EXE Microsoft 8086 object linker
MAPSYM.EXE Microsoft Symbol File Generator
CREF.EXE Microsoft Cross-Reference Utility
Manual de Lenguaje Ensamblador

LIB.EXE Microsoft Library Manager


MAKE.EXE Microsoft Program Maintenance Utility
EXEPACK.EXE Microsoft EXE File Compression Utility
EXEMOD.EXE Microsoft EXE File Header Utility
COUNT.ASM Sample source file for SYMDEB session
README.DOC Updated information obtained after the manua was printed.

El Microsoft Macro Assembler v4.0 crea código ejecutable para procesadores 8086, 8088, 80186, 80188,
80286, 8087 y 80287. Además es capaz de aprovechar las instrucciones del 80286 en la creación de código
protegido y no protegido. El término macroensamblador es usado para indicar que el ensamblador en cuestión
tiene la capacidad de poder ensamblar programas con facilidad de macro. Una macro es una pseudo-instrucción
que define un conjunto de instrucciones asociadas a un nombre simbólico. Por cada ocurrencia en el código de
esta macro, el ensamblador se encarga de sustituir esa llamada por todas las instrucciones asociadas y, en caso
de existir, se dejan los parámetros con los que se estaba llamando la macro y no con los que había sido
definida. Es importante señalar que no se deja una llamada, como a una subrutina o procedimiento, sino que se
incorporan todas las instrucciones que definen a la macro.1

6. SISTEMAS NUMÉRICOS

Comencemos por los sistemas de numeración que más utilizaremos al programar. El básico va a ser el
sistema hexadecimal, aunque debemos de explicar antes el binario, el sistema de numeración que utiliza el
ordenador. Los números que conocemos están escritos en base 10. Esto significa que tenemos, desde el 0 hasta
el 9, diez símbolos para representar cada cifra. Es decir, cada cifra ir de 0 a 9, y al superar el valor "9", cambiar a
0 y sumar uno a su cifra de la izquierda: 9+1: 10.

El sistema binario utiliza tan sólo dos símbolos, el "0" y el "1". Imaginemos que tenemos el número binario
"0". Al sumarle una unidad, este número binario cambiar a "1". Sin embargo, si volvemos a añadirle otra unidad,
a este número en formato binario ser el "10" (aumenta la cifra a la izquierda, que era 0, y la anterior toma el valor
mínimo).

Sumemos ahora otra unidad: el aspecto del número ser "11" (tres en decimal). Y podríamos seguir:

Binario : 0 ; 1 ; 10 ; 11 ; 100 ; 101 ; 110; 111 ; 1000 ; 1001 ; 1010,...


Decimal: 0 1 2 3 4 5 6 7 8 9 10

Esto nos permite establecer un sistema bastante sencillo de conversión del binario al decimal;

He aquí los valores siendo n el valor de la cifra:


Cifra menos significativa:
n*2^0 = 1 si n=1 o 0 si n=0
Segunda cifra:
n*2^1 = 2 si n=1 o 0 si n=0
Tercera cifra:
n*2^2 = 4 si n=1 o 0 si n=0
Cuarta cifra:
n*2^3 = 8 si n=1 o 0 si n=0
Manual de Lenguaje Ensamblador

etc.,...

Y así continuaríamos, aumentando el número al que se eleva 2. Traduzcamos entonces el número binario
'10110111'

2^7+ 0 +2^5+2^4+ 0 +2^2+2^1+2^0 = 128 + 0 + 32 + 16 + 4 + 2 + 1 = 183


1 0 1 1 0 1 1 1

De todos modos, esta transformación la he puesto simplemente para que se comprenda con más claridad
cómo funcionan los números binarios. Es mucho más aconsejable el uso de una calculadora científica que
permita realizar conversiones entre decimales, hexadecimales y binarios. Se hace su uso ya casi imprescindible
al programar. La razón del uso de los números binarios es sencilla. Es lo que entiende el ordenador, ya que
interpreta diferencias de voltaje como activado (1) o desactivado (0), aunque no detallaré esto. Cada byte de
información está compuesto por ocho dígitos binarios, y a cada cifra se le llama bit. El número utilizado en el
ejemplo, el 10110111, sería un byte, y cada una de sus ocho cifras, un bit. Y a partir de ahora, cuando escriba un
número binario, lo haré con la notación usual, con una "b" al final del número (ej: 10010101b)

La conversión a hexadecimal es muy utilizada en ensamblador, se trata de un sistema de numeración en


base dieciséis. Por tanto, hay dieciséis símbolos para cada cifra, y en vez de inventarse para ello nuevos
símbolos, se decidió adoptar las primeras letras del abecedario. Por lo tanto, tendremos ahora:

Hex Dec
1 --> 1
2 --> 2
3 --> 3
4 --> 4
5 --> 5
6 --> 6
7 --> 7
8 --> 8
9 --> 9
A --> 10
B --> 11
C --> 12
D --> 13
E --> 14
F --> 15
10 --> 16
11 --> 17
Etc,...

Como vemos, este sistema nos planteas bastantes problemas para la conversión, una calculadora
científica será casi imprescindible para realizar esta operación.
Por que utilizar este sistema? Bien sencillo. Volvamos al byte, y traduzcamos su valor más alto,
"11111111". Resulta ser 256. Ahora pasemos esta cifra al sistema hexadecimal, y nos resultará "FF". Obtenemos
un número más comprensible que el binario (difícil de recordar), y ante todo mucho más compacto, en el que dos
cifras nos representaran cada byte. Podremos además traducir fácilmente el binario a hexadecimal con esta tabla;
cada cuatro cifras binarias pueden traducirse al hexadecimal:
Manual de Lenguaje Ensamblador

Binario Hexadecimal
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
0111 7
1000 8
1001 9
1010 A
1011 B
1100 C
1101 D
1110 E
1111 F
Por ejemplo, el número binario:
1111 0011 1010 1110

En hexadecimal sería:
1111 0011 1010 1110
F 3 A E

Para referirnos a un número hexadecimal sin especificarlo, usaremos la notación que se suele usar al
programar, con un 0 al principio (necesario cuando hay letras) y una h al final, por ejemplo, el número anterior
sería 0F3AEh
7. OPERACIONES CON BYTES

Hay cuatro operaciones básicas que se pueden realizar con un número binario, y coinciden con
operaciones de la lógica matemática, con lo que cualquiera que la haya estudiado tendrá cierta ventaja para
entenderla. Para explicarlas, llamar‚ al valor 0 resultado "falso", y al valor 1 "verdadero". Las operaciones son
AND, OR, XOR y NOT

7.1. AND

Es un 'y' lógico. Se realiza entre dos cifras binarias confrontando cada cifra con su correspondiente, y el
resultado será "1" si las dos son verdaderas (si las dos valen "1”), y "0" (falso) en el resto de los casos.

AND
1er. numero 2do. numero Resultado
1 1 1
1 0 0
0 1 0
0 0 0

Vuelvo a la lógica para explicarlo más claramente: Imaginemos la frase: "El hombre es un mamífero y
camina erguido". El hecho de que el hombre sea un mamífero es cierto (1), y el de que camine erguido, otro (1).
Por lo tanto, al unirlos mediante una conjunción (‘y’ o ‘AND’), resulta que ya que se dan las dos, la oración es
verdadera.

Pongamos un ejemplo más complejo, queremos realizar un AND lógico entre dos bytes:
11011000 AND 01101001

Observemos lo que sucede:


11011000 216 AND 01101001
En sistema decimal sería: AND 105 (aunque en sistema decimal 01001000 es mas lioso) 72

Cuando coinciden dos valores de "verdad", el resultado es "verdad", si uno es falso, el resultado es "falso"
(no es verdad que "El hombre es un mamífero y respira debajo del agua”), y si los dos son falsos, el resultado es
falso (no es cierto que "El hombre es un ave y respira debajo del agua”)

7.2. OR

El "o" lógico. El resultado es "verdadero" cuando al menos uno de los factores es verdadero. O sea, es
"1" cuando al menos uno de los dos factores es "1". Sería como la frase "Voy a buscar el peine o la caja de
bombones", donde que uno sea cierto no significa que el otro no lo sea; es cierta la frase, es verdadera mientras
uno de los términos sean verdaderos.
Manual de Lenguaje Ensamblador
Manual de Lenguaje Ensamblador
Manual de Lenguaje Ensamblador

Ahora se desea más memoria que 64 Kb, y para esto se poseen los otros dos bytes. Para formar la
dirección completa, se toman los 16 bits del registro de segmento y se sitúan en los 16 bits superiores de la
dirección de 20 bits, dejando los otros cuatro a cero. Vamos, como si se añade cuatro ceros a la derecha, y se
suma a este valor de 20 bits, el Offset, da el resultado de la dirección real de memoria

A continuación se presentará una demostración gráfica para el mejor entendimiento:

Sea el valor de Segmento 0Ah (o decir, 10 decimal o 1010b, en binario). Y el del Offset que va a valer (en
binario) 01011111 00001010.

La suma para obtener la dirección de memoria sería tal que así:

0000 0000 0000 1010 0000 (segmento multiplicado*16, con 4 ceros mas)
+ 0101 1111 0000 1010 (el offset)
--------------------------------------
0000 0101 1111 1010 1010

Y esta sería la dirección *real* de memoria (05FAAh o 24490 Dec). Como puedes observar, y como
curiosidad final, distintos segments y offsets especifican direcciones de memoria distintas; por ejemplo, los pares
0040h: 0000 (donde el primero es el Segment y el segundo el Offset, así lo tomaremos a partir de ahora), son
iguales que 0000:0400h, y los dos se referirían a la misma posición de memoria física, la 0400h o 1024d. Espero
que haya quedado claro, aunque sea simplemente tener una ligera idea. Lo próximo serán los registros, y (y
ahora me pongo como los del Pcmanía cuando hablan de Windoze95) podremos empezar en serio con nuestro
lenguaje favorito X-)
8. JUEGO DE REGISTROS

Quizá alguno de vosotros se está preguntando a estas alturas: ¨ Y eso del Segment y Offset, dónde se
guarda, que indica al ordenador esos sitios en memoria, que indica al ordenador en que punto de la memoria
esta y que tiene que ejecutar? Pues bien, para esto y mucho mas sirven los registros.

Se trata de una serie de "variables", que contienen información que puede ser cambiada.

Comenzaré, al contrario que todos los libros, por los de segmento y offset actual: CS e IP.

El registro CS es una variable de un tamaño de dos bytes. Contiene el Segmento actual en que se
encuentra el programa. IP, es la variable, de dos bytes también, que contiene el Offset actual. Esto significa, el
ordenador va interpretando las secuencias de bytes, pero necesita "algo" que le indique donde tiene que leer. La
combinación CS: IP (tal y como me referí antes en lo de segments & Offsets) contiene la dirección en la que el
ordenador est interpretando información *en el momento*. O sea, indica la dirección de la próxima instrucción
que se va a ejecutar.

El registro DS y el registro ES también sirven para guardar direcciones de Segmentos, y también son
variables de dos bytes, ser n utilizada para por ejemplo mover datos en memoria, imprimir cadenas, bueno, un
etcétera largísimo. Digamos que son "punteros", que apuntan a cierta zona de memoria (siempre combinado con
otro que haga de Offset, claro).

El registro SS apunta a la pila, y el SP es el que contiene el offset de la pila, pero esto lo explicaré mas
adelante.

Luego tenemos una serie de registros que utilizaremos mas comúnmente: AX, BX, CX y DX.

Todas ocupan dos bytes, y se pueden utilizar divididas en dos partes de longitud un byte, cambiando de
nombre. AX se divide en AH y AL, BX en BH y BL, CX en CH y CL y DX en DH y DL. La 'H' se refiere a High en
inglés, alto (de mayor valor), y la 'l' a Low (de menor valor). Lo ilustro un poquillo:

AX
|-----------------------------|
11010110 10111000
AH AL

Las funciones de estos cuatro registros son diferentes:

AX se suele utilizar como propósito general, indica función a las interrupciones, etc., y es el más flexible,
ya que ser el único que permita multiplicaciones y divisiones. Se denomina a veces acumulador.

BX nos servirá mucho como "handler", para abrir/cerrar archivos, etc., y como registro de propósito
general al igual que AX, CX y DX. CX se suele usar como contador. DX suele ser el puntero, señalando haciendo
el papel de Offset lugares en memoria (suele combinarse con DS en la forma DS: DX).
Manual de Lenguaje Ensamblador
Y nos quedan ya sólo tres registros, BP, SI y DI, que son también punteros. SI y DI los utilizaremos a
menudo para copiar bytes de un lado a otro, etc. Ni que decir que, como el resto de registros, contienen dos
bytes. Igual sucede con BP, de otros dos bytes de tamaño.
Manual de Lenguaje Ensamblador
 PM Timers: para controlar el tiempo que debe permanecer inactivo el ordenador ( System) o el disco
duro (HDD) antes de que se active el ahorro de energía. Existen 3 grados de ahorro de energía:
o Doze: reduce la velocidad de la CPU (el microprocesador).
o Standby: reduce la actividad de todo el ordenador.
o Suspend: reduce al mínimo la actividad del ordenador; sólo debe utilizarse con CPUs tipo SL,
como son la mayoría de los 486 rápidos y superiores.
 PM Events: una larga serie de eventos o sucesos que deben ser controlados para saber si el ordenador
está inactivo o trabajando. Es habitual no controlar ( Disable) la actividad de la IRQ8 (reloj de la BIOS), ya
que rara vez se la puede considerar como totalmente inactiva.
 CPU Fan Off in Suspend: si el ventilador de la CPU va conectado a la placa base, lo apaga cuando el
equipo está en suspenso, ya que en ese momento la CPU está prácticamente parada.
 Modem Wake Up: activa el equipo cuando se detecta una llamada entrante en el módem. Necesita que
el módem soporte esta característica y que esté conectado a la placa base mediante un cable especial.
 LAN Wake Up: igual que la anterior, pero para la tarjeta de red. También necesita estar conectado a la
placa base mediante un cable.

9.4. Configuración de PNP y slots PCI

Un menú lleno de opciones complicadas (en esta página pocas no lo son), de la clase que sería
deseable no tener que alterar nunca; ése es mi consejo, escoja Auto todas las veces que pueda, pero si tiene
algún conflicto entre dispositivos (misma IRQ, sobre todo)... Probablemente se pregunte qué tiene que ver PNP
con PCI; pues bien, la gran mayoría de dispositivos PCI soportan PNP, a diferencia de las tarjetas ISA, mucho
más problemáticas. Por eso, si su placa no tiene slots PCI (como las primeras para 486), no se extrañe si este
menú no aparece.

Ah, para el que no lo conozca, el Plug&Play , PNP o P&P, es una tecnología que facilita la conexión de
dispositivos, ya que se supone que basta con enchufar y listo. Claro que no todos los dispositivos son PNP ni es
una tecnología perfecta, si fuera así este menú no existiría...

 PNP OS Installed: informa al sistema de si hay un sistema operativo PNP instalado , es decir, uno que
soporta Plug&Play, como Windows 95 (o eso dicen que hace...), en cuyo caso pasa a éste el control de
los dispositivos PNP. De cualquier forma, muchas veces lo que esta casilla indique no afecta al correcto
funcionamiento del sistema.
 Resources Controlled by: pues eso, recursos controlados bien manual, bien automáticamente. De
nuevo, muchas veces es indiferente una u otra opción... siempre que no haya problemas, claro.
 IRQx/DMAx assigned to: una lista de las interrupciones (IRQs) y canales DMA que podemos asignar
manualmente, bien a tarjetas PCI/ISA PnP (compatibles con PNP), bien a tarjetas Legacy ISA (tarjetas
ISA no PNP, que son las más conflictivas). Necesitaremos conocer los valores de IRQ y/o DMA a
reservar, que vendrán en la documentación del dispositivo problemático.
 PCI IDE IRQ Map to: algo que muy probablemente no necesite cambiar nunca, ya que sólo afecta a
controladoras IDE no integradas en la placa base, sino en forma de tarjeta, que no sean PNP.
 Assign IRQ to USB: pues eso, si el puerto USB debe tener una interrupción asignada o no. Si no tiene
ningún dispositivo USB conectado (¿y quién los tiene hoy en día?) puede liberar esa IRQ para otros
usos; suele ser la misma interrupción que para uno de los slots PCI o ISA.
Manual de Lenguaje Ensamblador
9.5. AUTOCONFIGURACIÓN DE LA BIOS

Este apartado comprende diversas opciones que se proporcionan para facilitar la configuración de la
BIOS, de las cuales las más comunes son:

 LOAD BIOS DEFAULTS: carga una serie de valores por defecto con poca o nula optimización,
generalmente útiles para volver a una posición de partida segura y resolver problemas observados al
arrancar.
 LOAD SYSTEM DEFAULTS: una opción cuyos efectos varían de unas BIOS a otras. En unos casos
carga unos valores por defecto seguros (como LOAD BIOS DEFAULTS), en otros carga unos valores ya
optimizados para conseguir un rendimiento adecuado, o incluso puede servir para cargar la última serie
de valores guardados por el usuario.
 LOAD TURBO DEFAULTS: carga los valores que estima óptimos para incrementar el rendimiento.

En cualquier caso, debe tenerse en cuenta que los cambios no suelen ser guardados automáticamente, sino que
deben confirmarse al salir de la BIOS.

9.6 Otras utilidades

Las BIOS pueden hacer todavía más cosas, dependiendo del modelo en concreto; algunas de las más
usuales están a continuación.

Autodetección de discos duros IDE

Esta opción permite detectar los discos duros que están conectados al sistema, así como su
configuración. Resulta muy útil para simplificar la tarea de instalar un disco nuevo, así como cuando los datos del
disco no están completos o no parecen funcionar en nuestra BIOS.
Manual de Lenguaje Ensamblador
Su uso es sencillísimo: se entra en este menú y se va detectando cada uno de los cuatro posibles
dispositivos IDE. Apunte las opciones que le aparezcan y pruebe a usarlas; recuerde usar el modo LBA para
discos de más de 528 MB. Tenga en cuenta que muchas veces sólo por entrar en esta utilidad se alteran
automáticamente los valores de configuración del disco, así que después de salir de ella compruebe si los
cambios corresponden a los que quería realizar.

Control por password

Es decir, por una clave de acceso en forma de palabra secreta que sólo conozca usted. Tenga en cuenta
que si la olvida se verá en graves problemas, hasta el punto de tener que borrar toda la BIOS para poder volver a
usar el ordenador, así que apúntela en algún lugar seguro.

Se suele poder seleccionar, bien en un menú específico o en las BIOS Features, entre tener que
introducir la clave cada vez que se arranca el ordenador o sólo cuando se van a cambiar datos de la BIOS. Lo
primero es el método ideal para seguridad, y además es gratis; lo segundo es útil cuando gente inexperta pero
manazas tiene acceso al ordenador (por ejemplo, su sobrinito el tocalotodo).

Formateo de disco duro a bajo nivel

O, en inglés, HDD Low Level Format . Se trata de un formateo mucho más intenso que el normal; no
sólo elimina los datos, sino que reorganiza la propia estructura del disco. Generalmente sólo debe usarse cuando
el disco está fallando muy a menudo o ha sido infectado por un virus tremendamente resistente, y aun así no
resulta recomendable.

Si será duro, que realizarlo ¡suele ser motivo de pérdida de la garantía del disco duro! En fin, si se atreve,
ármese con los datos de configuración del disco (cilindros, cabezas...) y rece por no tener que interrumpirlo por
nada del mundo, cortes de luz incluidos.

Antivirus

No, no crea que con esta opción podrá ahorrarse el comprar uno de esos programas antivirus tan
tristemente necesarios en los PC. En realidad, lo único que suele hacer esta opción (que en ocasiones tiene un
menú propio y en otras se engloba bajo el Standard Setup, tal vez con el nombre de Virus Warning) es no
permitir que se escriba sobre la tabla de particiones o el sector de arranque del disco duro, bien sólo durante el
arranque o en cualquier momento, dependiendo del modelo concreto de BIOS.

La idea es impedir que un virus destroce el disco duro sin darle oportunidad a cargar un disquete de
arranque con un antivirus para desinfectar el sistema; no impedirá la infección, pero es una medida más de
seguridad y gratis. Por cierto, puede ser necesario deshabilitar esta opción durante la instalación del sistema
operativo o al formatear el disco duro, no sea que la BIOS crea que se trata de un ataque viral.

9.7 SALIR DE LA BIOS

Pues es sencillo, pero revisémoslo para los que no entiendan inglés en absoluto. Generalmente existen dos
opciones:

 Save and Exit Setup: o bien Write to CMOS and Exit o algo similar; pues eso, grabar los cambios y
salir, con lo cual se reinicia el equipo. Debería pedirle confirmación, en forma de " Y/N?" (Yes o No).
Manual de Lenguaje Ensamblador
 Exit Without Saving: o Do Not Write to CMOS and Exit o Discard Changes and Exit o similar; lo
contrario, salir sin grabar los cambios. También debería pedir confirmación.

9.8. ACTUALIZAR LA BIOS

La BIOS maneja temas tan críticos como el soporte de uno u otro microprocesador; además, como programa
que es, no está exenta de fallos y se revisa periódicamente para eliminarlos o añadir nuevas funciones.

Antiguamente, la única forma de actualizar una BIOS era extraer el chip de BIOS y sustituirlo por otro, lo cual
no se lo recomiendo a nadie, tanto por las posibles incompatibilidades como por lo delicado y caro de la
operación.

Modernamente han aparecido BIOS que pueden modificarse con un simple programa software; se las
denomina Flash-BIOS, y no son un invento desdeñable. Lo que es más, la existencia de una de estas BIOS o
no debería ser argumento de peso a la hora de comprar una placa base, al menos entre los manitas informáticos.
Tenga en cuenta que mantener un registro de BIOS actualizadas es un servicio que sólo ofrecen los grandes
fabricantes de placas base... y aun así no siempre se puede encontrar la necesaria actualización.

Vaya a Actualizar la BIOS para conocer los pasos concretos a seguir; eso sí, le aviso de que deberá
asegurarse de que la operación de actualización no se interrumpe por nada del mundo, así que nada de hacer
multitarea, meterle prisa o tocar el teclado mientras se actualiza; son unos segundos, pero de importancia crítica.
¿Adivina qué día del año va a saltar la luz? Efectivamente, justo cuando esté actualizando su BIOS.

9.9 LA BIOS Y LA PILA

Como dijimos, la pila conserva los datos de la BIOS cuando el ordenador está apagado. Dura
mucho (unos tres años de media), pero al final se agota. Para cambiarla, apunte todos los datos
de la BIOS, desconecte todo y sustitúyala por una igual, o bien por un paquete externo de baterías
que se conectan a un jumper (un microinterruptor) de la placa base; ambas cosas las debería
encontrar en tiendas de electrónica.

Después conecte todo, arranque el ordenador, entre en la BIOS y reintroduzca todos los datos,
ya que se habrán borrado. ¿Se imagina si no tuviera una copia escrita qué aventura? A mí me pasó hace años, y
no me quedó más remedio que aprender sobre BIOS... bueno, no hay mal que por bien no venga.

9.10: CÓMO SALTARSE LA PASSWORD DE LA BIOS

No, no se trata de hacer ilegalidades en ordenadores ajenos, se trata de saber qué hacer si sufre una
repentina amnesia o si la BIOS trae una password ya introducida; por ejemplo, una BIOS con la que luché una
vez tenía como password por defecto "AMI", el nombre de su fabricante. Además, en ordenadores de segunda
mano pasa no pocas veces.

Los métodos son pocos; realmente sólo uno, y muy radical: borrar la BIOS entera. Para ello existen tres
formas:
Manual de Lenguaje Ensamblador
 Por software tipo "hacker": algunos programas se especializan en destrozar BIOS, y si tiene suerte
quizá incluso le digan cuál es la password sin tener que borrar la BIOS. Busque en los "bajos fondos" de
Internet... ¡y tenga cuidado con estos programas y con los posibles virus!
 Mediante un jumper en la placa base: en algunas, no todas, existe un jumper que al cerrarse (al
conectarse ambas patillas), y tras unos minutos de espera, permite borrar la BIOS limpiamente.
 Desconectando la pila: drástico, brutal, pero absolutamente efectivo.

En fin, sea como sea, recuerde tener su copia en papel de la BIOS y de la password para no tener que llegar
a estos extremos.

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 clásico 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
Manual de Lenguaje Ensamblador
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 del dólar 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 cargada 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
Manual de Lenguaje Ensamblador
 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, digitosB, digitosQ o digitosO, digitosD, digitosH. Si una D o
B aparecen al final de un número, éstas siempre se considerarán un especificador de raíz, e.g. 11B será tratado
como 112 (210), mientras que si se trata del número 11B 16 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 digitosR. También puede usarse una de las directivas DD, DQ, y
DT con el formato [+¦-] digitos.digitos [E [+¦-] dígitos]. 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 ensablamblado, 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.
Manual de Lenguaje Ensamblador

 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(default) Activa las instrucciones para el 8086 y 8088 e inhibe las del 80186 y 80286.
.8087(default) 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.
.289p 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/parráfo, 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
default que asimilan los registros de segmento. Existen dos formas de hacer esto:

ASSUME registrosegmento:nombre,,,
ASSUME NOTHING

NOTHING cancela valores previos.


Manual de Lenguaje Ensamblador
 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ádruplo), 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 accesadas 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

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 accesar en la forma segmento:offset.


Manual de Lenguaje Ensamblador

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

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:
Manual de Lenguaje Ensamblador
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´
Manual de Lenguaje Ensamblador
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:
Manual de Lenguaje Ensamblador
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 construir el programa en módulos, 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.
Manual de Lenguaje Ensamblador
 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
Manual de Lenguaje Ensamblador
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

Los 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

La distancia, que puede ser NEAR (default) 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 cúal 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 correponden
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.
Manual de Lenguaje Ensamblador

III.- CREACION Y DEPURACION DE PROGRAMAS EN LENGUAJE ENSAMBLADOR

1.- EDICION.

Los archivos fuente de código ensamblador deben estar en formato ASCII standard. Para esto puede
usarse cualquier editor que permita crear archivos sin formato, e.g. Edlin, Edit, Write, El editor del Turbo Pascal,
Works, Word, WordStar, etcétera. Las declaraciones pueden ser introducidas en mayúsculas y/o minúsculas.
Una buena práctica de programación es poner todas las palabras reservadas (directivas e instrucciones) en
mayúsculas y todo lo del usuario en minúsculas para fines de facilidad de lectura del código. Las sentencias
pueden comenzar en cualquier columna, no pueden tener más de 128 caracteres, no se permiten líneas
múltiples ni códigos de control, y cada línea debe ser terminada con una combinación de line-feed y carriage-
return. Los comentarios se declaran con ; y terminan al final de la línea.

2.- ENSAMBLADO.

El ensamblado se lleva a cabo invocando al MASM. Este puede ser invocado, usando una línea de
comando, de la siguiente manera:

MASM archivo [,[objeto][,[listado][,[cross]]]]][opciones][;]


donde:
Archivo Corresponde al programa fuente. Por default se toma la extensión .ASM.
Objeto Es el nombre para el archivo objeto.
Listado Nombre del archivo de listado de ensamblado.
Cross Es un archivo de referencias cruzadas.
Manual de Lenguaje Ensamblador

OpcionesPueden ser:
/A: escribe los segmentos en orden alfabético
/S: escribe los segmentos en orden del fuente
/Bnum: fija buffer de tamaño num
/C: especifica un archivo de referencias cruzadas
/L: especifica un listado de ensamble
/D: crea listado del paso 1
/Dsym: define un símbolo que puede usarse en el ensamble
/Ipath: fija path para búscar archivos a incluir
/ML: mantiene sensitividad de letras (mayús./minús) en nombres
/MX: mantiene sensitividad en nombre publicos y externos
/MU: convierte nombres a mayúsculas
/N: suprime tablas en listados
/P: checa por código impuro
/R: crea código para instrucciones de punto flotante
/E: crea código para emular instrucciones de punto flotante
/T: suprime mensajes de ensable exitoso
/V: despliega estadísticas adicionales en pantalla
/X: incluir condicionales falsos en pantalla
/Z: despliega líneas de error en pantalla

si el ; al final se omite es necesario poner todas las comas que se indican. Si no se quiere poner algún
valor basta con dejar la coma.

La otra forma de invocar al ensamblador es sólo tecleando MASM y respondiendo a la información que
se solicita. Para omitir algún valor sólo basta teclear ENTER si dar ningún valor.

3.- LINK.

De la misma forma que el ensamblado, la fase de liga se lleva a cabo con el LINK. Este puede ser
invocado de la misma forma que el MASM. Los parámetros que este requiere son:

LINK objeto [,[ejecutable][,[mapa][,[librería]]]]][opciones][;]

donde:

objeto Es el nombre para el archivo .OBJ


ejecutable Nombre del archivo .EXE
mapa Nombre del archivo mapa
librería Nombre del archivo biblioteca de rutinas
Manual de Lenguaje Ensamblador

opciones  Pueden ser:


/HELP muestra lista de opciones
/PAUSE pausa en el proceso
/EXEPACK empaca archivo ejecutable
/MAP crea mapa se símbolos públicos
/LINENUMBERS copia número de lineas al mapa
/NOIGNORECASE mantiene sensitividad en nombres
/
no usa bibliotecas por default
NODEFAULTLIBRARYSEARCH
/STACK:size fija el tamaño del stack a usar
/CPARMAXALLOC:número fija alojación máxima de espacio
/HIGH fija la dirección de carga más alta
/DSALLOCATE aloja grupo de datos
/NOGROUPASSOCIATION ignora asociaciones para direcciones
/OVERLAYINTERRUPT:número asigan nuevo número a la INT 03Fh
/SEGMENTS:número procesa un número de segmentos
/DOSSEG sigue la convención de orden de DOS

4.- EJECUCION.

Para la ejecución del programa simplemente basta teclear su nombre en el prompt de MS-DOS y teclear
ENTER. Con esto el programa será cargado en memoria y el sistema procederá a ejecutarlo. El proceso
completo para poder crear un programa ejecutable con el Microsoft Macro Assembler se muestra abajo.

Edición Ensamble Liga Ejecución


.asm .obj .exe
del
Archivo
Creación de MASM LINK programa
un archivo programa programa
sin formato
(ASCII)

y lo que se vería en pantalla sería lo siguiente:

C:\DATA\PROGRAMS\ASM>masm main
Microsoft (R) Macro Assembler Version 4.00
Copyright (C) Microsoft Corp 1981, 1983, 1984, 1985. All rights reserved.

Object filename [main.OBJ]:


Source listing [NUL.LST]:
Manual de Lenguaje Ensamblador
Cross-reference [NUL.CRF]:

50966 Bytes symbol space free

0 Warning Errors
0 Severe Errors

C:\DATA\PROGRAMS\ASM>masm task
Microsoft (R) Macro Assembler Version 4.00
Copyright (C) Microsoft Corp 1981, 1983, 1984, 1985. All rights reserved.

Object filename [task.OBJ]:


Source listing [NUL.LST]:
Cross-reference [NUL.CRF]:

51034 Bytes symbol space free

0 Warning Errors
0 Severe Errors

C:\DATA\PROGRAMS\ASM>link main+task

Microsoft (R) 8086 Object Linker Version 3.05


Copyright (C) Microsoft Corp 1983, 1984, 1985. All rights reserved.

Run File [MAIN.EXE]:


List File [NUL.MAP]:
Libraries [.LIB]:

C:\DATA\PROGRAMS\ASM>main
Entrando a un submodulo....
.......saliendo del submodulo.

C:\DATA\PROGRAMS\ASM>

5.- DEPURACION.

Para la depuración de un programa en ensamblador tenemos disponibles dos herramientas. Por un lado
tenemos el debuger que nos proporciona MS-DOS (DEBUG.EXE) y por otro lado tenemos el que nos
proporciona Microsoft (SYMDEB.EXE). Este último trabaja igual que el de MS-DOS pero nos proporciona
muchas ventajas más. Una de ellas es la facilidad de desplegar el código fuente correspondiente a la instrucción
que se esta ejecutando (si el programa ejecutable fue ensamblado o compilado con un ensamblador o
compilador compatible), nos permite ejecutar comandos del S.O. y nos permite obtener información de las
interrupciones de manera simbólica.

5.1.- Uso de DEBUG de DOS I


Manual de Lenguaje Ensamblador
DOS y la cónsola de Windows incluyen un programa llamado DEBUG. La palabra "debug" podríamos
traducirla por depurador, aunque realmente ésta no sea su traducción literal. Supuestamente, un depurador
está destinado a la eliminación de "bichos", cucarachas en nuestros programas. Es una especie de insecticida.
En terminos de programación, podemos decir que se trata de un programa para la localización de errores que
afectan sensiblemente el funcionamiento de nuestros programas.

Con el depurador podemos revisar paso por paso la ejecución de nuestro programa, revisar como va
modificándose el contenido de los registros hasta ubicar donde está el "bicho" y aplastarlo. Imagino que el
nombre se debe a que los primeros desperfectos en las primeras máquinas de cómputo electrónicas se debía
generalmente a la presencia de cucarachas u otros insectos en los circuitos de tubos. El debug de DOS
permite, entre otras cosas, editar archivos ejecutables y hasta escribir programas en lenguaje ensamblador.

5.2.- Uso de DEBUG II: Trazar un Programa

DEBUG, como muchos otros programas auxiliares de la programación, trabaja con el sistema
hexadecimal. Para ejecutar DEBUG escribimos DEBUG en el puntero de DOS o en el cuadro de ejecución
(Inicio-Ejecutar) de Windows. Aparecerá sólo un guión que es el apuntador del DEBUG.

Las diversas operaciones del DEBUG se indican con letras:

A: assemble (ensamblar)
C: compare (comparar)
D: display (desplegar)
E: enter (ingresar)
F: full (llenar)
G: go (ir)
H: Hexadecimal
I: in (entrada)
L: load (cargar)
M: move (mover)
N: name (nombrar)
O: out (salida)
P: proceed (proceder)
Q: quit (quitar)
R: register (registro)
S: search (buscar)
T: trace (trazar o rastrear)
U: unassemble (desensamblar)
W: write (escribir)

Ahora no las usaremos todas. Antes de escribir un programa con DEBUG, primero revisemos el que
escribimos al comienzo. Cargamos el programa en el DEBUG:

C:\tasm\works\primer>DEBUG primer.exe
Manual de Lenguaje Ensamblador

Ahora ingresamos la orden T, y obtendremos:

-t

AX=0E00 BX=0000 CX=0020 DX=0000 SP=0040 BP=0000 SI=0000 DI=0000


DS=0DEF ES=0DEF SS=0E01 CS=0DFF IP=0003 NV UP EI PL NZ NA PO NC
0DFF:0003 8ED8 MOV DS,AX

Se trata de la segunda instrucción de nuestro programa. La instrucción MOV es una instrucción


de asignación, parecida al operador "=" de C. Lo que hace es mover el contenido del segundo operando,
aquí AX, que para el momento tiene la dirección del segmento de datos, al primer operando, DS. Antes de la
ejecución de la instrucción DS=0DEF y AX=0E00; son valores hexadecimales.

Ingresemos de nuevo t:

-t

AX=0E00 BX=0000 CX=0020 DX=0000 SP=0040 BP=0000 SI=0000 DI=0000


DS=0E00 ES=0DEF SS=0E01 CS=0DFF IP=0005 NV UP EI PL NZ NA PO NC
0DFF:0005 BA0400 MOV DX,0004

Ahora DS=0E00. Se ha movido lo que estaba en AX a DS.

La siguiente instrucción mueve a DX la dirección de nuestra cadena. Habíamos escrito "LEA DX, string";
DEBUG lo ha interpretado como "mover a DX lo que está 4 bytes por encima del inicio del segmento de datos".
Si se quiere verificar escribimos: D DS:4, desplegar lo que está 4 bytes después del inicio del segmento
de datos:

-d ds:4
0E00:0000 48 6F 6C 61-20 67 65 6E 74 65 21 24 Hola gente!$
0E00:0010 C7 06 0E 96 3E 2B 2E C7-06 10 96 3D 3B E8 83 09 ....>+.....=;...
0E00:0020 73 13 B8 FF FF 53 26 8B-1D 26 3A 0F 73 03 B8 02 s....S&..&:.s...

En 0E00+4 vemos el código hexadecimal correspondiente a nuestra cadena, que en la columna


derecha podemos ver en ASCII. Podemos ver que el símbolo "$", que originalmente habíamos introducido como
36, y que en la columna del medio aparece en hexadecimal: 24.

Sigamos trazando:

-t

AX=0E00 BX=0000 CX=0020 DX=0004 SP=0040 BP=0000 SI=0000 DI=0000


DS=0E00 ES=0DEF SS=0E01 CS=0DFF IP=0008 NV UP EI PL NZ NA PO NC
0DFF:0008 B409 MOV AH,09

Ahora moveremos a la parte superior de AX un nueve:


Manual de Lenguaje Ensamblador
-t

AX=0900 BX=0000 CX=0020 DX=0004 SP=0040 BP=0000 SI=0000 DI=0000


DS=0E00 ES=0DEF SS=0E01 CS=0DFF IP=000A NV UP EI PL NZ NA PO NC
0DFF:000A CD21 INT 21

Vemos ahora que "AX=0900", que confirma que hemos movido 9 a la parte alta de AX. Se va a
ejecutar la interrupción 21h, servicio 9; recuérdese que anteriormente habíamos escrito "int 33". DEBUG
trabaja en hexadecimal, así que despliega la versión hexadecimal de lo que escribamos. Para trazar por
encima de la interrupción, no es bueno usar T, ya que esto nos llevará a la rutina de la interrupción. Así que
mejor ejecutamos P:

-p
Hola gente!
AX=0924 BX=0000 CX=0020 DX=0004 SP=0040 BP=0000 SI=0000 DI=0000
DS=0E00 ES=0DEF SS=0E01 CS=0DFF IP=000C NV UP EI PL NZ NA PO NC
0DFF:000C B410 MOV AH,10

Esto despliega nuestra cadena en la cónsola. Observa que todos los registros se han mantenido iguales
después de la interrupción, excepto AX; vemos que en la parte baja de AX, que es AL, aparece un 24h. Se trata
del caracter "$". Quiere decir que el servicio 9 de la interrupción 21h usa el registro AL para revisar la cadena.

Ahora moveremos 16 a AH. DEBUG lo presenta como "MOV AH, 10"; 10h = 16 decimal.

Hacemos T:

AX=1024 BX=0000 CX=0020 DX=0004 SP=0040 BP=0000 SI=0000 DI=0000


DS=0E00 ES=0DEF SS=0E01 CS=0DFF IP=000E NV UP EI PL NZ NA PO NC
0DFF:000E CD16 INT 16

Se va a ejecutar el servicio 16 o 10h de la interrupción 22 o 16h. Lo que hace este servicio es esperar
a que el usuario pulse un tecla, cuyo código devuelve en AH el código de rastreo y en AL el código de la tecla.
Para teclas de función extendida (F1 - F12) más importante es el código de rastreo, ya que como código de
tecla se pasará 00, para las teclas F1-F12, E0h para para otras teclas de control como Inicio o RePag. Hacemos
P y se ejecuta nuestra pausa; pulsamos cualquier tecla, en mi caso "enter":

-p

AX=1C0D BX=0000 CX=0020 DX=0004 SP=0040 BP=0000 SI=0000 DI=0000


DS=0E00 ES=0DEF SS=0E01 CS=0DFF IP=0010 NV UP EI PL NZ NA PO NC
0DFF:0010 B80000 MOV AX,0000

Código de rastreo en AH=1Ch y código de tecla en AL=0Dh. Ahora vamos a salir. Devolvemos el valor
FALSE (=0) en AX a DOS. Un simple gesto de educación.

AX=0000 BX=0000 CX=0020 DX=0004 SP=0040 BP=0000 SI=0000 DI=0000


DS=0E00 ES=0DEF SS=0E01 CS=0DFF IP=0013 NV UP EI PL NZ NA PO NC
0DFF:0013 CB RETF
Manual de Lenguaje Ensamblador

Por último regresamos a DOS. La instrucción RETF es una operación ceroaria (sin operandos) que
significa RETURN FAR, regresar lejos. Recordemos que hemos declarado nuestro procedimiento así:

main proc far

"main" es el nombre del procedimiento; proc es una directiva que indica inicio de una rutina o función
(en ensamblador se llaman procedimientos a las funciones) y far indica que el procedimiento puede ser
accedido desde otros segmentos. La instrucción RETF nos saca del procedimiento a la línea de órdenes.

Hay otras formas más limpias de salir de un programa, pero con esta basta por ahora. Nota también
que a la derecha de cada instrucción aparece la dirección en memoria, en sistema hexadecimal, de la
instrucción, y luego es seguida por otra cifra en hexadecimal. Ésta última es la versión en hexadecimal del
código de operación de la instrucción. Si no fuera por el sistema hexadecimal, ahí aparecerían una serie de
unos y ceros. Con DEBUG también podemos obtener el código fuente en ensamblador de un fichero ejecutable.
Esto se hace cargando el fichero y ejecutando la orden U, seguida por la dirección a partir de la cual
queremos que se inicie el desensamblado y el número de líneas a desensablar:

-u cs:00
0CE2:0000 B8E30C MOV AX,0CE3
0CE2:0003 8ED8 MOV DS,AX
0CE2:0005 BA0400 MOV DX,0004
2:0008 B409 MOV AH,09

0CE2:000A CD21 INT 21


0CE2:000C B410 MOV AH,10
0CE2:000E CD16 INT 16
0CE2:0010 B80000 MOV AX,0000
0CE2:0013 CB RETF

Tememos aquí un desensamblado de nuestro código. También podemos obtener este desensamblado
en un archivo de texto aparte:

DEBUG primer.exe<U CS:00 14>prim.asm

Esta orden volcará en prim.asm un desensablado de los primeros 20 (14h) bytes de primer.exe, que
podemos revisar con un editor.

Podríamos escribir un fichero disasm.bat:

cls
@echo off
@echo U CS:00 13>t_$
@echo.>>t_$
@echo Q>>t_$
2echo.>>t_$
@echo Desensamblando....
DEBUG %1.exe<t_$>%1_.asm
Manual de Lenguaje Ensamblador
EDIT %1_.asm

Luego ejecutamos:

disasm primer

y ya! Podríamos mejorarlo para que desensamble exactamente las instruccciones del segmento de
código; e incluso hacer que nuestro .BAT cambie algunos valores hexadecimales en el .EXE, pero no es este
el lugar para explicarlo.

5.3.- Uso de DEBUG III: Escribir un Programa

Escribamos ahora un programa con el DEBUG. Antes una observación sobre el formato de los
ejecutables en DOS. Existen dos formatos: .COM y .EXE. El primero, .COM, es el formato original. En este
formato, todo, código y datos, es puesto en un único segmento cuyo tamaño no debe exeder los 64KB.

En el formato .EXE, que es posterior, se reserva un segmento para datos, uno para código y uno para la
pila.

Nuestro programa fue escrito en formato .EXE. Con el DEBUG se pueden escribir programas en
formato .COM, que son bastante más pequeños.

El programa debe comenzar a ejecutarse en la dirección 256 [100h], ya que los ejecutables de DOS
reservan los primeros 256 bytes para colocar ahí una estructura de datos conocida como PSP, cuando es
cargado en la memoria. El PSP (Program Segment Prefije: Prefijo de Segmento del Programa) contine
información que será utilizada por el cargador de DOS.

Así que comenzamos escribiendo A 100 [enter]

-a 100
0DAB:0100 jmp 108
0DAB:0102 db "hola$"
0DAB:0107 nop
0DAB:0108 mov dx,102
0DAB:010B mov ah,9
0DAB:010D int 21
0DAB:010F mov ah,10
0DAB:0111 int 16
0DAB:0113 int 20
0DAB:0115

La primera instrucción ,"jmp 108", es saltar a la localidad 108. Necesitamos un espacio para nuestra
cadena. Como no sabemos el tamaño del programa y no podemos determinar al comienzo donde estará la
cadena si la ponemos al final del programa, la ponemos al comienzo, y para evitar que el programa
comience a ejecutarse en la cadena (lo que daría error) le pasamos por encima. La instrucción jmp 108 tiene
dos bytes, reservamos 6 bytes para la cadena. Nos sobra un byte, así que escribimos "nop" en 107, un
operador ceroario que significa No operación. Vemos que la dirección de la cadena es 102, así que ponemos
Manual de Lenguaje Ensamblador
este valor en DX; luego llamamos al servicio 9 (mov ah, 9) de la interrupción 21h (int 21) para desplegar la
cadena. Luego ejecutamos el servicio 10h de la interrupción 16h (que detiene la ejecución del programa) y por
último regresamos a DOS con la interrupción 20h. Pulsamos dos veces enter para salir de la orden A.

Podemos probar de inmediato nuestro programa con la orden G:

-g
hola

Debes pulsar ENTER para salir del programa. Se desplegará:

"El programa ha finalizado con normalidad"

Ahora debemos escribir nuestro programa a un archivo .COM en disco. Para ello empleamos la orden W
[write]. Para realizar esta escritura correctamente, primero elegimos un nombre para el programa, como
pr.com. La extensión debe ser .COM, porque W no puede crear ejecutables con formato .EXE; la orden es:

-n pr.com

También debemos especificar el tamaño de nuestro ejecutable. W creará un archivo con el tamaño
indicado en el registro CX; así que deberíamos poner en CX el tamaño de nuestro ejecutable empleando la
orden R:

-r cx
CX 0000
:15

Finalmente, escribimos el archivo en disco:

-w
Escribiendo 00015 bytes

Eso es todo. Luego lo ejecutamos. Salimos de DEBUG:

-q

y ejectamos pr.com:

hola

¿Todo bien?

Observa el tamaño del archivo: 21 bytes (15h = 21 decimal)


Manual de Lenguaje Ensamblador
6.- LA UTILERIA EXE2BIN Y LOS ARCHIVOS .EXE Y .COM .

Para MS-DOS sólo existen dos tipo de archivos ejecutables los .COM y .EXE. Ambos archivos difieren en
algunas cosas. Primero, las ventajas de los .EXE son dobles, nos permiten tener archivos reubicables y el uso de
hasta cuatro segmentos (STACK, DATA, EXTRA y CODE) de hasta 64KB cada uno. Un archivo .COM sólo
puede tener un segmento de 64KB, en el que se tiene tanto código como pila, y datos. La desventaja de los
.EXE es que agregan 512 bytes como cabecera con información para la reubicación del código. Un .COM no es
reubicable, siempre inicia en la dirección 0100H.

Si nuestro programa no es muy grande 64KB son mas que suficientes. Por lo que conviene crearlo
como .COM, para esto se cuenta con la utilería EXE2BIN.EXE que nos proporciona el sistema operativo. y que
nos permite crear .COM a partir de .EXE . Las restricciones para esto son las suguientes: el archivo a convertir
no debe estar empacado, no debe tener segmento de stack, debe tener sólo segmento de código y su tamaño
debe ser menor a 64KB.

Resumen del DEBUG

Uso del DEBUG.

Ingreso: Tipeamos la instrucción que se menciona seguidamente desde el prompt.

C: \ > DEBUG Después de dar enter aparecerá un guión

C:\>debug
-
Ya estamos posicionados en el DEBUG y éste está preparado para recibir instrucciones en
ASSEMBLER.

Ejemplo 1: Ver las instrucciones permitidas en el ASSEMBLER básico.


Tipeamos lo siguiente:
-??
Y nos mostrará la información que sigue. Tipee y compruebe

Ensamblar A [dirección]
Comparar C dirección de intervalo
Volcar D [intervalo]
escribir E dirección [lista]
llenar F lista de intervalos
ir G [=dirección] [direcciones]
hex H valor1 valor2
entrada I puerto
cargar L [dirección] [unidad] [primer_sector] [número]
mover M dirección de intervalo
nombre N [nombre_ruta] [lista_argumentos]
salida O byte de puerto
Manual de Lenguaje Ensamblador
proceder P [=dirección] [número]
salir Q
registrar R [registrar]
buscar S lista de intervalos
seguimiento T [=dirección] [valor]
desensamblar U [intervalo]
escribir W [dirección] [unidad] [primer_sector] [número]
asignar memoria expandida XA [#páginas]
desasignar memoria expandida XD [identificador]
asignar páginas de memoria expandida XM [Lpágina] [Ppágina] [identificador]
mostrar estado de la memoria
XS
expandida
-

Note que al terminar aparece el guión que nos indica que el DEBUG tiene nuevamente el
control de las operaciones, con lo cual Uds. podrán ingresar más comandos

Cada una de la instrucciones que se mostraron anteriormente se irán explicando a lo largo de este
apunte, donde aplicaremos un ejercicio específico para cada una de ellas incrementando las posibilidades, desde
las más simples hasta las más complejas, para perfeccionar nuestro dominio de la programación.

Ejemplo 2: Ver los registros.

Este ejercicio nos permite que miremos el valor de varios registros y del registro de estado de la ALU
(STATUS REGISTER). Para eso tipeamos:

-r

Y no muestra lo que sigue:

AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000


DS=1987 ES=1987 SS=1987 CS=1987 IP=0100 NV UP EI PL NZ NA PO NC
1987:0100 8936C7DE MOV [DEC7],SI DS:DEC7=0000
-
El significado de los diferentes registros se da en la tabla siguiente.

REF Nombre Que es lo que hace


AX Acumulador Almacena el resultado de operaciones
matemáticas; lectura / escritura (I/O)
dede/hacia los puertos que están conectados
con los periféricos; almacena también un
área de memoria temporal.
BX Registro Base Alacena direcciones o valores y puede
interactuar con el Acumulador.
Manual de Lenguaje Ensamblador
CX Registro Contador Utilizados para la ejecución de LOOPS y
puede ser incrementado o decrementado
según la instrucción que sea necesaria
DX Registro de Datos De acuerdo a la estructura de computadores
analizada por nosotros corresponde al MDR
(MEMORY DATA RGISTER) y es el paso
intermedio para el ingreso de datos.
DS Registro de segmento de Es un puntero que nos indica la dirección
datos donde se encuentran los datos.
ES Segmento extra para Igual que el anterior pero apuntando a una
datos dirección mayor.
SS Registro de segmento de Indica donde se encuentra el STACK.
pila
CS Registro de segmento Indica donde esta almacenado el código para
código la ejecución del programa.
BP Puntero del STACK Registro complementario para no modificar el
SS.
SI Registro de Indice de Alacena una dirección que nos indica donde
segmento se encuentra el vector de datos.
DI Indice destino Contiene una dirección donde se copiara el
vector de datos indicado por SI.
SP Puntero de Pila (STACK POINTER) Permite saber donde se
encuentra el último dato almacenado en el
STACK. Siempre debe ser mayor o igual al
SS o BP
IP Apuntador de la siguiente (INSTRUCTION POINTER) También llamado
instrucción PROGRAM COUNTER. Tiene almacenada la
dirección de la próxima instrucción a ser
ejecutada.

Ahora pasemos al registro bandera o STATUS REGISTER, que nos da el resultado de la


actuación de la ALU

Señal REF Función REF Función


Overflow NV No hay OV Hay
Direction UP Hacia adelante DN Hacia atrás
Interrupts DI Desactivadas EI Desactivadas
Sign PL Valor positivo NG Valor negativo
Zero NZ No es cero ZR Es cero
Auxiliary NA No hay acarreo AC Hay acarreo
Carry
Parity PO Paridad impar PE Paridad Par
Carry NC No hay acarreo CY Se produjo acarreo
Manual de Lenguaje Ensamblador

El comando r (registro) nos permite interactuar con los registros de la CPU, posibilitando modificar sus
contenidos. Cuando tipeamos r sin ningún otro parámetro nos muestra todos los registros con sus respectivos
contenidos, pero si agregamos un parámetro nos va a mostrar solo el contenido del registro que nosotros
mencionamos:

-r bx
BX 0000
:
Como podemos observar no nos devuelve el signo – sino que ahora nos muestra : para que ingresemos
el valor que queremos que sea almacenado en el registro BX.

En el ejemplo vemos que BX tiene un valor 0000. Si ingresamos 5555 en : tendremos:

-r bx
BX 0000
:5555
-r
AX=0000 BX=5555 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0100 NV UP EI PL NZ NA PO NC
1987:0100 8936C7DE MOV [DEC7],SI DS:DEC7=0000
-
En negrilla vemos que el registro BX quedó modificado con el nuevo valor tipeado.

Suma y Resta.

Ejemplo S1: Se pide sumar dos valores (200 y FFF) .

Vamos a ingresar las siguientes instrucciones en el orden como se dan. Los números indicados

- a 100 Indica que se debe comenzar a ingresar datos a


partir de la posición 100
XXXX: 0100 MOV AX, 200 Mover 200 al acumulador (AX)
XXXX: 0103 MOV BX, FFF Mover FFF al Registro Base (BX)
XXXX: 0106 ADD AX, BX Sumar BX a AX
XXXX: 0108 INT 20
XXXX: 010A Dar enter
- Aparece el signo del DEBUG que tiene el control.

-a
1987:0100 mov ax,200
1987:0103 mov bx,FFF
1987:0106 add ax,bx
1987:0108 int 20
1987:010A
-
Ingresemos ahora el comando –r
Manual de Lenguaje Ensamblador

-u
Este comando nos muestra nuestro programa compilado y muchas otras instrucciones que se
encuentran en máquina pero que a nosotros no os interesan y que solo sirven para ensuciar nuestro
razonamiento, por ello es preferible el comando

–u XXXX XXXX (desde-hasta).

1987:0100 B80002 MOV AX,0200


1987:0103 BBFF0F MOV BX,0FFF
1987:0106 01D8 ADD AX,BX
1987:0108 CD20 INT 20
1987:010A B2E8 MOV DL,E8
1987:010C 7421 JZ 01,2F
1987:010E A0C9DE MOV AL,[DEC9]
1987:0111 0AC9 OR CL,CL
1987:0113 7505 JNZ 011A
1987:0115 3A45FF CMP AL,[DI-01]
1987:0118 7401 JZ 011B
1987:011A AA STOSB
1987:011B 8B34 MOV SI,[SI]
1987:011D 007619 ADD [BP+19],DH

por ello es preferible el comando


–u XXXX XXXX (desde-hasta).

Si ingresamos el comando –u0100 0108 nos muestra el programa compilado. No tipeamos hasta 010A
porque esta posición de memoria no la utilizamos y aparece instruccines que nosotros no hemos introducido en
el sistema.

-u 0100 0108
1987:0100 B80002 MOV AX,0200
1987:0103 BBFF0F MOV BX,0FFF
1987:0106 01D8 ADD AX,BX
1987:0108 CD20 INT 20
-
Hagamos un alto y veamos como queda nuestro programa. En la primer columna tenemos las posición de
nuestras instrucciones comenzando desde la posición 0100 (las 4 primeras indican también la posición pero a
ellas no nos referiremos por el momento).
En la segunda aparece la compilación. Aquí hay algo realmente curioso:

B80002 – El primer byte es el código de operación. Fácil de entender. El segundo corresponde a los dos
últimos dígitos del número que nosotros queremos cargar en el acumulador. El tercer byte tiene los dos primeros
número del número que nosotros ingresamos. Es decir que 0200 se representa en memoria como 0020.
Complicado pero así funciona el INTEL y no vamos ahora a entrar en detalle. En la tercera y cuarta las
instrucciones que nosotros ingresamos en lenguaje simbólico.
Manual de Lenguaje Ensamblador

Llegó el momento de analizar los diferentes tamaños de las instrucciones que utilizamos: La primera y
segunda corresponden a la sentencia MOV (mover) y tienen un longitud de 3 (tres ) bytes. La tercera
corresponde a una instrucción de suma y tiene solo dos. La cuarta también tiene dos.

Ejemplo S2: Como queda en memoria almacenado nuestro programa.

Para ver nuestro programa almacenado debemos tipear el comando:

-d
(DISPLAY) que nos muestra la memoria. Esta está dividida en dos partes:
a. La comprendida por caracteres hexadecimales que nos permiten ver todo el contenido de la
memoria porque la combinación de bits en un nibble (4 bits) siempre tiene representación en
hexadecimal.

b. En caracteres, en castellano o ingles, pero noten que es inentendible porque normalmente la


configuración de memoria no tiene una estructura semántica y sintáctica que podamos comprender. En
otros ejercicios veremos que si podemos leer desde la memoria caracteres comprensibles.

c. El guión que separa en dos partes la memoria expresada en hexadecimal no indica el


cambio de palabra.

Hasta donde llega nuestro programa está subrayado.

1987:0100 B8 00 02 BB FF 0F 01 D8 - CD 20 B2 E8 74 ......... ..t!..


1987:0110 21 A0 C9 ...u.:E.t...4.v.
1987:0120 DE 0A C9 75 05 3A 45 FF - 74 01 AA 8B 34 .u..............
1987:0130 00 76 19 .t.......^_ZY[.Q
1987:0140 C0 75 FA BA B2 E8 C7 06 - 19 DF CA 83 E8 RWV......k......
1987:0150 10 00 0A ...t..........re
1987:0160 DB 74 04 9D F8 EB 02 9D - F9 5E 5F 5A 59 ...........>...
1987:0170 5B C3 51 t.........R.;...
52 57 56 9C 8B FA 8B F2 - E8 6B F4 A2 DB
E2 BF 98
DE 0A C0 74 03 BF F3 E5 - B9 13 00 E8 1E
F5 72 65
C7 06 CA DE 00 00 C6 06 - CC DE 00 80 3E
DB E2 00
74 07 81 C6 04 01 E8 A8 - F5 E8 52 00 3B
06 CA DE
-

El resto es basura que no nos pertenece. Si nosotros podemos leer esa basura, se pueden imaginar que
importante puede ser todo esta que queda en la memoria para un HACKER.

Ejercicio 5: Se pide sumar dos direcciones.


Manual de Lenguaje Ensamblador
Para este ejercicio nos falta conocer como se informan las direcciones. La manera es simple, entre
corchetes [ ]. Ingrese las instrucciones que se dan a continuación.

-a
1987:0100 mov ax,[0200]
1987:0103 mov cx,[0FFF]
1987:0107 add ax,cx
1987:0109 int 20
1987:010B
-
Ahora vamos a ejecutar el programa. Partimos del comando r para que nos muestre el estado de los
registros y la primera instrucción del programa.

-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0100 NV UP EI PL NZ NA PO NC
1987:0100 A10002 MOV AX,[0200] DS:0200=A708
-

Vemos que AX = 0000 que CX= 0000 y que la dirección [0200] = A708 que está indicada por el registro
DS que se encuentra en la parte inferior derecha del ejemplo y en negrilla. Ahora vamos a usar el comando
TRACE que se representa por la letra t. Este nos permite ver paso a paso que sucede en nuestro computador.

-t
AX=A708 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0103 NV UP EI PL NZ NA PO NC
1987:0103 8B0EFF0F MOV CX,[0FFF] DS:0FFF=DB52
-

Vemos que la primera instrucción modificó el contenido del registro AX con el valor de DS. Esto está bien, porque
es lo que nosotros pedimos.
Si volvemos a tipear t ejecutaremos la segunda instrucción.

-t
AX=A708 BX=0000 CX=DB52 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0107 NV UP EI PL NZ NA PO NC
1987:0107 01C8 ADD AX,CX
-

Vemos que ahora no aparece el registro DS porque en la instrucción que se va a ejecutar no mencionamos
direcciones.
-t
AX=825A BX=0000 CX=DB52 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0109 NV UP EI NG NZ NA PE CY
1987:0109 CD20 INT 20
-

Se ha realizado la suma de los dos registros y algunas cosas se han modificado.


Manual de Lenguaje Ensamblador

a) El registro AX se ha modificado pero notemos que el nuevo valor es inferior al que ya teníamos
almacenado. ¿Porqué? La respuesta es simple porque se ha producido un acarreo. Se recomienda
realizar la suma manualmente para comprobar que lo sucedido es cierto.

b) Veamos que el STATUS REGISTER ha modificado su valor indicándonos que eso ha sucedido, para
mayor claridad lo marcamos en negrilla y en color.

c) Compárelo con el DISPLAY anterior.

Por último ejecutemos INT 20.

-t
AX=825A BX=0000 CX=DB52 DX=0000 SP=FFE8 BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=00CA IP=0FA8 NV UP DI NG NZ NA PE CY
00CA:0FA8 90 NOP
-

Observemos que la nueva instrucción que aparece es un NOP (NO OPERATION) indicándonos que el
programa ha terminado.

NOTA: Podemos ejecutar directamente (t) pero nos mostrará la segunda instrucción a ejecutarse y la
primera ya ejecutada. Tipee q en la línea de comando, vuelva a tipear DEBUG y cargue nuevamente el
programa, posteriormente empiece directamente con el TRACE.

Resolver e informar.
Utilice el comando TRACE (t) para ejecutar el programa.
Ejercicio 1. Sumar el contenido de una dirección y de una constante.
Ejercicio 2: Reste dos números utilizando registros. El primero menor que el segundo.
La instrucción para restar es SUB.
Verifique el valor de los bits del STATUS REGISTER
Ejercicio 3: Suma y resta. Sume dos números y reste otro. Utilice constantes, no direcciones.
Verifique el valor de los bits del STATUS REGISTER
Ejercicio 4: Sume dos números y reste una dirección.

División del acumulador en dos partes.

Si ejecutamos el comando r podemos ver el contenido del acumulador.

-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0100 NV UP EI PL NZ NA PO NC
1987:0100 A10002 MOV AX,[0200] DS:0200=A708
-
Como vemos AX = 0000 si tipeamos el comando r ax nos permite modificar el el acumulador, como
muestra el ejemplo.

-rax
Manual de Lenguaje Ensamblador
AX 0000
:faf7
-r
AX=FAF7 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0100 NV UP EI PL NZ NA PO NC
1987:0100 A10002 MOV AX,[0200] DS:0200=A708
-

Comprobamos que está modificado. Pero también podríamos hacerlo de otra manera.
Como dice el título el registro AX lo podemos dividir en parte alta y parte baja siendo sus códigos de
reconocimiento AH (ACUMULATOR HIGH) y AL (ACUMULATOR LOW).
Entones podemos escribir lo siguiente:

-a 0100
1987:0100 mov ah,03
1987:0102 int 20
1987:0104

y ejecutarlo como lo hacemos hasta el presente (con el TRACE).


-r
AX=FAF7 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0100 NV UP EI PL NZ NA PO NC
1987:0100 B403 MOV AH,03

Observemos que cuando se ejecutó la instrucción MOV AH,03 el registro AX quedo modificado en su
parte superior por el valor (03) que ingresamos.

-t
AX=03F7 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0102 NV UP EI PL NZ NA PO NC
1987:0102 CD20 INT 20

-t
AX=03F7 BX=0000 CX=0000 DX=0000 SP=FFE8 BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=00CA IP=0FA8 NV UP DI PL NZ NA PO NC
00CA:0FA8 90 NOP
-
Así terminamos de ejecutar esta parte. Hagamos lo mismo con la parte inferior.

-a100
1987:0100 mov al,bb
1987:0102 int 20
1987:0104
-r
AX=03FF BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0100 NV UP EI PL NZ NA PO NC
1987:0100 B0BB MOV AL,BB
Manual de Lenguaje Ensamblador
-t
AX=03BB BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0102 NV UP EI PL NZ NA PO NC
1987:0102 CD20 INT 20

-t
AX=03BB BX=0000 CX=0000 DX=0000 SP=FFE8 BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=00CA IP=0FA8 NV UP DI PL NZ NA PO NC
00CA:0FA8 90 NOP
-

Ahora vamos a sumar las dos partes que componen el acumulador (AX). Para eso hacemos el siguiente
programa.
-a100
00CA:0100 add ah,al
00CA:0102 int 20
00CA:0104
y lo ejecutamos.
-r
AX=03BB BX=0000 CX=0000 DX=0000 SP=FFE8 BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=00CA IP=0100 NV UP DI PL NZ NA PO NC
00CA:0100 00C4 ADD AH,AL

-t
AX=BEBB BX=0000 CX=0000 DX=0000 SP=FFE8 BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=00CA IP=0102 NV UP DI NG NZ NA PE NC
00CA:0102 CD20 INT 20

Podemos realizar la comprobación del resultado muy fácilmente.

Multiplicación y división.

En lo referente a la multiplicación podemos determinar dos tipos de instrucciones:

MUL multiplica valores sin signo.


IMUL multiplica valores con signo.

Aquí tenemos algunas variantes que no se observaron el la suma y en la resta. Podemos multiplicar
entidades de 8 bites y de 16 bits (una palabra) o 16 y 32 o... dependiendo del tamaño de la palabra y del
procesador que estamos utilizando. Independientemente si queremos multiplicar por media palabra el valor debe
estar en la parte baja del registro que vamo a utilizar, por ejemplo en AL y si queremos multiplicar por la palabra
deberá ocupar todo el registro, es decir que deberá estar en AX por ejemplo.

Ejemplo M1: Multiplicar valores de 8 bits sin signo

Ingresamos el programa:
Manual de Lenguaje Ensamblador
C:\>debug
-a
1987:0100 mov al,4
1987:0102 mov cl,5
1987:0104 mul al,cl
1987:0106 int 20
1987:0108

-g106
AX=0010 BX=0000 CX=0005 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0106 NV UP EI PL NZ NA PO NC
1987:0106 CD20 INT 20

Dos cosas importantes. La primera es que el programa se ha ejecutado y vemos el resultado en AX y


podemos comprobar que está correcto y la segunda es que estamos usando un nuevo comando que es g que
nos permite ejecutar completamente el programa que nosotros cargamos y nos muestra el resultado. Este tiene
dos opciones:
En el ejemplo M1 hemos ejecutado el programa parcialmente puesto que apuntamos hasta la instrucción
almacenada en 0106 que es int 20, pero podemos ejecutar el programa hasta la 108 o 0108 ysucedería esto:
-g108

El programa ha finalizado con normalidad


-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0100 NV UP EI PL NZ NA PO NC
1987:0100 B004 MOV AL,04
-
Se ejecutó el programa totalmente y nos informa su correcta terminación pero no se muestran las
variaciones de los registros.

Ejecicio M2: Multiplicación de valores que provoquen un desbordamiento.

En este caso vamos a utilizar dos valores que multiplicados superen la capacidad de almacenamiento de
la parte baja del registro.
-a
1987:0100 mov al,bb
1987:0102 mov cl,aa
1987:0104 mul al,cl
1987:0106 int 20
1987:0108
-g108

El programa ha finalizado con normalidad


-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0100 NV UP EI PL NZ NA PO NC
1987:0100 B0BB MOV AL,BB
Manual de Lenguaje Ensamblador
En este caso no nos sirve de mucho, porque no nos deja ver nada, no muestra los resultados y solo
podemos saber que el programa hace algo y que no da error, pero nada más. Por lo tanto siempres es preferible
ingresar por el TRACE (t) para ver paso a paso que sucede.
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0100 NV UP EI PL NZ NA PO NC
1987:0100 B0BB MOV AL,BB
-t
AX=00BB BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0102 NV UP EI PL NZ NA PO NC
1987:0102 B1AA MOV CL,AA
-t
AX=00BB BX=0000 CX=00AA DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0104 NV UP EI PL NZ NA PO NC
1987:0104 F6E0 MUL AL
-t
AX=8899 BX=0000 CX=00AA DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0106 OV UP EI PL NZ NA PO CY
1987:0106 CD20 INT 20

Realicen el cálculo de la multiplicación manualmente y observen que es lo que sucedió y porduzcan un


informe con ello.

Ejercicio M3: Multiplicación de valores con signo.


-a
1987:0100 mov al,8
1987:0102 mov cl,-2
1987:0104 imul cl
1987:0106 int 20
1987:0108
-u 0100 0108
1987:0100 B008 MOV AL,08
1987:0102 B1FE MOV CL,FE
1987:0104 F6E9 IMUL CL es igual que decir mul ax,cl
1987:0106 CD20 INT 20
1987:0108 CC INT 3

Observemos que ya hay modificaciones . ¿Determine cuales y que es lo que sucedió y porqué?
Ejecutamos el programa paso a paso.
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFE2 BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0100 NV UP DI PL NZ NA PO NC
1987:0100 B008 MOV AL,08
-t
AX=0008 BX=0000 CX=0000 DX=0000 SP=FFE2 BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0102 NV UP DI PL NZ NA PO NC
1987:0102 B1FE MOV CL,FE
-t
Manual de Lenguaje Ensamblador
AX=0008 BX=0000 CX=00FE DX=0000 SP=FFE2 BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0104 NV UP DI PL NZ NA PO NC
1987:0104 F6E9 IMUL CL
-t
AX=FFF0 BX=0000 CX=00FE DX=0000 SP=FFE2 BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0106 NV UP DI PL NZ NA PO NC
1987:0106 CD20 INT 20
-
Analicen que sucedió. ¿Qué pasa si tomamos AX en lugar de AL? ¿Qué sucedió con los bits
de bandera? ¿Están correctos o hay errores en ellos? Realicen el informe correspondiente.
Realice el mismo programa con la diferencia de la siguiente instrucción
Mov cl,-1 e informe el análisis particular y comparativo
Realizados los ejemplos anteriores y convenientemente analizados podemos pasar a realizar ejercicios
con registros completos.
Informe cada uno de los ejercicios.

Ejercicio M1: Multiplique dos números.


Ejercicio M2: Multiplique un número positivo por un número negativo
a) Con valores pequeños.
b) Con valores grandes.
Ejercicio M3: Multiplique dos números con signo negativo.
Ejercicio M4: Ingrese el siguiente programa y analice los resultados
-a 100
1987:0100 mov bx,ffff
1987:0103 mov cx,-1
1987:0106 imul cx
1987:0108 imul bx
1987:010A int 20
1987:010C
-
Manual de Lenguaje Ensamblador
División.

También está compuesta por dos instrucciones:

DIV que divide dos valores sin signo.


IDIV que divide dos valores con signo.

En una división de valores enteros vamos a suponer que el dividendo siempre será mayor que el divisor,
por lo menos para esta primera parte de nuestro estudio.

De acuerdo a lo expresado pasemos a realizar nuestro primer ejemplo.

Ejemplo D1: Dividir dos número enteros.

Aclaraciones: Para un divisor de 8 bits el dividendo será de 16 bits, por lo tanto el dividendo lo
ubicaremos en AX (16 bits) y el divisor en CL (8bits)

El programa es:
-a
1987:0100 mov ax,8
1987:0103 mov cl,4
1987:0105 div cl Daría lo mismo escribir div ax,cl
1987:0107 int 20
1987:0109
-t

AX=0008 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000


DS=1987 ES=1987 SS=1987 CS=1987 IP=0103 NV UP EI PL NZ NA PO NC
1987:0103 B104 MOV CL,04
-t

AX=0008 BX=0000 CX=0004 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000


DS=1987 ES=1987 SS=1987 CS=1987 IP=0105 NV UP EI PL NZ NA PO NC
1987:0105 F6F1 DIV CL
-t

AX=0002 BX=0000 CX=0004 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000


DS=1987 ES=1987 SS=1987 CS=1987 IP=0107 NV UP EI NG NZ AC PE CY
1987:0107 CD20 INT 20
-
Ejercicio D1: Realicen un programa utilizando dos números que no sean múltiplos y analaice
el registro AX.
Ejercicio D2: Realice un programa cuyo divisor sea cero e informe que sucede y porqué.
Ejercicio D3: Realice un programa que permita dividir los siguientes valores FFFF y 2, pero en
ste caso el divisor será BX. Observe todos los registros e informe.
Ejercicio D4: Realice un programa que permita la diviidr FFFF por –1 y analice.
Manual de Lenguaje Ensamblador
Bifurcaciones condicionales, incondicionales y repeticiones.

Cuando hacemos un programa seguramente tendremos ue realizar en algún momento una pregunta que
nos permita testear un registro, variable, archivo, etc., y optar por realizar tal o cual cosa dependiendo de que
resultado nos devuelva el dato consultado. Por otro lado también existe la psibilidad de que después de ejecutar
una serie de instrucciones debamos continuar el programa por otro lado saltando un montón de instrucciones, en
este caso estamos utilizando un salto inconsicional , no hay ninguna condición previa , y por último podemos
realizar una serie de repeticiones o iteraciones o LOOPS dentro de nuestro programa. Esto sucede enla mayoría
de los programas y es por eso que esta nueva sección investiga las tres posibilidades.

Ejemplo B1: Realizar un programa con un salto condicional.

Para visualizar mejor las posibilidades de Verdadero y Falso que se pueden presentar utilizaremos dos
variables alfabéticas. El programa

-a
1987:0100 jmp 0113
1987:0102
-e 102 "Saltamos varias instrucciones verdad " 0d 0a "$"
-a 113
1987:0113 mov dx,102
1987:0116 mov ah,9
1987:0118 int 21
1987:011A int 20
1987:011C
-
-g
Saltamos varias instrucciones verdad

El programa ha finalizado con normalidad


-
Para analizar las instrucciones que saltamos debemos hacer:

-u 100 011a
1987:0100 EB11 JMP 0113
1987:0102 53 PUSH BX
1987:0103 61 DB 61
1987:0104 6C DB 6C
1987:0105 7461 JZ 0168
1987:0107 6D DB 6D
1987:0108 6F DB 6F
1987:0109 7320 JNB 012B
1987:010B 7661 JBE 016E
1987:010D 7269 JB 0178
1987:010F 61 DB 61
1987:0110 7320 JNB 0132
1987:0112 69 DB 69
1987:0113 BA0201 MOV DX,0102
Manual de Lenguaje Ensamblador
1987:0116 B409 MOV AH,09
1987:0118 CD21 INT 21
1987:011A CD20 INT 20
-
En negrilla todas las instrucciones que saltamos, que por supuesto no son de nuestro programa. Otra
manera de ver la ejecución de nuestro programa es utilizando el comando p. En este caso aconsejamos hacerlo
indicando además la instrucción desde donde debe comenzar como en la secuencia que sigue.

-p 100
AX=0000 BX=0000 CX=0000 DX=0102 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0116 NV UP EI PL NZ NA PO NC
1987:0116 B409 MOV AH,09
AX=0900 BX=0000 CX=0000 DX=0102 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0118 NV UP EI PL NZ NA PO NC
1987:0118 CD21 INT 21
HEMOS SALTADO VAR¦ ¦ -!¦ UCCIONES VERDAD
AX=0924 BX=0000 CX=0000 DX=0102 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=011A NV UP EI PL NZ NA PO NC
1987:011A CD20 INT 20

El programa ha finalizado con normalidad


-
Manual de Lenguaje Ensamblador
Pasaje de datos entre registros.

Para realizar este ejercicio vamos a utilizar una nueva intrucción que es XCHG, que realiza el pasaje del
contenido entre dos registros. No importa la manera en que de especifiquen los registros en la instrucción. Para
ello damos el siguiente ejemplo:

C:\>debug
-a 100
1987:0100 mov ax,333
1987:0103 mov bx,222
1987:0106 xchg bx,ax
1987:0107 int 20
1987:0109
-t 3

AX=0333 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000


DS=1987 ES=1987 SS=1987 CS=1987 IP=0103 NV UP EI PL NZ NA PO NC
1987:0103 BB2202 MOV BX,0222

AX=0333 BX=0222 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000


DS=1987 ES=1987 SS=1987 CS=1987 IP=0106 NV UP EI PL NZ NA PO NC
1987:0106 93 XCHG BX,AX

AX=0222 BX=0333 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000


DS=1987 ES=1987 SS=1987 CS=1987 IP=0107 NV UP EI PL NZ NA PO NC
1987:0107 CD20 INT 20
o bien
IP 0108
:100
-u100 108
1987:0100 B83303 MOV AX,0333
1987:0103 BB2202 MOV BX,0222
1987:0106 87C3 XCHG AX,BX
1987:0108 CD20 INT 20
-r
AX=0222 BX=0333 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0100 NV UP EI PL NZ NA PO NC
1987:0100 B83303 MOV AX,0333
-t3

AX=0333 BX=0333 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000


DS=1987 ES=1987 SS=1987 CS=1987 IP=0103 NV UP EI PL NZ NA PO NC
1987:0103 BB2202 MOV BX,0222

AX=0333 BX=0222 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000


DS=1987 ES=1987 SS=1987 CS=1987 IP=0106 NV UP EI PL NZ NA PO NC
1987:0106 87C3 XCHG AX,BX
Manual de Lenguaje Ensamblador
AX=0222 BX=0333 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=1987 ES=1987 SS=1987 CS=1987 IP=0108 NV UP EI PL NZ NA PO NC
1987:0108 CD20 INT 20
Como podemos observar lo dicho anteriormente se cumple.

Manejo de Disco con el DEBUGER

Para comenzar vamos a formatear un diskette de la manera tradicional. El comando para realizar el
formateo, para los que no lo recuerdan, es: FORMAT A:/S.

C:\>format a:/s
Inserte un nuevo disco en la unidad A:
y presione Entrar cuando esté listo...

Comprobando el formato del disco.


Comprobando 1,44 MB
Formato completado.
Sistema transferido

¿Nombre del volumen? (11 caracteres, Entrar para ninguno)? PRUEBA1

1.457.664 bytes de espacio total en disco


389.632 bytes utilizados por el sistema
1.068.032 bytes disponibles en disco

512 bytes en cada unidad de asignación.


2.086 unidades de asignación libres en disco.

El número de serie del volumen es 0A1B-1ED8

¿Desea formatear otro disco (S/N)?N

Bueno ya lo tenemos formateado.


Ahora vamos a verificar la información que tenemos en el diskette. Para ello entramos en el DEBUG
Como lo hacemos habitualmente.
Una vez en el DEBUG vamos a utilizar el comando D (DUMP) indicándole la dirección desde la cual
queremos empezar.

Y mostrará lo siguiente:
Manual de Lenguaje Ensamblador

Co Significado
mando
?? Permite ver el set de
instrucciones básico del Assembler
a Abre el DEBUG para ingresar
sentencias
g Permite la ejecutar un
programa en forma completa o parcial
r Permite ver el estados de los
registros
t Muestra paso a paso la
ejecución. Corresponde al comando
TRACE.
Manual de Lenguaje Ensamblador
APENDICE A.- MODELO DE REGISTROS DEL 8086

Bit 15 Bit 1

Bit 7 Bit 0Bit 7 Bit 0

AH AL AX (Acumulador)
BH BL BX (Base)
CH CL CX (Contador)
DH DL DX (Datos)

SP (Apuntador de pila)
BP (Apuntador de base)
SI (Indice a fuente)
DI (Indice de destino)
Manual de Lenguaje Ensamblador

APENDICE B.- REGISTROS EXPANDIDOS DEL 8087.


Manual de Lenguaje Ensamblador
Manual de Lenguaje Ensamblador
Manual de Lenguaje Ensamblador

APENDICE C.- MODELO DE SEGMENTACION DE MEMORIA.


Manual de Lenguaje Ensamblador

APENDICE D.- DIRECTIVAS DEL MASM.

.186 Permite reconocer instrucciones para


microprocesadores 8086 y 80186.

.286c Permite ensamblar instrucciones 80286 en


modo no protegido, 8086 y 80186.

.286p Activa el ensamble de instrucciones 80286 en


modo protegido, 80186, 8086.

.287 Ensamblar instrucciones 80287 y 8087.

.8086 Modo por defecto. Activa el ensamble de


instrucciones 8086 (idénticas para el 8088).

.8087 Activa el ensamble de instrucciones 8087.


Modo por defecto.

nombre=expresión Asigna el valor de expresión a nombre.

ASSUME
segmentregister:segmentname... Selecciona segmentregister que sea el
segmento por defecto para todos los símbolos
en el segmento o grupo nombrados. Si
segmentname es NOTHING, ningún registro
es seleccionado.

COMMENT delimitador texto delimitador Trata como un comentario a todo texto dado
entre delimitador y delimitador.

.CREF Restablece el listado de referencia cruzada de


símbolos.

[nombre] DB valor...
[nombre] DW valor...
[nombre] DD valor...
[nombre] DQ valor...
[nombre] DT valor...

Aloja e inicializa un byte (DB), una palabra (DW, 2 bytes), una palabra doble (DD), una
palabra cuádruple (DQ), y 10 bytes ( (DT) de almacenamiento para cada valor.

ELSE Señala el inicio de un bloque alterno dentro de


un bloque condicional.
Manual de Lenguaje Ensamblador
END [expresión] Señala el fin de un bloque y, opcionalmente,
fija el punto de entrada a expresión.

ENDIF Termina un bloque condicional.

ENDM Termina una macro.

nombre ENDP Marca el final de la definición de un


procedimiento

nombre ENDS Marca el final de la definición de una


estructura o segmento

nombre EQU expresión Asigna expresión a nombre.

.ERR Genera error.

.ERR1 Genera error en el paso 1 únicamente.

.ERR2 Genera error en el paso 2 únicamente.

.ERRB <argumento> Genera error si argumento es blanco.

.ERRDEF nombre Genera error si nombre ya ha sido definido.

.ERRDIF <cadena1>,<cadena2> Genera error si los nombres son diferentes.

.ERRE expresión Genera error si expresión es falsa (o)

.ERRIDN <cadena1>,<cadena2> Genera error si las cadenas son idénticas.

.ERRNB <argumento> Genera error si el argumento no es blanco.

.ERRNDEF nombre Genera error si nombre no se ha definido.

.ERRNZ expresión Genera error si expresión no es cero


(verdadera).

EVEN Si es necesario, incrementa el contador de


localidad a un número par y genera una
instrucción NOP.

EXITM Termina la expansión del bloque de repetición


o macro y comienza a ensamblar la siguiente
declaración fuera del bloque.

EXTRN nombre:tipo... Declaración de una variable, etiqueta o


símbolo externo.
Manual de Lenguaje Ensamblador

nombre GROUP segmentname... Asocia el nombre de un grupo con uno o más


segmentos.

IF expresión Permite el ensamble si expresión es


verdadera (no cero).

IF1 Ensambla sólo en el paso 1.

IF2 Ensambla sólo en el paso 2.

IFB <argumento> Ensambla si argumento es blanco.

IFDEF nombre Ensambla si nombre ya ha sido definido.

IFDIF <argumento1>,<argumento2> Ensambla sólo si los argumentos son


diferentes.

IFE expresión Ensambla si expresión es falsa (0).

IFIDN <argumento1>,<argumento2> Ensambla si los argumentos son idénticos.

IFNB <argumento> Ensambla si el argumento no es blanco.

IFNDEF nombre Si nombre no ha sido definido permite el


ensamble

INCLUDE filename Inserta código fuente desde el archivo


indicado

IRP nombre,<parámetro...> Marca el inicio de un bloque de código que


será repetido tantas veces como parámetros
existan y en cada repetición nombre será
reemplazado por cada parámetro.

IRPC nombre,<cadena> Marca el inicio de un bloque de código que


será repetido tantas veces como caracteres
tenga la cadena indicada y en cada repetición
será substituido nombre por cada carácter.

nombre ETIQUETA tipo Crea una nueva variable o etiqueta asignando


el valor actual del contador de localidades y el
tipo dado a nombre.

.LALL Lista todas las declaraciones en una macro.


Manual de Lenguaje Ensamblador
.LFCOND Restablece el listado de bloques
condicionales.

.LIST Restablece el listado de declaraciones en el


listado del programa.

LOCAL nombre... Declara nombre dentro de una macro para


que sea reemplazado por el nombre actual,
cuando la macro sea expandida

nombre MACRO parámetro... Marca el inicio de la macro nombre y


establece cada parámetro para ser substituido
por los respectivos parámetros cuando la
macro sea llamada.

NAME módulo Fija el nombre del módulo.

PURGE macro... Borra las macros indicadas.

ORG expresión Fija el contador de localidades expresión.

%OUT texto Despliega texto en pantalla.

nombre PROC tipo Señala el inicio de un procedimiento de


determinado tipo.

PAGE longitud,ancho Fija los márgenes para el listado el programa


(en caracteres).

PAGE+ Incrementa la numeración de página.

PAGE Genera un corte de página en el listado.

PUBLIC nombre... Hace que cada variable, nombre, etiqueta o


símbolo absoluto declarado esté disponible
para el resto de módulos en el programa.

.RADIX expresión Fija la base a expresión para la introdución de


números.

nombre RECORD
campo:ancho[=expresión].. Define una estructura para un registro de 8 ó
16 bits que contiene uno o más campos.

REPT expresión Marca el incio de un bloque será repetido el


número de veces indicado por expresión.
Manual de Lenguaje Ensamblador
.SAL Suprime el listado de las expansiones de
macros.

nombre SEGMENT
[alineación][combine]['clase'] Marca el inicio de un segmento del programa
llamado nombre y que posee los atributos
indicados.

.SFCOND Suprime el listado de cualquier bloque


condicional subsecuente cuya condición IF
sea falsa.

nombre STRUC Marca el comienzo en la definción de una


estructura.

SUBTTL [texto] Define un subtítulo para el listado.

.TFCOND Fija por defecto el listar los bloques


condicionales.

TITLE texto Títulos para los listados de los programas.

.XALL Lista sólo aquellas macros que generen


código o datos.

.XCREF [nombre...] Suprime el listado de las referencias


cruzadas.

.XLIST Suprime el listado de las líneas que sigan.


Manual de Lenguaje Ensamblador

APENDICE E.- JUEGO DE INSTRUCCIONES DEL 8086, 8087, 80186, 80286 Y 80287.

En la siguiente lista de instrucciones, para la descripción y su sintaxis se recurre a las


siguientes abreviaturas:

acum. Uno de los acumuladores: AX o AL.


reg Cualquiera de los registros
segreg Uno de los registros de regmento
Uno de los operandos generales: registro, memoria, basado, indexado o
r/m
basado-indexado
inmed Constante o símbolo de 8 o 16 bits
mem Un operando de memoria: símbolo, etiqueta, variable.
etiqueta Etiqueta de instrucciones.
src Fuente en operaciones de cadena
dest Destino en operaciones de cadena.

Para el 8086/8088

AAA Ajuste ASCII para adición.


AAD Ajuste ASCII para división.
AAM Ajuste ASCII para multiplicación.
AAS Ajuste ASCII para división.
ADC acum,
inmed
r/m,inmed Suma con acarreo.
r/m, reg
reg, r/m
ADD
acum,inmed
r/m,inmed Suma.
r/m, reg
reg, r/m
AND
acum,inmed
r/m,inmed Operación AND a nivel bit.
r/m, reg
reg, r/m
CALL etiqueta
Llamado.
r/m
CBW Convierte byte a palabra.
CLC Limpia bandera de acarreo.
Manual de Lenguaje Ensamblador
CLD Limpia bandera de dirección.
CLI Limpia bandera de interrupción.
CMC Complementa bandera de acarreo.

CMP
acum,inmed
r/m,inmed Comparación
r/m, reg
reg, r/m
CMPS src,dest Comparación de cadenas.
CMPSB Compara cadenas byte por byte.
CMPSW Compara cadenas palabra por palabra.
CWD Convierte palabra a palabra doble.
DAA Ajuste decimal para adición.
DAS Ajuste decimal para substracción.
DEC r/m
Decremento.
reg
DIV r/m División.
ESC inmed, r/m Escape con 6 bits.
HLT Alto.
IDIV r/m División entera.
IMUL r/m Mutiplicación entera.
IN
accum,inmed Entrada desde puerto.
acum, DX
INC r/m
Incremento.
reg
INT 3 Interrupcion 3 codificada como un byte.
INT inmed Interrupción 0-255.
INTO Interrupción en overflow.
IRET Retorno de interrupción.
Brinco incondicional. J(condición) etiqueta Brinca de
acuerdo a las condiciones: A (arriba), AE (arriba o
igual), B (siguiente), BE (siguiente o igual), C (acarreo),
CXZ (CX en cero), E (igual), G (mayor), GE (mayor o
igual), L (menor), LE (menor o igual), NA (no anterior),
JMP etiqueta NAE (no anterior o igual), NB (no siguiente), NBE (no
r/m siguiente o igual), NC (no acarreo), NE (no igual), NG
(no mayor), NGE (no mayor o igual), NL (no menor),
NLE (no menor o igual), NO (no sobreflujo), NP (no
paridad), NS (no signo), NZ (no cero), O (sobreflujo), P
(paridad), PE (paridad par), PO (paridad impar), S
(signo), Z (cero).
Manual de Lenguaje Ensamblador
LAHF Carga AH con las banderas.
LDS r/m Carga DS.
LEA r/m Carga la dirección.
LES r/m Carga ES.
LOCK Cierra bus.
LODS src Carga cadena.
LODSB Carga byte de cadena en AL.
LODSW Carga palabra de la cadena en AX.
LOOP etiqueta Ciclo.
LOOPE etiqueta Ciclo mientras igual.
LOOPNE
Ciclo mientras no igual.
etiqueta
LOOPNZ
Ciclo mientras no cero.
etiqueta
LOOPZ etiqueta Ciclo mientras cero.

MOV acum,mem
r/m,inmed
mem, acum
r/m, reg
Mueve un valor del segundo al primer operando
r/m,segreg
reg, inmed
reg,r/m
segreg,r/m

MOVS dest, src Mueve cadena.


MOVSB Mueve cadena byte por byte.
MOVSW Mueve cadena palabra por palabra.
MUL r/m Multiplicación.
NEG r/m Niega (complemento a 2).
NOP Operación ociosa.
NOT r/m Invierte valores de bits (complemento a1).
OR acum,
inmed
r/m,inmed Operación OR a nivel de bit.
r/m, reg
reg,r/m
OUT DX, accum Salida por el puerto dado por el primer operando
inmed, acum (inmediato de 8 bits).
POP r/m
reg Recupera valor de la pila.
segreg
POPF Recupera banderas.
Manual de Lenguaje Ensamblador
PUSH r/m
reg Guarda valor en la pila.
segreg
PUSHF Guarda banderas.
RCL r/m,1
Rotación a la izquierda con acarreo.
r/m,CL
RCR r/m, 1
Rotación a la derecha con acarreo.
r/m, CL
REP Repite.
REPE Repite si igual.
REPNE Repite si no igual.
REPNZ Repite si no cero.
REPZ Repite si cero.
RET [inmed] Regresa después de recuperar bytes de la pila.
ROL r/m,1
Rotación a la izquierda.
r/m, CL
ROR r/m,1
Rotación a la derecha.
r/m, CL
SAHF Carga banderas con el valor de AH.
SAL r/m, 1
Desplazamiento aritmético a la izquierda.
r/m, CL
SAR r/m, 1
Desplazamiento aritmético a la derecha.
r/m, CL
SBB acum,
inmed
r/m,inmed Substracción con acarreo.
r/m, reg
reg,r/m
SCAS dest Explora cadena.
SCASB Explora cadena para el byte en AL.
SCASW Explora cadena por la palabra en AX.
SHL r/m, 1
Desplazamiento a la izquierda.
r/m, CL
SHR r/m, 1
Desplazamiento a la derecha.
r/m, CL
STC Fija bandera de acarreo.
STD Fija bandera de dirección.
STI Fija bandera de interrupción.
STOS dest Guarda cadena.
STOSB Guarda byte en AL en la cadena.
STOSW Guarda palabra en AX en la cadena.
Manual de Lenguaje Ensamblador
SUB accum,
inmed
r/m,inmed Substracción.
r/m, reg
reg,r/m
TEST acum,
inmed
r/m,inmed Comparación.
r/m, reg
reg,r/m
WAIT Aguarda.
XCHG acum, reg
r/m,inmed
Intercambio.
r/m, reg
reg,r/m
XLAT Traduce.
XOR acum, reg
r/m,inmed
Operación XOR a nivel bit.
r/m, reg
reg,r/m

Para el 8087

F2XM1 Calcula 2x-1.


FABS Toma valor absoluto del tope de la pila.
FADD mem Suma real.
FADD ST, ST(i) Suma real desde la pila.
FADD ST(i), ST Suma real a la pila.
FADDP ST(i), ST Suma real y saca de pila.
FBLD mem Carga un decimal empacado de 10 bytes en la pila.
FBSTP mem Almacena un decimal empacado de 10 bytes y saca de pila.
FCHS Cambia signo del valor en el tope de la pila.
FCLEX Borra las excepciones después de un WAIT.
FCOM Compara real.
FCOM ST Compara real con el tope de la pila.
FCOM ST(i) Compara real con la pila.
FCOMP Compara real y saca de pila.
FCOMP ST Compara real con el tope de la pila y saca de pila.
FCOMP ST(i) Compara real con el tope de la pila y saca.
FCOMPP Compara real y saca dos valores de la pila.
FDECSTP Decrementa el apuntador de la pila.
Manual de Lenguaje Ensamblador
FDISI Deshabilita las interrupciones depués de un WAIT.
FDIV mem División real.
FDIV ST, ST(i) División real desde la pila.
FDIV ST(i), ST División real en la pila.
FDIVP ST(i), ST División real sacando valor de la pila.
FDIVR División real invertida.
FDIVR mem División real invertida desde memoria.
FDIVR ST, ST(i) División real invertida desde la pila.
FDIVR ST(i), ST División real invertida desde la pila.
FDIVRP ST(i), ST División con un real invertido y sacando valor de la pila.
FENI Habilita interrupciones después de un WAIT.
FFREE Libera elemento de la pila.
FIADD mem Suma entera de un número de 2 ó 4 bytes.
FICOM mem Compara enteros de 2 ó 4 bytes y saca de la pila.
FICOMP mem Compara entero de 2 ó 4 bytes y saca de pila.
FIDIV mem División entera.
FDIVR mem División entera invertida.
FILD mem Carga un entero de 2, 4 u 8 bytes en la pila.
FIMUL mem Multiplicación entera de 2 ó 4 bytes.
FINCSTP Incrementa el apuntador de la pila.
FINIT Inicializa el procesador después de WAIT.
FIST mem Almacena entero de 2 ó 4 bytes.
FISTP mem Almacena entero de 2, 4 u 8 bytes y saca de la pila.
FISUB mem Resta entero de 2 ó 4 bytes.
FISUBR mem Resta entera de 2 ó 4 bytes invertida.
FLD mem Carga real de 4, 8 ó 10 bytes en la pila.
FLD1 Pone +1.0 en el tope de la pila.
FLDCW mem Carga la palabra de control.
FLDENV mem Carga entorno 8087 (14 bytes).
FLDL2E Carga log2 e en el tope de la pila.
FLDL2T Carga log2 10 en el tope de la pila.
FLDLG2 Carga log10 2 en el tope de la pila.
FLDLN2 Carga loge 2 en el tope de la pila.
FLDPI Carga  en el tope de la pila.
FLDZ Carga +0.0 en el tope de la pila.
FMUL Multiplicación real.
MUL mem Multiplicación real desde memoria.
FMUL ST, ST(i) Multiplica real desde pila.
FMUL ST(i), ST Multiplica real a la pila.
Manual de Lenguaje Ensamblador
FMULP ST, ST(i) Multiplica real y saca de la pila.
FNCLEX Borra excepciones sin WAIT.
FNDISI Inhabilita interrupciones sin WAIT.
FNENI Habilita interrupciones sin WAIT.
FNINIT Inicializa procesador sin WAIT.
FNOP Operación nula.
FNSAVE mem Salva estado 8087 (94 bytes) sin WAIT.
FNSTCW mem Almacena la palabra de control sin WAIT.
FNSTENV mem Guarda el entorno 8087 sin WAIT.
FNSTSW mem Almacena el estado 8087 sin WAIT.
FPATAN Función arcotangente parcial.
FPREM Residuo parcial.
FPTAN Función tangente parcial.
FRNDINT Redondea a entero
FRSTOR mem Restablece estado 8087 (94 bytes)
FSAVE mem Conserva el estado 8087 (94 bytes).
FSCALE Escala
FSQRT Raíz cuadrada
FST Almacena real.
FST ST Almacena real desde el tope de la pila.
FST ST(i) Almacena real desde la pila.
FSTCW mem Almacena palabra de control con WAIT.
FSTENV mem Guarda el entorno 8087 después de WAIT.
FSTP mem Guarda real de 4, 8 ó 10 bytes y saca de la pila.
FSTSW mem Almacena la palabra de estado depués de WAIT.
FSUB Substrae real.
FSUB mem Substrae real desde memora.
FSUB ST, ST(i) Substrae real desde la pila.
FSUB ST(i), ST Substrae real a la pila.
FSUBP ST, ST(i) Substrae real y saca de pila.
FSUBR Substracción real invertida.
FSUBR mem Substracción real invertida desde memoria.
FSUBR ST, ST(i) Substracción real invertida desde la pila.
FSUBR ST(i), ST Substracción real invertida a la pila.
FSUBRP ST(i), ST Substracción real invertida a la pila y saca.
FTST Prueba el tope de la pila.
FWAIT Aguarda que la última operación 8087 termine.
FXAM Examina el elemento del tope de la pila.
FXCH Intercambia contenido de los elementos de la pila.
Manual de Lenguaje Ensamblador
FFREE ST Intercambia el elemento del tope de la pila.
FREE ST(i) Intercambia el elemento del tope de la pila y el i-ésimo
elemento.
FXTRACT Extrae el exponente y significando.
FYL2X Calcula Y log2 X.
FYL2PI Calcula Y log2 (x+1)

Para el 80186/80188/80286 (modo no protegido)

BOUND reg, mem Detecta valor fuera de rango.


ENTER inmed16, inmed8 Hace elemento en la pila para llamada a procedimiento.
IMUL reg, inmed
Multiplicación entera.
reg, r/m, inmed
INS mem, DX Entrada de cadena desde el puerto DX.
INSB mem,DX Entrada de cadena de bytes desde el puerto DX.
INSW mem DX Entrada de cadena de palabras desde el puerto DX.
LEAVE Deja procedimeinto.
OUTS DX, mem Salida de cadena de bytes o palabras al puerto DX.
OUTSB DX, mem Salida de cadena de bytes al puerto DX.
OUTSW DX, mem Salida de cadena de palabras al puerto DX.
POPA Saca todos los registros de la pila.
PUSH inmed Introduce a la pila
PUSHA Introduce todos los registros.
RCL r/m, inmed Rotación a la izquierda con acarreo
RCR r/m, inmed Rotación a la derecha con acarreo.
ROL r/m, inmed Rotación a la izquierda.
ROR r/m, inmed Rotación a la derecha.
SAL r/m, inmed Desplazamiento aritmético a la izquierda.
SAR r/m, inmed Desplazamiento aritmético a la derecha.
SHL r/m, inmed Desplazamiento a la izquierda.
SHR r/m, inmed Desplazamiento a la derecha.

Para el 80286 (modo protegido)

ARPL mem, reg Ajusta petición de nivel de privilegio.


CLTS Limpia bandera de conmutación de tareas.
LAR reg, mem Carga derechos de acceso.
LGDT mem Carga la tabla de descriptores globales (8 bytes).
LIDT mem Carga tabla de descriptores de interrupciones (8 bytes).
LLDT mem Carga la tabla de descriptores locales.
Manual de Lenguaje Ensamblador
LMSW mem Carga la palabra de estado.
LSL reg, mem Carga el límite del segmento.
LTR mem Carga registro de tareas.
SGDT mem Guarda la tabla de descriptores locales (8 bytes).
SIDT mem Guarda tabla de descriptores de interrupciones (8 bytes).
SMSW mem Guarda la palabra de estado.
STR mem Guarda registro de tareas.
VERR mem Verifica acceso de lectura.
VERW mem Verifica acceso de escritura.

Para el 80287

FSETPM Fija modo protegido.


FSTSW AX Almacena palabra de estado en AX (aguarda).
FNSTSW AX Almacena palabra de estado en AX (no aguarda).
Manual de Lenguaje Ensamblador
APENDICE F.- INTERRUPCIONES.

Dirección Interrupción
Función
(Hex) (Hex)
0-3 0 Division by zero
4-7 1 Single step trace
8-B 2 Nonmaskable interrupt
C-F 3 Breakpoint instruction
10-13 4 Overflow
14-17 5 Print screen
18-1F 6,7 Reserved
20-23 8 Timer
24-27 9 Keyboard interrupt
28-37 A,B,C,D Reserved
38-3B E Diskette interrupt
3C-3F F Reserved
40-43 10 Video screen I/O
44-47 11 Equipment check
48-4B 12 Memory size check
4C-4F 13 Diskette I/O
50-53 14 Communication I/O
54-57 15 Cassete I/O
58-5B 16 Keyboard input
5C-5F 17 Printer Output
60-63 18 ROM Basic entry code
64-67 19 Bootstrap loader
68-6B 1A Time of day
6C-6F 1B Get control on keyboard break
70-73 1C Get control on timer interrupt
74-77 1D Pointer to video initialization table
78-7B 1E Pointer to diskette parameter table
7C-7F 1F Pointer to table for graphicscharacters ASCII 128-255
80-83 20 DOS program terminate
84-87 21 DOS function call
88-8B 22 DOS terminate address
90-93 24 DOS fatal error vector
94-97 25 DOS absolute disk read
98-9B 26 DOS absolute disk write
9C-9F 27 DOS terminate, fix in storage
A0-FF 28-3F Reserved for DOS
Manual de Lenguaje Ensamblador
100-1FF 40-7F Not used
200-217 80-85 Reserved by BASIC
218-3C3 86-F0 Used by BASIC interpreter
3C4-3FF F1-FF Not Used
Manual de Lenguaje Ensamblador

BIBLIOGRAFIA.

- Murray, Williams H. & Papas, Chris H.; 80286/80386. PROGRAMACION EN LENGUAJE


ENSAMBLADOR; Osborne/McGraw-Hill, 1989.

- Shildt, Herbert; TURBO PASCAL AVANZADO. PROGRAMACION Y TECNICAS;


Osborne/McGraw-Hill, 1987.

- DOS 5 A SU ALCANCE; Osborne/McGraw-Hill, 1992.

- MICROSOFT MS-DOS VERSION 5.0. GETTING STARTED, USER´S GUIDE &


REFERENCE; Microsoft Corporation, 1991.

- MICROSOFT MACROASSEMBLER VERSION 4.0 FOR THE MS-DOS OPERATING


SYSTEM. USER´S GUIDE & REFER.

Páginas consultadas

- www.monografías.com
- www.lawebdelprogramador.com
- www.google.com
-

Vous aimerez peut-être aussi