Вы находитесь на странице: 1из 61

CAPITULO I INTRODUCION AL LENGUAJE ENSAMBLADOR

En este capítulo encontraras el fundamento para aprender a programar en lenguaje ensamblador, programación,
procesadores, lenguajes de computación, sistemas de números y software para desarrollo de herramientas.

Nuevos términos

Procesador. El circuito, en la tarjeta principal de la máquina, que ejecuta las operaciones básicas de la
computadora.

Conjunto de instrucción. El conjunto de todas las posibles instrucciones que el procesador puede
ejecutar.

Lenguaje Máquina. El conjunto de operaciones básicas, ejecutadas por el procesador, además de las
reglas para usarlas.

Lenguaje de alto nivel. Un lenguaje cuyas instrucciones están muy lejanas del lenguaje máquina.

Lenguaje de bajo nivel. Un lenguaje cuyas instrucciones están más cercanas al lenguaje máquina.

Lenguaje Ensamblador. Es un tipo de lenguaje de bajo nivel en el cual abreviaciones mnemotécnicas


representan operaciones máquina y almacenan localidades. El lenguaje ensamblador hace posible
programar al procesador directamente sin usar lenguaje máquina.

Compilador. Un programa que traduce programas de alto nivel a lenguaje máquina para que estos puedan
ser ejecutados.

Interprete. Un programa que traduce programas de alto nivel a lenguaje máquina, una línea a la vez,
conforme se van ejecutando.

Compilador Incremental. Un programa que combina compilación e interpretación para traducir un


programa de alto nivel a una forma intermedia (pseudo-código) que es después interpretado.

Pseudo-Código. La forma en la cual un programa de alto nivel es parcialmente compilado por un


compilador incremental.

Ensamblador. (1) Un programa que traduce programas de bajo nivel a lenguaje máquina. (2) Un término
usado para nombrar al Lenguaje Ensamblador.

Coprocesador. Un procesador opcional usado para ejecutar operaciones matemáticas, especialmente


cálculos de punto flotante.

Modo Real. Un estado de operación en el cual un 286, 386, o 486 se comportan como un 8088 rápido; en
el modo real, la computadora puede ejecutar muchos programas escritos por los miembros viejos de la
familia PC.

Modo Protegido. Un estado de operación en el cual un 286, 386, o 486 pueden tener acceso a una gran
cantidad de memoria; este estado también permite las multitareas, particularmente para trabajos en
sistemas operativos y otros software’s sofisticados.

Memoria Virtual. Un sistema de administración de la memoria en el cual la memoria del disco es usada
para simular grandes cantidades de memoria regular interna.

1
Multitareas. Ejecutar más de un programa al mismo tiempo por medio de rápidos cambios entre los
programas.

Modo Virtual 86. Un estado de operación en el cual un procesador 386, o 486 pueden correr múltiples
programas, donde cada uno tiene su propio procesador 8086.

Código. (1) Usado como verbo el término se refiere a un programa escrito. (2) Usado como sustantivo el
término se refiere a las instrucciones de un programa.

Editor. Un programa que permite teclear un programa y hacerle cambios cuando sea necesario.

Enlazador (Linker). Un programa que procesa un programa traducido de ensamblador a una forma en
que puede ser ejecutado.

Programa Fuente ó Código Fuente. Un programa en lenguaje ensamblador que va a ser traducido a
Ensamblador. Los programas fuente son almacenados en archivos con la extensión ASM.

Modulo Objeto. La traducción a lenguaje máquina del programa fuente; los módulos objeto son
almacenados en archivos con extensión OBJ.

Modulo Cargable (Load Module ó Run Module). Una versión ejecutable de un modulo objeto, también
llamado un run module, producido por el Enlazador. Los módulos cargables son almacenados en archivos
con la extensión EXE (o algunas veces COM).

Depurador (Debugger). Un programa que provee un ambiente para probar load modules.

1.1 Orientación.

En términos informales el cerebro de la computadora es el procesador, un chip electrónico que, en


muchas computadoras, se encuentra en la tarjeta del sistema principal, las computadoras personales IBM
usan procesadores de la familia Intel 86. Estos procesadores son conocidos por un número de modelo,
desde los menos poderosos hasta los actuales, estos son: 8086, 8088, 186, 286, 386, 486, Pentium, etc.

Dentro de cada número de modelo hay variaciones, por ejemplo existen procesadores 386 SX y 386 DX.
La más importante idea de IBM es que todos sus procesadores, menos alguno, corran los mismos
programas, asegurándose que los miembros de la familia Intel 86 sean mutuamente compatibles. Las
habilidades que aprendas del lenguaje ensamblador te permitirán programar cualquier PC, sin importar que
procesador uses.

1.2 Lenguaje Máquina.

La lista de todas las posibles instrucciones que cada procesador puede ejecutar es denominada
conjunto de instrucciones. La tabla 1-1 muestra el tamaño del conjunto de instrucciones para varios
miembros de la familia Intel 86.

Tabla 1-1 Procesador Tamaño del conjunto de instrucciones


8086/8088 115

2
186 126
286 142
386 200
486 206
Pentium 216

El conjunto de instrucciones que el procesador puede ejecutar directamente, y las reglas para usar esas
instrucciones se llama lenguaje máquina. En lenguaje máquina cada instrucción esta codificada con un
número. Si tú ves un programa en lenguaje máquina veras una gran cadena de números.

Los seres humanos no pueden programar en lenguaje máquina, debido a que un programa consiste de
cientos de miles de números. De una forma más simple nosotros podemos escribir programas usando
lenguajes de computación. Nosotros podemos dividir los lenguajes de computación en dos grupos: alto
nivel y bajo nivel. Los lenguajes de alto nivel tales como C, C++, Pascal o Basic, están muy lejanos del
lenguaje máquina. Cuando tú usas un lenguaje de alto nivel, te concentras en el problema a resolver, y no
necesitas saber nada acerca de como el procesador ejecutará el programa. Por supuesto antes de ejecutarlo
este tiene que ser convertido a lenguaje máquina.

Sin importar el lenguaje que uses, este debe ser traducido a instrucciones en lenguaje máquina, el cual es el
que comprende el procesador. Existen tres tipos de sistemas para traducir lenguajes de alto nivel:
Compiladores, Intérpretes y Compiladores Incrementales.

Los lenguajes de bajo nivel son una representación del lenguaje máquina de forma que las personas puedan
trabajar con el. El Lenguaje Ensamblador es un lenguaje de bajo nivel, cuando usas un lenguaje de bajo
nivel, tú tienes control directo sobre el procesador, otra ventaja es que los programas son más pequeños y
más rápidos.

Cuando trabajas con lenguajes de bajo nivel, el programa que traduce tu código a lenguaje máquina es
llamado Ensamblador.

1.3 Lenguaje Ensamblador.

Un Ensamblador lee un programa en lenguaje ensamblador de bajo nivel y genera un programa


equivalente en lenguaje máquina.

Para programar en ensamblador debes conocer algunos aspectos extra: debes comprender no sólo las
instrucciones que vas a usar sino también la arquitectura de la máquina, necesitas saber como se almacenan
los datos, como el procesador manipula los datos, etc.

Cuando programas en lenguajes de bajo nivel debes hacer más que solo describir la solución formal del
programa, debes dividir el programa en pasos pequeños y básicos. Estos pasos le dirán al procesador que
hacer, una instrucción a la vez.

1.4 El Programador de lenguaje Ensamblador.

Para programar en cualquier lenguaje de computación, necesitas buenas habilidades organizacionales.


Fundamentalmente necesitas ser capaz de conceptuar un plan de acción basado en tiempo. Esto es, la
habilidad de tomar un problema y diseñar una solución que involucre ejecutar ciertos pasos en cierto
orden.

3
Para programar en lenguaje ensamblador necesitas más, debes tener una personalidad que disfrute atender
los detalles, y tener buenas habilidades aritméticas, en algunos casos debe tenerse mucha calma para
rastrear los problemas del código (conocidos como bugs)
La recompensa de aprender y practicar con el lenguaje ensamblador es desarrollar una idea sólida de como
opera una computadora, además de escribir programas rápidos y pequeños para ejecutar tareas que no son
practicas (demasiado lentas) o no son posibles o demasiado complejas en lenguajes de alto nivel.

Los mejores programadores de ensamblador son obsesivos, esto no es necesario pero ayuda.

1.5 Procesadores y Coprocesadores.

Ya mencione anteriormente que el procesador es la parte de la computadora que hace casi todo el
trabajo. Las computadoras personales IBM y compatibles usan el procesador de la familia Intel 86 o un
procesador compatible de otra compañía, (ver tabla 1-1).

Los procesadores de la tabla 1-1 son de propósito general. Existen también coprocesadores matemáticos
especiales que pueden ser usados para aumentar al procesador principal. Estos Coprocesadores ejecutan
operaciones matemáticas, incluyendo aritmética de punto flotante, y son extremadamente rápidos.

Los procesadores 486 y Pentium tiene un coprocesador matemático ínter construido, el cual es
funcionalmente equivalente a un 387. Así puedes considerar un 486 como un mejorado 386 + 387 (en la
tabla 1-1, el tamaño de las instrucciones no incluye las instrucciones matemáticas).

Los Coprocesadores matemáticos tienen su propio conjunto de instrucciones y requieren de una


programación especial, para referencia la tabla 1-3 muestra el tamaño de los varios conjuntos de
instrucciones matemáticas.

Tabla 1-2 Procesador Coprocesador Math


8086/8088 8087
186 8087
286 287
386 387
486 ---
Pentium ---

Tabla 1-3 Coprocesador Tamaño del conjunto de instrucciones


8087 77
287 74
387 80
486/487 80
Pentium 80

1.6 Programando para los Procesadores Intel 86

La Tabla 1-4 muestra el número de transistores en varios modelos del chip Intel 86. El número de
transistores de cada tipo de procesador se ha incrementado cuantitativamente, aunque es difícil definir lo

4
que significa un transistor, sin embargo, el número total de transistores es un buena indicación de la
complejidad de un chip.

Programar en lenguaje Ensamblador en 386, 486 o Pentium es casi lo mismo que programar en el 8088.
Con cada nuevo procesador que Intel hace, se asegura que los programas hechos para los procesadores
anteriores funcionen. Esto significa que la computadora más rápida y moderna puede correr los programas
escritos para la IBM PC original (la cual usaba el procesador 8086).

Tabla 1-4 Procesador Número aproximado Año de Introducción


de transistores
8086 29,000 1978
8088 29,000 1979
186 100,000 1982
286 134,000 1982
386 375,000 1985
486 1,200,000 1989
Pentium 3,100,000 1993

Una de las más grandes limitantes de los primeros procesadores 8086, 8088, 186, era su incapacidad de
usar más de 1 megabyte de memoria. El 286 resolvió ese problema operando en dos modos diferentes. En
modo real, una 286 actúa como una 8086, 8088, o 186. El modo Real da la compatibilidad para todos los
programas de DOS que dependen de la arquitectura original de la 8088.

En modo protegido, la 286 ofrece tres funciones importantes: Primero, la 286 pude utilizar 16 megabytes
de memoria. Segundo, la 286 puede usar almacenamiento externo de disco para proveer 1 gigabyte de
memoria virtual, la cual es memoria simulada. Finalmente, el 286 ofrece hardware multitareas, la habilidad
de cambiar rápidamente de un programa a otro.

Infortunadamente, pocas personas podían tomar ventaja de las avanzadas facilidades del 286 debido a que
casi todos usaban DOS y DOS está basado en el viejo 8088. Esto significa que para propósitos prácticos,
se usaba el modo real y teníamos efectivamente rápidos 8088´s.

El procesador 386 fue un importante cambio debido a su nueva facilidad añadida llamada modo virtual 86.
Esto permitió al procesador correr más de un programa DOS a la vez, cada uno de los cuales pensaba que
corría en sus propia máquina 8086. Además, un 386 en modo protegido puede usar más de 4 gigabytes de
memoria real y más de 64 terabytes de memoria virtual.

El 486 y Pentium combinan la funcionalidad de un 386 con un procesador matemático ínter construido
como también un controlador de cache (el cual controla la memoria especial de alta velocidad del
procesador). Así desde el punto de vista de programación, podemos considerar al 486 y al Pentium casi
iguales al 386.

Sin embargo todas las instrucciones extra que fueron adicionadas después del 8088 son para programar en
modo protegido. A menos que tú hagas software avanzado tal como un sistema operativo, un dispositivo
de driver o un compilador, no es factible que necesites esas instrucciones extra.
Así casi todos los programadores en lenguaje ensamblador, programan para cualquier procesador de Intel
igual que como programaban para el 8086 original. Esto es especialmente cierto si tú haces programas
para DOS.

1.7 Cuando usar el Lenguaje Ensamblador.

Te puedes preguntar: ¿Cuando debo usar el Lenguaje Ensamblador y cuando puedo usar el Lenguaje
de alto nivel? Los lenguajes de alto nivel son menos detallados y más conceptuales, y ellos deben ser tu
primera opción la mayoría de las veces. Comparados con los programas en lenguaje Ensamblador, los

5
programas de alto nivel son más fáciles de comprender y modificar. Tú sólo debes usar ensamblador
cuando sea necesario.

La razón principal del porque usar ensamblador es si quieres hacer algo que es imposible o muy poco
práctico con un lenguaje de alto nivel. Debido a que el ensamblador te da control completo sobre el
procesador, puedes hacer cualquier cosa que la máquina sea capaz de hacer. Los lenguajes de alto nivel no
te dan este tipo de control.

La segunda razón del porque usar ensamblador es acelerar un programa lento. Los programas hechos con
lenguajes de alto nivel se ejecutan más lentamente que sus equivalentes en ensamblador. El que tanto más
rápido se ejecutan los programas depende de que tan bueno sea el compilador para generar código en
lenguaje máquina eficiente.

En ciertas situaciones, necesitas acelerar cierta parte de un programa de alto nivel que es demasiado lento,
en este caso puedes analizar el programa para encontrar los puntos lentos. Como regla general, los
programas gastan el 90 % de su tiempo ejecutando el 10 % de las instrucciones. Usualmente estas
instrucciones son las que necesitan ser mejoradas. Si es posible manteniendo la mayoría del programa
como alto nivel, rescribiendo partes seleccionadas en Lenguaje Ensamblador, y uniendo todas las piezas
juntas.

La tercera razón para usar Ensamblador es para diseñar programas tan pequeños como sea posible. Ahora
la memoria de la computadora es más barata, y ahorrar espacio de memoria no es muy importante. Sin
embargo, cuando tienes que “meter” un programa en espacios de memoria pequeños, usar el ensamblador
es la mejor salida, puedes diseñar un programa que use menos espacio que uno generado por un
compilador.

La última razón para escoger Lenguaje Ensamblador es la más importante: por diversión. Mucha gente
prefiere lenguaje Ensamblador a los lenguajes de alto nivel debido a que les divierte trabajar a nivel del
procesador.

1.8 El Sistema Operativo.

El sistema operativo es el control maestro de programas que corre la computadora. Como


programador en Lenguaje Ensamblador, puedes usar el sistema operativo de dos maneras:

 Primero, sin importar que computadora uses, usas comandos que ejecuta el sistema
operativo: comandos para copiar archivos, iniciar un programa, listar un directorio,
etc.

 Segundo, tú llamas al sistema operativo dentro de tus programas para hacer ciertas
tareas; por ejemplo: entrada/salida, acceso al reloj, trabajar con archivos y directorios,
etc. Estas tareas pueden ser muy complicadas para hacerlas por ti mismo. De hecho,
puedes llamar al sistema operativo para que haga el trabajo por ti.

La mayoría de nosotros usamos el sistema operativo DOS (Disk Operating System), si tú estas
programando para Microsoft Windows, también puedes trabajar bajo DOS.

1.10 Desarrollo de un Programa en Ensamblador

6
Una vez que diseñas un programa en Lenguaje Ensamblador, hay varios pasos que debes hacer antes
de poderlo ejecutar.
Primero debes meter el programa a la computadora. Para hacer esto, puedes usar un programa llamado
editor y salvar el programa a disco. Una vez que el programa esta almacenado en un archivo, usa un
Ensamblador para ejecutar la traducción a lenguaje máquina. Sin embargo un programa en lenguaje
máquina no puede ser ejecutado directamente; este debe ser procesado por un enlazador. El enlazador crea
una forma de programa que está lista para ejecutarse. También permite que hagas programas separados
que serán unidos juntos para ejecutarse como un todo (de ahí el nombre “enlazador”).

Bajo DOS existen ciertas convenciones para nombrar a los archivos:

ASM Archivos de código fuente.


OBJ Archivos producidos por la conversión de código fuente a código máquina.
EXE Archivos generados por el enlazamiento de archivos OBJ.

Por ejemplo si creas un programa fuente llamado TEST.ASM, el ensamblador lee el archivo, generando un
modulo objeto llamado TEST.OBJ y este a su vez puede ser leído por el enlazador para generar el
programa ejecutable o el load module llamado TEST.EXE.

Cuando un programa esta totalmente terminado, puede ser probado bajo los auspicios de un debugger. Un
debugger es un programa que provee un ambiente para probar load modules. Usando un debugger, puedes
desplegar un programa, ejecutarlo una instrucción a la vez, ver los resultados de cada instrucción,
desplegar áreas de la memoria, etc.

1.11 Que Software necesitas.

Sistema DOS en cualquier versión o Windows versión 3.x o 95.


Programa editor de textos. De preferencia el EDIT de DOS.
Programa MASM ó TASM (MacroAssembler ó TurboAssembler).
Programa LINK ó TLINK (enlazadores de MicroSoft ó Turbo).
Programa DEBUG.

Nota: No utilices editores de textos sofisticados (a menos que sea en modo texto), para teclear tus
programas ya que estos adicionan información extra a los archivos que ocasionan problemas al ensamblar
el código fuente.

1.12 Que Conocimientos necesitas antes de empezar.

Hay ciertos prerrequisitos para programar en lenguaje Ensamblador:

Primero, debes conocer como utilizar el sistema operativo, no necesitas ser experto pero debes estar
familiarizado con las funciones básicas, tales como manipulación de archivos y uso de discos.

Segundo, debes conocer como usar tú editor.

Tercero, debes tener alguna experiencia de programación, no debes ser experto, pero debes tener alguna
idea de como usar la computadora para resolver problemas. Si no has programado antes mejor empieza
con un programa de alto nivel como Pascal ó C.

7
Finalmente, no debes escribir programas en Ensamblador a menos que comprendas la arquitectura y la
memoria de la computadora y la aritmética hexadecimal, que se verán en los siguientes capítulos.

{_____________o___________}

CAPITULO 2 SISTEMAS NUMERICOS

Nuevos Términos.

Bit. Abreviación de “dígito binario”. La unidad fundamental de almacenamiento de la memoria de la


computadora, un bit tiene uno de dos valores 1 ó 0.

Byte. Una unidad de memoria que contiene 8 bits.

8
Word. Una unidad de memoria de contiene 16 bits (2 bytes).

Doubleword. Una unidad de memoria que contiene 32 bits (4 bytes, 2 words).

Quadword. Una unidad de memoria que contiene 64 bits (8 bytes, 4 words).

Paragraph (Párrafo). Una unidad de memoria que contiene 128 bits (16 bytes, 8 words).

ASCII. Abreviación de “Código Estándar Americano para Intercambio de Información”. Un código


estándar, usado para almacenar caracteres de texto en la memoria, en la cual cada carácter está
representado por un patrón único de 8 bits.

Sistema Decimal ó Base 10. Nuestro sistema aritmético de todos los días, basado en 10 dígitos del 0 al 9.

Sistema Binario ó Base 2. Un sistema aritmético usado por las computadoras, cuya base son los dígitos
0, 1.

Sistema Hexadecimal (Hex) ó Base 16. Un sistema aritmético usado con las computadoras, que está
basado en 16 dígitos. Para representar 16 dígitos, el sistema hexadecimal usa 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A,
B, C, D, E, F.

Kilobyte ó K byte ó Kb. Una unidad de memoria que contiene 1024 bytes (210).

Megabyte ó M byte ó MB. Una unidad de memoria que contiene 1,048,576 bytes (2 20). 1 Mb contiene
1024 Kilobytes.

2.1 Bits y Bytes.

Para propósitos de programar en lenguaje ensamblador, piense en la memoria de la computadora


como en una secuencia larga de números, 0´s y 1´s. Por ejemplo:

100100110110010111001001000100010011101011...

Cada 0 y 1 es llamado un bit, las computadoras almacenan información como una secuencia larga de bits,
cada grupo de 8 bits es llamado un byte. Existen otras agrupaciones, como se puede ver en la tabla 2-1.

Tabla 2-1 Número de bits Nombre


8 Byte
16 Word (2 bytes)
32 Doubleword (4 Bytes)
64 Quadword (8 bytes)
128 Paragraph (16 bytes)

2.2 Como son almacenados los caracteres.

9
Si una computadora sólo puede almacenar secuencias largas de 0´s y 1´s ¿Como puede almacenar
información diferente a esa? Existen varias maneras. El texto, el cual es información consistente de
caracteres, es representado por un código.
Como sabemos, cada byte contiene 8 bits y cada uno de estos bits puede ser un 0 ó un 1; entonces hay 2 8 ó
256 posibilidades diferentes. Esto es, existen sólo 256 patrones diferentes.
Estos 256 patrones son usados para formar un código. Un patrón es un símbolo. Por ejemplo la letra “A”
es 01000001; el patrón para la letra “Q” es 01010001.
Este conjunto de patrones, con todos los caracteres que lo forman es llamado “American Standard Code
for Information Interchange, o ASCII.
Así, si quieres decodificar una cadena larga de bits, hay que dividirlos en grupos de 8 para ver que patrones
forman.

2.3 El Sistema Binario.

Si la computadora sólo almacena 0´s y 1´s, tiene sentido que la aritmética que la computadora usa
este basado en estos números. Nosotros utilizamos normalmente el sistema decimal sin embargo la
computadora utiliza el sistema binario o base 2.
Como en el sistema decimal nosotros podemos representar un cadena de números posicionales donde cada
número representa la multiplicación de ese dígito con una potencia de 10 de derecha a izquierda. En el
sistema binario es exactamente lo mismo sólo que se utiliza una potencia de 2.
Cuando usamos más de un sistema numérico debemos indicar que sistema estamos usando cada vez que
escribimos un número, por convención dentro del ensamblador se utiliza una “B” al final de cada número
binario, ejemplo:

1000 (mil en decimal)


1000B (8 en binario)

La Tabla 2-2 muestra los números decimales del 0 al 15. A un lado de cada número decimal esta su
equivalente en binario:

Tabla 2-2 Decimal Binario


0 0
1 1
2 10
3 11
4 100
5 101
6 110
7 111
8 1000
9 1001
10 1010
11 1011
12 1100
13 1101
14 1110
15 1111

2.4 El Sistema Hexadecimal.

10
La principal ventaja del sistema binario es que nos permite trabajar con números exactamente como
son almacenados, es decir, como 0´s y 1´s. Sin embargo hay un inconveniente con el binario, que lo hace
muy difícil de utilizar. La razón es que es mucho más largo que sus números decimales equivalentes; y
además, las cadenas de 0´s y 1´s son muy confusas.
Por ejemplo, puedes comprender inmediatamente la ecuación:

125 + 75 = 200

Sin embargo en binario, quedaría escrito como sigue:

1111101B + 1001011B = 11001000B

Obviamente necesitamos un sistema que sea compacto (como el decimal) y que pueda ser usado para
trabajar con los valores de la memoria (como el binario). El sistema hexadecimal, o base 16 tiene esas
características.
Hexadecimal, o Hex, es un sistema que usa 16 dígitos. Debido a que sólo tenemos diez símbolos para
dígitos (0 al 9), usaremos las letras A, B, C, D, E, y F como los otros seis dígitos. En hex, la “A” es el diez,
“B” el once, etc. La tabla 2-4 muestra los equivalentes en decimal, binario y hexadecimal.
Cuando escribimos números en hexadecimal, ponemos una “H” al final para reconocerlos, por ejemplo
“12H”.

La tabla 2-3 muestra algunas equivalencias entre decimales y hexadecimales.

Tabla 2-3 Decimal Hex


10 A
15 F
100 64
500 1F4
2730 AAA
65536 10000

Tabla 2-4 Decimal Binario Hex


0 0 0
1 1 1
2 10 2
3 11 3
4 100 4
5 101 5
6 110 6
7 111 7
8 1000 8
9 1001 9
10 1010 A
11 1011 B
12 1100 C
13 1101 D
14 1110 E
15 1111 F

2.5 Medidas del tamaño de la Memoria.

11
Las computadoras manejan datos en bytes, no en bits. Por eso, cuando describes el tamaño de la
memoria, dices cuantos bytes contiene. Debido a que la computadora tiene miles o millones de bytes, se
usan términos como kilobytes ó megabytes. El prefijo kilo y mega esta tomado del sistema métrico que
significa mil o un millón. Sin embargo mil o un millón no son números redondos en el sistema binario. De
hecho, usamos 1024 (210) y 1048576 (220).

De esta manera 64 kilobytes no significa 64000 bytes; sino 64x1024 (65536) bytes. El prefijo kilo se
abrevia K y el prefijo Mega se abrevia M. Una relación que debes recordar siempre es que 1024 K bytes
son 1 Mega.

2.6 Conversión entre Hexadecimal y Binario.

El sistema hexadecimal es importante debido a que proporciona un modo compacto de representar


largas cadenas de bits. En la tabla 2-5 se encuentran los primeros 16 números hexadecimales y sus
equivalentes en binario, note que cada número binario esta formado por sólo 4 dígitos, adicionando ceros
cuando sea necesario (Por supuesto adicionar ceros a la izquierda de un número no altera su valor).

Podemos representar cualquier secuencia de dígitos binarios por una secuencia más pequeña de dígitos
hex, por ejemplo 00001111B es igual a 0FH.

El siguiente es un ejemplo que convierte el número binario 10011010110101101B a hex. Primero


dividimos la cadena de derecha a izquierda en grupos de 4 bits:

1 0011 0101 1010 1101

Después adicionamos 3 ceros al grupo de más a la izquierda para tener 4 bits.

0001 0011 0101 1010 1101

Tabla 2-5 Hex Binario


0 0000
1 0001
2 0010
3 0011
4 0100
5 0101
6 0110
7 0111
8 1000
9 1001
A 1010
B 1011
C 1100
D 1101
E 1110
F 1111

Ahora usemos la tabla 2-5 para encontrar el dígito equivalente en hex para cada grupo:

0001 0011 0101 1010 1101


1 3 5 A D

12
Así vemos que 10011010110101101B es 135AD en hex.

Similarmente si queremos convertir un número hexadecimal a binario todo lo que necesitamos es


reemplazar cada dígito hexadecimal por su equivalente de 4 bits. Por ejemplo para convertir 72AD2EH a
binario. Primero escribimos cada dígito separado.

7 2 A D 2 E

Luego usamos la tabla y escribimos el equivalente número de 4 bits debajo del dígito en hex.

7 2 A D 2 E
0111 0010 1010 1101 0010 1110

Ahora, ponemos los bits juntos para formar el número binario, entonces 72AD2EH es
11100101010110100101110B en binario.

Mencione antes que puedes considerar a la memoria de la computadora como una larga secuencia de bits.
Sin embargo como puedes ver, las cadenas de bits se pueden convertir fácilmente a dígitos en hexadecimal
(y viceversa). Entonces es más correcto decir que la memoria de la computadora puede ser considerada
como una secuencia de dígitos hex.

2.7 Conversión de Hexadecimal a Decimal.

Debes aprender a convertir entre decimal y hex y de hex a decimal, porque la gente maneja decimal
pero la computadora almacena la información en hex.
Para convertir de hex a decimal, necesitas recordar la posición de cada dígito. Por ejemplo, considera el
número decimal 50786. Este tiene el valor:

5x10000 + 0x1000 + 7x100 + 8x10 + 6 ó

5x104 + 0x103 + 7x102 + 8x101 + 6x100

Si quieres encontrar el valor decimal de un número en hexadecimal, puedes hacer lo mismo que acabas de
ver pero usando potencias de 16, por ejemplo el número AD65H expresando en potencias de 16 es:

Ax163+ Dx162 + 6x161 + 5x160

Recordando los valores de los dígitos en hexadecimal expresados con letras podemos sustituir lo anterior
por:

10x163 + 13x162 + 6x16 + 5 = 10x4096 + 13x256 + 6x16 + 5 = 44389

En general, el procedimiento para convertir de hex a decimal es como sigue:

1. Separe cada dígito hexadecimal y coloque de derecha a izquierda la potencia de 16 adecuada


empezando desde cero.

2. Sustituya los dígitos A, B, C, D, E, F por los valores en decimal apropiados.

3. Haga las operaciones correspondientes.

13
2.8 Conversión de Decimal a Hex.

Para convertir de decimal a hexadecimal, debes dividir el número decimal entre 16 repetidamente.
Los residuos te darán los dígitos hexadecimales, leyéndolos de derecha a izquierda. Por ejemplo convierta
14855 de decimal a hex:

Cociente Residuo
14855 / 16 928 7
928 / 16 58 0
58 / 16 3 10
3 / 16 0 3

Ahora se escriben todos los residuos de abajo hacia arriba de izquierda a derecha, sustituyendo los valores
de 10, 11, 12..etc., por A, B, C..etc.

14855 = 3A07H

Este es el procedimiento general para convertir de decimal a hex:

1. Divida el número decimal entre 16 repetidamente hasta que el cociente sea 0.

2. Para obtener los dígitos en hexadecimal, escriba abajo los residuos, de derecha a izquierda.

3. Si cualquiera de los residuos es 10, 11, 12, 13, 14 ó15, cámbielos por A, B, C, D, E ó F
respectivamente.

2.9 Conversión de Binario a Decimal.

Existen dos maneras de convertir un número binario en decimal. La primera es convertir el número
binario a hex y luego el número en hex a decimal. Como ejemplo, convirtamos 10110011B a decimal.
Primero convertiremos a hex separando los bits en grupos de 4:

1011 0011

Bajo cada grupo, escribimos el equivalente número hexadecimal:

1011 0011
B 3

Entonces, 10110011B es igual a B3H en hex. Ahora expresemos B3H como la suma de los dígitos
multiplicándolo por la potencia de 16 apropiada (recuerde cambiar la B por 11):

11x16 + 3

Evaluando lo anterior obtenemos el resultado en decimal, 179.

14
El segundo método para convertir de binario a decimal es hacer lo anterior directamente como lo hicimos
para el hexadecimal. Expresemos el número binario como la suma de los dígitos multiplicando por las
potencias de 2 apropiadas. Así para evaluar la expresión 10110011B tenemos:

1x27 + 0x26 + 1x25 + 1x24 + 0x23 + 0x22 + 1x21 + 1x20

o lo que es lo mismo:

1x128 +0x64 + 1x32 + 1x16 + 0x8 + 0x4 + 1x2 + 1 = 179.

2.10 Conversión de Decimal a Binario.

Existen dos formas de convertir de decimal a binario. Primero, conviertes el número decimal a hex y
después a binario. Por ejemplo suponga que quiere convertir 23406 a binario. Para convertirlo a hex,
dividimos repetidamente entre 16:

Cociente Residuo
23406 / 16 1462 14
1462 / 16 91 6
91 / 16 5 11
5 / 16 0 5 = 5B6EH

Para convertir el número hexadecimal 5B6EH separamos cada dígito hex y encontramos su equivalente en
binario:

5 B 6 E
0101 1011 0110 1110 = 101101101101110B

La segunda forma para convertir de decimal a binario es usar el mismo método de división para convertir a
hexadecimal, sólo debemos dividir repetidamente entre 2 en lugar de entre 16. Con este método
los residuos son escritos de abajo hacia arriba y de izquierda a derecha, dándonos el número binario
directo:

Cociente Residuo
23406 / 2 11703 0
11703 / 2 5851 1
5851 / 2 2925 1
2925 / 2 1462 1
1462 / 2 731 0
731 / 2 365 1
365 / 2 182 1
182 / 2 91 0
91 / 2 45 1
45 / 2 22 1
22 / 2 11 0
11 / 2 5 1
5/2 2 1
2/2 1 0
1/2 0 1 = 101101101101110B

15
2.11 Sumando en Hexadecimal.

Algunas veces necesitaras sumar o restar dos números hexadecimales. Por supuesto puedes convertir
ambos números de hex a decimal calcular el resultado y regresar la respuesta a hex. La figura 2-6 contiene
la tabla de adición hex.

Tratemos de sumar 28A70H más 90B6H como ejemplo. Escribimos los números como en una suma
normal:

28A70
+ 90B6

Sumamos cada columna de derecha a izquierda. Primero 0H + 6H = 6H

28A70
+ 90B6
6

A continuación, 7H + BH es igual a 12H. Escribimos 2 bajo la segunda columna y tenemos un acarreo de


1.

1
28A70
+ 90B6
26

Ahora, AH + 0H + 1H es igual a BH. Escribimos B bajo la tercera columna.

28A70
+ 90B6
B26

Después, 8H + 9H igual a 11H. Escribimos un 1 y acarreamos un 1.

1
28A70
+ 90B6
1B26

Finalmente, 2H + 1H es igual a 3.

28A70
+ 90B6
31B26

Tabla 2-6 Adición en Hex

0 1 2 3 4 5 6 7 8 9 A B C D E F
0 0 1 2 3 4 5 6 7 8 9 A B C D E F
1 1 2 3 4 5 6 7 8 9 A B C D E F 10
2 2 3 4 5 6 7 8 9 A B C D E F 10 11
3 3 4 5 6 7 8 9 A B C D E F 10 11 12

16
4 4 5 6 7 8 9 A B C D E F 10 11 12 13
5 5 6 7 8 9 A B C D E F 10 11 12 13 14
6 6 7 8 9 A B C D E F 10 11 12 13 14 15
7 7 8 9 A B C D E F 10 11 12 13 14 15 16
8 8 9 A B C D E F 10 11 12 13 14 15 16 17
9 9 A B C D E F 10 11 12 13 14 15 16 17 18
A A B C D E F 10 11 12 13 14 15 16 17 18 19
B B C D E F 10 11 12 13 14 15 16 17 18 19 1A
C C D E F 10 11 12 13 14 15 16 17 18 19 1A 1B
D D E F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C
E E F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D
F F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E

2.12 Substracción en Hex.

Para restar en hexadecimal necesitas la habilidad de encontrar la diferencia entre dos números. La
figura 2-7 muestra la tabla de substracción en hex.

Los números a la izquierda representan el número más grande. Los números de arriba representan los
números más pequeños. Para hacer una resta, primero busque el renglón y luego la columna.
Por ejemplo cuanto será FH - 8H = 7H

Para restar números grandes, use la misma metodología que para restar decimales. Por ejemplo reste
DB4H - 2A1H:

DB4
- 2A1
B13

Aquí hay un ejemplo más complicado; suponga que quiere restar 74AH - 2EFH:

74A
- 2EF

Para empezar debe restar FH de AH -pero FH es más grande que AH. Proceda justo como lo haría
normalmente. Adicione 1 a AH (borrow) para convertirlo en 1AH. Ahora calcule 1AH - FH lo cual es igual
a BH

74A
- 2EF
B

Ahora, necesita calcular 4H - EH - 1H (el borrow). De nuevo existe un préstamo; esta vez necesita
cambiar 4H a 14H. Calcule 14H - EH - 1H. El resultado es 5H bajo la segunda columna.

74A
- 2EF
5B

Finalmente, calcule 7H - 2H - 1H (el borrow). La respuesta es 4H.

17
74A
- 2EF
45B

Tabla 2-6 Tabla de substracción en hex

0 1 2 3 4 5 6 7 8 9 A B C D E F
0 0
1 1 0
2 2 1 0
3 3 2 1 0
4 4 3 2 1 0
5 5 4 3 2 1 0
6 6 5 4 3 2 1 0
7 7 6 5 4 3 2 1 0
8 8 7 6 5 4 3 2 1 0
9 9 8 7 6 5 4 3 2 1 0
A A 9 8 7 6 5 4 3 2 1 0
B B A 9 8 7 6 5 4 3 2 1 0
C C B A 9 8 7 6 5 4 3 2 1 0
D D C B A 9 8 7 6 5 4 3 2 1 0
E E D C B A 9 8 7 6 5 4 3 2 1 0
F F E D C B A 9 8 7 6 5 4 3 2 1 0
10 10 F E D C B A 9 8 7 6 5 4 3 2 1
11 11 10 F E D C B A 9 8 7 6 5 4 3 2
12 12 11 10 F E D C B A 9 8 7 6 5 4 3
13 13 12 11 10 F E D C B A 9 8 7 6 5 4
14 14 13 12 11 10 F E D C B A 9 8 7 6 5
15 15 14 13 12 11 10 F E D C B A 9 8 7 6
16 16 15 14 13 12 11 10 F E D C B A 9 8 7
17 17 16 15 14 13 12 11 10 F E D C B A 9 8
18 18 17 16 15 14 13 12 11 10 F E D C B A 9

18
19 19 18 17 16 15 14 13 12 11 10 F E D C B A
1A 1A 19 18 17 16 15 14 13 12 11 10 F E D C B
1B 1B 1A 19 18 17 16 15 14 13 12 11 10 F E D C
1C 1C 1B 1A 19 18 17 16 15 14 13 12 11 10 F E D
1D 1D 1C 1B 1A 19 18 17 16 15 14 13 12 11 10 F E
1E 1E 1D 1C 1B 1A 19 18 17 16 15 14 13 12 11 10 F
1F 1F 1E 1D 1C 1B 1A 19 18 17 16 15 14 13 12 11 10

{____________o___________}

CAPITULO 3 LA ORGANIZACION DE LA MEMORIA

Como programador de lenguaje ensamblador, debes tener una idea firme de como está organizada la
memoria de la computadora. En este capitulo aprenderás acerca de las unidades fundamentales de la
memoria y como se almacenan. Aprenderás también dos conceptos importantes del lenguaje ensamblador,
los Registros y la Pila.

Nuevos Términos.

Dirección. El número asignado a un byte, que indica su posición en la memoria; el primer byte tiene una
dirección 0.

Parte baja de la Memoria. En la memoria, son aquellos bytes que están cercanos al byte 0.

Parte alta de la Memoria. En la memoria, son aquellos bytes cercanos al byte con la dirección más alta.

Bits más significativos ó Bits de más alto orden. Los bits de más a la izquierda en un byte.

Bits menos significativos ó Bits de menor orden. Los bits más a la derecha en un byte.

Limite. Una dirección que es un múltiplo de un número especifico.

Limite Word. Una dirección que es un múltiplo de 2.

Alineado (a un limite). Describe a un byte cuya dirección es un límite específico.

Limite Doubleword. Una dirección que es múltiplo de 4.

19
Limite Quadword. Una dirección que es múltiplo de 8.

Limite Paragraph. Una dirección que es un múltiplo de 16.

Memoria Primaria. Memoria con que el procesador puede trabajar directamente.

Memoria Secundaria. Memoria con que el procesador no puede trabajar directamente. La memoria
secundaria reside en los discos.

Read Only Memory ( ROM ). Memoria primaria que puede ser leída pero no cambiada.

Random Access Memory ( RAM ). Memoria primaria, también llamada de lectura-escritura, que puede
ser leída y modificada.

Registro. Uno de los 14 words de memoria lectura-escritura ínter construida en el procesador.

Acumulador. Otro nombre del registro AX.

Registro Base. Otros nombres de los registros BX ó BP.

Registro Contador. Otro nombre del registro CX.

Registro de Datos. Otro nombre del registro DX.


Cargar ( Load ). Copiar un dato a un registro.

Almacenar ( Store ). Copiar datos del contenido de un registro.

Pila ( Stack ). Una estructura de datos que te permite almacenar y recuperar datos de manera last-in, first-
out, (el último que entra es el primero en salir).

Vacía ( Empty ). Describe una pila que no contiene información.

Meter ( Push ). Almacenar un dato en la pila.

Sacar ( Pop ). Recuperar un dato de la pila.

Tope de la pila ( Top ). La locación en la pila que contiene el último campo del dato que fue metido.

Procedimiento, Función o Subrutina. Un modulo auto contenido, también llamado una función o una
subrutina, que es una parte de un programa grande.

Llamada a un procedimiento. Empezar a ejecutar un procedimiento.

Regreso de un procedimiento. Cuando un procedimiento se termina, para continuar ejecutando el


programa que lo llamo.

Dirección de regreso. La locación a que un procedimiento regresa cuando este termina.

3.1 Direcciones de Memoria.

La memoria de la computadora esta organizada en bytes. Todos los bytes están numerados
empezando desde 0. El número de un byte es llamado su dirección. Cuando el procesador necesita leer un

20
byte, este manda la dirección de la memoria, la memoria regresa entonces la información en ese byte al
procesador. Entonces cuando el procesador necesita escribir un byte, este manda dos señales a la memoria:
la dirección del byte y la información a ser escrita. La memoria copia la información al byte específico.

Los bytes cercanos al byte 0 se conocen como la parte baja de la memoria. Los bytes cercanos a la más
alta dirección son llamados parte alta de la memoria. Piensa que los bytes están acomodados en orden,
uno hasta arriba de los otros, con el byte de la dirección 0 hasta abajo.

Te puedes referir a un byte con su dirección. Cuando te refieras a una palabra (word), doble palabra
(doubleword), palabra cuádruple (quadword), o párrafo (paragraph), puedes usar la dirección de su primer
byte. Por ejemplo, si dices que una palabra esta almacenada en las locaciones 10926H y 10927H, te puedes
referir a ellas como la doble palabra en 10926H. Se asume que el segundo byte está en la dirección de más
arriba.

3.2 Como son almacenadas las Palabras ( words ).

Los viejos procesadores podían transferir de 1 a 2 bytes a la vez, pero los 386 y 486 pueden transferir
más de 4 bytes a la vez.
Cuando los bytes son escritos a la memoria, estos son almacenados de manera recta. Sin embargo, cuando
las palabras son escritas a la memoria, los dos bytes de cada palabra son almacenados en orden inverso.
Aquí hay un ejemplo digamos que tu programa escribe una palabra que contiene 1234H en la dirección de
la memoria 500H. Los dos bytes de la palabra son almacenados en 500H y 501H. Podrías esperar que la
información fuera almacenada como:

4FEH 4FFH 500H 501H 502H 503H

Sin embargo, están actualmente almacenadas como:

4FEH 4FFH 500H 501H 502H 503H

De los detalles se encargan automáticamente la memoria y el procesador. Cuando el procesador necesita


una palabra, sabe cambiar el orden de los bytes que recibe.

La única vez que tienes que preocuparte acerca del orden de los bytes es cuando usas un debugger para
desplegar un área de la memoria. En tales casos debes mantener en mente que las palabras están
almacenadas con los bytes en orden inverso.

Si los datos están almacenados como bytes esto si están en el orden normal.

3.3 Como están almacenados los Bits.

Un byte es la unidad más pequeña que puede ser almacenada o recuperada de la memoria. El único
modo de trabajar con un bit es acceder al byte en el cual el bit esta contenido. Para hacer esto hay
instrucciones especiales que te permiten examinar y cambiar bits particulares.

21
En un byte, el orden de los bits es especialmente importante, por esta razón tenemos un modo estándar de
referirnos a cada bit, los bits están numerados desde el 0 al 7, iniciando por la derecha. La figura 3-1
muestra un byte que contiene el código ASCII de la letra “D”.

Figura 3-1

7 6 5 4 3 2 1 0

Los bits de más a la izquierda son llamados los más significativos y los de más a la derecha son los menos
significativos. Cuando almacenas información todos los bits son importantes.

3.4 Limites.

En el capitulo 2 explique como los bits se agrupan en bytes, palabras, palabras dobles, palabras
cuádruple y párrafos. Como sabemos la memoria de la computadora consiste de muchos bytes. La
dirección del primer byte es 0H, la del segundo 1H y así. Algunas veces los datos necesitan ser
almacenados de manera que empiecen en algún tipo de dirección particular, llamado un limite (boundary).
Un límite es una dirección que es el múltiplo de un número específico.

Todas las direcciones que tiene un límite de 2 se dice que tiene límite de una palabra. Dicho de otra forma,
empezando con la dirección 0H, cada segundo byte en la memoria tiene como limite una palabra. Si el
primer byte de algún dato esta en una dirección limite hexadecimal, podemos decir que el dato esta
alineado con el limite de la palabra (esto es, el dato inicia en el limite de la palabra). Por ejemplo, el dato
que inicia en la locación 108A6H esta alineado con el limite de una palabra (acaba en par); los datos que
inician en 108A7H no están alineados con el limite palabra.

Similarmente, cada cuatro bytes, empezando desde 0H, decimos que es el límite de una palabra doble. Los
límites de las palabras dobles son direcciones que finalizan en 0H, 4H, 8H, o CH. Para continuar con la
analogía los límites de palabras cuádruples son direcciones que finalizan en 0H o 8H; los límites de un
párrafo terminan siempre en 0H. Ocasionalmente cierto tipo de datos deben de estar alineados a un límite
particular. Algunas veces el ensamblador lo hace automáticamente pero otras tendrás que hacerlo tú
mismo.
Como referencia, la tabla 3-2 tiene los límites de las direcciones de memoria.

Tabla 3-2 Límites de alineación

Limite Definición La dirección finaliza en...


Word Cada 2 bytes 0H, 2H, 4H, 6H, 8H, AH, CH, ó EH
Doubleword Cada 4 bytes 0H, 4H, 8H, CH
QuadwordCada 8 bytes 0H, ó 8H
Paragraph Cada 16 bytes 0H

3.5 Memoria Primaria y Secundaria.

22
Generalmente cada computadora tiene dos tipos de memorias: memoria primaria y secundaria. Los
procesadores pueden trabajar directamente sólo con la memoria primaria, la cual es la memoria que es
almacenada en pequeños chips incluidos en los circuitos de la tarjeta en la computadora.

Una computadora personal utiliza dos tipos de memoria chip. Read-Only Memory (ROM), que puede ser
leída pero no puede ser cambiada. La ROM es usada para mantener datos importantes que están definidos
para ella. Estos datos no desaparecen cuando la computadora se apaga.

Los chips Read-Write Memory, proveen memoria que puede ser leída y cambiada, estos chips son
usualmente llamados random-access memory (RAM).

La RAM es usada para mantener en la memoria primaria lo que usa la computadora. Cuando usas un
programa de computadora, debes preparar ciertas áreas de la memoria para almacenar datos. Cuando
corres un programa, las instrucciones con sus propias áreas de datos deben ser cargadas dentro de la RAM
por el sistema operativo.

La memoria secundaria reside en los discos. El procesador no puede acceder directamente a este tipo de
memoria. Antes de que los datos puedan ser usados, estos deben ser leídos a la memoria primaria. Cuando
trabajes con datos en discos debes leer la información a un área de la memoria primaria que tú apartas
cuando diseñas tu programa. Similarmente, el procesador no puede escribir directamente a los discos,
debes transferir los datos de un área de la memoria.

Por conveniencia, cuando use el término “memoria” significará memoria primaria.

3.6 Registros.

Debe parecer un poco lento el que el procesador trabaje sólo con la memoria primaria, cada byte a
ser usado debe ser transferido y regresado. Sin embargo hay una pequeña cantidad de memoria construida
dentro del procesador. Esta memoria consiste de 14 palabras de memoria read-write. Cada una de estas
palabras es llamada un registro y cada registro tiene su propio nombre y propósito.

La tabla 3-3 es una lista de los registros y sus abreviaciones. Como podemos ver los registros caen en tres
categorías: Registros generales, registros de desplazamiento y registros de segmento.

Tabla 3-3 Los registros.

Nombre Abreviación Categoría


Acumulador AX (AH, AL) Registro General
Registro Base BX (BH, BL) Registro General
Registro Contador CX (CH, CL) Registro General
Registro de Datos DX (DH, DL) Registro General
Apuntador Base BP Registro de Desplazamiento
Apuntador de Instrucción IP Registro de Desplazamiento
Apuntador de Pila SP Registro de Desplazamiento
Índice Destino DI Registro de Desplazamiento
Índice Fuente SI Registro de Desplazamiento
Registro de Segmento de Datos DS Registro de Segmento
Registro de Segmento Extra ES Registro de Segmento
Registro de Segmento de Pila SS Registro de Segmento
Registro de Segmento Código CS Registro de Segmento

23
Registro de Banderas (ninguno) (ninguno)

3.7 Lo Registros Generales.

Existen cuatro registro generales: AX, BX, CX y DX. Estos registros ofrecen un almacenamiento
temporal muy conveniente para cualquier tipo de información. Ellos son usados especialmente para
aritmética, debido a que los registros están construidos dentro del procesador, las instrucciones que los
usan se ejecutan de manera más rápida que las instrucciones que utilizan la memoria regular.
Puedes usar estos registros como desees. Sin embargo AX, BX y CX también tienen propósito especiales.

AX es el registro principal usado para instrucciones aritméticas (también puedes usar los otros registros
generales). AX puede ser usado para guardar el resultado de un cálculo. Por esta razón algunas veces es
llamado acumulador.

BX (también BP) algunas veces es llamado registro base, debido a que pude ser usado para mantener una
dirección base.

CX es usado con ciertas instrucciones para ejecutar operaciones repetitivas. En tales casos, CX debe
contener el número de veces que quieres repetir una operación. Así CX, es considerado como el registro
contador.

DX es llamado registro de datos porque es usado para mantener datos para propósitos generales.

Cuando copias información al registro, Cargas datos dentro del registro. Así se pueden almacenar datos.
Todos estos registros contienen 2 bytes (16 bits), puedes cargar y almacenar 2 bytes a la vez. Sin embargo,
a veces es necesario cargar y almacenar sólo 1 byte a la vez. El procesador permite hacer esto
reconociendo nombres alternativos para estos registros. Estos nombres alternativos se refieren a una mitad
del registro.

Los nombres alternativos para el registro AX, son AH y AL. La “H” y “L” significan “High” y “Low”. AH
se refiere a la parte alta del registro AX; AL se refiere a la parte baja de ese registro. Puedes acceder al
registro completo usando AX, o la parte izquierda o derecha usando AH o AL.

De hecho, si decimos que AX contiene 1234H, AH contiene 12H, y AL contiene 34H. Si cargas 00H a AH,
AX contendrá 0034H. Como se muestra en la siguiente figura:

AX AX

AH AL AH AL

Puedes tener el mismo acceso para los registros BX, CX y DX.

3.8 La Pila.

Una pila es una estructura de datos que te permite almacenar y recuperar información de manera last-
in, first-out. Casi todos los programas en ensamblador utilizan una pila, así que al principio de cada
programa debe tener algunas instrucciones para dar de alta a la pila.

24
Cuando tu programa inicia, la pila no tiene datos en ella, Nosotros podemos decir que la pila esta vacía.
Cuando usas la pila para almacenar un dato se dice que haces un push a la pila. Cuando recuperas
este dato, haces un pop fuera de la pila. Existen instrucciones especiales para hacerles push y pop a los
datos.

Cuando hace un pop de dato, este es removido de la pila. Como parte del pop puedes especificar a donde
va a ir el dato que sacaste. Por ejemplo podrías hacer un pop al registro AX.

Piense que la pila es una pila de platos de una cafetería. Haces push a los platos uno a la vez para
colocarlos en la pila de platos, y cuando necesitas un plato haces un pop de un plato a la vez. El plato al
que le hiciste un pop siempre es el último que colocaste en la pila. La locación de almacenamiento que
contiene el último campo con dato que fue puesto en la pila se conoce como el tope (top) de la pila.

La figura 3-3 muestra un ejemplo, que ilustra las operaciones de push y pop a la pila. El ejemplo inicia con
una pila vacía en la cual se hace un push al número 10 y 87, después se le hace un pop al tope de la pila

Para recuperar el 87, se le hace un nuevo push a la pila con un valor de 68, y después dos pop para
recuperar los números 68 y 10 respectivamente.

Tabla 3-3 Un ejemplo del uso de la pila

push push pop push pop pop


10 87 68

Cuando escribes un programa, debes instruir al ensamblador para apartar un espacio para la pila. Cada
entrada de la pila es de 1 palabra (2 bytes) y la pila puede tener una longitud de 64K bytes. Así la pila
puede mantener 32K (32768) entradas. En otras palabras, puedes hacer un push a 32768 palabras de datos
a la pila.

Usualmente, no es necesario tener una pila tan grande. Sin embargo, puedes escoger el tamaño que sea
más adecuado. Como regla general si no estas seguro del espacio que necesitas, aparta 256 palabras (200H
bytes). Los programadores que trabajan con programas pequeños dejan sólo 128 palabras (100H bytes).

3. 9 Como es usada la Pila.

La pila esta diseñada para mantener información temporalmente. Esto es especialmente importante
cuando estas trabajando con procedimientos. Muchos programas de tamaño medio y grande están
separados en módulos auto contenidos llamados procedimientos. (En lenguajes de alto nivel estos se
conocen como funciones o subrutinas). Cuando un procedimiento invoca a otro procedimiento, decimos
que el primero llama al segundo. Cuando el segundo procedimiento termina, el programa regresa al primer
procedimiento en la siguiente instrucción después de la llamada.

25
Como parte de la ejecución de una instrucción de llamada, el procesador debe salvar la locación dentro del
primer procedimiento en el cual continuará cuando el otro procedimiento termine. Esta locación es
conocida como la dirección de regreso.

El procesador salva la dirección de regreso poniéndola en la pila. Cuando el segundo procedimiento


termina, el procesador saca la dirección de regreso de la pila y continúa con la ejecución en esa locación.
Debido a la estructura de la pila un procedimiento puede llamar a otros y esos otros a otros y siempre se
regresará a la locación correcta.

Otro uso importante de la pila es salvar el contenido de los registros antes de llamar a un procedimiento.
Antes de que el procedimiento regrese los registros pueden ser restaurados sacando los viejos valores de la
pila, esto permite a los procedimientos hacer un uso libre de los registros.

La pila también puede pasar información de un procedimiento a otro. Un procedimiento puede poner sus
datos en la pila (parámetros) y entonces el segundo procedimiento acceda a los datos. Si es necesario, el
segundo procedimiento podría usar la pila para devolver datos también.

Un uso final de la pila es mantener resultados temporalmente sobre todo para operaciones complejas. Cada
método a escoger varia dependiendo de la lógica de un programa especifico.

{____________o___________}

26
CAPITULO 4 TIPOS DE DIRECCIONAMIENTO

En este capitulo aprenderás a apreciar más la arquitectura de los procesadores de la familia Intel 86, y
también las varias maneras de referirse a las locaciones de memoria en un programa.
Este tópico, el direccionamiento es muy complejo, si eres un principiante te recomiendo leer este capitulo
más de una vez. La primera vez lee todo como esta y trata de absorber tanto como puedas, no te
preocupes por comprender todo. Después una vez que inicies a hacer tus primeros programas vuelve a
leerlo con calma.

Nuevos Términos.

Dirección efectiva. Una dirección completa de 20 bits.

Dirección de Segmento. Un número de 20 bits que contiene el valor base desde el cual una dirección
efectiva es calculada.

Offset. Un número de 16 bits que es adicionado a una dirección de segmento para calcular una dirección
efectiva.

Segmento. Un área de la memoria, con más de 64 Kb de longitud, que contiene una parte del programa.

Registro de Segmentos. Un registro que contiene los 16 bits más significativos de una dirección de
segmento; uno de los registros CS, SS, DS o ES.

Segmento de Código. Un segmento que contiene las instrucciones máquina de un programa.

Segmento de Datos. Un segmento que contiene los datos usados por el programa.

Segmento Extra. Un segundo segmento que contiene datos usados por el programa.

Segmento de Pila. Un segmento que contiene a la pila que usa el programa.

Direccionamiento Directo. Una dirección en la cual es desplazamiento esta indicado directamente.

Direccionamiento Indirecto. Una dirección en la cual el desplazamiento esta especificado por el valor de
un registro.

27
Índice. Usado para calcular un offset adicionando un valor (llamado un desplazamiento) a una locación
fija (llamada la dirección base).

Dirección Base. Una locación fija que sirve como una base para calcular un offset.

Desplazamiento. Un valor que adicionado a la dirección base nos da un offset.

Registro Índice. Un registro que contiene un desplazamiento; uno de los registros DI o SI.

4.1 El esquema básico de Direccionamiento en la PC.

Las instrucciones máquina trabajan con direcciones de hasta 16 bits. Esta es una limitante en el modo
real que DOS usa. Así, las posibles direcciones son desde 0000H hasta FFFFH (0000000000000000B a
1111111111111111B). Esto da como resultado 10000H (64K) direcciones diferentes. Es otras palabras,
usando un esquema de este tipo, el procesador sólo tiene hasta 64K de memoria disponible.

Pero 64K no es mucha memoria, si tuviéramos que construir programas con sólo 64K estaríamos
severamente limitados, Así que es imperativo que el procesador pueda accesar más memoria.

La solución es ingeniosa, pero algo complicada. Es importante que comprendas este esquema, así que lee
la siguiente sección con cuidado.

Vamos a hacer una analogía. Suponga que tiene un robot con un brazo mecánico que puede mover y que
tiene un largo de 64 pulgadas. Si el robot esta en un cuarto fijo en un lugar, el brazo sólo puede alcanzar
los objetos que están a 64 pulgadas de distancia máxima. Sin embargo si el robot se pudiera mover por el
cuarto, el brazo podría alcanzar cualquier objeto. Si un objeto no esta dentro de las 64 pulgadas del brazo
lo único que tiene que hacer el robot es moverse más cerca.
En otras palabras, piense que si el brazo del robot tiene una distancia 64 pulgadas, puede tomar cualquier
objeto en el cuarto estableciendo una base a 64 pulgadas del objeto.

El procesador de Intel tiene un esquema parecido. Si bien una dirección de 16 bits puede accesar sólo 64K
bytes, se puede usar una muy grande cantidad de memoria considerando la dirección de 16 bits como la
dirección base relativa. Para accesar otros 64 K diferentes, todo lo que hay que hacer es mover la base.

Dentro de un programa, la dirección consiste de dos partes: una dirección de segmento y un offset. La
dirección de segmento es un número de 20 bits que corresponde a la posición base del robot. El
desplazamiento es un número de 16 bits que corresponde al brazo movible.

La dirección actual es llamada la dirección efectiva. Para calcular la dirección efectiva, todo lo que tienes
que hacer es sumar el offset al la dirección de segmento.

Aquí hay un ejemplo: Digamos que la dirección de segmento es 50000H y el offset es 62A3H. La dirección
efectiva es 50000H + 62A3H, lo cual es igual a 56A3H.
Ahora, si el segmento de dirección es 50000H y el desplazamiento puede tomar los valores desde 0000H
hasta FFFFH, la dirección efectiva varia desde 50000 hasta 5FFFFH. Así, con un segmento de dirección de
50000H, puedes accesar los 64K (10000H) bytes de memoria que empiezan en 50000H.

28
Suponga que quiere accesar algunos datos de la dirección 8726BH. Esta dirección no esta en el mismo
rango, así que necesitas cambiar de segmento con sus 64K que este entre 8726H. Por ejemplo, podrías
cambiar la dirección de segmento a 80000H y usar un offset de 726BH.

Actualizando la dirección de segmento apropiadamente, puedes accesar cualquier byte en una memoria
grande. Asegurándote que el byte este dentro de los 64K del segmento de dirección.

El hardware calcula la dirección efectiva de modo que el resultado siempre es un número de 20 bits. Así la
dirección efectiva puede tener un rango desde 00000H hasta FFFFFH. Esto da 100000H posibles
direcciones.

En otras palabras usando una combinación de dirección de segmento y offsets, el procesador puede
direccionar más de 100000H bytes de memoria. Esto es igual a 1024K bytes o 1 Megabyte, mucho mejor
que sólo 64K.
Es importante comprender que diferentes combinaciones de direcciones de segmento y desplazamientos
puede dar la misma dirección efectiva. De hecho los pares de direcciones de segmentos y desplazamientos
en la tabla 4-1 dan la misma dirección especifica 8726BH.

Tabla 4-1 Direcciones de Segmento Offset Dirección efectiva


80000H + 726BH = 8726BH
87000H + 026BH = 8726BH
87260H + 000BH = 8726BH
85AD0H + 179BH = 8726BH

De los detalles se encargan automáticamente el procesador y el ensamblador.

4.2 Segmentos y Registros de Segmento.

Para poder implementar el esquema de direcciones explicado en el punto anterior, el procesador


requiere que todos los programas sean divididos en segmentos. Un segmento es un área de memoria de
más de 64K., si necesitas diseñar un programa largo, debes dividirlo en piezas donde cada pieza quepa en
un segmento. Cuando escribes un programa, existen ciertas instrucciones del ensamblador que indican
donde empieza y termina cada segmento.

Cuando el programa se ejecuta, el procesador mantiene una dirección de segmento para cada segmento.
Estas direcciones son mantenidas para los registros CS, DS, ES y SS.

Un programa puede tener varios segmentos, pero sólo cuatro pueden estar activos a la vez. Los cuatro
segmentos están estandarizados; y se conocen como segmento de código, segmento de datos, segmento
extra y segmento de pila. Todas las direcciones usadas para un segmento dado usan el registro de
segmento como base.

El segmento de código es la parte del programa que contiene las instrucciones máquina actuales (algunas
veces llamadas “código”). El registro CS contiene la dirección de segmento de ese segmento.

El segmento de datos es la parte del programa que contiene los datos. El segmento extra también puede
ser usado para almacenar datos, si es necesario. El registro DS contiene la dirección del segmento de
datos; el registro ES contiene la dirección de registro del segmento extra

El segmento de pila contiene a la pila, el registro SS, contiene la dirección del segmento de pila.

29
Todos los programas deben tener al menos un segmento de código y un segmento de pila. Estrictamente
hablando, los segmentos de datos y extra son opcionales, pero la mayoría de los programas tienen también
un segmento de datos.

4.3 Como son usados los Registros de Segmento.

Como sabes todas las direcciones tiene dos partes: la dirección de segmento y el offset. En un
programa escribes una dirección especificando las dos partes separadas por dos puntos. La primera parte
es el nombre del registro de Segmento.

Por ejemplo, digamos que quieres referirte a información en el segmento de datos. Si el desplazamiento de
la información es 6AH, puedes escribir la dirección como DS:6AH. Para referirte al segmento extra con un
desplazamiento de 1A5H, escribes ES:1A5H.

Cuando el programa se ejecuta el procesador adiciona el offset al contenido del registro de segmento para
obtener la dirección efectiva. Así si la dirección es DS:6AH, el procesador adiciona 6AH al contenido de
DS.

Por supuesto, este esquema sólo trabaja si cada registro de segmento tiene su propio valor. Así es como
pasa: Antes de que un programa sea ejecutado, este debe ser copiado a la memoria, este proceso es
llamado carga, y es realizado por el sistema operativo, cuando un programa es cargado este se copia a un
área de la memoria que este actualmente disponible. Este automáticamente fija las direcciones que el
programa usará.

Cuando escribes un programa no sabes que direcciones tendrá. De hecho cada vez que un programa corre,
los segmentos pueden ser cargados en locaciones diferentes. Sin embargo es importante que un registro de
segmento sea inicializado con un valor apropiado si el programa va a correr correctamente.

Como parte del proceso de carga, el sistema operativo inicializa los registros CS y SS para apuntar al
principio del código y la pila, respectivamente. Así, las instrucciones (el segmento de código) y la pila son
accesados automáticamente.

Debido a que no todos los programas tiene segmento de datos o segmento extra, el sistema operativo no
inicializa automáticamente los registros DS y ES. Esto debes hacerlo tú mismo, hasta que lo hagas, los
datos es esos segmentos no estarán accesibles. Afortunadamente, todas las inicializaciones requieren pocas
instrucciones estándar que colocas al inicio de cada programa.

4.4 El contenido del Registro de Segmento.

Como mencione antes, el registro de segmento contiene la dirección de un segmento. La dirección de


segmento debe tener al menos 20 bits, pero todos los registros tienen sólo 16 bits. ¿Como puede contener
un registro de 16 bits, 20 bits?

La solución es asumir que el registro contiene sólo los 16 bits más significativos de la dirección y
asumimos que los otros 4 bits son todos ceros. En otras palabras, si cada dirección de segmento es de 5
dígitos hex (20 bits), un registro de segmento contiene sólo los primeros 4 dígitos hex; el último dígito hex
se considera como 0H.

Aquí hay un ejemplo: Digamos que el sistema operativo carga el segmento de código en 217A0H y el
segmento de pila en 1F8A0H. El registro CS estará dado por el valor 217AH y el valor del registro SS es

30
1F8AH. En cada caso debes extender los valores por 0H (0000B) para obtener la verdadera dirección de
segmento.

Como puedes ver, asumiendo que todas las direcciones de 20 bits terminan en 0H, sólo necesitas
almacenar los primeros 16 bits de cada uno. La carga de los segmentos es hecho por el ensamblador y el
sistema operativo y los segmentos siempre estarán alineados al limite de un párrafo.

La única vez que necesitas preocuparte acerca de todo esto es cuando calculas una dirección efectiva.
Cuando lo hagas recuerda adicionar el valor extra 0H al valor del registro de segmento para obtener la
verdadera dirección de segmento.
Veamos como hacerlo. Digamos que estas usando un debugger y quieres examinar algunos datos en el
segmento de datos. El offset es 17A5H y el valor de DS es 20ABH. Primero escribe el valor del registro de
segmento:

20ABH

Después adiciona 0H al valor para obtener la dirección de segmento verdadera.

20AB0H

Finalmente adiciona el offset a la dirección de segmento:

20AB0H
+17A5H
22255H

Puedes instruir al debugger para desplegar los datos en esa dirección.

4.5 Como es implementada la Pila.

La imagen de los platos en la cafetería es buena para aprender como trabaja el concepto de una pila,
pero su implementación es un poco diferente, esto es debido a que no es posible mover todos los datos
hacia arriba o hacia abajo cada vez que metemos o sacamos un elemento. En su lugar, la pila es manejada
con un segmento con dos registros especiales.

El registro SS siempre contiene la dirección de segmento de la pila. El sistema operativo pone ese valor
cuando el programa se carga. Cuando el programa se ejecuta, el registro SS permanece sin
cambios (a menos que se use un registro de pila diferente).

El registro SP (apuntador de pila) contiene un offset que apunta al tope de la pila. Esto es, en cualquier
caso, la dirección efectiva del tope de la pila estará formada por los contenidos de los registros SS y SP.

La pila esta organizada como una lista con entradas de 16 bits (2 bytes). El registro SS apunta a la
dirección más baja de la lista. La primera instrucción push coloca datos en la entrada con la dirección más
alta, la siguiente instrucción push coloca datos en la entrada con la siguiente dirección más alta, y así.
Todas las veces, el registro SP apuntará al último dato que fue metido (el tope de la pila).

En otras palabras, la pila inicia desde la dirección más alta y crece hacia abajo hasta su base. La figura 4-1
muestra un diagrama de la pila que fue cargada en 10000H. La pila tiene una longitud de 200H (512
bytes).

31
En este ejemplo, dos campos son empujados a la pila, el campo nombrado data1 fue empujado primero, en
la locación 101FEH. El campo nombrado data2 fue metido en segundo en 101FCH. Note que cada dato
irá quedando más cerca de la base de la pila. Actualmente la dirección base o registro SS contiene 1000H y
el registro SP contiene 01FCH.

Figura 4-1 La Pila

En general, esto es lo que pasa cuando un dato es introducido a la pila.

1. SP es decrementado en 2 para apuntar a la siguiente locación libre.

2. El campo a ser metido es copiado al offset especificado por SP.

Esto pasa cuando un dato es sacado de la pila:

1. El campo al que SP apunta es copiado a la locación apropiada.

2. SP es incrementado en 2 para apuntar a la siguiente entrada de la lista.

En el ejemplo anterior el registro SP esta inicializado a 200H. EL primer push decrementará SP a 01FEH
antes de almacenar el data1, si todos los campos en la pila son sacados y la pila esta vacía SP apuntará de
nuevo a 0200H.

4.6 Direccionamiento Directo.

Dentro de un programa puedes referirte a locaciones de memoria específicas. Para acceder esas
locaciones de memoria, el procesador necesita conocer la dirección del segmento y el offset. Puedes
escribir esta información especificando el nombre del segmento y un offset, separado por dos puntos.

Por ejemplo, para especificar la locación en el segmento de datos que tiene un offset de 10AH, puedes
escribir

DS:10AH

Como es un poco problemático referirte a los desplazamientos usando números, el ensamblador te permite
usar nombres. Cuando creas el segmento de datos y el segmento extra, dejas un espacio para los datos que
usaras, así puedes asignarle nombres a esos campos de datos.

32
El ensamblador mantiene una tabla con cada nombre y su desplazamiento. Cuando usas una instrucción
refiriéndose a un nombre, el ensamblador lo substituye por su desplazamiento actual. Así cuando te refieres
a un campo dato, se dice que usas un direccionamiento directo.
Por ejemplo, digamos que el nombre SUM tiene un desplazamiento de 10AH en el segmento de datos.
Puedes referirte a esa locación como

DS:SUM

Lo bueno de esto, es que no tienes que rastrear su desplazamiento actual por ti mismo.

Muchas de las veces, puedes simplificar las cosas aún más. Cuando haces referencia a una locación de
memoria, el procesador asume que está en el segmento de datos a menos que especifiques otra cosa. Así
puedes referirte a la locación sólo como:

SUM

Tiene que especificar un registro de segmento si el dato no está en el segmento de datos. Por ejemplo, si
SUM estuviera en el segmento extra, debes hacer referencia a la locación como:

ES:SUM

4.7 Direccionamiento Indirecto.

Algunas veces, quieres usar un desplazamiento que esta en un registro. Para hacer referencia a esa
locación, debes especificar el nombre del registro encerrado entre paréntesis cuadrados.

Por ejemplo, digamos que el registro SI contiene 1000H, si una instrucción contiene

SI

Se refiere al valor en SI, llamada 1000H. Sin embargo, si la instrucción contiene

[SI

Este se refiere al dato en el offset 1000H. En otras palabras, cuando un registro esta encerrado entre
paréntesis cuadrados, este representa al dato en el desplazamiento contenido en el registro, esto es llamado
direccionamiento indirecto.

Cualquiera de los registros SP, BP, BX, SI, y DI pueden ser usados para direccionamiento indirecto.
Muchas de las veces, no es necesario especificar un registro de segmento debido a que el procesador
asume uno.

Cuando usas SP o BP, el procesador asume que el desplazamiento se refiere al segmento de pila y usa el
registro de segmento SS. Cuando usas BX, SI, y DI, el procesador asume que te refieres al segmento de
datos y usa el registro de segmento DS. Así no es necesario especificar el registro a menos que quieras
suprimir el default.

Por ejemplo, digamos que BX contiene el offset de datos del segmento extra, tú puedes usar

ES:[BX

33
Hay una excepción importante a estas reglas. Cuando usas una instrucción que mueve strings, el
procesador asume que el registro DI contiene un desplazamiento al segmento extra.

Los defaults implícitos para los registros de segmentos se muestran en la tabla 4-2

Tabla 4-2 Registro Registro de segmento implícito


[SP SS
[BP SS
[BX DS
[SI DS
[DI DS (ES con instrucciones para strings)

4.8 Direccionamiento Indexado.

Algunas estructuras de datos consisten de más de una sola parte. Por ejemplo, un arreglo
unidimensional puede ser considerado como una lista de datos. Una tabla bidimensional puede considerarse
como renglones y columnas.

En todos los casos, los datos actuales están almacenados como una secuencia de bytes, palabras, dobles
palabras, etc. Sin embargo, cuando usas esos datos, ayuda darles un orden. De hecho, cuando usas un
arreglo es conveniente pensar que el arreglo completo tiene un sólo nombre y campos separados son los
elementos del arreglo.

Para hacer esto, tienes la facilidad de utilizar una indexación. La indexación permite hacer referencia a
campos relativos a una locación fija. De hecho puedes accesar cada elemento del arreglo relativo por su
inicio. Esta locación fija es llamada la dirección base.

Considere un arreglo de 100 elementos que existe en el segmento de datos como 100 bytes consecutivos.
Cada byte contiene un elemento. El nombre del arreglo es LIST. Estrictamente hablando, LIST es el
desplazamiento del primer byte. Así puedes accesar el primer elemento del arreglo usando

LIST

Si quieres accesar otros elementos, el ensamblador te permite adicionar valores al nombre. Por ejemplo,
para accesar el siguiente elemento puedes especificar

LIST+1

El número que adicionas a la dirección base es llamada un desplazamiento, en el último ejemplo, el


desplazamiento fue de 1. Puedes accesar cualquier elemento del arreglo usando el desplazamiento
apropiado. Aquí hay algunos ejemplos.

LIST+57
LIST+82
LIST+99

No hay un desplazamiento dado para el primer elemento del arreglo, dando un desplazamiento de 1
obtenemos el segundo elemento, y así. Si hay cien elementos, el rango de desplazamientos va desde 0 a 99.
El desplazamiento máximo de n elementos es n-1. Otro modo de verlo es que empezamos a contar desde
cero. Por eso, LIST+57 nos da el elemento 58.
34
Por supuesto estos desplazamientos funcionan en arreglos consistentes de bytes. Considere un arreglo
llamado BIGLIST consistente de 100 palabras. Cada palabra ocupa 2 bytes, los desplazamientos relativos a
los elementos son como sigue:

Primer elemento  BIGLIST


Segundo elemento  BIGLIST+2
Tercer elemento  BIGLIST+4
.
.
.
Ultimo elemento  BIGLIST+198

4.9 Los Registros Índice.

Ocasionalmente, necesitas accesar los elementos de la estructura de datos adicionando un


desplazamiento constante a un offset, por ejemplo LIST+4. Sin embargo, es más útil usualmente
almacenar el desplazamiento en un registro. Los registros SI y DI son usados para este propósito y son
algunas veces referidos como el registro índice.

Por ejemplo, considera el arreglo LIST consistente de 100 bytes. Decimos que el programa necesita una
referencia cada elemento en turno, desde el primero hasta el último. Puedes variar el contenido de SI desde
0 hasta 99 y usar.

LIST[SI]

Note dos cosas: primero, cuando usas un registro para mantener un desplazamiento, debes indicar al
ensamblador el nombre del registro entre paréntesis cuadrados, igual a cuando usas un direccionamiento
indirecto.

Segundo, necesitas adicionar un + antes del nombre del registro. La expresión anterior significa “el valor
de del registro SI adicionado al desplazamiento de LIST”.

En el ejemplo anterior, puede indexar cualquier elemento de LIST actualizando SI al valor apropiado. De
hecho, para accesar todos los elementos del último al primero, varía SI de 99 hasta 0; para accesar el
elemento 57, pon SI a 56.

Ahora considera el arreglo BIGLIST, el cual consiste de 100 palabras. Digamos que quieres indexarla
usando el registro DI.

BIGLIST[DI]

Cada elemento de BIGLIST es de 2 bytes de longitud, debes actualizar a DI de acuerdo al tamaño. Por
ejemplo, para accesar todos los elementos del primero al último, cambia a DI a 0, 2, 4, 6... 198. Para
accesar el elemento 57, pon DI a 112, para accesar el elemento n, pon DI a (n-1) x 2.

Si necesitas manejar palabras dobles, debes manejar el índice en múltiplos de 4; con palabras cuádruples,
múltiples de 8.

En lenguajes de alto nivel esto lo hacen por ti, en lenguaje ensamblador, debes hacer esto tú mismo.

35
4.10 Los Registros Base: El registro BX.

En la sección previa, los ejemplos usaban el nombre para almacenar la locación como una dirección
base. Puedes también utilizar un registro para mantener una dirección base. BX es usada para este
propósito en el segmento de datos; BP es usada por la pila. Estos registros son algunas veces mencionados
como los registros base.

Por ejemplo, puedes indexar el arreglo LIST usando

LIST[DI]

Si pones el desplazamiento de LIST en BX, puedes usar

[BX][DI]

Como puedes ver, el registro base esta encerrado en paréntesis cuadrados. (Note que el último ejemplo usa
direccionamiento indirecto.)

Usando un registro para mantener la dirección base es útil si quieres accesar más de una estructura de
datos con la misma instrucción. Por ejemplo, puedes tener un programa que procese varios arreglos. Todo
lo que tienes que hacer es poner a BX al desplazamiento de cada arreglo en turno y entonces usar DI o SI
para mantener el desplazamiento.

Por ejemplo, digamos que tienes dos arreglos llamados LIST1 y LIST2. Para accesar al primer arreglo,
pon BX a LIST1; para accesar el segundo pon BX a LIST2.
Para estructuras más complicadas, el ensamblador permite usar un nombre, una dirección base, y un
desplazamiento, por ejemplo:

TABLE[BX][SI]

El contenido de ambos registros es adicionado al desplazamiento del campo de datos. De hecho, si BX


mantiene un 20 y DI mantiene un 4, el ejemplo anterior tiene el valor

TABLE+24

La ventaja de esta doble indexación es permitirte establecer una dirección base dentro de una estructura de
datos. Por ejemplo, digamos que TABLE es una tabla de bytes, almacenada renglón por renglón. Hay 30
renglones y cada renglón tiene 100 elementos; esto es, TABLE tiene 30 renglones y 100 columnas.

Puedes poner a BX apuntando a un renglón particular y usar DI o SI para indexar a través de los
elementos de ese renglón. En este caso, cada renglón tiene 100 bytes. El primer renglón consiste del byte 0
al 99; el segundo renglón del byte 100 a 199; y así. Para accesar el elemento 57 del renglón 18, ponemos
BX a 1700 y SI a 56, y usamos

TABLE[BX][SI]

Esto podría ser lo mismo que

TABLE+1756

36
Si usas una estructura de datos que mantiene elementos más grandes de un byte, puedes ajustar la
indexación de acuerdo al tamaño. Por ejemplo, si TABLE mantiene palabras en lugar de bytes, los
renglones empezarían en 0, 200, 400, etc.

4. 11 Los Registros Base: El registro BP.

Muchas veces, el direccionamiento en una pila es manejado con los registros SS y SP. SS mantiene el
registro de dirección y SP mantiene el offset del tope de la pila. Cuando usas una pila haciendo push´s y
pop´s, el procesador actualiza SP automáticamente y te permite accesar la información en el tope de la pila.

Sin embargo, algunas veces quieres accesar información dentro de la pila. En este caso, usas el registro BP
para mantener una dirección base en la pila.

Por ejemplo, digamos que tienes dos procedimientos llamados A y B. El procedimiento A pasa información
a B, y llama al procedimiento B. Si la cantidad de información es pequeña, esta puede ser pasada en uno o
varios de los registros generales. Sin embargo si necesitas pasar más información una estrategia es que el
procedimiento A empuje la información en la pila antes de llamar al procedimiento B.

Así, tan pronto como el procedimiento B inicia, este puede obtener información para accesar la pila. Sin
embargo, el procedimiento B no puede hacer esto de manera directa. Ciertamente, el procedimiento B
puede empujar datos por si mismo. Esto hace difícil rastrear la locación de la información que fue
empujada por el procedimiento A.

La solución esta en el procedimiento B, tan pronto como toma el control, pude salvar el offset del tope de
la pila en BP. En otras palabras, la primera cosa que el procedimiento B debe hacer es copiar el contenido
de los registros SP o BP. Esto significa que no importa que tipo de información sea metida en la pila, los
datos del procedimiento A pueden ser accesados usando el contenido de BP como una dirección base.

Por ejemplo, digamos que el procedimiento A mete 10 palabras de información en la pila. Cuando el
procedimiento B inicie, la primera cosa que hará es salvar el desplazamiento del tope de la pila copiando su
contenido al SP o al BP. Ahora el procedimiento B puede accesar la diez palabras, a su conveniencia, desde
SS:BP hasta SS:BP+18. (Recuerde que 10 palabras ocupan 20 bytes). Por ejemplo la segunda palabra
puede ser accesada como

SS:BP+2

En cualquier punto dentro del procedimiento B. Piense que esto se refiere a la palabra que esta dos bytes
más allá del tope de la pila cuando el procedimiento B empezó.

Para hacer las cosas fáciles, siempre que uses a BP como el registro base, el procesador asume que BP
contiene un offset en el segmento de pila y automáticamente utiliza SS como el registro de segmento. En el
ejemplo anterior también podría usar

BP+2

4.12 Regla generales para especificar una dirección.

37
Cuando especificas una dirección directa, usas el nombre de campo dato y un registro base,
registro índice, o desplazamiento constante opcional.

Aquí hay algunos ejemplos:

TABLE
TABLE[SI]
TABLE[DI]+8
TABLE[BX][SI]+8

Cuando especificas una dirección indirecta, puedes usar SP, BP, BX, SI o DI. Con BP o BX, puedes usar
SI ó DI como índice. Con todos los direccionamientos indirectos, puedes usar un desplazamiento opcional.
Veamos algunos ejemplos.

[BP]
[SI]
[BX][DI]
[SI]+4
[BP][DI]+4

Con un direccionamiento directo o indirecto, no puedes usar más de un registro base ni más de un registro
índice.

El ensamblador te permite especificar los valores indexados de varias maneras, sujetas a las siguientes
reglas:

 Los valores índices pueden estar en cualquier orden

 Los nombres de registros deben ir entre paréntesis cuadrados

 Puedes combinar nombres de registros y desplazamientos constantes en un conjunto de paréntesis


cuadrados si los separas con +.

 Si pones un desplazamiento constante enfrente de un nombre de registro, no necesitas usar un +.

Aquí hay varias direcciones directas equivalentes que ilustran estas reglas.

TABLE[BX][SI]+8
TABLE[BX+SI+8]
TABLE[8+SI+BX]

Aquí hay varias direcciones indirectas equivalentes.

[BP][SI]+12
12[BP][SI]
12[BP+SI]

Obviamente, la mejor idea es tomar un patrón y utilizarlo. Te recomiendo los siguientes patrones. Para la
dirección directa, usa

nombre[base][indice]+constante

38
por ejemplo :

TABLE[BX][SI]+8

Para un direccionamiento indirecto, usa

[base][indice]+constante

por ejemplo :

[BP][DI]+8

4.13 Direccionamiento con el Segmento de Código.

Un programa puede tener cuatro segmentos activos: el segmento de código, el segmento de pila, el
segmento de datos, y el segmento extra. De estos cuatro, solo el segmento de código no puede ser
modificado o explícitamente accesado mientras el programa es ejecutado. El segmento de código no puede
ser accesado porque es el contenido de las instrucciones actuales del programa.

La dirección en el segmento de código es hecha automáticamente por el procesador con los registros CS e
IP. El registro CS contiene la dirección de segmento del segmento de código. El registro IP contiene el
offset de la siguiente instrucción a ser ejecutada. Cuando cada instrucción se ejecuta, el registro IP es
actualizado apuntando a la siguiente instrucción en el programa.

Cuando el programa cambia la secuencia de instrucciones, el procesador actualiza el registro IP


apropiadamente. Por ejemplo, cuando un procedimiento llama a otro procedimiento, el desplazamiento del
otro procedimiento es colocado en IP. Si el segundo procedimiento esta en un segmento diferente, el
registro CS debe ser actualizado también. Esto también lo realiza el procesador cuando llama una
instrucción CALL.

El direccionamiento dentro de un segmento de código es automático, puedes ignorar los registros CS e IP


muchas de las veces. Sin embargo, son importantes cuando usas un debugger para probar un programa. Si
ejecutas un programa una instrucción a la vez, puedes examinar CS e IP para mantener el rastreo. Si algo
falla, puedes usar a CS e IP para ayudarte a determinar la dirección de la instrucción que causo el error.

{_____________o___________}

CAPITULO 5 LAS PARTES ATOMICAS DE UN PROGRAMA EN ENSAMBLADOR

Los programas en lenguaje ensamblador son más complejos que los de alto nivel. Cada programa en
ensamblador tiene parte diferentes que debes comprender. En este capitulo aprenderás las diversas partes y
las reglas para crearlas. Este material es básico y debes dominarlo para escribir tus programas.

39
Nuevos Términos

Programa Principal. Dentro de un programa, es el primer procedimiento a ejecutarse.

Estatuto. Una línea de un programa en ensamblador.

Comentario. Todas las partes de un estatuto que son ignorados por el ensamblador; los comentarios son
usados para mantener información descriptiva.

Instrucción. Un estatuto que puede ser traducido a lenguaje máquina.

Directiva ó Pseudo-operación. Un estatuto que da instrucciones al ensamblador.

Opcode. La parte de una instrucción o directiva que identifica una operación especifica.

Operando. La parte de una instrucción o directiva que representa el valor sobre el cual la instrucción o
directiva actúa.

Tabla de símbolos. Una tabla de nombres creada por el ensamblador para procesar un programa.

Referencia forward. En un programa, la aparición de un nombre, que no ha sido definido aún.

Nombre reservado. Una palabra a la cual el ensamblador asigna un significado especial; las palabras
reservadas no pueden ser usadas como nombres.

5.1 Como ve el programador un programa.

Como programador piensa que el programa tiene dos partes -instrucciones y datos. Las instrucciones
describen la lógica y los datos es el material sobre el que actúan las instrucciones. Cuando veas un
programa puedes hacerte las siguientes preguntas ¿Que hace este programa (instrucciones)? ¿Con qué
trabaja el programa (datos)?

La parte de instrucciones de un programa consiste de uno o más procedimientos (o subrutinas). El primer


procedimiento a ejecutar es llamado el programa principal. Un programa pequeño podría consistir solo del
programa principal y los datos. Un programa grande consiste de datos y de varios procedimientos.

5.2 Como ve el ensamblador un programa.

Cuando tú ves un programa que consiste de instrucciones y datos, el ensamblador ve un programa


fuente consistente de una secuencia de estatutos, uno por línea. El ensamblador lee esos estatutos (dos o
más veces), y genera un programa en lenguaje máquina consistente de segmentos.

Este programa en lenguaje máquina debe conocer los requerimientos del procesador, estos requerimientos
son altamente técnicos y detallados. Afortunadamente el ensamblador y el Linker se encargan de la mayoría
de esos detalles.

Esto significa que, desde el punto de vista del programador el ensamblador es un conjunto de estatutos que
tiene oscuros significados de lo que hace un programa. Esto es el porque los programas en ensamblador

40
son más difíciles de comprende que los de alto nivel. Tú defensa contra esta confusión es desarrollar bueno
hábitos de programación.

La conexión entre tu punto de vista del programa y el del ensamblador es que las instrucciones están
contenidas en el segmento de código. Sí tu programa tiene más de un procedimiento, todos estos estarán
contenidos dentro del segmento de código. La parte de datos estará contenida en los segmentos de pila,
datos y extra.

5.3 Como ve el programa el Linker.

El linker no ve el programa como una entidad conceptual o como una secuencia de estatutos. En su
lugar, el linker ve uno o más archivos que contienen módulos. El trabajo del linker es usar estos archivos
para crear un sólo archivo que contenga un modulo ejecutable.

Por conveniencia, puedes mantener un conjunto de módulos objeto en una colección llamada una librería.
El linker buscará la librería y extraerá los módulos objeto particulares que especifiques.

5.4 Que pasa durante el proceso de Ensamblamiento.

Piense que el ensamblador lee el programa línea por línea. Algunas líneas son instrucciones y otras
son requerimientos para definir datos.

El ensamblador crea el modulo objeto, byte por byte. Si una línea en el programa fuente contiene una
instrucción, el ensamblador genera la instrucción máquina equivalente y lo adiciona al modulo objeto. Si la
línea especifica un requerimiento de dato, el ensamblador adiciona el número apropiado de bytes (para
almacenar el dato) al modulo objeto. Así, el orden de las instrucciones y las áreas de datos en el modulo
objeto depende del orden en el cual el ensamblador encuentra las líneas del programa.

5.5 Comentarios.

Los programas en lenguaje ensamblador consisten de una secuencia de estatutos. Aquí hay tres tipos
de estatutos: comentarios, instrucciones y directivas.

Los comentarios son partes de estatutos que son ignorados por el ensamblador. Puedes usar los
comentarios para incluir descripciones, en el programa. Los comentarios son partes importantes del
programa ya que en posiciones claves de tu programa sirven como guías maestras del significado y
propósito del programa.

Hay varias maneras de incluir comentarios. La primera es que cualquier estatuto diferente del blancos que
este después de un punto y coma es considerado un comentario e ignorado por el ensamblador.

; Este es un comentario

Segundo, puede adicionar un comentario al final de una instrucción o directiva marcando el comentario
con un punto y coma. La siguiente instrucción pone le valor 99 en el registro AX:

MOV AX,99 ; Aquí hay un comentario

41
En general la regla es esta: mientras procesa una línea el ensamblador ignora todo lo que este después de
un punto y coma. Hay una excepción, ocasionalmente el programa pondrá caracteres con comillas simples
o dobles. En estos casos, el punto y coma no define comentarios, como en el siguiente ejemplo.

“ Hola; adiós “
‘ Hola; adiós ‘

5.6 Instrucciones y Directivas.

En general, una instrucción es un estatuto que pude ser traducido a lenguaje máquina. Una directiva
es un estatuto que le dice que hacer al ensamblador. Las directivas no pueden ser traducidas a lenguaje
máquina; sin embargo, son necesarias para que un programa se ensamble apropiadamente.

Consideremos algunas ejemplos que ilustran esta distinción. Veamos las siguientes dos instrucciones. La
primera adiciona el contenido del registro BX al contenido del registro AX. La segunda copia el contenido
de AX a la locación de memoria llamada SUM.

ADD AX,BX
MOV SUM,AX

Lo que siguen son dos directivas. La primera le dice al ensamblador que aparte un byte de memoria y le de
el nombre SUM. La segunda le dice al ensamblador que empieza el segmento llamado CSEG.

SUM DB 1
CSEG SEGMENT

Existen muchas instrucciones y directivas en el ensamblador, y al principio es difícil diferenciarlas, sin


embargo si quieres aprender a diferenciarlas pregúntate si generan una instrucción en lenguaje máquina o si
le dan una orden al ensamblador. (Por supuesto también puedes ver el manual).

La ventaja es que las directivas te dan un control sobre las cosas que están pasando. Esto es especialmente
importante si escribes programas sofisticados. La desventaja es que cada directiva requiere una línea
separada, la cual tiende a obscurecer el diseño lógico del programa.

5.7 El formato de los estatutos del lenguaje Ensamblador.

Las reglas del lenguaje ensamblador dependen del ensamblador que uses. Aquí hay algunas reglas
generales:
 Cada línea contiene solo un estatuto.

 Un estatuto puede empezar donde sea en una línea.

 Puedes usar mayúsculas o minúsculas como desees.

Las instrucciones y directivas tienen un formato más estructurado. Cada uno de estos tipos de elementos
tiene tres partes: un nombre, un opcode y un operando(s). Las tres partes siempre deben estar en este
orden, y ellas deben estar separadas por lo menos con un espacio.
Aquí hay un ejemplo de un par de estatutos de un programa:

; actualizar el total

42
NEWSUM LABEL NEAR
MOV AX,TOTAL
ADD AX,SUBTOTAL
PUSH AX

Los programadores se refieren a un estatuto por su opcode. De hecho, en el ejemplo anterior, puedes decir
que el primer estatuto es la directiva LABEL y los otros estatutos son las instrucciones MOV, ADD y
PUSH.

Los ejemplos previos ilustran tres puntos importantes. Primero, para hacer las instrucciones entendibles,
usa mayúsculas.
Segundo, algunos estatutos contienen dos operandos como el estatuto

MOV AX,TOTAL

En tales casos debes separar los operandos con una coma.

Tercero, todas las instrucciones tendrán un opcode pero no todas tendrán un nombre y/o operandos.

Para mantener cada parte en su lugar, haz el hábito de separar esto por columnas. Empieza con los
nombres en la columna 1, los opcodes en la columna 9, y los operandos en la columna 17, si incluye
comentarios pon estos a partir de la comuna 41. Esto ayuda a estandarizar el programa. La figura 5-1
muestra una instrucción MOV con este formato:

Nombre Opcode Operando Comentario

SETUP MOV DX,OFFSET MESSAGE ;actualizar DX

col 1 col 9 col 17 col 41

5.8 Como usar los Nombres.

El nombre es la parte de una instrucción o directiva que comprende la dirección donde esta
localizada. Por ejemplo, considere la siguiente directiva, la cual le dice al ensamblador que inicia un nuevo
procedimiento:

GETDATA PROC

Esta directiva tiene un nombre GETDATA, y un opcode, PROC, pero no tiene ningún operando.
Como parte del proceso de ensamblamiento, el ensamblador mantiene una tabla de nombres llamada tabla
de símbolos. Cada vez que el ensamblador encuentra un nuevo nombre, lo adiciona a la tabla con la
dirección (el offset) en el cual apareció. Entonces cuando otras instrucciones se refieren a ese nombre, se
puede usar como una dirección con un operando. El ensamblador puede buscar el nombre en la tabla de
símbolos y sustituir la dirección.

Por ejemplo, una vez que GETDATA es establecida como la representación de una dirección al principio
del procedimiento, una instrucción CALL puede llamar al procedimiento así:

CALL GETDATA

La instrucción tiene un opcode CALL y un operando GETDATA pero no tiene nombre.


Aquí hay otro ejemplo. La siguiente directiva le dice al ensamblador que aparte 5 palabras de memoria:

43
SUM DW 5 DUP(?)

Esta directiva tiene un nombre, SUM; un opcode, DW; y un operando 5 DUP(?). El nombre SUM
representa la dirección donde está la primera de las 5 palabras. Así la siguiente instrucción copia el
contenido del registro AX a esta palabra:

MOV SUM, AX

Esta instrucción tiene un opcode MOV, y un operando SUM,AX; pero no tiene nombre.
SUM se refiere a la dirección de la primera de las 5 palabras, para referirte a la dirección de la segunda
palabra, puedes usar SUM+2. Así si quieres copiar el contenido del registro AX al la segunda palabra
puedes usar:

MOV SUM+2,AX

Sin embargo que pasa si el ensamblador no encuentra aún el estatuto de la definición de un nombre. Por
ejemplo, que pasa si la instrucción anterior está antes de la directiva en la cual SUM es definida. Esta es
llamada una referencia forward. El ensamblador maneja la referencia forward dejando un espacio para la
dirección, la cual debe ser llenada después.

Los programadores experimentados evitan el forward siempre que es posible, debido a que el ensamblador
no le es posible crear código eficiente cuando encuentra una referencia forward. La mejor forma de evitar
esto es colocando los segmentos que define los datos antes del segmento que contiene las instrucciones.

5.9 Las reglas para especificar Nombres.

Los nombres son símbolos que escoges para representar de una manera más sencilla direcciones de
memoria en un programa. Debes crear un nombre de acuerdo a las siguientes reglas:

 Los nombres pueden usar letras, dígitos y los siguientes caracteres especiales:
? @ _ $

 El primer carácter no debe ser un dígito. Esto le permite al ensamblador distinguir entre nombres y
números.

 No es buena idea usar el carácter @ al principio de un nombre. Los nombres que empiezan con @
tienen un propósito especial.
 Los nombre pueden ser tan largos como quieras; sin embargo, el ensamblador solo reconoce
los primeros 31 caracteres.

Aquí hay algunos ejemplos de nombres validos:

HELLO $MARKET A12345 LONG_NAME PART_3

Aquí hay algunos nombres inválidos:

LONG-NAME 3_PART

Como siempre la recomendación es que escojas nombres concisos, pero que representen el uso práctico de
la variable o sus posibles valores.

Esta absolutamente prohibido, como en los lenguajes de alto nivel, que utilices nombre idénticos a las
instrucciones y directivas de ensamblador.

44
5.10 Reglas para especificar Números.

Puedes utilizar números en decimal, hexadecimal, y binario. Para especificar números decimales
escríbelos con dígitos como siempre. Por ejemplo la siguiente instrucción carga el registro AX con el valor
decimal 855.

MOV AX,855

Para especificar un número en hexadecimal, use los dígitos hex del 0 al 9 y de la letra A-F. Debes adicionar
una H al final del número para especificar que es un hex, la siguiente instrucción carga el registro AX con
el valor hex 855H (2133 decimal).

MOV AX,855H

Si el número inicia con una letra de la A-F, debes poner un 0 antes del número, para que el ensamblador no
piense que es un nombre de dirección de memoria. Por ejemplo la siguiente instrucción carga el registro
AX con el valor FFH (255 decimal):

MOV AX,0FFH

Si usas

MOV AX,FFH

el ensamblador asumirá que FFH es un nombre y ocurrirá un error.

Para especificar un número binario, use solo los dígitos 1 y 0, y adicione una letra B al final del número.
Por ejemplo si quisiera cargar el registro AX con el valor binario 011010010110B (1686 decimal, 696H)

MOV AX,011010010110B

{_____________o___________}

CAPITULO 6 COMPRENDER UN PROGRAMA EN LENGUAJE ENSAMBLADOR

En este capitulo aprenderás como construir un programa básico en lenguaje ensamblador, usando un
programa de ejemplo aprenderás cual es la función de cada parte y como se construye. Cuando termine
este capítulo debes sentirte cómodo con un esqueleto que es común para cualquier tipo de programa en
ensamblador.

Nuevos Términos

Listado. Un reporte o impresión que el ensamblador genera cuando procesa un programa.

Punto de entrada. La dirección en la cual un procedimiento inicia su ejecución.

Salvar (un registro). Almacenar un registro para recuperarlo después.

Restaurar (un registro). Cambiar el valor de un registro por un valor anterior.

45
Side effect. Un cambio inadvertido en el ambiente de un programa.

6.1 Un programa Prototipo.

En este capítulo el programa de la figura 6-1 es usado como prototipo de un programa de propósito
general en lenguaje ensamblador. Después de que leas este capítulo usa un editor para hacer tu propia
copia del programa prototipo. De este modo cuando veas como ensamblar y linkear un programa podrás
hacerlo con este.

Note que en la figura hay un número de línea, este sólo es utilizado como referencia, y no debe aparecer en
la copia que hagas con el editor. Cuando explique las diferentes instrucciones y directivas solo las explicare
ligeramente. Estas serán explicadas más a fondo en otros capítulos adelante.

Figura 6-1 Un programa prototipo

1 PAGE 58,132
2 ;------------------------------------------------------------------------------------
3 ; DISPLAY
4 ;
5 ; Proposito:
6 ; para desplegar un mensaje en la pantalla
7 ; -----------------------------------------------------------------------------------
8
9 ; Ajustar el titulo y el conjunto de instrucciones
10 TITLE DISPLAY - programa prototipo
11 .286
12
13 ;-------------------------------------------------------------- segmento STACK
14 SSEG SEGMENT STACK
15 DB 32 DUP(“STACK---”)
16 SSEG ENDS
17
18 ;--------------------------------------------------------------- segmento DATA
19 DSEG SEGMENT
20 MESSAGE DB “Hola, ¿como estás viejo?”, 0DH, 0AH
21 L_MESSAGE EQU $-MESSAGE
22 DSEG ENDS
23
24 ;--------------------------------------------------------------- segmento CODE
25 CSEG SEGMENT ‘CODE’
26 ASSUME CS:CSEG, SS:SSEG; DS:DSEG
27
28 PAGE
29 ;----------------------------------------------------------------------------------
30 ; MAIN (programa principal)
31 ;
32 ; Proposito:
33 ; Desplegar un mensaje en la pantalla
34 ;
35 ; Entrada:
36 ; -- ninguna --

46
37 ;
38 ; Salida:
39 ; El mensaje es desplegado en la pantalla
40 ;
41 ; Procedimientos:
42 ; -- ninguno --
43 ;----------------------------------------------------------------------------------
44
45 ; Procedimiento: MAIN
46 MAIN PROC FAR
47
48 ; Salvar la direccion para poder regresar a DOS
49 PUSH DS
50 PUSH 0
51
52 ; Actualizar el registro de segmento
53 MOV AX,DSEG
54 MOV DS,AX
55
56 ; Desplegar el mensaje
57 MOV BX,0001H
58 LEA DX,MESSAGE
59 MOV CX,L_MESSAGE
60 MOV AH,40H
61 INT 21H
62
63 ; Regresar a DOS
64 RET
65
66 ; Fin de procedimiento: MAIN
67 MAIN ENDP
68
69 ; Fin de segmento de codigo
70 CSEG ENDS
71
72 ;--------------------------------------------------------------- Fin de programa
73 END MAIN

6.2 Como usar los comentarios bien.

Es importante que todo programa en ensamblador tenga un encabezado que explique brevemente el
propósito del programa y su nombre línea 2-7. Ahora vea los comentarios en las líneas 29-43. Estos
muestran información general acerca del programa principal. Debes tener un conjunto de comentarios que
antecedan cada procedimiento de tu programa.

Puedes ver que después del nombre del procedimiento (línea 30), siguen cuatro encabezados estándar:

 “Propósito” describe que es lo que hace el procedimiento. Este debe ser un comentario de dos líneas.

 “Entrada” y “Salida” describen que necesita el procedimiento y que va a obtener como resultado.

 “Procedimientos” nos indica cuales procedimientos llama el programa principal.

47
Ahora vea los comentarios que anteceden a cada bloque pequeño de instrucciones y directivas - líneas 9,
13, 18, 24, 45, 48, 52, 56, 63, 66, 69 y 72. Estos comentarios son de dos tipos. El primero de los
comentarios describe el principio o el fin de segmento o procedimiento. Este tipo de comentarios consisten
de simples anuncios.

El segundo tipo de comentarios -los cuales son bastantes en un programa - describen el propósito de los
estatutos que están a continuación. Por ejemplo, la línea 56 dice “Desplegar el mensaje”. Note que no dice
“Desplegará el mensaje” ó “Mensaje desplegado”. Los mejores comentarios son directos: “Hace esto o
aquello”, en lugar de “Hizo o va a hacer esto o aquello”.

Puedes ayudarte a diseñar un procedimiento, empezando con una serie de estatutos, que te indiquen una
guía sobre lo que tiene que realizar el programa. Por ejemplo el diseño para este programa es

 Salvar direcciones para poder regresar a DOS.

 Actualizar los registros de segmento.

 Desplegar el mensaje en la pantalla.

 Regresar a DOS.

Una vez que terminas el diseño puedes transformar los estatutos en un par de líneas del lenguaje
ensamblador.

Evita como a la peste a aquellos programadores y programas de ensamblador que tienden a poner
comentarios a cada una de las instrucciones y directivas en el programa, (reserva este caso solo para partes
del programa demasiado oscuros o complicados), esto solo hace muy confuso el programa e indica que ni
el programador sabe exactamente que hace su programa, hay que ser breve pero conciso. Para evitar esto
aprende a diseñar tu programa usando metas iniciales como comentarios principales. Debes ser capaz de
comprender un programa completamente leyendo los comentarios en secuencia.

Siempre documenta tus programas como si fueran para otra persona que tuviera que comprenderlos sin
ayuda. Esto te permite desarrollar programas rápidos y de manera más precisa, también te permite recordar
el propósito de tus programas viejos que no has visto por un tiempo.

6.3 Indicar el fin de un programa.

El último estatuto en todo programa debe ser una directiva END. Esta directiva tiene dos propósitos:
indicar el fin de un programa, y decirle al ensamblador donde va a empezar a ejecutarse un programa.
En este caso, el operando de la directiva END es MAIN (línea 73). Esto le dice al ensamblador que cuando
el programa sea cargado, la ejecución debe empezar con el estatuto con el nombre MAIN (línea 46).

6.4 Actualizando el listado.

El listado es un reporte, que se puede imprimir, que el ensamblador genera cuando procesa un
programa. El listado contiene cada estatuto en el programa con las instrucciones máquina correspondientes
al estatuto. Si el ensamblador encuentra un error en un estatuto, el listado contendrá un mensaje de error
después del estatuto. Al final del programa, el listado muestra información acerca de los nombres que el
programa usa.

48
Hay varias directivas que puedes usar para actualizar el listado. Una es la directiva PAGE (línea 1 y 28) y
la directiva TITLE (línea 10).

La directiva PAGE puede ejecutar dos funciones. Cuando es usada con operandos PAGE controla el ancho
y las páginas del listado. En la línea 1 del programa prototipo

PAGE 58,132

La directiva PAGE pone una longitud de cada página de 58 líneas y un ancho en cada página de 132
caracteres. Esta directiva debe ir en la primera línea del programa, antes de los comentarios introductorios,
para asegurarnos que imprimirá los comentarios adecuadamente.

Cuando la directiva PAGE no tiene operandos, le dice al ensamblador que inicie una nueva página en el
listado.

La directiva TITLE (línea 10) especifica un titulo a ser impreso cerca del tope de cada página. En este
caso, el titulo es “DISPLAY - programa prototipo”.

6.5 Especificar el Conjunto de Instrucciones.

Por default, el ensamblador puede reconocer sólo las instrucciones 8086. Sin embargo puedes poner
una directiva especial al inicio del programa para decirle al ensamblador que planeas usar un procesador
diferente. El ensamblador te permite entonces usar todas las instrucciones que funcionan en ese
procesador.
Como explique en el capitulo 1, las instrucciones 8086 son todas las que necesitas muchas de las veces. Sin
embargo, existen un par de instrucciones que no son del 8086 que son muy convenientes de usar. Estas
instrucciones requieren por lo menos un procesador 286. (Específicamente me refiero a las instrucciones
PUSHA y POPA usadas en la pila).

Para decirle al ensamblador que quiero usar el conjunto de instrucciones 286 (en modo real, uso la
directiva .286 (línea 11).

Puedes encontrar un par de instrucciones 286 extra que son útiles en un programa, por ejemplo en la línea
50 meto un valor 0 en la pila. El procesador 8086 no acepta este tipo de instrucciones. El 8086 necesita
que primero le pases el valor a algún registro y de ahí a la pila.

Así si no tuviéramos la directiva .286 no podrías usar la instrucción

PUSH 0

Tendría que poner un cero en algún registro AX por ejemplo, y entonces meter el registro:

MOV AX,0
PUSH AX

6.6 Actualizar los Segmentos.

49
Muchos de tus programas tendrán un segmento de pila, un segmento de datos, y un segmento de
código. Usualmente puedes ponerlos en ese orden. Algunas veces necesitaras usar el segmento extra, en tal
caso el segmento extra debe estar entre el segmento de datos y el segmento de código.

La directiva SEGMENT marca el inicio de un segmento; la directiva ENDS marca el fin de un segmento.
Cada segmento debe tener un nombre. Por ejemplo, si el segmento es el de pila, la directiva SEGMENT
debe tener el operando “STACK”.

En el programa prototipo, el segmento de pila es actualizado en los estatutos de las líneas 14 y 16:

SSEG SEGMENT STACK


SSEG ENDS

El segmento de datos es actualizado en las líneas 19 y 22:

DSEG SEGMENT
DSEG ENDS

Y el segmento de código es actualizado en las líneas 25 y 70:

CSEG SEGMENT ‘CODE’


CSEG ENDS

Si tienes un segmento extra, puedes definirlo con estatutos similares:

ESEG SEGMENT
ESEG ENDS
Puedes escoger cualquier nombre que quieras en lugar de SSEG, DSEG, CSEG y ESEG. Sin embargo
estos nombres creo que están bien y dan la idea.

Cuando defines el segmento de código, debes usar el operando ‘CODE’ con la directiva SEGMENT. LA
razón es obscura y no esta documentada en el manual.

Note que he usado comentarios con una línea de guiones (líneas 13, 18 y 24) para marcar el principio de
cada segmento. El segmento de código consiste de instrucciones máquina que el ensamblador generará a
partir de las instrucciones fuente. Los segmentos de pila, datos y extra consisten de espacio de memoria
que el ensamblador apartará.

El espacio para el segmento de datos es requerido por la directiva DB en la línea 20. Brevemente, DB
significa “definir byte”. Esta directiva le dice al ensamblado el número de bytes que tiene que apartar.
Además de especificar los bytes también se pueden inicializar a un valor particular.
La directiva DB en la línea 20:

MESSAGE DB “Hola, ¿como estas viejo?”, 0DH, 0AH

le dice al ensamblador que aparte bytes, tantos como caracteres o valores estén especificados.
Los primeros 24 contienen los caracteres “Hola, ¿como estas viejo?”. Los siguientes 2 bytes contienen los
valores hex DH y AH. Estos dos valores hex hacen que el cursor avance al principio de la siguiente línea
después de que el mensaje es desplegado. Así la directiva DB aparta 26 bytes. Los detalles de la definición
de espacio de memoria se explicaran en un capitulo posterior.

En resumen, el segmento de código consiste a las instrucciones máquina que corresponden a las
instrucciones entre

50
CSEG SEGMENT ‘CODE’

CSEG ENDS

El segmento de datos consiste de las áreas de memoria que serán reservadas por medio de directivas que
están entre

DSEG SEGMENT

DSEG ENDS

Si necesitas un segmento extra, las directivas que reservan su área de memoria estarán entre

ESEG SEGMENT

ESEG ENDS

Las directivas que reservan área de memoria para la pila están entre

SSEG SEGMENT STACK

SSEG ENDS

6.7 Apartar área de memoria para la Pila.

El estatuto que aparta espacio de memoria para la pila es la directiva DB, en el programa prototipo,
esta en la línea 15:

DB 32 DUP(“STACK---”)

Veamos esta instrucción más al detalle. Supongamos que quieres reservar 256 bytes de memoria para la
pila. Existen varias maneras en las que puedes apartar tal área de memoria. La más simple es apartar los
256 bytes directamente con la directiva DB.

DB 256 DUP(?)

significa “aparta 256 bytes”. DUP significa duplicado. El signo de interrogación significa que no es
necesario inicializar los valores de la pila a un valor específico. Sin embargo, puede que quieras inicializar
cada uno de los 256 caracteres de la pila a un carácter particular. Cuando corres un programa usando un
debugger, puedes examinar la memoria del programa ejecutado. Si la pila contiene caracteres reconocibles
cuando el programa inicia, puedes encontrarlos o distinguirlos más fácilmente.

51
Por ejemplo si quisieras apartar 256 bytes para la pila cuyo contenido sean puros asteriscos (*) . Los haces
con la siguiente directiva:

DB 256 DUP(“*”)

Sin embargo es probable que otras direcciones de memoria contengan también una buena cantidad de
asteriscos lo que en cierto punto seria un poco molesto de comprobar y segundo con una secuencia de
asteriscos no podríamos reconocer rápidamente cuanta cantidad de la pila esta siendo ocupada por los
datos.

La siguiente directiva aparta 256 bytes y los inicializa a “STACK---”. Cada grupo de caracteres de
STACK--- requiere 8 bytes, así tienes una requisición de 32 grupos para apartar 256 bytes (32 x 8 = 256).

DB 32 DUP (“STACK---”)

Si necesitas una pila más grande o pequeña, ajusta el estatuto DB como necesites. Por ejemplo, si quieres
una pila de 1024 bytes puedes usar:

DB 128 DUP(“STACK---”)

así 128 x 8 = 1024.

6.8 Direccionar los segmentos de Pila, Datos y Código.

El ensamblador se encarga de casi todos los detalles involucrados con la generación de las
direcciones apropiadas. Sin embargo, puedes ayudarle al ensamblador de dos maneras: Primero, debes
decirle al ensamblador que valores contendrán los registros de segmento cuando el programa se ejecute y
segundo, debes asegurarte que los registros de segmento contiene esos valores actualmente.

Para completar el primer requerimiento, usamos las directiva ASSUME. En el programa prototipo en la
línea 26:

ASSUME CS:CSEG, SS:SSEG, DS:DSEG

Esto le dice al ensamblador que asuma qué cuando el programa se ejecute, el registro CS contendrá la
dirección de CSEG; SS estará contenido en la dirección SSEG; y DS contendrá la dirección de DSEG.
Basados en estos reconocimientos, el ensamblador generará los desplazamientos correctos para los
nombres de los segmentos varios. En otras palabras, cuando uses un nombre como parte de un operando,
el ensamblador reemplazará el nombre con el offset relativo correcto, para el registro de segmento
apropiado.

Afortunadamente no necesitas actualizar los registros CS y SS por ti mismo. Cuando DOS carga un
programa, este automáticamente pone CS apuntando al primer byte del segmento de código y SS
apuntando al primer byte de la pila. Sin embargo si necesitas actualizar los registros DS y ES. Esto puedes
hacerlo al principio del programa, antes de cualquier referencia a nombres usados en el segmento de datos
o extra.

Para actualizar el registro DS, todo lo que tienes que hacer es cargar la dirección del primer byte del
segmento de datos. Si llamaste a tu segmento de datos DSEG, usa la instrucción MOV para copiar el valor
de DSEG al registro DS. Sin embargo la instrucción MOV no permite copiar directamente una locación de
memoria a un registro de segmento. En otras palabras no puedes usar

MOV DS,DSEG 

52
Sin embargo, si puedes copiar DSEG a un registro general, digamos AX, y entonces de AX a DS. Esto
esta dado por las líneas 52 y 54 del programa prototipo:

; Actualizar el registro de segmento


MOV AX,DSEG
MOV DS,AX

6.9 Direccionar el Segmento Extra.

Existen dos maneras de usar un segmento extra. La primera, es usa el segmento extra como una
segunda área de datos distinta del segmento de datos. En este caso, el segmento extra estará después del
segmento de datos definido entre las líneas:

ESEG SEGMENT
ESEG ENDS

Como en el segmento de datos, las directivas del segmento extra para apartar memoria estarán entre los
estatutos anteriores.

en este caso la directiva ASSUME debe decirle al ensamblador que ES contendrá la dirección del
segmento extra:

ASSUME CS:CSEG, SS:SSEG, DS:DSEG, ES:ESEG

Cuando actualizas el registro DS, también debes actualizar el registro ES. En otras palabras, reemplaza las
líneas 52 a la 54 del programa prototipo con lo siguiente:

; Actualizar los registros de segmento


MOV AX,DSEG
MOV DS,AX
MOV AX,ESEG
MOV ES,AX

La segunda manera de usar el segmento extra es hacer que ocupe el mismo espacio que el segmento de
datos. Esto te permite referenciar los campos de datos que están en ambos segmentos (como una persona
que responde a dos nombres diferentes).

Esto es útil con ciertas instrucciones: MOVS, MOVSB, y MOVSW, como explicare después. Estas
instrucciones tiene dos operandos: uno debe estar en el segmento de datos y otro debe estar en el
segmento extra. Sobrelapar los dos segmentos de datos te permiten usar el mismo campo de dato en
ambos operandos.

Cuando actualizas el segmento de datos y extra para que sea la misma área de memoria, use los estatutos
SEGMENT y ENDS para definir un área de datos única:

DSEG SEGMENT
DSEG ENDS

Y cambie el estatuto ASSUME por este:

ASSUME CS:CSEG, SS:SSEG, DS:DSEG, ES:DSEG

53
Note que DS y ES están en DSEG. Esto le indica al ensamblador que ambos registros DS y ES apuntan al
mismo segmento DSEG.

Para actualizar los registros DS y ES, copie los valores de DSEG a ambos registros , reemplazando las
líneas 52-54 del programa prototipo por estas líneas:

; Actualizar los registros de segmento


MOV AX,DSEG
MOV DS,AX
MOV ES,AX

6.10 Actualizar el programa MAIN

Los programas están divididos en uno o más procedimientos. El procedimiento que empieza la
ejecución del programa es llamado main.

El programa principal puede llamar a otros procedimientos, los cuales pueden llamar a otros, etc. Por
supuesto muchos programas pequeños consisten de un solo procedimiento principal.

La dirección en la cual un procedimiento comienza a ejecutarse es llamada el punto de entrada. El punto


de entrada del programa principal en donde el programa comienza. La directiva END en el programa
prototipo (línea 73) tiene un operando MAIN, el punto de entrada de este programa es el nombre MAIN.
La primera instrucción que será ejecutada actualmente será el PUSH de la línea 49.

Para poder ser ejecutado un procedimiento debe ser llamado por otro procedimiento, y el procedimiento
principal no es la excepción; este debe ser llamado por un procedimiento DOS, tu programa principal es
sólo otro procedimiento.

Las directivas SEGMENT y ENDS le dicen al ensamblador donde empieza y termina un segmento, las
directivas PROC y ENDP le dicen al ensamblador donde empieza y termina un procedimiento, en el
programa prototipo, el programa principal empieza en la línea 46:

MAIN PROC FAR

y termina en la línea 67:

MAIN ENDP

En la línea 46, la directiva PROC tiene un operando FAR. Este le dice al ensamblador que el procedimiento
será llamado desde otro segmento. (En este caso, el procedimiento principal será llamado por un
procedimiento DOS, que definitivamente esta en otro segmento.)

La última instrucción de cualquier procedimiento debe ser la instrucción RET. Esta instrucción regresa al
procedimiento que hizo la llamada. En el caso del programa principal, la instrucción RET regresa al
sistema operativo DOS.

La instrucción RET espera la dirección de regreso que se encuentra en la pila. Recuerda que una dirección
consiste de un desplazamiento y una dirección de segmento. El offset debe estar en el tope de la pila, la
dirección de segmento debe ser lo segundo en la pila.

54
Cuando DOS encuentra y carga un programa, el registro DS contiene el registro de dirección al cual el
programa volverá cuando termine. Esta dirección es exacta sólo necesita un offset de 0.
Una de tus responsabilidades es que el programa principal meta la dirección de regreso a la pila. Esto es
mejor hacerlo al principio de cada programa antes de cambiar el contenido del registro DS. En el programa
prototipo esta en la línea 48-50:

; salvar la dirección de regreso a DOS


PUSH DS
PUSH 0

La dirección de segmento es metida primero, seguida de un desplazamiento 0. Como los valores están en
Last-in, first-out, el desplazamiento será recuperado primero, seguido por la dirección de segmento. Esto
es lo que la instrucción RET necesita y lo hace automáticamente sin que tengas que intervenir.

Si colocas datos en la pila en el transcurso de tu programa, debes asegurarte de sacarlos antes de que una
instrucción RET se ejecute. En otras palabras debes asegurarte que la instrucción RET encuentre los
valores apropiados en la pila.

6.11 Los Estatutos que hacen el trabajo.

¿Donde están las instrucciones que despliegan el mensaje? En el programa prototipo están en las
líneas 56-61:

; Desplegar el mensaje
MOV BX,0001H
LEA DX,MESSAGE
MOV CX,L_MESSAGE
MOV AH,40H
INT 21H

Para escribir un programa diferente, sólo tienes que reemplazar estas líneas por instrucciones diferentes y
reemplazar el segmento de datos por nuevos datos. La mejor forma de empezar un programa nuevo es
modificar el programa prototipo.

6.12 Actualizar un programa para llamar procedimientos.

El programa prototipo de la figura 6-1 tiene un sólo procedimiento, el programa principal, sin
embargo frecuentemente diseñaras programas que tengan varios procedimientos.
Actualizar un programa para adicionarle algunos procedimientos es fácil. Sólo sigue estas guías:

 Todos los procedimientos deben estar dentro del segmento de código.

 Use las directivas PROC y ENDP para indicarle al ensamblador donde empieza y acaba el
procedimiento.

La figura 6-2 muestra una guía de un programa que tiene varios procedimientos. Note que los tres
procedimientos son llamados MAIN, PROC1 y PROC2. Dentro de un procedimiento, usas la instrucción
CALL para llamar a otro procedimiento. Por ejemplo, si quisieras llamar al procedimiento PROC1 de la
siguiente manera:

CALL PROC1

55
Para regresar al procedimiento que hizo la llamada, usa

RET

La instrucción CALL automáticamente empuja la dirección de regreso en la pila, en preparación para la


instrucción RET

Figura 6-2 Patrón de un programa que llama procedimientos

comentarios introductorios

SSEG SEGMENT STACK


-Segmento de la pila-
SSEG ENDS

DSEG SEGMENT
-Segmento de datos-
DSEG ENDS

ESEG SEGMENT
-Segmento extra-
ESEG ENDS

CSEG SEGMENT ’CODE’

MAIN PROC FAR


-Programa principal-
MAIN ENDP

PROC1 PROC
-Procedimiento 1-
PROC1 ENDP

PROC2 PROC
-Procedimiento 2-
PROC2 ENDP

CSEG ENDS

END MAIN

6.13 El prototipo de un Procedimiento.

Excepto por el programa principal, todos los procedimientos son llamados dentro del programa.
Estas llamadas a los procedimientos tienen una estructura similar a la del programa principal. La figura 6-3
contiene un prototipo de un procedimiento.

Figura 6-3 Prototipo de un procedimiento.

1 PAGE

56
2 ;-----------------------------------------------------------------------------------------
3 ; DISPM1
4 ;
5 ; Proposito:
6 ; Desplegar el primero de dos mensajes
7 ;
8 ; Entrada:
9 ; -ninguna-
10 ;
11 ; Salida:
12 ; El mensaje se despliega en pantalla
13 ;
14 ; Procedimientos:
15 ; -ninguno-
16 ;-----------------------------------------------------------------------------------------
17
18 ; inicio del procedimiento: DISPM1
19 DISPM1 PROC
20
21 ; salvar los registros
22 PUSHA
23
24 ; despliega el primer mensaje
25 MOV BX,0001H
26 LEA DX,MSG1
27 MOV CX,L_MSG1
28 MOV AH,40H
29 INT 21H
30
31 ; restaurar registros
32 POPA
33
34 ; regresar al procedimiento principal
35 RET
36
37 ; fin del procedimiento: DISPM1
38 DISPM1 ENDP

6.14 La estructura de un Procedimiento.

Demos un vistazo al procedimiento en la figura 6-5. Note que el procedimiento empieza con la
directiva página sin operandos.

Las líneas 2 a la 16 son comentarios similares al programa main, incluyendo el nombre, propósito, entrada
de datos, salida, etc. El procedimiento esta definido por las directivas PROC y ENDP (líneas 19 y 38). El
procedimiento regresa usando una instrucción RET (línea 35).

6.15 Salvar y restaurar los registros.

57
La diferencia más notable entre el procedimiento y el programa principal, son las instrucciones para
salvar y restaurar registros (líneas 21-22 y 31-32). Salvar un registro significa recordar cual era su valor.
Restaurar los registros significa copiar los valores recuperados de regreso a los registros.

Cada llamada a un procedimiento obliga a este a dejar los registros tal y como estaban después de que este
termina. La idea es que un procedimiento no debe cambiar el ambiente. Esto es importante. Cuando un
procedimiento inadvertidamente cambia algo del ambiente, tal como el valor de un registro, lo llamamos un
cambio side effect. Debes aprender a diseñar programas para minimizar los side effects, que pueden causar
errores obscuros y perplejos.

La mejor manera de salvar los registros es poner sus valores en la pila. Esto es hecho por la instrucción
PUSHA (push all) en la línea 22. Esta instrucción salva todos los registros exceptuando los registros de
segmento. La instrucción POPA (pop all) restaura los registros (línea 32). Salvar y restaurar registros tiene
dos propósitos fundamentales. Primero evita el riesgo que una llamada a un procedimiento cause un side
effect cambiando accidentalmente el valor de algún registro, y segundo, permite a los procedimientos
utilizar los registros de manera libre ya que estos son restaurados antes de regresar del procedimiento.

6. 16 Un prototipo para un programa que llama procedimientos.

La figura 6-4 contiene un prototipo de un programa que llama procedimientos. Este programa de
ejemplo, DISPLAY manda dos mensajes a la pantalla. Para hacer esto, DISPLAY llama a dos
procedimientos: DISPM1, para desplegar el primer mensaje y DISPM2, para desplegar el segundo, este
programa es un prototipo; normalmente no debes separar en procedimientos algo tan sencillo como
desplegar mensajes.

Figura 6-4 Un programa prototipo para llamar procedimientos

PAGE 58,132
;------------------------------------------------------------------------------------
; DISPLAY2
;
; Proposito:
; para desplegar dos mensajes en la pantalla
; -----------------------------------------------------------------------------------

; Ajustar el titulo y el conjunto de instrucciones


TITLE DISPLAY2 - programa prototipo con procedimientos
.286

;-------------------------------------------------------------- segmento STACK


SSEG SEGMENT STACK
DB 32 DUP(“STACK---”)
SSEG ENDS

;--------------------------------------------------------------- segmento DATA


DSEG SEGMENT
MSG1 DB “Este es el mensaje 1”, 0DH, 0AH
L_MSG1 EQU $-MSG1
MSG2 DB “Este es el mensaje 2”, 0DH, 0AH
L_MSG2 EQU $-MSG2
DSEG ENDS

58
;--------------------------------------------------------------- segmento CODE
CSEG SEGMENT ‘CODE’
ASSUME CS:CSEG, SS:SSEG; DS:DSEG

PAGE
;----------------------------------------------------------------------------------
; MAIN (programa principal)
;
; Proposito:
; Desplegar dos mensajes en la pantalla
;
; Entrada:
; -- ninguna --
;
; Salida:
; Los mensajes son desplegados en la pantalla
;
; Procedimientos:
; DISPM1 -- despliega el primer mensaje
; DISPM2 -- despliega el segundo mensaje
;----------------------------------------------------------------------------------

; Procedimiento: MAIN
MAIN PROC FAR

; Salvar la direccion para poder regresar a DOS


PUSH DS
PUSH 0

; Actualizar el registro de segmento


MOV AX,DSEG
MOV DS,AX

; Desplegar los mensajes


CALL DISPM1
CALL DISPM2

; Regresar a DOS
RET

; Fin de procedimiento: MAIN


MAIN ENDP

PAGE
;-----------------------------------------------------------------------------------------
; DISPM1
;
; Propósito:
; Desplegar el primero de dos mensajes
;
; Entrada:
; -ninguna-
;
; Salida:

59
; El mensaje se despliega en pantalla
;
; Procedimientos:
; -ninguno-
;-----------------------------------------------------------------------------------------

; inicio del procedimiento: DISPM1


DISPM1 PROC

; salvar los registros


PUSHA

; despliega el primer mensaje


MOV BX,0001H
LEA DX,MSG1
MOV CX,L_MSG1
MOV AH,40H
INT 21H

; restaurar registros
POPA

; regresar al procedimiento principal


RET

; fin del procedimiento: DISPM1


DISPM1 ENDP

PAGE
;-----------------------------------------------------------------------------------------
; DISPM2
;
; Proposito:
; Desplegar el segundo de dos mensajes
;
; Entrada:
; -ninguna-
;
; Salida:
; El mensaje se despliega en pantalla
;
; Procedimientos:
; -ninguno-
;-----------------------------------------------------------------------------------------

; inicio del procedimiento: DISPM2


DISPM2 PROC

; salvar los registros


PUSHA

; despliega el segundo mensaje


MOV BX,0001H
LEA DX,MSG2

60
MOV CX,L_MSG2
MOV AH,40H
INT 21H

; restaurar registros
POPA

; regresar al procedimiento principal


RET

; fin del procedimiento: DISPM2


DISPM2 ENDP

; Fin de segmento de código


CSEG ENDS

;--------------------------------------------------------------- Fin de programa


END MAIN

{_____________o___________}

61

Вам также может понравиться