Ensamblador - Monografias.com

Anuncio
Ensamblador
por Emiliano Llano Díaz
Ensamblador
Emiliano Llano Díaz
El autor y Exa Ingeniería ®no están afiliados a ningún fabricante.
Se han realizado todos los esfuerzos posibles para proveer una información veraz y completa. Sin embargo,
el autore no se hace responsables de omisiones, uso al que se destine la información o por infracciones a patentes u otros derechos de terceros que resulten.
Derechos Reservados ©por el autor 2002. Derechos mundiales reservados. Ninguna parte de esta publicación puede ser reproducida o almacenada en ningún medio de retransmisión, fotocopiado o reproducción de
ningún tipo, incluyendo pero no limitándose a fotocopia, fotografía, fax, almacenamiento magnético u otro registro, sin permiso expreso del autor y de la editorial.
Compuesto totalmente en computadora por:
Exa Ingeniería SA de CV ®
Bajío 287-101
Col. Roma México, D.F.
5564-10-11 5564-02-68 FAX 5264-61-08
ISBN 970-91050-0-0
SEP 10137/91
Registrado ante la SEP en la propiedad intelectual del autor
Impreso y hecho en México.
1era edición junio 2002.
Contenido
Contenido · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · i
Introducción · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · xiii
Capítulo 1 · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 1
Interpretando el Contenido de la Memoria . . . . . . . . . . . . . . 5
Registros· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 9
Forma de Usar los Registros. . . . . . . . . . . . . . . . . . . . . 11
Banderas de Estado . . . . . . . . . . . . . . . . . . . . . . . . . 15
Memoria de Acceso Aleatorio · · · · · · · · · · · · · · · · · · · 24
Puntos Importantes del Capítulo · · · · · · · · · · · · · · · · · · 32
Capítulo 2 · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 33
.Direccionamiento Implícito . . . . . . . . . . . . . . . . . . . . 41
Direccionamiento directo a memoria · · · · · · · · · · · · · · · · 42
Direccionamiento Indirecto · · · · · · · · · · · · · · · · · · · · 42
Intérpretes · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 46
Compiladores· · · · · · · · · · · · · · · · · · · · · · · · · · · · 47
Puntos Importantes del Capítulo · · · · · · · · · · · · · · · · · · 50
3. Sistemas Numéricos · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 53
i
Ensamblador
Contando en el Sistema Binario· · · · · · · · · · · · · · · · · · · 58
Conversión de Sistema Decimal a Binario · · · · · · · · · · · · · 59
Suma y Resta en Sistema Binario · · · · · · · · · · · · · · · · · · 60
Multiplicación y División Binaria . . . . . . . . . . . . . . . . . 60
Complementos · · · · · · · · · · · · · · · · · · · · · · · · · · · 66
Puntos Importantes del Capítulo · · · · · · · · · · · · · · · · · · 71
Capítulo 4 · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 73
Las Reglas del Juego · · · · · · · · · · · · · · · · · · · · · · · · 74
Los Caracteres · · · · · · · · · · · · · · · · · · · · · · · · · · · 75
Los Separadores · · · · · · · · · · · · · · · · · · · · · · · · · · 76
Los Identificadores · · · · · · · · · · · · · · · · · · · · · · · · · 76
Palabras Reservadas · · · · · · · · · · · · · · · · · · · · · · · · 76
Comandos · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 79
Las Instrucciones del Ensamblador· · · · · · · · · · · · · · · · · 81
Capítulo 5 · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 87
Respuesta de la UPC a una Interrupción· · · · · · · · · · · · · · · 90
Código de Selección de un Dispositivo que Interrumpe · · · · · · · 92
Inmediata · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 97
Directo · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 98
Base · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 98
Indice · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 99
Indice y base · · · · · · · · · · · · · · · · · · · · · · · · · · · 100
Base y Desplazamiento (Directo o Inmediato)· · · · · · · · · · · 101
Indice y Desplazamiento (Directo o Inmediato) · · · · · · · · · · 101
ii
Introducción
Base, Indice y Desplazamiento (Directo o Inmediato) · · · · · · · 101
Número de Bits en una Instrucción. . . . . . . . . . . . . . . . . 102
Puntos Importantes del Capítulo . . . . . . . . . . . . . . . . . . 103
Capítulo 6 · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 105
Y Lógico (AND) · · · · · · · · · · · · · · · · · · · · · · · · · 114
O Lógico (OR) · · · · · · · · · · · · · · · · · · · · · · · · · · 115
No Lógico · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 115
O Exclusivo Lógico (XOR) · · · · · · · · · · · · · · · · · · · · 115
Prueba Lógica (TEST) · · · · · · · · · · · · · · · · · · · · · · 116
Las Banderas · · · · · · · · · · · · · · · · · · · · · · · · · · · 116
Complemento a 2 (NEG) · · · · · · · · · · · · · · · · · · · · · 116
La instrucción OUT · · · · · · · · · · · · · · · · · · · · · · · · 117
La instrucción IN · · · · · · · · · · · · · · · · · · · · · · · · · 117
Capítulo 7 · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 121
Corrimientos y Rotaciones · · · · · · · · · · · · · · · · · · · · 128
Corrimientos Lógicos (Rotaciones)· · · · · · · · · · · · · · · · 129
Corrimientos Aritméticos · · · · · · · · · · · · · · · · · · · · · 130
Operaciones Aritméticas · · · · · · · · · · · · · · · · · · · · · 131
Suma y Resta (ADD y SUB) · · · · · · · · · · · · · · · · · · · · 131
Decrementos · · · · · · · · · · · · · · · · · · · · · · · · · · · 132
Multiplicación y División· · · · · · · · · · · · · · · · · · · · · 132
Capítulo 8 · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 141
Manejo de la Pila · · · · · · · · · · · · · · · · · · · · · · · · · 142
Guardando las Banderas · · · · · · · · · · · · · · · · · · · · · 150
iii
Ensamblador
La Instrucción REP · · · · · · · · · · · · · · · · · · · · · · · · 152
La Dirección de la Operación (CLD y STD) · · · · · · · · · · · · 152
Mover Cadenas (MOVSB y MOVSW) · · · · · · · · · · · · · · 152
REPE ó REPZ y REPNE ó REPNZ · · · · · · · · · · · · · · · · 154
Compara Cadenas (CMPSB y CMPSW) · · · · · · · · · · · · · 154
Busca Carácter (SCASB y SCASW) · · · · · · · · · · · · · · · 155
LODSB y LODSW · · · · · · · · · · · · · · · · · · · · · · · · 156
STOSB y STOSW · · · · · · · · · · · · · · · · · · · · · · · · 156
Capítulo 9 · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 163
El vídeo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
Ventanas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
Pantallas de graficación . . . . . . . . . . . . . . . . . . . . . . 168
Problemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
Bibliografía . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
Capítulo 10 · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 183
Manejadores de Archivos · · · · · · · · · · · · · · · · · · · · · 197
Capítulo 11 · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 207
La Asignación de Memoria . . . . . . . . . . . . . . . . . . . . 207
Transfiriendo el Control a Ensamblador · · · · · · · · · · · · · · 208
Pasando Argumentos · · · · · · · · · · · · · · · · · · · · · · · 208
Pascal y Ensamblador· · · · · · · · · · · · · · · · · · · · · · · 208
C y Ensamblador · · · · · · · · · · · · · · · · · · · · · · · · · 212
Ensamblador Residente · · · · · · · · · · · · · · · · · · · · · · 214
Apéndice A. · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 219
iv
Introducción
Las instrucciones del 80x86 . . . . . . . . . . . . . . . . . . . . 219
Errores comunes al ensamblar un programa . . . . . . . . . . . . 234
Apendice B · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 235
Interrupciones . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
Índice · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · 239
v
Introducción
¿Por qué usar ensamblador?
Las razones para APRENDER ensamblador no son las mismas, en general, que las razones para USAR ensamblador.
Una de las malas razones para querer usar ensamblador es porque se
piensa que ejecutará mucho más rápido que una secuencia en particular
de un lenguaje de propósito general como Pascal, Fortran, C o hasta BASIC compilado. Aunque esto en general es verdadero, el tiempo y
recursos invertidos en el desarrollo pueden al final no valer la pena con
respecto a la ganacia de velocidad obtenida.
Por otro lado, los lenguajes de alto nivel tienden a aislar al usuario de la
máquina, en lo que radica su fuerza y su debilidad. La gran mayoría de los
lenguajes moderno de alto nivel proveen mecanismos para escribir
secuencias en ensamblador, aunque algunos de forma torpe (PEEK y
POKE de BASIC). Esto crea complicaciones en la implementación y en
el seguimiento y mantenimiento de programas elaborados de esta forma.
Algunas veces el sistema o lenguaje hace muy poco por nosotros; por
ejemplo, con el adaptador asíncrono, el sistema provee mecanismos que
quedan cortos de nuestra espectativa y deben ser irremediablemente
mejorados o pagar las consecuencias en una comunicación de alto nivel
(redes, interfaces, etc.).
Otras veces el sistema o lenguaje hace demasiado por nosotros; por
ejemplo en el control de las teclas que conforman un teclado, donde
previamente se han definido todas las secuencias que se generan al
presionar una tecla.
vii
Ensamblador
Otras más, se hace el trabajo exacto pero de una forma ineficiente no
aceptable a ciertas aplicaciones, como en el caso del manejador de la
pantalla que normalmente usan los canales oficiales para comunicarse
con la electrónica que controla los dispositivos externos.
Es en estas ocaciones donde el ensamblador actúa con su mayor
fortaleza: el control total de todos los elementos que conforman el
sistema. Una parte crítica de un sistema es la más suceptible de
optimizarse vía programación en ensamblador. No deseamos hacer esto
en términos de eficiencia de programación si se puede evitar con algún
truco del lenguaje de alto nivel o alguna otra argucia; pero existen
numerosos casos en los que se cae constantemente en la necesidad de
hacerlo.La razón por la cual el ensamblador es entonces mejor que un
lenguaje de alto nivel, es la misma por la que Pascal es mucho mejor para
escribir un programa de nómina que Ensamblador: es mucho más fácil de
mantener y programar. Debemos dejar que los lenguajes de alto nivel
hagan lo que saben hacer mejor y reconocer las ocaciones en las que
debemos ensuciarnos las manos con ensamblador.
Las razones para aprenderlo caen en la categoría de una formación
completa e integral en las áreas de cómputo de forma tal que se pueda
decidir como jefe de proyecto o programador, en qué casos es mejor
utilizar una herramienta y en qué otros utilizar otra. Como profesionista,
se debe ser capaz de identificar una buena codificación de una mala en
ensamblador y poder sugerir caminos alternativos para las
implementaciones de una solución genérica apropiada al problema que
se trata de resolver.
Dividimos el aprendizaje del ensablador en fases o pasos que sugerimos
seguir al pie de la letra:
· Fase 1 Conocer las herramientas de trabajo para crear
aplicaciones en ensamblador.
· Fase 2 Conocer la arquitectura de la máquina en la que se pretende
realizar la programación.
· Fase 3 Conocer las instrucciones del lenguaje ensamblador de la
máquina específica en la que se aplicará.
· Fase 4 Conjuntar todos los elementos anteriores en programas de
aplicación que realicen cosas prácticas o útiles desde nuestro
punto de vista.
viii
Introducción
· Fase 5 Actualizarse constantemente.
En todo lo anterior aplicamos una palabra clave: Selección. Hay que ser
selectivo en lo que se aprende y tener la capacidad de juzgar de antemano
qué nos puede servir o no antes de emplear el esfuerzo y tiempo en
aprenderlo aunque siempre se debe tener en cuenta que las necesidades,
tecnologías y tendencias cambian constantemente el la computación, por
lo menos a lo que el presente y futuro cercano nos concierne.
Trataremos de cumplir a lo largo de este libro la palabra selectividad y
de llenar todos los objetivos planteados en esta introducción.
Emiliano Llano Díaz
Septiembre 1995
ix
Capítulo 1
1.1 Componentes Básicos de un Sistema
Si examinamos las microcomputadoras actuales, vemos que aunque hay
diferencias en el tamaño físico de los componentes, de los sistemas y entre dist intos ti pos de computadoras (mini computadoras,
macrocomputadoras y computadoras personales o microcomputadoras),
la arquitectura empleada en todos los casos es muy similar. Esto se debe
al hecho de que las computadoras usan lógica binaria (de unos y ceros) y
emplean el sistema binario en casi todos sus componentes, lo que exige
un cierto tipo de estrategia para resolver los problemas derivados de esta
selección.
Figura 1.1 Los componenetes de una computadora.
1-1
Ensamblador
Como se muestra en la figura 1.1, tenemos cinco partes principales de
un sistema de cómputo que se encuentran de una forma u otra en todo
sistema actual:
· 1. Unidad de Proceso Central (UPC o CPU en inglés). Que
forma el cerebro de la computadora y, en general, controla todo el
funcionamiento de los demás componentes.
· 2. Memoria. Dispositivo de almacenaje electrónico donde se
guardan las instrucciones y datos necesarios para el
funcionamiento del sistema.
· 3. Dispositivos de Entrada y Salida (E/S o I/O). Son el eslabón
entre la máquina y el humano. Varían en complejidad y tipo de
acuerdo a los requerimientos de procesamiento.
· 4. Interfases de Entrada/Salida. Son los dispositivos
intermedios entre la UPC y los dispositivos de E/S y proveen los
controles para que se puedan entender los dispositivos externos de
E/S y la UPC.
· 5. Un programa. Controla todo lo anterior y da la secuencia de
eventos que permiten que un sistema funcione como tal y además
pueda resolver problemas de procesamiento genérico para lo cual
se construyó el sistema en primer lugar.
Las computadoras digitales usan la lógica digital para cumplir con
cierta meta de procesamiento. Esta lógica digital o lógica binaria como
también se le conoce, se basa en el hecho de que ciertos circuitos
electrónicos pueden estar conduciendo electricidad o no (prendidos o
apagados), estado que se determina por las características de operación.
Esto nos da pie a definir dos estados o condiciones bien diferenciados,
mutuamente excluyentes y opuestos.
Si contamos con un sólo circuito digital que nos indique uno de los dos
posibles estados, se dice de este circuito contiene un dígito binario (BInary digiT) o BIT de información. Este dígito se puede designar como
prendido o apagado o más comunmente 1 ó 0 que corresponden a los
únicos dígitos utilizados dentro de los sistemas actuales de computadoras
digitales.
Para sernos más utiles, los bits se agrupan en palabras que contienen N
número de bits de acuerdo al fabricante, modelo de computadora u otros
factores, aunque todos coinciden en que son potencias de 2. La longitud
1-2
Componentes Básicos de un
Sistema
más común de palabra en las microcomputadoras es de 8 bits que se
nombra byte, de 16 bits, de 32 ó de 64 bits.
Quizá el único atributo que diferencia a las computadoras de otras
máquinas es que se les puede decir qué deben hacer. Esto se logra con
palabras que contienen patrones de bits que son significativos para la
computadora. Este significado preciso se lo da el fabricante de
computadoras y así, un patrón puede significar suma dos números
mientras que otro ligeramente distinto significará quizá guarda un
resultado en una localidad específica.
El grupo de patrones definido por el fabricante forma lo que se llama el
juego de instrucciones. La variedad de operaciones distintas que
contiene este juego de instrucciones obedece a criterios de diseño del
circuito. En general todo juego de instrucciones cuenta con operaciones
aritméticas, de manipulación de bits, de comparaciones, de entrada y
salida, etc. mismas que analizaremos en detalle para un circuito
específico en los capítulos siguientes.
Todos los datos, tanto de instrucciones de programa como datos, son
almacenados en una serie de circuitos conocidos como memorias pues
tienen la particularidad de recordar la información contenida en ellos.
Las memorias son organizadas como casilleros únicos en los que se
guarda la información. Cada casillero o localidad de memoria tiene un
identificador que lo hace distinto a cualquier otro casillero para
propósitos de identificación y de almacenamiento de información. A este
identificador se le llama dirección y sin entrar en más detalles hasta la
siguiente sección baste decir que la dirección es única e irrepetible para
evitar confusiones y que la capacidad del casillero forma la palabra de la
computadora (número de bits que es posible almacenar en la memoria).
1.2 La Unidad de Proceso Central
Toda computadora tiene una especie de circuito de control genérico o
UPC (central processing unit o CPU), que forma el “cerebro” de la
máquina computadora. La UPC está formada por diferentes partes
interconectadas entre sí en forma tal que permite ciertas operaciones
lógicas. Las computadoras de hace 10 años requerían de un chasís
bastante grande para albergar los componentes de la UPC. Varias tarjetas
bastante grandes que contenían cientos de circuitos interconectadas por
cables del tamaño de una manguera no era una vista extraña. Hoy en día,
la UPC está contenida en unos pocos circuitos de alta integración o en el
1-3
Ensamblador
caso de las microcomputadoras en uno solo. La microcomputadora, junto
con unos cuantos circuitos externos, forman una computadora funcional.
Los componentes del sistema de computación deben de interconectarse
de alguna forma. La manera en que se realicen las conexiones y cómo se
haga la comunicación, afecta las características de desempeño del
sistema profundamente.
En las primeras computadoras y en algunas de las actuales, la UPC se
interconectaba con cada uno de los dispositivos externos a la UPC por
medio de un cable separado. Este método tiene la desventaja de que
muchos cables (llamados buses) distintos deben ser controlados y se
requiere de una cantidad considerable de circuitos.
Para realizar la interconexión de forma más económica y para
estandarizar la lógica de interfase necesaria, una técnica muy popular
consiste en interconectar a todos los sistemas externos a la UPC por
medio de un sólo juego de cables o bus de interconexión. El bus consiste
en los cables necesarios para encontrar a los distintos componentes,
controlarlos y transferir datos de y hacia ellos.
En la figura 1.2 vemos el esquema de una unidad de procesamiento central. Se forma por la unidad aritmética y lógica (UAL o ALU, arithmetic
logic unit) encargada de las operaciones lógicas y aritméticas, la unidad
de control encargada de la coordinación general, una serie de registros
donde se almacena información requerida por la UPC y la ALU de forma
provisional y un bus de datos interno que interconecta a todas las
unidades.
1-4
Componentes Básicos de un
Sistema
Figura 1.2 Esquema de una Unidad de Proceso Central.
La función principal de la UPC será la de interpretar la información que
reside en la memoria principal como instrucciones o datos y actuar de
acuerdo a un plan determinado por el código de la instrucción generando
las operaciones necesarias de acuerdo a ese plan. Ningún programa
puede ser ejecutado por la UPC si no reside antes, en forma parcial o total, en la memoria principal (descrita en la sección siguiente).
1.2.1 Interpretando el Contenido de la Memoria
Los datos almacenados en la memoria pueden ser interpretados como:
· 1. Datos binarios puros
· 2. Datos codificados en binario
· 3. Un código de carácter
· 4. Un código de instrucción
Estas cuatro categorías de interpretar datos pueden dividirse en dos más
genéricas: datos e instrucciones.
Los datos binarios puros, los datos codificados en binario y los
caracteres codificados tienen una cosa en común: son datos. El contenido
de las palabras almacenadas en memoria puede ser usado por sí solo para
1-5
Ensamblador
operaciones o combinado con otras palabras de memoria en formas
dirigidas por un programa.
1.2.1.1 Datos binarios puros
Consideremos primero a los datos binarios puros que no están sujetos a
ninguna interpretación. Es importante resaltar que podemos representar
este tipo de datos en papel como un número binario, octal o hexadecimal
(por sencillez) sin que tenga ningún impacto en el dato de la palabra. Por
ejemplo, una palabra arbitraria de 16 bits, en la que es posible representar
216 combinaciones, puede ser:
11011011100010112=1556138=DB8B16
Se puede observar que la notación en hexadecimal, además de ser más
compacta, es mucho más fácil de apuntar y recordar, también evita
confusiones, como veremos en el siguiente capítulo, por lo que la base 16
o hexadecimal es la preferida entre la gente dedicada a diseño lógico o
programación a nivel máquina.
1.2.1.2 Datos Codificados en Binario
El contenido de una palabra de memoria, interpretada como datos
binarios puros, puede existir por sí solo o ser válido sólo cuando se
combina con otras palabras para formar un código o un número mayor
que sea utilizado con fines prácticos. No hay razón por la cual un dato
binario no pueda formarse, por ejemplo con 2 o más palabras; más aún,
las palabras pueden no ocupar localidades continuas de memoria, aunque
se prefiere por fines prácticos que así sea.
La palabra puede también estar codificada de acuerdo a un código
establecido de antemano como puede ser exceso 3, BCD, números con
signo, etc. (todos ellos analizados en el capítulo 2).
1.2.1.3 Códigos de Caracteres
Una computadora no sería muy práctica si tuviésemos que meter cada
uno de los datos como largas secuencias de unos y ceros o si las
respuestas consistieran de números o letras en una representación
binaria. Debe ser posible para la computadora el manejar todo tipo de
texto y otra información no numérica.
1-6
Componentes Básicos de un
Sistema
Para que el manejo de texto sea completo debe de incluir al menos las 26
letras en minúsculas y mayúsculas, los números y una serie de signos
especiales que usamos diariamente en nuestra comunicación como son el
punto, la coma, etc. En conjunto, los caracteres más usados suman
alrededor de 87. Una palabra de 7 bits es suficiente para su representación
pero es universalmente aceptada la codificación utilizando 8 bits (ver
capítulo 2).
En algunos sistemas con longitud de palabra de 8 bits, se usa el código
ASCII de 7 bits y el bit libre se utiliza para verificar errores en los datos
por medio de un procedimiento llamado paridad. En otros sistemas se
usa un código de 8 bits ASCII y se agrega a la memoria un bit más que
sirve para la comprobación de paridad. A este bit se le conoce como bit de
paridad.
La paridad consiste en una verificación sencilla de errores en la que se
agrega un bit al código. La paridad puede ser:
· Par
· Impar
El bit de paridad se cambia a 1 ó a 0 para que el número de bits 1 de una
palabra sea siempre par o impar, según el tipo de paridad que se haya
escogido. Si el tipo de paridad que se escoge es par, el bit de paridad se
limpia o fija para que el número total de unos de la palabra sea siempre
par. Para clarificar el punto incluímos un ejemplo:
Palabra a proteger con un bit de paridad: 1010101
Como el número de 1 de la palabra ya es par, el bit de paridad será en este
caso 0.
Muchos esquemas muy elaborados son usados para verificar la
consistencia de los datos y asegurarse que no contengan errores, aún más,
existen algoritmos para determinar dónde están los errores y tratar de
corregirlos. Estas técnicas de detección de errores no tienen que ver nada
en particular con la arquitectura de las computadoras o la programación
en ensamblador y, por lo tanto, no se discuten aquí. Para mayor
información consulte la bibliografía, en es pe cial el libro
Telecomunicaciones y Teleproceso.
1-7
Ensamblador
1.2.1.4 Código de Instrucciones
Las palabras de memoria hasta ahora analizadas son conformadas por
información de un tipo u otro pero el contenido también puede ser
interpretado como un código solicitando una operación que se requiere
de la computadora.
Considere el sencillo ejemplo de una suma binaria en donde solicitamos
que la palabra almacenada en cierta área de memoria sea sumada a otra
contenida en otra área distinta y que el resultado sea almacenado en una
tercera área. Los pasos necesarios para lograr esta suma serían:
· 1. Identificar la dirección de la primera palabra a sumar.
· 2. Transferir el contenido de la dirección a los registros
provisionales de almacenamiento dentro de lo que forma la
Unidad de Procesamiento Central.
· 3. Identificar la dirección de la segunda palabra a sumar.
· 4. Sumar el contenido de esta última palabra a la que se encontraba
en los registros de la UPC, obtenida en el paso 2, por medio de la
Unidad Lógica y Aritmética (UAL).
· 5. Identificar la dirección de memoria donde el resultado se
almacenará.
· 6. Transferir la suma almacenada en el registro adecuado de la
UPC a la dirección obtenida en el paso 5.
Ilustrar el concepto de un programa de computadora con estos seis
sencillos pasos es sólo el comienzo. Tendremos que responder a lo largo
del libro a las preguntas: ¿Cómo realiza la computadora las operaciones
requeridas por los códigos de instrucciones que conforman el programa?
¿Qué es lo que la computadora demanda de la lógica externa para poder
realizar su trabajo? ¿Cómo se escribe un programa de computadora?
1-8
Componentes Básicos de un
Sistema
1.3 Componentes de la Unidad de Proceso
Central
Si consideramos el sencillo programa de suma descrito en la sección
1.2, podemos examinar algunos de los requerimientos internos a la UPC
para poder satisfacer lo que se le solicita por medio del código de
instrucción SUMA.
1.3.1 Registros
Es necesario un almacenamiento provisional (o varios) donde guardar
los números para trabajar con ellos, así como un registro donde
almacenar el resultado de las operaciones que llamamos acumulador y
abreviaremos con A. En el acumulador podemos almacenar información
tanto antes de operar con ella, como el resultado de las operaciones con
los datos binarios. Por el momento y para simplificar las cosas
consideremos que tenemos sólo un acumulador y que éste es de 8 bits.
Se requiere de este tipo de almacenamiento provisional por dos razones:
· 1. El contenido de la memoria sólo se puede leer o escribir, no es
posible operar sobre los registros de la memoria principal debido
al diseño de las computadoras actuales.
· 2. Es posible tomar los datos directamente de memoria, operar
sobre ellos y regresar el resultado otra vez a la memoria principal,
pero una cantidad adicional importante e innecesaria de circuitos
lógicos serían requeridos para esto, complicando el diseño,
subiendo el costo y disminuyendo la rapidez de las operaciones
como será aparente según avancemos en el libro.
Para poder accesar una palabra de memoria, tanto para leer como para
escribir su contenido, necesitamos tener su dirección y ésta se almacena
en un registro especial que llamaremos registro contador de datos (data
counter register) que abreviaremos con sus siglas en inglés: DC.
El tamaño de este registro depende de la cantidad de memoria principal
que la computadora pueda accesar directamente. En la figura 1.3
suponemos un registro contador de datos de 16 bits que nos da una
capacidad de memoria de 65,536 palabras o 64K bytes (si la palabra es de
1 byte).
1-9
Ensamblador
Una computadora puede constar de más de un registro de este tipo pero
una vez más, por simplicidad y por el momento, sólo consideraremos
uno.
Necesitamos también un registro donde la instrucción sea contenida de
forma que la UPC pueda hacer referencia a ese registro para ver de que
instrucción, de las muchas de una computadora, se trata. A este tipo de
registro se le denomina registro de instrucción (instruction register) que
abreviaremos con I.
La dirección de la memoria de la que traeremos la siguiente instrucción
(es nuestro ejemplo la de SUMA) es almacenada en el llamado contador
de programa (program counter) que abreviaremos con sus siglas en
inglés: PC.
El contador de programa es análogo al de datos pero se asume que uno
contiene la dirección de los datos en memoria y el otro la dirección de la
siguiente instrucción en memoria. Para explicar un poco más este punto:
si la operación SUMA consta de un código de operación que sólo ocupa
una palabra y le deben seguir dos palabras más de memoria que
contengan los datos a sumar, el apuntador a la siguiente instrucción debe
actualizarse sumando tres localidades para que salte los datos y apunte
efectivamente a la siguiente instrucción; de forma similar el contador de
datos se actualizará sumando 1 al registro contador de programa y luego 1
más para poder localizar el siguiente dato.
Como se puede ver en la explicación anterior, existe una diferencia conceptual grande entre el contador de programa y el contador de datos, pues
mientras el contador de programa debe de incrementarse continuamente
para encontrar la siguiente instrucción usando para ello la longitud en
palabras de que consta la instrucción anterior, el contador de datos puede
o no incrementarse de acuerdo a si la instrucción requiere de datos o no.
Debe, por lo tanto, permitírsele gran flexibilidad al programador para
poder modificar el registro contador de datos, mientras que, usualmente,
al registro contador de programas no se tiene acceso más que de forma
indirecta.
1-10
Componentes Básicos de un
Sistema
Figura 1.3 Registros típicos de una UPC.
1.3.2 Forma de Usar los Registros
Para poder comprender en su totalidad el uso de los distintos registro
que forman parte de la UPC, realizaremos paso a paso una suma como se
describe en el algoritmo de la sección 1.1.2.4.
Para realizar la suma asumiremos que la UPC puede interpretar 3
instrucciones sencillas: CARGA, SUMA y GUARDA. Cada una de estas
instrucciones está definida de antemano rígidamente y se deben seguir
los pasos que el fabricante explica. Supondremos que las instrucciones
realizan estos pasos:
SUMA: Toma el contenido de la dirección formada por el contenido de
las siguientes dos palabras y súmala a la que está en el Acumulador
dejando el resultado en el Acumulador. Inventamos el código de
instrucción 30 hexadecimal que al ser encontrado por la UPC en una
localidad de memoria sea interpretado como la instrucción SUMA.
CARGA: Toma el contenido de la dirección formada por el contenido
de las siguientes dos palabras y guárdala en el Acumulador. Inventamos
el código de instrucción 40 hexadecimal que al ser encontrado por la
1-11
Ensamblador
UPC en una localidad de memoria sea interpretado como la instrucción
CARGA.
GUARDA: Toma el contenido del Acumulador y guardalo en la
dirección de memoria formada por el contenido de las siguientes dos
palabras. Inventamos el código de instrucción 50 hexadecimal que al ser
encontrado por la UPC en una localidad de memoria sea interpretado
como la instrucción GUARDA.
Recuerde que el Contador de Programa debe de incrementarse
continuamente para apuntar así a la siguiente dirección de memoria
donde se almacena una instrucción o dato.
Nuestro programa de suma sería entonces (las direcciones en hexadecimal):
CARGA
SUMA
GUARDA
E0E0
E0E1
E0E2
Notemos que aquí se introduce un nuevo concepto llamado mnemónico
que nos auxilia en recordar qué realiza cada código de instrucciones de la
computadora en cuestión, pues en la palabra de memoria no se guarda las
letras de la instrucción sino un simple código que representa a la
instrucción:
40 E0 E0
30 E0 E1
50 E0 E2
Recordemos a su vez, que cada número en hexadecimal tiene su
equivalente en binario y a su vez, cada número en binario se resume en si
hay corriente o no en cada uno de los circuitos que conforman el registro
en cuestión, ya sea de memoria o interno a la UPC.
Coloquemos estos códigos en un mapa de memoria en una localidad
arbitraria, por ejemplo en la dirección C000 hexadecimal (suponemos
que tenemos una memoria total de 64K bytes por lo que bastan con 16
líneas de dirección, 16 bits binarios o 4 hexadecimales para conocer su
localización). En la figura 1.4 colocamos de alguna forma nuestro
programa en la memoria.
1-12
Componentes Básicos de un
Sistema
Figura 1.4 El manejo de los registros en un programa.
Podemos seguir paso a paso el contenido de los registros hasta ahora
analizados en la siguiente secuencia:
1-13
Ensamblador
· 1. De una forma por ahora desconocida, el registro contador de
programa o PC contiene la dirección de la primera instrucción:
C0 00.
· 2. La UPC carga el contenido de la palabra de memoria apuntada
por el PC al registro de instrucciones o I, en este caso el número 40
hexadecimal (CARGA), e incrementa en uno el contenido del
registro PC.
· 3. El código 40 que aparece en el registro de instrucciones hace
que la lógica de la UPC realice dos pasos. Primero, el contenido de
la palabra de memoria direccionado por el registro PC es traído de
la memoria pero se almacena en el bit más significativo del
registro contador de datos (DC). La Unidad de Procesamiento
Central incrementa posteriormente el contenido del PC en uno. El
segundo paso consiste en traer el contenido de la palabra de
memoria apuntado por PC y guardado en el bit menos
significativo del registro DC. Ahora, se utiliza el contenido del
registro DC como apuntador para traer la palabra de memoria
indicada al registro acumulador (A). Una vez más la UPC
incrementa el contenido del registro PC en uno. Con esto la
instrucción número 1 (CARGA) se completa.
· 4. Una vez terminada la instrucción anterior la UPC trae el
contenido de la palabra de memoria direccionado por el registro
PC e incrementa en uno este registro. Puesto que no le hemos dado
ninguna otra instrucción específica a la UPC, el contenido de la
palabra que se trae de la memoria se almacena en el registro I para
ser interpretado como una instrucción.
· 5. Algo muy similar al paso 3 sucede en esta etapa. El registro DC
contiene ahora la dirección de la palabra de memoria a suma (E0
E1) y la UPC pasa el contenido de esta palabra y el del acumulador
a la UAL para su suma, el resultado se almacena en el acumulador
y el contador de programa se ha incrementado en 2 para quedar
apuntando a la siguiente instrucción.
· 6. La última instrucción de nuestro pequeño programa es algo
muy similar a la instrucción 1 pero en lugar de traer los datos de la
dirección apuntada por el registro DC, se almacena en esa
dirección.
1-14
Componentes Básicos de un
Sistema
1.3.3 Banderas de Estado
Muchas veces es necesario saber en detalle qué es lo que realiza la UAL
para poder decidir si la operación que se ordenó fue completada con éxito
o no o para tomar ciertas medidas con el resultado que nos entrega la
UAL.
A los registros especializados en decirnos el resultado de las
operaciones se les llama registros de estado (estatus register) o banderas
y consisten en una memoria de un bit que nos indica varias situaciones
dentro de la UAL.
Algunas de estas situaciones en las que las banderas son modificadas
que nos son de interés se resumen en los siguientes puntos:
· 1. El resultado fue cero.
· 2. Hay un acarreo.
· 3. Hay signo en la operación.
· 4. Hubo un desbordamiento en la operación.
· 5. Indicador de paridad.
· 6. Si la operación a realizar es suma o resta.
· 7. Acarreo a la mitad de los bits que forman la palabra.
Aquellas instrucciones que afectan o no a las banderas de estado son
cuidadosamente seleccionadas por el fabricante y varían de computadora
a computadora.
1.4 Ejecución de Instrucciones
Como en casi toda la lógica digital, la UPC es controlada por un reloj.
Este reloj puede ser tan sencillo como una sola señal o una combinación
de varias señales de reloj requeridas por la UPC.
La ejecución de una instrucción puede ser dividida en dos partes
principales:
· Traer la instrucción (fetch en inglés)
· Ejecutar la instrucción (ejecute en inglés)
1-15
Ensamblador
Si recordamos nuestro ejemplo ante rior de un programa, cada
instrucción comienza con el código que se carga en el registro de
instrucción (I). A esta operación le llamamos traer la instrucción (fetch).
Durante la etapa donde se trae la instrucción, la UPC tiene que colocar
el contenido del registro del contador de programa (PC) en el bus de
direcciones junto con las señales necesarias para informar a la lógica exterior que se trata de una lectura a la palabra de memoria indicada por el
contenido del bus de direcciones. Por lo que a la lógica externa concierne,
esto se trata de una simple lectura.
Mientras la lógica externa responde, la UPC usa su lógica interna para
sumar 1 al contenido del registro PC. El contador de programas ahora
apunta a la siguiente localidad de memoria de la que el código de la
instrucción se trajo.
Una vez que el código de instrucción se encuentra en el registro de
instrucciones, dispara una serie de eventos controlados por la Unidad de
Control que constituyen la ejecución de la instrucción.
En su forma más sencilla, dos periodos de reloj son necesarios para
ejecutar la instrucción. Uno de ellos marca el tiempo para traer la
instrucción y el otro para ejecutarla.
Si consideramos las interconexiones necesarias para esto necesitaremos
hasta el momento por lo menos las siguientes:
· 1. Las líneas necesarias para la dirección de memoria. Por
simplicidad consideraremos un diseño en el que se puedan accesar
únicamente 65,535 localidades distintas de memoria ó 64K bytes
para lo que se requiere de 16 líneas. En conjunto forman el
llamado bus de direcciones.
· 2. Las líneas necesarias para leer o escribir información a la
memoria. Una vez más, por simplicidad, consideraremos un
diseño de 8 bits de longitud de palabra por lo que se requieren de 8
líneas de datos que en conjunto forman el llamado bus de datos.
· 3. La alimentación del circuito/circuitos que forma la UPC
formado como mínimo con una línea de voltaje y otra de tierra.
· 4. Las señales adecuadas para informar a la lógica externa que se
trata de una lectura o escritura a la memoria.
1-16
Componentes Básicos de un
Sistema
· 5. La línea del reloj.
Mostramos en la figura 1.5 el diagrama parcial y esquematizado de un
circuito de UPC.
Figura 1.5 Una microcomputadora con sus líneas externas.
Algunas veces se requieren de más líneas que las que físicamente
permite el empaque. Se utiliza entonces una técnica llamada
multiplexión en la que las mismas interconexiones externas sirven para
varios propósitos. Por ejemplo, si se requieren de 20 líneas de dirección y
sólo tenemos disponibles 14 conexiones, podemos mandar en el
momento t la dirección parte baja por las 14 conexiones disponibles y las
restantes 6 en el momento t+1 por las mismas interconexiones.
1.5 La Unidad de Control
La unidad de control se puede representar funcionalmente como una
caja negra tal como en la figura 1.1, pero en realidad consiste de un gran
número de elementos lógicos que pueden ser activados por una secuencia
de señales habilitadoras. La IEEE define a la unidad de control como la
parte de una computadora que lee las instrucciones en secuencia, vigila
su aplicación y genera los comandos y señales adecuadas para la Unidad
Aritmética y Lógica (UAL) y otros circuitos para su correcta aplicación.
1-17
Ensamblador
La unidad de control puede utilizar otros elementos contenidos en la
UPC. Por ejemplo, la UC puede utilizar el complementador de la Unidad
Aritmética y Lógica. Una señal única de la Unidad de Control activa al
complementador.
Sin embargo, el complementar el contenido de una memoria provisional, aunque útil, no nos sirve de mucho. Lo que sí tiene valía es
complementar el contenido del Acumulador y esto implica mover la
información al complementador y una vez realizada la operación,
regresar el resultado al Acumulador.
Complementar el contenido del Acumulador requiere entonces de 5
pasos:
· 1. Mover el contenido del Acumulador al Bus de Datos interno.
· 2. Mover el contenido del Bus de Datos al Complementador.
· 3. Activar la lógica del complementador.
· 4. Mover el contenido del Complementador al Bus de Datos
interno.
· 5. Mover el contenido del Bus de Datos al Acumulador.
A cada uno de estos 5 pasos se les llama microinstrucción. Cada
microinstrucción es habilitada por una señal de la Unidad de Control. Si
la Unidad de Control manda la combinación de señales adecuadas,
cualquier número de microinstrucciones pueden ser ejecutadas para responder a una macroinstrucción que es la respuesta aceptada de la
Unidad de Procesamiento Central a un código de una instrucción de
lenguaje de máquina, muy similares a las ya descritas (SUMA, CARGA y
GUARDA).
Para complementar el contenido del Acumulador en nuestro ejemplo
previo, la Unidad de Control requirió de 5 códigos binarios que
dispararon las señales de control apropiadas. La secuencia de códigos
binarios que residen en la Unidad de Con trol son llamados
microprogramas y generar esta secuencia de códigos binarios se le
conoce como microprogramación.
Existe un paralelo muy cercano entre lo que es microprogramar y la
programación en lenguaje de ensamblador. Un microprograma se guarda
como una serie de códigos binarios dentro de la Unidad de Control. Un
1-18
Componentes Básicos de un
Sistema
programa en ensamblador es guardado como una serie de códigos
binarios dentro de la memoria principal (RAM o ROM). Al programa en
ensamblador se le conoce con el nombre de macroprograma y a cada
macroinstrucción le corresponden uno o más microprogramas dentro de
la Unidad de Procesamiento Central que deben ser ejecutados para
generar la respuesta esperada.
Un microprograma guardado en la Unidad de Control tiene memoria
para datos, que consiste en los registros de la UPC, ademas de
almacenamiento interno a la misma Unidad de Con trol. Un
macroprograma también tiene una área de almacenamiento, ya sea provisional (RAM) o definitiva (ROM) como describimos en la siguiente
sección.
La complejidad de las operaciones asociadas con una macroinstrucción
es función direct a del tamaño del microprograma que la
macroinstrucción inicia. Pero existe el límite impuesto por el tamaño
físico que puede tener la Unidad de Control y esto se hace más patente en
las microcomputadoras, donde toda la UPC se encuentra contenida en un
solo circuito.
La Unidad de Control de cada computadora no es más que un
microprograma. Si se permite al usuario modificarlo se dice que la
computadora es microprogramable. Si la Unidad de Control se diseña
como parte del diseño lógico de la computadora y forma parte inalterable
de la Unidad de Procesamiento Central, entonces, la computadora no es
microprogramable.
1.6 Unidad Aritmética y Lógica
La Unidad Aritmética y Lógica (UAL, en inglés ALU) que es parte de
la Unidad de Procesamiento Central (UPC, Central Processing Unit
CPU) es la sección de la computadora que realiza las operaciones
matemáticas y lógicas en los datos de entrada procesados por el
computador. Esta sección de la máquina puede ser relativamente
pequeña consistiendo en quizá uno o dos circuitos de integración a gran
escala (LSI), formar parte de la propia UPC como en el caso de la
microcomputadora o ser una serie considerable de circuitos lógicos de
alta velocidad como en las macrocomputadoras o supercomputadoras.
No importando el tamaño y la complejidad de estos circuitos, las
máquinas pequeñas realizan generalmente las mismas operaciones
lógicas y aritméticas usando los mismos principios que en las grandes
1-19
Ensamblador
máquinas. Lo que cambia es la velocidad de las compuertas lógicas y los
circuitos utilizados; también, técnicas especiales son utilizadas para
realizar varias operaciones en paralelo.
Aunque muchas funciones pueden ser realizadas por los UAL de las
máquinas de hoy en día, las operaciones aritméticas básicas (suma, resta,
multiplicación y división) continúan siendo las operaciones más
utilizadas. Inclusive las especificaciones de una computadora nos dan
evidencia de la naturaleza fundamental de estas operaciones: en cada
máquina nueva se describen los tiempos requeridos para la suma y
multiplicación como características significativas.
Es importante resaltar que existe, como ya se explicó, otra parte de la
computadora llamada Unidad de Control que dirige las operaciones del
UAL. Todo lo que hace el UAL es sumar, restar, acarrear, etc. cuando se le
provee de la secuencia correcta de las señales de entrada. Depende del
elemento de control el proveer de estas señales así como es función de la
unidad de memoria proveer a los elementos aritméticos de la
información que usarán.
1-20
Componentes Básicos de un
Sistema
Figura 1.6 La Unidad Aritmética y Lógica esquematizada.
1.7 La Memoria
La memoria de la computadora no está concentrada en un sólo lugar; los
dispositivos de almacenaje están distribuidos en toda la máquina. En la
parte más interna encontramos a los registros de operación que son
registros utilizados por la unidad de control y la aritmética de la
computadora. Los cálculos se realizan con los datos que se toman de
estos registros, por ejemplo, la suma, multiplicación y corrimientos son
todos realizados en estos almacenamientos provisionales. El proceso actual de información se realiza en la localidad de estos registros.
Viendo hacia afuera, la siguiente categoría de registros de
almacenamiento que encontramos es llamada memoria de alta
velocidad, memoria interna o memoria principal. Esta sección de la
memoria de la computadora consiste en un conjunto de registros de
almacenamiento, cada uno de los cuales es identificado con una dirección
1-21
Ensamblador
(localización de cada registro de memoria) que permite a la unidad de
control, ya sea escribir o leer, de un registro en particular. Para que un
programa (conjunto de instrucciones) pueda ser ejecutado es necesario
que resida antes en esta memoria.
Es deseable que la velocidad de operación en esta sección de la memoria
de la computadora sea tan rápida como sea posible, puesto que la mayoría
de las transferencias de datos de y hacia la sección de procesamiento de la
información de la máquina será a través de la memoria principal; por esta
razón los dispositivos de almacenamiento con un tiempo de acceso
(tiempo que transcurre entre el momento que se solicita la información y
el momento en que el dispositivo tiene una respuesta) rápido son
generalmente elegidos para la memoria principal.
Desafortunadamente los dispositivos disponibles en el momento, que
son lo suficientemente rápidos para realizar esta función
satisfactoriamente, no poseen la capacidad de almacenamiento que
algunas veces es requerida ni un costo aceptable. Como resultado,
memoria adicional, llamada memoria auxiliar o memoria secundaria,
es agregada a la mayoría de las máquinas. Esta sección de la memoria de
la computadora se caracteriza por un bajo costo por dígito almacenado,
pero generalmente tiene una velocidad de operación mucho menor que
los registros de operación o que la memoria principal. Esta sección de la
memoria es algunas veces designada como memoria de respaldo, pues
su función es manejar cantidades de datos en exceso a las que pueden ser
almacenadas en la memoria interna.
Los dispositivos más externos y últimos de almacenamiento son usados
para introducir información a la computadora del “mundo externo” y
para guardar los resultados de la computadora para el usuario de la
computadora. Este medio de almacenaje consiste generalmente en
tarjetas perforadas (en desuso), cinta de papel perforada (en desuso),
cintas magnéticas o discos magnéticos y las salidas de la máquina
consisten generalmente en caracteres impresos ya sea en pantalla o en
papel.
Cada una de las divisiones de la memoria tiene ciertas características.
Por ejemplo la importancia de velocidad es muy alta en los registros de
operación. Estos registros generalmente deben realizar operaciones a
varias veces la velocidad de la memoria principal. La memoria principal
también requiere grandes velocidades de operación, pero como es
deseable almacenar grandes cantidades de datos (de 600,000 a 109 bits)
1-22
Componentes Básicos de un
Sistema
en esta sección de la memoria, un compromiso entre el costo y la
velocidad debe buscarse. El mismo tipo de compromiso se realiza en el
caso de la memoria auxiliar en el que se almacenan entre 360,000 y 1015
bits, siendo muy caro utilizar los mismos dispositivos que para la
memoria principal.
Es importante darse cuenta al considerar la velocidad de operación, que
antes de que una palabra puede ser leída, es necesario localizarla. El
tiempo para localizar y leer una palabra de memoria es llamado tiempo
de acceso. El procedimiento para localizar información puede ser
dividido en dos clases:
· Acceso Aleatorio
· Acceso Secuencial
Los dispositivos de almacenaje que tiene acceso aleatorio son aquellos
en los que la localización dentro del dispositivo puede ser seleccionada
arbitrariamente sin seguir ningún orden y el tiempo aproximado de
acceso es aproximadamente igual para cada una de las localidades de
memoria del dispositivo. Los registros internos de trabajo o la memoria
principal es un ejemplo de un dispositivo de almacenamiento de acceso
aleatorio.
Por otro lado los dispositivos de acceso secuencial necesitan ser
recorridos en orden para llegar a cierta localidad de memoria, por lo que
el tiempo de acceso varia de acuerdo a la localidad.
Otra forma de subdividir los dispositivos de almacenaje es de acuerdo a
si son dispositivos del tipo
· Dinámicos
· Estáticos
En los estáticos, la información, una vez grabada en su localidad, no es
olvidada o borrada por el tiempo en que el dispositivo tenga energía
eléctrica o la información no sea cambiada explícitamente. Como
contraparte, en un dispositivo dinámico, una vez guardada la
información en una localidad, ésta se olvida en un tiempo finito, por lo
que se debe recordar o “refrescar” constantemente.
1-23
Ensamblador
1.7.1 Memoria de Acceso Aleatorio
La memoria de una computadora se organiza en palabras de X longitud,
cada una de ella ocupa una localidad o dirección predeterminada dentro
de la memoria. Como se puede observar en la figura 1.7 una memoria
dada se divide generalmente en N palabras donde N es una potencia de 2.
Cada palabra tiene el mismo número de bits llamados la longitud de la
palabra.
Figura 1.7 La memoria vista como arreglo lineal.
La dirección de los números en la memoria corren consecutivamente
comenzando con la dirección cero hasta la máxima que la computadora
puede direccionar. Es importante entender la diferencia entre contenido
de la memoria y la dirección de la memoria. Una memoria es como un
gran gabinete que contiene tantos cajones como direcciones hay en
memoria. En cada cajón hay una palabra y la dirección de la palabra está
escrita en la parte externa del cajón. No removemos la palabra en una
dirección cuando la movemos; únicamente cambiamos el contenido de la
dirección cuando almacenamos o escribimos una nueva palabra.
Desde el punto de vista externo, una memoria principal de alta
velocidad es muy similar a una “caja negra” con un número de
localidades o direcciones en las que los datos pueden ser almacenados o
leídos. Cada dirección o localidad contiene un número fijo de bits
1-24
Componentes Básicos de un
Sistema
binarios, y a este número se le llama longitud de la palabra de la
memoria. Una memoria que contenga 4,096 localidades distintas de
memoria, cada una de ellas capaz de almacenar 16 bits binarios, es
llamada “memoria de 16 bits 4,096 palabras” o en el lenguaje vernáculo
de la computadora “memoria de 4K 16 bits”. Puesto que las memorias
generalmente vienen en potencias de 2, el sufijo K significa en este caso
1,024 y no 103, como en el caso decimal, pues se entiende que las 2N
localidades de memoria están disponibles. Por ejemplo una memora de
16 bits de 216 palabras es llamada “memoria de 64K 16 bits”.
Las memorias puede ser leídas (los datos pueden ser tomados de) o
escritas (los datos son almacenados en la memoria). Las memorias en las
que es posible leer y escribir son llamadas memorias de lectura y
escritura y erróneamente conocidas como RAM (Random Access Memory, memoria de acceso aleatorio) ya que las memorias de sólo lectura
(ver siguiente párrafo) también entran en esta clasificación. A las
memorias en las que solamente es posible leer y no permiten escritura son
llamadas memorias de sólo lectura o ROM (Read only memory).
Una palabra de información binaria puede ser almacenada en grupos,
por ejemplo 8 bits en unidades de memoria. Las entradas y salidas de
estas unidades se encuentran interconectadas de forma que el grupo de
bits binarios entre y salga de forma serial o secuencial (uno a la vez), o
sean movidos en paralelo (todo el grupo a la vez).
Cuando se trata de registros dentro de la UPC (CPU) los programadores
usualmente los representan como pequeñas cajas una junto a otra y
dentro de ellas un cero o uno lógico que representa su contenido.
1.7.2 Dirección
Incrementando el número de unidades de memoria de almacenamiento
podemos aumentar el número de bits disponibles. Pero un problema fundamental surge: ¿Cómo poder accesar a cada una de las localidades sin
confusión?
La solución es usar una forma de dirección. Podemos aplicar un único
grupo de dígitos binarios a una serie de líneas de selección y de esta forma
una localidad única queda disponible. En la figura 1.8 mostramos una
memoria de semiconductores estática típica arreglada de forma que sus
“celdas” puedan ser seleccionadas independientemente con un código
binario único llamado palabra de dirección.
1-25
Ensamblador
Figura 1.8 La memoria y sus conexiones extrenas.
Una vez que la celda ha sido localizada, un 0 o un 1 puede ser escrito a la
celda. Dentro del circuito las celdas se organizan en una matriz de x por y,
con la mitad de las líneas de dirección localizando las coordenadas de y.
Pero para nuestros propósitos es suficiente considerar a las celdas como
una larga cadena y a la dirección como un selector de alguna forma de la
celda correcta.
1.7.3 La Memoria de Sólo Lectura
La memoria de sólo lectura (ROM Read Only Memory) es un
dispositivo de memoria utilizado para guardar información que no
cambia. Hay una operación inicial en que la información se graba en la
memoria y posteriormente sólo es posible leer de ella y no sobre escribir
la información. Generalmente la información es puesta en su lugar por el
fabricante del dispositivo. Es deseable, sin embargo, en muchas
ocasiones poder grabar la información uno mismo en el dispositivo pues
el costo de fabricar unos pocos ROMs es prohibitivo. Existen en el
mercado una serie de circuitos que cumplen con esta característica y que
1-26
Componentes Básicos de un
Sistema
son programables: las memorias de sólo lectura programables o PROM
(pro gram ma ble read only mem ory)memoria de sólo lectura
programable. Estos circuitos permiten grabar la información una sola vez
y de ahí en adelante se comportan exactamente igual a un ROM. Si se
realizan circuitos experimentales y son constantes los cambios de diseño
de datos y programas en la etapa inicial o se requiere de la flexibilidad de
poder borrar de vez en cuando el contenido de la memoria ROM y
reprogramarla, se utilizan los circuitos de memoria de sólo lectura
programables y borrables o EPROM (erasable programmable read only
memory). Estos circuitos pueden ser programados, tal como los PROM
pero tienen una ventana de cuarzo en la parte superior del empaque por la
que se puede borrar la información usando una luz ultravioleta.
El atributo más importante de la memoria ROM es que la información
grabada en ella no se borra si el voltaje que requiere el circuito para
funcionar es interrumpido. Tales memorias son conocidas como no
volátiles. En contraste, casi todo tipo de memoria RAM es volátil.
La memoria ROM tiene muchas aplicaciones en sistemas digitales.
Puede ser usada, por ejemplo, para contener valores arbitrarios de una
tabla de verdad. Cuando una tabla de verdad tiene muchas variables
lógicas de entrada y salida y su realización requiere de un gran número de
compuertas, puede ser económicamente substituida por una memoria
ROM. Se usa también en conversión de códigos para despliegue
numérico. Tiene una amplia aplicación en cálculo numérico donde se
requiere de muchas operaciones que pueden ser substituidas por una
búsqueda en tab las, tales como las funciones trascendentales,
multiplicación, división, logaritmos, etc. Pero principalmente guardan el
programa que forma el Sistema Operativo de la máquina en cuestión.
1.8 Arquitectura de sistemas 80x86
Hemos escogido para presentar como ejemplo de la UPC al circuito
80486 de Intel. Esta selección obedece al hecho de que en México, casi
todas las computadoras personales se basan de una forma u otra en este
circuito o en uno de la misma familia (todas las compatibles con IBM).
Las microcomputadoras han encontrado su camino hacia muchas de las
aplicaciones reservadas con anterioridad a la minicomputadora. El
abaratamiento de los circuitos y su incesante popularidad ha dado
nacimiento a circuitos muy avanzados. El circuito 80486 (y los de su familia) cuentan entre sus características con:
1-27
Ensamblador
· Arquitectura interna de 32 bits (64 para P5 y P6)
· Soporta directamente 4 gigabytes (232) de memoria principal y 64
tetrabytes de memoria virtual (246)
· Aritmética de 8, 16 y 32 bits con y sin signo, binario y decimal;
incluye multiplicación y división
· 14 palabras de registros de 32 bits divisibles en 16 u 8 bits cuatro
de ellos
· Capacidad de acceso directo a memoria
· Soporte de coprocesadores (modelo SX, DX lo incluye)
· Operaciones con cadenas
· Soporta E/S por medio de mapa de memoria
Los registros generales de la UPC han aumentado conforme los
requerimientos de cómputo han evolucionado. En un circuito de este tipo
encontramos 14 registros de 32 bits (de los cuales sólo mencionaremos
los 16 bits bajos):
1-28
Componentes Básicos de un
Sistema
Tabla 1.1 Registros
Registros generales
AX
acumulador
BX
base
CX
cuenta
DX
datos
Grupo de apuntadores e índices
SP
apuntador del stack
BP
apuntador base
SI
apuntador fuente
DI
apuntador datos
IP
apuntador de instrucción
F
banderas del sistema
Registros segmentos
CS
segmento de código
DS
segmento datos
ES
segmento extra
SS
segmento pilas
El grupo de registros generales consta de 32 bits al igual que los demás
pero puede leerse en dos partes de 16 bits y a su vez la parte baja de 16 bits
se puede leer en dos partes: una baja especificada con el sufijo L y una alta
especificada con el sufijo H. Por ejemplo, el registro AX se puede dividir
en AL y AH.
Algunos de estos registros tienen usos específicos y algunas
instrucciones requieren de ciertos registros:
1-29
Ensamblador
Tabla 1.2 Registros y su uso
REGISTRO
OPERACION
AX
Multiplica o divide palabras
AL
Multiplica o divide bytes
AH
Multiplica o divide bytes
BX
Traduce
CX
Operaciones con cadenas o contador en
lazos
CL
Rota o corre variable
DX
Multiplica o divide palabras
SP
Operaciones con la pila
SI
Operaciones con cadenas
DI
Operaciones con cadenas
IP
No es accesible más que con saltos
El registro de banderas consta de 16 bits como se muestra en la tabla 1.3.
Las decisiones de la computadora se basan muchas veces en el estado de
estos bits.
Tabla 1.3 Banderas
Bandera
Función
OF
saturación
DF
dirección (operación con cadenas)
IF
habilita interrupciones
TF
trampa (ejecución paso a paso)
SF
signo (resultado negativo)
ZF
cero
AF
acarreo auxiliar
PF
pariedad (par)
CF
acarreo
IOPL
Nivel de privilegio para E/S (2 bits)
NT
tarea anidada
RF
Continua operación
VM
Modo virtual
Este circuito ve a la memoria como un grupo de unidades lógicas
llamadas segmentos. Cada segmento puede ser de hasta 4 gigabytes,
siendo los de 64K bytes de longitud para compatibilidad con modelos
1-30
Componentes Básicos de un
Sistema
anteriores, y son controlados por los registros de segmentos, cada uno
indicando el final del segmento en particular.
Sólo existe una restricción con respecto a los segmentos: deben
comenzar en un párrafo que es una dirección que es di visible
exactamente entre 16. Para esto, los 4 bits menos significativos deben ser
cero.
Los segmentos pueden ser adyacentes, disjuntos, sin traslape o con
traslape parcial o total. Si requerimos usar un registro como apuntador a
memoria, necesitaremos sumar un desplazamiento para poder completar
las 32 líneas de direccionamiento disponibles. Este desplazamiento se indica en la tabla 1.4.
Tabla 1.4 Uso del Desplazamiento
Tipo de referencia
Segmento base por omisión
Segmento base alterno
Desplazamiento
Traer instrucción
CS
Ninguno
IP
Operación con pila
SS
Ninguno
SP
Vari ables (excepto los que DS
siguen)
CS ES SS
dirección efectiva
Fuente cadena
DS
CS ES SS
SI
Destino cadena
ES
Ninguno
DI
BP usado como registro base
SS
CS ES SS
dirección efectiva
El circuito cuenta con una cola de instrucciones donde el procesador va
guardando las siguientes instrucciones a ejecutar disminuyendo con esto
el ciclo de instrucción del típico traer instrucción, decodificar instrucción
y ejecutarla a sólo dos pasos, pues mientras se interpreta la instrucción se
trae la siguiente al mismo tiempo.
1.9 Resumen
Se introduce a los componentes de una UPC y se muestra como
funciona está. Se mencionan y explican brevemente algunos
componentes externos a la UPC en especial circuitos auxiliares como la
memoria y sus clasificaciones. En este capítulo introducimos el concepto
de instrucción y su forma de interpretación dentro de la UPC, los
registros indispensables para el funcionamiento de la UPC y el ALU.
Se introduce un circuito típico de gran popularidad en México y el
mundo: el Intel 80x86.
1-31
Ensamblador
1.9.1 Puntos Importantes del Capítulo
· Un sistema se conforma por 5 componentes básicos: UPC,
memoria, dispositivos de E/S, interfases de E/S y un programa
· El juego de patrones definidos por el fabricante forman el juego de
instrucciones.
· La UPC consta de la UAL, los registros, la unidad de control y un
bus interno para interconexiones
· Dentro de la memoria son almacenados datos que luego se
interpretan en la UPC, estos datos tienen muchas interpretaciones
según el programador y el diseñador del circuito.
· Existe un registro especializado llamado de banderas de estado
que nos indica varias condiciones dentro del la UPC.
· Para ejecutar las instrucciones es necesario traerlas de la memoria
(fetch) y ejecutarlas (ejecute) esto se realiza en uno o variso ciclos
de reloj que mide entonces la velocidad de ejecución de la
instrucciones.
· La unidad de control de una UPC puede o no programarse.
· La UAL ejecuta un conjunto de operaciones aritméticas y lógicas
básicas.
· La memoria puede dividirse en Interna, secundaria, ROM, RAM,
dinámica, estática y todas ellas son accesadas por medio de una
dirección que identifica las distintas localidades que la
conforman.
1.10 Bibliografía
Llano Emiliano Sistemas Digitales y Arquitectura de Computadoras.
Unam 1993. Capítulo 10 y 11.
1-32
Capítulo 2
2.1 Otros lenguajes y el ensamblador
No se debe pasar por alto que un sistema de computación, no
importando la complejidad de los varios componentes electrónicos que
lo formen, no es más que un juguete caro a menos de que exista un
propósito de su existencia y un programa que haga que este propósito se
cumpla. Es este programa, formado por una serie de instrucciones
precisas y arreglado en un orden lógico, que hace de la computadora una
herramienta útil.
Existen muchos tipos de programas y de sistemas de programas, a los
que en conjunto se conocen como Programas (software), en oposición a
los elementos mecánicos y electrónicos llamados en conjunto Hardware. Algunos de estos programas se usan para un tarea en particular
como puede ser el caso de una nómina. Otros programas se usan para
manejar los distintos puertos de entrada y salida de una computadora. La
operación tanto mecánica como lógica de los dispositivos externos al
sistema por donde la información es introducida y entregada, es
controlada directamente por estos programas. Existe aún otro tipo de
programas que realizan funciones muy específicas de computación como
realizar las funciones que la UAL no puede realizar o realiza muy
despacio. Algunas veces se conoce a los programas como subrutinas pues
forman parte de programas mucho más grandes. Para un sistema de
computación dado, muchos programas pueden reunirse y guardarse en un
sistema de almacenamiento externo tal como disco magnético flexible
(medio muy común), posteriormente se utiliza un programa que pueda
extraer a los primeros del medio donde fueron almacenados y ejecutarlos.
A este programa se le conoce como sistema operativo (operating sys-
2-33
Ensamblador
tem) y si se encuentra almacenado en un disco magnético se le llama
sistema operativo de disco (disk operating system o DOS).
La primera consideración sería ¿Cómo hacer que la computadora
comience a funcionar? Aplicando la energía eléctrica únicamente no es
suficiente para hacer que la computadora comience a funcionar como el
usuario desea. Debe existir un programa que haga la inicialización del
sistema y determine las metas a alcanzar para poder hacer que el sistema
esté en posibilidad de aceptar entradas de los dispositivos externos como
pueden ser el teclado o los discos magnéticos de almacenamiento. A este
programa se le conoce como programa de arranque (start-up program).
Usualmente se le conoce en inglés con el nombre de bootstrap o
programa de arranque en frío (cold-start), los programas de arranque no
necesariamente tienen que ser muy grandes. El procedimiento común
consiste en colocar en una localidad de memoria predefinida del tipo
ROM, el pequeño programa de arranque. Toda computadora tiene una
forma determinada de comenzar la ejecución de un programa en una
localidad específica al aplicarles la energía eléctrica, esta característica
se aprovecha para guardar en esa localidad el inicio del programa de
arranque.
El programa de arranque inicia una cadena de eventos que termina con
la ejecución en memoria principal de una parte del sistema operativo
conocida como procesador de comandos que queda en todo momento
activo para recibir órdenes posteriores de otros programas o del usuario
directamente en una parte del sistema operativo llamado BIOS o sistema
básico de entrada y salida (Basic Input Output System).
Lo que sucede a partir del punto en que el sistema operativo toma control de la Unidad de Proceso Central, varía de sistema a sistema pero el
objetivo fundamental de este programa es coordinar las distintas
operaciones del computador y llevar registro del estado de varios eventos
tales como interrupciones, peticiones a dispositivos externos, etc. Una
discusión a fondo de sistemas operativos queda fuera del alcance de este
libro pero se puede consultar la bibliografía para ahondar en el tema.
2.2 Los Programas en la Memoria
Como hemos visto en la descripción de la Unidad de Procesamiento
Central y en especial de la Unidad de Control, la computadora sólo es
capaz de ejecutar una instrucción a la vez (en las arquitecturas
tradicionales no incluyendo en esto arquitecturas en paralelo) por lo que
2-34
Otros Lenguajes y el Ensamblador
se requiere de una memoria donde guardar las instrucciones que forman
el programa. La cantidad de memoria necesaria depende de la
complejidad del programa y de la cantidad de datos que genere o use.
La ejecución del programa consiste en tomar una instrucción de la
memoria principal e interpretarla en la Unidad de Control de la UPC. El
registro de Contador de Programa (PC) es utilizado para ir llevando un
apuntador de memoria de la siguiente instrucción a ejecutar. Como ya
hemos visto, el PC se debe incrementar un poco antes de realizar la
transferencia (fetch) de la siguiente instrucción en memoria al Registro
de Instrucciones (I).
Las instrucciones son utilizadas para especificar la secuencia lógica que
debe ocurrir dentro del sistema de cómputo. Para usar una computadora
debemos, por lo tanto, seleccionar primero los dispositivos que nos darán
la capacidad lógica suficiente y posteriormente realizar la secuencia
lógica que llenará nuestras necesidades. La secuencia lógica forma el
programa y programar es crear estas secuencias.
Nada nos impide crear estas secuencias lógicas en forma binaria y de
alguna forma colocarlas en la memoria en las localidades escogidas y de
hecho, las primeras computadoras eran programadas de esta forma: una
secuencia de 1 y 0 era cuidadosamente codificada y luego introducida a la
máquina por medio de alambres que cambiaban el contenido de la
memoria (en las computadoras más antiguas) o por medio de
interruptores que se conectaban directamente a la memoria por medio del
bus de datos (en la siguiente generación).
Pronto los programadores escogieron representar los códigos de
instrucciones en hexadecimal en lugar de binario para abreviar y facilitar
su labor (es muchas veces más fácil identificar un error en una secuencia
de números hexadecimales que en una cadena de unos y ceros) y luego
buscar la forma en que la máquina trabajará más ayudando a la
conversión.
Un código escrito en un papel u otro medio, forma lo que llamamos
código fuente, este código es introducido luego a la máquina y el
programa que lo lee, lo convierte y coloca en su localidad de memoria, es
llamado un editor y es la forma más elemental de colocar los unos y ceros
en donde deben estar. Estos unos y ceros colocados en la memoria principal y listos a ser interpretados como un programa, son llamados el código
objeto y no son más que la representación exacta de lo que en un
2-35
Ensamblador
principio teníamos en papel pero ahora colocado en la memoria de la
computadora.
Pronto se vio que el camino correcto consistía en hacer el trabajo del
programador más fácil mientras que el de la máquina debería ser el más
difícil. Simplificar el trabajo del programador es una mejora sustancial en
nuestro equipo. Llevando este razonamiento un paso más allá, ¿Por qué
no usar un lenguaje que sea más sencillo de aprender, programar e
introducir a la máquina que una secuencia de números sin sentido más
que para el más experto?
La respuesta a esto, en nivel sistema de computadora, se encuentra en el
lenguaje ensamblador que no es más que convertir las instrucciones que
entiende la Unidad de Proceso Central (lenguaje de máquina) en una
serie de símbolos alfabéticos y números mucho más convenientes para
los programadores humanos. Los lenguajes ensambladores son, en una
primera aproximación, versiones simbólicas y mucho más fáciles de leer
de los lenguajes de programación de máquinas computadoras. Al
incrementarse el uso de las computadoras, los programas eran cada vez
más complejos y de mayor tamaño y se reconoció la necesidad de
construir los programas en unidades o módulos más pequeños e
independientes. Estas características fueron pronto introducidas en los
lenguajes ensambladores que permitieron que módulos de programas
escritos como unidades independientes fuesen integrados o ensamblados
en uno más grande, de ahí el nombre de ensamblador. En la época actual,
casi todo tipo de computadora, sea grande o pequeña, se vende con un
lenguaje ensamblador y otros programas asociados que nos permiten
introducir nuestras instrucciones a la memoria de la máquina. El lenguaje
ensamblador es definido por el fabricante y es único a cada modelo de
UPC. Ocasionalmente, varios lenguajes ensambladores pueden existir
para la misma máquina pero el programa que se encarga de colocar los
unos y ceros en la memoria, debe entregar el mismo resultado: el
programa objeto ejecutable por la computadora.
Aunque algunos estándares para lenguaje ensamblador han sido
propuestos, hay poca evidencia de que los fabricantes se apeguen a el,
pero un patrón general ha emergido:
· Cada instrucción ocupa una línea.
· Cada línea se forma de cuatro campos:
Etiqueta
2-36
Otros Lenguajes y el Ensamblador
Mnemónico
Operando
Comentario
Cada línea fuente de programa representa una línea objeto traducida por
el programa que se denomina ensamblador.
El campo del mnemónico es el más importante y es el único campo
obligatorio que aparece en cada línea.
etiqueta
operando
mnemónico
LIM
LMA
AIA
BZ
resultado es cero
SRA
INC
datos
JMP
byte
SALIR
DC0,ADDR1
AQUI
H"0F"
SALIR
comentario
;carga dirección fuente en DC
;Carga palabra en acumulador
;quita 4 bits de la palabra
;salir de programa si el
DC0
;Guarda bits modificados
;Incrementa el contador de
AQUI
;regresa a procesar siguiente
;siguiente instrucción
Nótese que lo único sagrado e inalterable son los códigos binarios de las
instrucciones; los mnemónicos del ensamblador pueden ser alterados
siempre y cuando se reprograme al programa ensamblador para
reconocerlos y hacer la traducción adecuada.
El campo de las etiquetas puede o no tener información y si la contiene,
sirve al ensamblador para llevar apuntadores de sitios interesantes dentro
del programa a los que seguramente queremos regresar. Estos sitios son
apuntadores en memoria pues no tenemos forma de usar la dirección
verdadera al no saber en qué lugar la pondrá el ensamblador. La dirección
en donde comienza a ensamblarse el programa depende de la longitud del
sistema operativo, del tamaño del ensamblador, de instrucciones
específicas, etc.
El campo de los operadores sirve para proveer información necesaria a
la instrucción en caso de que exista. Por ejemplo, no podemos sumar un
número al registro Acumulador si no indicamos en el campo de operando
este número.
2-37
Ensamblador
Los comentarios pueden o no ir, pero su uso es casi obligatorio pues
contiene información que nos hace más fácil comprender qué es lo que
hace el programa y en su caso modificar o dar mantenimiento a
programas escritos por otra persona o por nosotros mismos hace tiempo.
La información contenida en este campo es de vital importancia para el
programador pero no influye en nada al resultado del ensamblador.
2.3 Conjunto de Instrucciones
El grupo de instrucciones usados por una Unidad de Procesamiento
Central particular es conocido como conjunto de instrucciones. Cada
fabricante incorpora un conjunto de instrucciones ligeramente distinto en
cada UPC, de acuerdo al uso al que se destinará el producto. Algunos son
muy sencillos consistiendo en un puñado de instrucciones, mientras que
otros son bastante complejos, con su total llegando a los varios cientos.
Existen varios grupos de instrucciones, cada uno de ellos con un
propósito especifico tales como:
· de entrada y salida
· de transferencia
· condicionales
· aritméticas
· de interrupción
· etc.
Usualmente se les da un nombre que es una abreviatura en la forma de
un grupo de caracteres, claro que, como ya sabemos, todo esto llega
finalmente a ser unos y ceros almacenados en una localidad de la
memoria principal.
2.4 Ensambladores
Un programa sencillo como el mostrado en la sección 1.3, no puede ser
ensamblado tal como se presentó; es necesario informar al programa
ensamblador de varios elementos antes de que pueda comenzar a realizar
su trabajo. Por ejemplo, es necesario informarle en qué localidad de
memoria queremos comenzar a ensamblar el programa.
2-38
Otros Lenguajes y el Ensamblador
Existen una clase de instrucciones llamadas directivas del
ensamblador que no forman parte del lenguaje de la máquina pero
proveen al ensamblador de información que no puede deducir por sí
mismo.
Para poder armar las direcciones de las etiquetas que el programa
contiene, es necesario especificar el origen del programa. Esto se logra
con la directiva “origen” (ORG) que es la única directiva obligatoria y
necesaria.
Para terminar el programa se usa la directiva “fin” (END) que informa al
ensamblador que ya no siguen más instrucciones.
La directiva “iguala” (EQU) que nos hace más fácil la labor de
programar al asignar valores a variables para su uso posterior.
Otra de las directivas muy usadas es la asignación de una palabra o de
dos palabras para asignar valores a variables (DB y DW) para su posterior
uso.
Se debe recordar que las abreviaciones de las directivas del
ensamblador cambian de uno a otro y siempre se debe consultar el manual. Existen ensambladores bastante complejos que permiten toda una
serie de comandos complicados que hacen más sencillo el programar a
nivel ensamblador.
Nuestro programa, usando las directivas explicadas, quedaría:
mascara
AQUI
ORG
EQU
LIM
LMA
AIA
BZ
H"03FF"
H"0F"
DC0,ADDR1
mascara
SALIR
;carga dirección fuente en DC
;Carga palabra en acumulador
;quita 4 bits de la palabra
;salir de programa si el resultado es
cero
SALIR
SRA
INC
JMP
END
DC0
AQUI
;Guarda bits modificados
;Incrementa el contador de datos
;regresa a procesar siguiente byte
;siguiente instrucción
Una vez definido el inicio y fin de un programa, el ensamblador puede
llevar a cabo su trabajo y nos entregará una secuencia de números
hexadecimales que forman el programa objeto que correrá directamente
en la máquina tal como se muestra a continuación (el programa se
2-39
Ensamblador
modificó ligeramente para ser ejecutado en un procesador del tipo 80x86
y se le agregaron todas las directivas del ensambaldor necesarias para
poder ensamblarlo):
Microsoft (R) Macro Assembler Version 5.00 4/15/93 06:30:48
Page 1-1
;****************************************************************
;Programa anterior modificado ligeramente para un procesador
;tipo 80x86 con resultado de un ensamble.
;Nótese todas las directivas agregadas al ensamblador
;****************************************************************
;definiciones previas usadas en el programa
000F
mascara EQU 0Fh
; ————————————————————————————————
0000
datarea SEGMENT ; área definición dirección a convertir
0500
ORG
500h
; dirección de inicio de la zona de
datos
0500 ???? addr1 DW ?
0502
datarea ENDS
;*****************************************************************
0000
ejemplo SEGMENT
;Programa ejemplo
;—————————————————————————————————
———————————————0000
codigo PROC FAR
0100
ORG 100h
ASSUME CS:ejemplo, DS:datarea, ES:ejemplo ; donde está el
código
0100
inicio:
0100 8D 1E 0500 LEA
BX,addr1
carga dir fuente en
reg auxiliar
0104 89 07 AQUI: MOV [BX],AX
;mover datos de dir a Acum
0106 25 000F
AND AX,mascara ;quita 4 bits de la palabra
0109 74 05
JZ SALIR
;salir si el resultado es cero
010B 8B 07
MOV AX,[BX]
;Guarda bits en dirección
;apuntada por el registro BX
010D 43
INC BX
;Incrementa contador datos
010E EB F4
JMP AQUI
;regresa por siguiente byte
0110
SALIR:
;siguiente instrucción
0110
codigo ENDP
;—————————————————————————————————
————————————————
0110
ejemplo ENDS
;******************************************************************
END inicio
Symbols-1
Segments and Groups:
2-40
Otros Lenguajes y el Ensamblador
Name
Length
DATAREA . . . . . . . 0502
EJEMPLO . . . . . . . . 0110
Symbols
Name
Type
ADDR1
L WORD
AQUI
L NEAR 0104
CODIGO F PROC 0000
INICIO
L NEAR 0100
MASCARA
NUMBER
SALIR
L NEAR 0110
FILENAME
TEXT ensambla
64 Source Lines
64 Total Lines
10 Symbols
51078 + 264170 Bytes symbol space free
0 Warning Errors
0 Severe Errors
Align Combine Class
PARA NONE
PARA NONE
Value Attr
0500 DATAREA
EJEMPLO
EJEMPLO
Length = 0110
EJEMPLO
000F
EJEMPLO
2.5 Direccionamiento a Memoria
Aunque realicemos solamente operaciones con los registros internos a
la UPC, eventualmente necesitaremos guardar la información en una
localidad de memoria principal. Para realizar esta operación, se debe
proporcionar como operando una dirección de la que se traerán o
almacenarán datos. Las formas de especificar la dirección dentro de la
instrucción pueden ser muy variadas pero contamos con tipos muy
específicos como los que describimos a continuación.
2.5.1 Direccionamiento Implícito
Una instrucción que usa el direccionamiento implícito, utiliza el
contenido del registro contador de datos (DC) como dirección de
memoria.
Esto implica un proceso de dos pasos:
· Cargar la dirección de memoria requerida en el registro DC.
· Una instrucción sin operandos se ejecuta y ésta utiliza al registro
DC como un apuntador a memoria.
2-41
Ensamblador
2.5.2 Direccionamiento directo a memoria
Algunas instrucciones pueden especificar la dirección con la que
trabajarán directamente en su operando; a éstas se les conoce como
instrucciones con direccionamiento directo a memoria.
Toda computadora tiene instrucciones con un rango limitado de
memoria en la que pueden actuar. Si todas las instrucciones de una
computadora están limitadas por esta restricción, a la computadora se le
conoce como paginada.
Para formar la dirección final a la que la UPC direccionará, se usa la
parte alta del Contador de Programas (PC)y se une a la dirección de
página para formar la dirección efectiva. Este término se aplica a toda
dirección de memoria que se calcule de alguna forma utilizando para ello
la información provista por la misma instrucción.
Los bits tomados del PC son llamados el número de página y los bits
proporcionados por la instrucción forman la dirección dentro de la
página. Combinando ambos tenemos la dirección efectiva.
La imposición más severa impuesta por un esquema de páginas fijas es
que el programa no puede referirse a otra página y los programas no
pueden residir en los límites de las páginas cruzándolas, por lo que el
programador gasta mucho tiempo calculando las páginas y localidades
de memoria para su programa.
Un método para eliminar algunas de las restricciones del
direccionamiento de páginas, consiste en proveer a la computadora de
una página base y un registro de apuntador de página que pueda ser
alterado por el programa a su conveniencia.
Una variación del esquema anterior consiste en realizar saltos relativos
al PC sumando un desplazamiento contenido en la instrucción, a esta
variación se le llama direccionamiento relativo.
2.5.3 Direccionamiento Indirecto
Una última variación es la de usar una localidad de memoria que será
utilizada como dirección para encontrar los datos que queremos llevar o
recuperar de la memoria. Este esquema es flexible y permite usar
apuntadores para aplicar todas las técnicas de programación avanzada a
las computadoras directamente (uso de apuntadores, colas, pilas, etc.).
2-42
Otros Lenguajes y el Ensamblador
2.6 Tipos de Instrucciones
A grandes rasgos las instrucciones pueden ser divididas en grupos muy
genéricos, una vez más, depende del fabricante si incluye todos los tipos
(o más) o un subconjunto de ellos. La aplicación final del producto dicta
muchas veces el tipo de instrucción necesario y el superfluo.
Aunque no exhaustiva, la siguiente clasificación puede muy bien servir
a nuestros propósitos:
· 1. Instrucciones que mueven datos. Incluyen instrucciones que
mueven datos entre un registro y la memoria; entre dos registros e
instrucciones inmediatas que cargan a los registros con un valor
especificado en el operando de la instrucción.
· 2. Instrucciones aritméticas y lógicas. Son generalmente
instrucciones que trabajan con dos operandos y realizan sumas,
restas, multiplicaciones, divisiones, Y, O así como sumas y restas
con acarreos. Las banderas generalmente se afectan con el
resultado.
· 3. Corrimientos. Son de tres tipos: corrimientos, corrimientos
aritméticos y corrimientos lógicos. En algunos casos la palabra se
corre a través de la bandera de acarreo.
· 4. Saltos y subrutinas. Incluyen saltos incondicionales, saltos
condicionales basados en las banderas, llamadas a subrutinas y
regresos de subrutinas.
· 5. Incrementos y decrementos de los registros y la memoria.
Usualmente incrementan en la unidad la memoria o los registros
para llevar una cuenta en lazos o para accesar a localidades
contiguas de memoria.
· 6. Comparaciones y pruebas. Comparan dos operandos y
modifican el registro de banderas de acuerdo al resultado o
realizan pruebas sobre un operando.
· 7. Meter y sacar información de la pila (stack). Decrementan o
incementan el registro de apuntador a la pila de acuerdo a la
operación realizada: empuja o saca.
· 8. Instrucciones con las banderas de estatus. Limpian o fijan
alguna de las banderas del registro de banderas de la UPC antes de
realizar alguna operación. Habilitan o deshabilitan las
interrupciones a la UPC.
2-43
Ensamblador
· 9. Instrucciones especiales. Todas las que no caigan dentro de las
8 anteriores como son las de entrada y salida, que algunos
prefieren clasificar en un grupo extra y otras.
Puesto que las instrucciones son distintas entre las computadoras, se
discute cada grupo en general y a lo largo del libro daremos un ejemplo de
microcomputadora y sus instrucciones.
Otra clasificación de las muchas existentes agrupa las instrucciones en 3
divisiones aún más genéricas:
–
–
–
1. Instrucciones de transferencia de datos que mueven
información (direcciones, valores de operandos, instrucciones,
etc.) sin cambio de una parte de la computadora a otra.
2. Instrucciones de procesamiento de datos que transfieren datos
realizando las operaciones necesarias requeridas.
3. Instrucciones de control de programa usadas para determinar
el orden de ejecución de las otras instrucciones.
2.7 Lenguajes de Alto Nivel
La programación a nivel máquina es necesaria para que ésta funcione,
pero un usuario común y corriente no puede invertir tanto tiempo en
aprender a programar una computadora cuando su objetivo principal es la
resolución de problemas.
Los lenguajes de alto nivel son la parte del sistema operativo
(aplicaciones) con la que el usuario avanzado se familiarizará (se prefiere
hoy en día las soluciones “enlatadas” en forma de programas de
aplicación con cierta programación integrada: hojas de cálculo,
procesadores de palabras, comunicaciones, autoedición, etc.). Para poder
hacer que la computadora se comporte como queremos, debe haber una
forma de comunicar nuestros deseos a la máquina. La mayoría de las
computadoras existentes tienen alguna forma de lenguaje desarrollado
para ellas. Existen tantos lenguajes como existen computadoras y cada
uno de ellos llena un sitio en especial aunque todos tienen en común que
tratan de ser de propósito general y “fáciles de usar”.
Aunque es verdad que las diferencias de cada máquina nos limitan en su
forma de programarla en lenguaje ensamblador (u objeto), esto no se
aplica en su gran mayoría en los llamados lenguajes de alto nivel. ¿Por
2-44
Otros Lenguajes y el Ensamblador
qué el nombre de alto nivel? Pues porque comparados con los primeros
métodos de programar una computadora son mucho más potentes y
flexibles. De los lenguajes de alto nivel más conocidos y entre los más
viejos se encuentra el BASIC. Es el lenguaje de alto nivel más usado y
popular en los sistemas pequeños de computadoras (computadoras
personales) y en muchos de los sistemas grandes. Debe su popularidad a
la sencillez de su sintaxis y a que es muy sencillo aprenderlo no tomando
más que unas cuantas horas para poder realizar nuestro primer programa
funcional.
Esta misma sencillez del lenguaje BASIC, es su principal desventaja
pues conforme las técnicas de programación han ido mejorando, se
necesita cada vez de herramientas más poderosas que llenen los
requerimientos; han surgido así una serie de lenguajes que se substituyen
unos a otros y cada uno clama ser la última y mejor herramienta para la
programación:
LENGUAJE
FORTRAN
COBOL
SMALLTALK
C
SIMULA
PASCAL
C++
VISUAL BASIC
USO
programación científica
negocios
objetos
sistemas
simulación
enseñanza de técnicas correctas de programación
interfases gráficas y ventanas, objetos
interfases gráficas y ventanas, objetos
y muchísimos otros más cada uno con sus méritos, ventajas y
debilidades.
2.8 Intérpretes y Compiladores
No importando qué tan sofisticado sea el programa o el lenguaje en que
esté escrito, nuevamente llegamos al punto en que la UPC debe
interpretarlo y ejecutarlo a la máxima velocidad posible. Esto implica
traducir cada una de las instrucciones del lenguaje de alto nivel o
ensamblador a unos y ceros que son colocados en las localidades precisas
de memoria para que sean ejecutadas.
Las dos formas existentes de traducir las líneas de código de un lenguaje
de alto nivel son usando programas intérpretes y programas
compiladores.
2-45
Ensamblador
2.8.1 Intérpretes
Un programa intérprete toma el código desarrollado por el usuario y
escrito en un procesador de palabras o generalmente en un procesador de
texto proporcionado por el mismo intérprete y al darle la orden de
ejecutar un programa, traduce una a una las instrucciones del código
fuente a lenguaje de máquina.
Esto tiene una serie de ventajas y desventajas:
Ventajas:
–
–
–
–
Muy fácil corregir errores y desarrollar programas pues en todo
momento tenemos control sobre la ejecución, podemos
interrumpir, poner puntos de espera, preguntar el contenido de
variables, ejecutar el programa paso a paso, etc.
Fácil de entender y manejar; como no permite tantas opciones, no
tenemos tantos comandos que aprender para controlar el proceso.
Los errores se corrigen rápidamente y se ve de inmediato el
resultado de estas correcciones.
Las instrucciones del programa siempre están a disposición del
usuario y éste puede modificarlas a su conveniencia.
Desventajas:
–
–
–
–
–
Para la distribución del programa terminado debemos incluir el
intérprete y las instrucciones para usarlo además de las
instrucciones para usar el programa.
No da flexibilidad pues no permite unir el programa con otros
desarrollados en otros lenguajes.
No es portátil de un ambiente a otro pues el intérprete usualmente
es distinto de máquina a máquina a menos que se apegue
estrictamente a un estándard.
El proceso de traducir las instrucciones una a una lleva tiempo y
si por cualquier causa es necesario ejecutar alguna instrucción
que ya fue traducida, es necesario volver a traducirla una y otra
vez.
Se requiere de la memoria necesaria para poder ejecutar el
intérprete, más la memoria necesaria para el programa.
2-46
Otros Lenguajes y el Ensamblador
–
–
Como las instrucciones son interpretadas una a una, no es posible
optimizar la traducción pues no tenemos el concepto de
programa completo sino de instrucción en instrucción.
Si se quiere vender el programa final, se debe incluir cada una de
las instrucciones permitiendo que el programa sea copiado y/o
modificado ilegalmente las veces que se desee.
Por todas las razones expuestas, es muchas veces preferible utilizar un
compilador (ver siguiente sección) aunque algunas veces (si es posible)
se prefiere desarrollar en un intérprete y entregar la versión final del
programa en un compilador que aproveche las ventajas de estos
programas.
2.8.2 Compiladores
Un programa compilador ejecutará el mismo programa que un
intérprete, pero la acción se realiza de un modo distinto en cada caso. El
compilador toma un programa fuente como una unidad y lo traduce a
programa objeto en varios pasos dejando un programa ejecutable que ya
no requiere de ningún otro programa para ejecutarse a excepción del
sistema operativo.
El programa que entrega el compilador puede ejecutarse directamente
desde el procesador de comandos del sistema operativo. Para que esto
suceda, deben de existir varias condiciones:
–
–
–
Que el programa fuente esté libre de errores de sintaxis
Que el programa fuente no tenga errores de ejecución
Que se le agregen al programa fuente todas aquellas rutinas que
necesita para poder realizar su trabajo.
Para que todo esto se cumpla se requiere de un trabajo adicional por
parte del usuario y un esfuerzo mucho mayor que en el uso de un
intérprete.
Es tal la popularidad de los sistemas de computación y de los lenguajes,
que las compañías que los fabrican han invertido mucho tiempo y dinero
para entregar un producto que sea rápido y evite muchas de las penurias
que se encontraban en los primeros compiladores comerciales.
El ciclo de desarrollo de un programa con un compilador consiste en
escribir el programa en un procesador de texto separado del programa y
2-47
Ensamblador
ejecutar el compilador; si no existen errores de sintaxis se procede a ligar
con las librerías de funciones que el programa requiere durante su
ejecución; se ejecuta el programa objeto, si existe algún error, por
pequeño que este sea, se repite todo el proceso.
Como se ve en la descripción anterior el proceso es minucioso,
elaborado, complicado y tardado. Debe existir alguna ventaja en usar un
compilador para que éste exista. La principal ventaja que se obtiene es la
rapidez de ejecución del programa final. Pero siguen existiendo ciertas
ventajas y desventajas con respecto a un intérprete:
Ventajas:
–
–
–
–
–
–
Programa objeto muy rápido en su ejecución (hasta 100 veces
más rápido que con un intérprete dependiendo de las
instrucciones y otros factores).
Se puede unir el programa con otros desarrollados en otros
lenguajes por lo que tenemos acceso a una librería desarrollada
por otros.
Las instrucciones son traducidas una única vez.
No existen pedazos de código con errores potenciales pues el
programa se traduce como un todo y no instrucción a instrucción.
Al traducir instrucción a instrucción puede ser que hayan
pedazos del programa que nunca sean ejecutados en nuestras
pruebas.
Se puede optimizar el resultado final pues se conocen todas las
referencias y todo lo que hace el programa en términos de
instrucciones.
No se requiere de ningún programa auxiliar para poder ejecutar el
producto terminado.
Desventajas:
–
El ciclo de desarrollo es tardado: programación, compilación,
ligar y ejecutar. Mientras que en el intérprete sólo es un paso:
ejecutar.
Como la única desventaja aparente es la del desarrollo, los
compiladores modernos ya incluyen un ambiente integral en el que se
escribe el programa, se compila, liga y ejecuta en un sólo paso utilizando
la memoria para ello y sólo se graba el programa final como un módulo
2-48
Otros Lenguajes y el Ensamblador
ejecutable cuando el ciclo de desarrollo está completo y así lo
solicitamos. Estos productos tratan de combinar las ventajas de un
intérprete con las de un compilador y eliminar la mayoría de las
desventajas de los intérpretes.
2.9 Sistemas Operativos Avanzados
La evolución de las computadoras sigue aún un ritmo desenfrenado.
Muchas cosas sorprendentes son realizadas con las computadoras y
quedan aún muchas más por realizar: Computadoras gigantescas que
realizan todas las transacciones de un banco incluyendo a sus cajeros
automatizados; transacciones internacionales de computadora a
computadora, etc; computadoras para las aerolíneas que llevan los vuelos
y reservaciones a nivel mundial; computadoras de tráfico aéreo que
llevan el control vía radar de todos los vuelos que entran y salen de un
aeropuerto y muchas aplicaciones más.
¿Puede usted imaginar la cantidad de potencia de cómputo requerido
para esto y la complejidad de un sistema operativo y los programas
necesarios para cumplir con el cometido asignado a la computadora?
Todo esto puede ser atribuido al nacimiento de grandes e increiblemente
complejos sistemas operativos, todos ellos no prácticos hace apenas unos
cuantos años. Hace poco tiempo el uso de más de 64K bytes de memoria
en un sistema personal era impensable e incosteable. Hoy son pocos los
sistemas operativos que caben en esa cantidad de memoria.
La tendencia sigue siendo hacia compartir los datos por lo que términos
como multitareas, multiusuario y multiproceso no serán del todo
desconocidos en un futuro. La necesidad de velocidad es tanta que
nuevas arquitecturas surgen como alternativas al uso de un sólo
procesador central: arquitectura paralela y redes neuronales son la
tendencia tecnológica del futuro.
2.10 Resumen
Una de las partes primordiales de un sistema de cómputo y sin el cual su
funcionamiento no es posible es el de la programación. Por medio de un
programa podemos guiar todas las acciones de la electrónica para poder
llegar a una aplicación práctica y útil.
2-49
Ensamblador
El aspecto de la programación es muy extenso e incluye muchos temas
complejos. Pero su aspecto fundamental se describe brevemente en este
capítulo.
El sistema operativo (SO) forma la parte sobre la cual actuan todos los
demás programas, todo requerimiento a la computadora es encauzado
por el canal adecuado por medio de la parte de entrada y salida básica del
SO llamada BIOS.
Todo programa para poder ser ejecutado debe residir en la memoria y
debe ser puesto de alguna forma en ella en comandos que la UPC pueda
entender e interpretar para lo cual se han desarrollado toda una serie de
programas llamados intérpretes y compiladores que se encargan de esta
labor.
2.10.1 Puntos Importantes del Capítulo
· Un programa de arranque inicia el sistema de cómputo y le
permite funcionar.
· El procesador de comandos de un sistema operativo queda
residente todo el tiempo para recibir órdenes de otros programas o
del mismo usuario.
· Un programa consiste en una secuencia lógica de instrucciones y
se conoce como programar a crear estas secuencias.
· El código fuente consiste en las instrucciones que el programa
ensamblador interpretará para crear un código objeto.
· El código objeto es el resultado de un ensamblador y consiste en el
programa fuente interpretado y colocado en la memoria para su
ejecución.
· Toda Unidad de Procesamiento Central (UPC)consta de un juego
de instrucciones definidas por el fabricante durante su
construcción.
· Entre las formas de indicar una localidad o dirección de memoria
contamos con: direccionamiento directo, implícito o indirecto.
· Las instrucciones que permite una UPC son clasificados de
acuerdo a su función y existen varias de estas clasificaciones.
· Un intérprete traduce instrucción a instrucción un programa
fuente mientras que un compilador lo hace todo de una vez.
2-50
Otros Lenguajes y el Ensamblador
2.11 Actividades
2.1 Investigue qué programa ensamblador viene junto con los circuitos
basados en una UPC de Motorola, tales como los Apple.
2.2 Encuentre el conjunto de algunas instrucciones de una UPC 68000
de Motorola.
2.3 Haga un programa sencillo para multiplicar dos números de 8 bits en
lenguaje ensamblador inventado por usted. Use el registro Acumulador y
dos auxiliares llamados B y C. No emplee una instrucción que haga
directamente la multiplicación.
2.12 Bibliografía
Llano Emiliano Sistemas Digitales y Arquitectura de Computadoras.
Unam 1993. Capítulo 13.
Macro 86 Columbia Data Products, Inc. 1982.
MPC Operations Guide Columbia Data Products, Inc. 1982.
2-51
3. Sistemas Numéricos
Como matemático, Laplace1 apreciaba enormemente el sistema
numérico decimal. Comprendía que cientos de años de esfuerzos
mentales así como de buena suerte dieron como resultado el sistema que
usamos y estaba en una posición en la que podía apreciar sus ventajas.
Nuestro presente sistema numérico provee a los matemáticos modernos y
científicos con grandes ventajas sobre los usados por anteriores
civilizaciones y es un factor importante de nuestro rápido avance.
Puesto que las manos son la herramienta más conveniente con la que la
naturaleza nos dotó, los seres humanos siempre hemos tendido a usarlas
para contar. Es entonces natural y afortunado que nuestro sistema de
contar se base en el número de dedos que tenemos. Sin embargo pasó
bastante tiempo antes de poder representar estos números gráficamente.
Las primeras representaciones gráficas encontradas consisten en marcas
verticales y horizontales. El número 1 es un ejemplo de esto; es
interesante hacer notar que el 2 consistía de dos marcas horizontales
unidas con una línea y el 3 de tres marcas horizontales unidas (sistema
arábigo). Los números Romanos son un buen ejemplo de líneas usadas
como base para números (ver figura 3.1).
1
Pierre Simon marqués de Laplace (1749-1827). Astrónomo y matemático francés que dio base
científica a la Hipótesis de las Nebulosas. Entre sus trabajos matemáticos más notables se destaca el
perfeccionamiento de la teoría de las probabilidades.
3-53
Ensamblador
Figura 3.1 Comparación de algunos sistemas de números.
3-54
Sistemas Numéricos
El sistema decimal ha sido tan aceptado y adoptado por nuestra
civilización que rara vez consideramos la posibilidad de otros sistemas
en uso. De todas formas, no es razonable pensar que un sistema basado en
el número de dedos que tenemos en las manos sea el más eficiente para
usar en las máquinas que construimos. El hecho es que un sistema muy
poco usado para otra cosa, pero muy sencillo, el sistema binario, ha
probado ser el más natural y eficiente para su uso en máquinas
computadoras.
3.1 El Sistema Decimal
Nuestro sistema actual se forma por 10 símbolos distintos: 0, 1, 2, 3, ..., 9
llamados dígitos Arábigos. Con este esquema nos veríamos forzados a
detenernos en 9 o inventar otros símbolos para los demás números si no
fuese porque usamos una notación de posición. Un ejemplo de esto lo
podemos encontrar en los números romanos que son en esencia aditivos:
III=I+I+I, XXV=X+X+V. Se requieren nuevos símbolos conforme la
serie crece (X, M, C, etc.). Así, se usa V en lugar de IIIII=5 la única
importancia en posición en los números Romanos es si un símbolo precede o antecede a otro (VI=6, IV=4). La torpeza de este sistema resalta en
cuanto deseamos realizar cualquier operación con dos números, por
ejemplo multiplicar XII por XIV (12x14). El cálculo con números
Romanos es tan engorroso que los primeros matemáticos se vieron
forzados a usar casi exclusivamente el ábaco o tablas de contar y luego
traducir el resultado a números Romanos. Los cálculos con papel y lápiz
son tan increiblemente complicados en este sistema que la habilidad para
hacerlo era muy apreciada entre los antiguos Romanos.
La gran simplicidad y belleza de nuestro sistema decimal puede
entonces apreciarse en detalle. Sólo es necesario aprenderse 10 dígitos y
el sistema de notación de posición para contar cualquier cantidad.
Después de aprender de memoria 10 tablas de multiplicar y de sumar así
como aprender unas sencillas reglas, es posible realizar cualquier
operación aritmética. Nótese la sencillez para realizar la operación 12 por
14:
14
x 12
28
14
168
3-55
Ensamblador
El significado del número 168 puede notarse al decir la cantidad “ciento
s e s e n t a y o c h o ” . Bá s i c a m e nt e e l n ú me r o c o n s i s t e e n
(1x102)+(6x101)+(8x100). La importancia es que el valor de cada dígito
está determinado por su posición. El 3 en 3000 vale distinto que el 3 en 30
y se denota al hablar diciendo tres mil o treinta (tres decenas).
La regla general para representar cualquier número en notación decimal
es:
(31
. ) a1 10 n-1 + a2 10 n- 2 +K+ an = a1 a2 K an
donde n denota el número de dígitos a la izquierda del punto decimal.
La base del sistema es definido como la cantidad de dígitos distintos
usados en cada posición en un sistema de notación. El sistema decimal
tiene base 10, esto significa que el sistema tiene 10 dígitos distintos para
representar cualquier cifra (0, 1, 2, 3, ..., 9). La historia registra el uso de
varias bases. El sistema quinario (base 5) prevalece entre los esquimales
y los indios de Norte América, la base doce aún se usa en relojes, pies,
docenas; el de base 60 (usado por los Babilonios) en segundos y minutos.
3.2 El Sistema Binario
El matemático del siglo XVII, Leibnitz2, era un fanático del uso de la
base 2 que sólo usa los símbolos 0 y 1 para representar cifras. Puede
parecer extraño que un matemático eminente use un sistema tan sencillo,
pero debe recordarse que en esa época casi todos los matemáticos eran
también filósofos y religiosos. Su preferencia al sistema base dos se
debió a razones míticas, el uno representando a la deidad y el cero a la
nada.
Cualesquiera fuesen las razones de Leibnitz para usar el sistema binario,
en los últimos años se ha vuelto muy popular. Todas las computadoras
modernas se construyen para operar usando el sistema binario o sistemas
codificados en binario y todo indica que en un futuro cercano seguirán
siendo construidas de esta forma.
2
Gottfired Wilhelm barón von Leibniz o Leibnitz (1646-1716). Filósofo y matemático alemán
también erudito en ciencia, historia y derecho. Desarrolló el cálculo infinitesimal sin conocer la obra
de Newton en el mismo campo. Su filosofía se apoya fundamentalmente en la concepción de un
universo compuesto por un número infinito de unidades de fuerza espiritual o materia a la que llama
mónadas.
3-56
Sistemas Numéricos
Los componentes básicos de las primeras computadoras eran los
relevadores y contactos que son binarios por naturaleza pues sólo pueden
estar en dos estados posibles: cerrados (1) o abiertos (0). Los principales
componentes de las computadoras actuales son los transistores similares
a los usados en televisores y radios. La necesidad de un funcionamiento
confiable llevó a los diseñadores a utilizar a los transistores en sus
estados de corte y saturación reduciendo así sus estados posibles a dos
fácilmente identificables, conduciendo (1) o no (0). Una simple analogía
puede realizarse entre estos dos estados y un foco de luz eléctrica. En
cierto momento el foco está prendido (transistor conduciendo) emitiendo
luz o apagado (transistor no conduciendo). Aunque el foco esté viejo y no
produzca tanta luz, se puede decir con certeza si está prendido o apagado.
Lo mismo sucede con un radio, si éste está viejo o con las pilas gastadas,
hay que compensar subiendo el volumen pero por muy bajo que esté,
siempre se puede decir si está prendido o no.
Debido al gran número de partes que forman una computadora, es
altamente deseable utilizarlas de tal forma que los cambios en sus
características no afecten el desempeño total del sistema. La mejor forma
de lograr esto es usando los circuitos en su forma biestable (de dos
estados posibles).
3-57
Ensamblador
3.2.1 Contando en el Sistema Binario
El mismo tipo de notación de posición que usamos en el sistema decimal
es el que se usa en el sistema binario. Obsérvense los primeros 16
números en el sistema binario:
Tabla 3.1 Conversión de Decimal a Binario
Decimal
Binario
Decimal
Binario
0
0
8
1000
1
1
9
1001
2
10
10
1010
3
11
11
1011
4
100
12
1100
5
101
13
1101
6
110
14
1110
7
111
15
1111
Debe notarse que el sistema decimal usa potencias de 10 mientras que el
binario potencias de 2 y en general el sistema n usará potencia n. Mientras
que el número 35 en decimal equivale a 3 x 101 + 5 x 100, el mismo
número en binario (35) se representa como 1000112 significa 1 x 25 + 0 x
24 + 0 x 23 + 0 x 22 + 1 x 21 + 1x20. Los siguientes ejemplos ilustran la
conversión de una base a la otra, debe notarse que la potencia puede
substituirse por potencia n para sistema n (por ejemplo 16 para sistema
base hexadecimal u ocho para sistema octal).
111 = 1x2 2 + 1x21 + 1x2 0 = 4 + 2 + 1 = 7
1011 = 1x2 3 + 0x2 2 + 1x21 + 1x2 0 = 6 + 0 + 2 + 1 = 9
11011
.
= 1x21 + 1x2 0 + 0x2 -1 + 1x2 -2 + 1x2 -3 = 2 + 1 + 0 +
1 1
3
+ =3
4 8
8
Los números fraccionarios se forman al igual que en el sistema decimal:
3-58
Sistemas Numéricos
0135
.
= 1x10 -1 + 3x10 -2 + 5x10 -3
3.2.2 Conversión de Sistema Decimal a Binario
Existen muchos métodos de conversión entre bases pero el primero y
más obvio es restar todas las potencias de la base al número en base 10
hasta que el resultado sea cero. En base 2 restaremos la potencia mayor de
2 del número; al restante se le aplica el mismo procedimiento hasta que el
resultado sea cero. Para convertir 2510 a base 2 restaremos de 25 la
potencia mayor de 2 que no exceda a 25, en este caso 24 quedando
25-16=9 del que restamos la siguiente potencia mayor (23=8) y así
continuamos hasta que no tengamos nada que restar. El número queda
como 24 + 23 + 20 ó 11001.
Un método más empleado para números grandes es dividir entre la base
y apuntar el residuo resultante de derecha a izquierda, volviendo a dividir
el resultado entre la base hasta que el resultado sea cero. Por ejemplo
12510:
125ÿ2=62+1, 62ÿ2=31+0, 31ÿ2=15+1, 15ÿ2=7+1, 7ÿ2=3+1,
3ÿ2=1+1, 1ÿ2=0+1
tomando los residuos de derecha a izquierda: 11111012.
En el caso de fracciones se debe dividir el número en dos partes, la
entera en la que se aplica cualquiera de los métodos antes expuestos y la
fraccionaria.
La conversión de fracciones a la base de interés se logra restando de ésta
las potencias negativas de la base hasta que no tengamos residuo o hasta
la precisión que se requiera. Este primer método es engorroso para
fracciones grandes por lo que se prefiere multiplicar por la base y tomar
lo que queda a la izquierda del punto para formar la fracción y lo que
queda a la derecha para volver a aplicar el procedimiento. Por ejemplo
0.437510 a base 2:
2x0.4375=0.8750, 2x0.8750=1.750, 2x0.750=1.50, 2x0.50=1.0
tomando los números a la izquierda del punto decimal de izquierda a
derecha tenemos que 0.437510=0.01112.
3-59
Ensamblador
3.2.3 Suma y Resta en Sistema Binario
La suma y resta en sistema binario son mucho más sencillas de aprender
que en cualquier otro sistema pues las reglas son muy sencillas. Para la
suma tenemos que
0+0=0
0+1=1
1+0=1
1 + 1 = 0 y se lleva 1
Unos ejemplos bastan para comprender el procedimiento que es similar
al decimal al que ya estamos acostumbrados:
101
110
1011
1111
11.01
10100 101.11
100011 1001.00
La resta tiene también reglas muy sencillas:
0-0=0
1-0=1
1-1=0
0 - 1 = 1 con un préstamo de 1
Y la forma de hacerla es similar al sistema decimal:
1001
-101
100
10000
-11
1101
110.01
-100.10
1.11
3.2.4 Multiplicación y División Binaria
La tabla de multiplicar usada por el sistema binario sólo tiene cuatro
reglas a diferencia de las 100 usadas para la multiplicación en sistema
decimal:
0x0=0
1x0=0
0x1=0
1x1=1
3-60
Sistemas Numéricos
Sólo es necesario copiar de nuevo el factor si se multiplica por 1 ó poner
ceros si es por 0:
1100
x1010
0000
1100
0000
1100
1111000
1100110
x1000
1100110000
Nuevamente la división es sumamente sencilla:
0ÿ1 = 0
1ÿ1 = 1
A continuación dos ejemplos de la división:
101
101
11001
101
001
101
100
111.01
11101.00
110
101
100
3.3 Representando Números en Otras Bases
Hemos dicho ya que los números en otras bases tienen tantos símbolos
como la base de que se trate, esto es, en base dos tenemos dos símbolos
distintos, en base 10, diez símbolos distintos para representar cualquier
número. ¿Pero qué símbolos usar para bases mayores que diez? Distintas
culturas han empleado distintos símbolos para resolver este problema,
pero actualmente la única base usada mayor que la decimal es la base 16 o
hexadecimal.
Para representar números en base 16 usamos los nueve símbolos ya
conocidos 0, 1, 2, ..., 9 y agregamos las letras para representar al 10, 11,
12, 13, 14 y 15. Así, la A representa al símbolo 10, B al símbolo 11, etc.
Para convertir de base 10 a cualquier otra empleamos los mismos
métodos de la base dos, es decir dividiendo consecutivamente entre la
3-61
Ensamblador
base y anotando el residuo. Para el caso de base 16 dividiremos entre
dieciséis.
Encontramos la equivalencia de un número en cualquier base con
respecto a la decimal usando notación de posición y multiplicando por la
base elevada a la potencia de la posición menos la unidad:
F09516 = (15 x 163) + (0 x 162) + (9 x 161) + (5 x 160) = 6158910
Como la base 16 es divisible entre la base dos, el sistema hexadecimal es
una forma conveniente de representar en notación corta al sistema
binario evitando errores (con este mismo propósito se utilizó mucho
tiempo la base 8). La forma de convertir de sistema hexadecimal a binario
es sustituir cada uno de los símbolos usados en el número en hexadecimal
por su equivalencia en binario:
F09516 = 1111 0000 1001 01012
o cualquier número en binario dividiendo el número en grupos de 4 de
derecha a izquierda y sustituyendo por su símbolo equivalente en base
16:
100010101102= 0100 0101 01102 = 45616
Para estas conversiones es conveniente tener una tabla a la mano:
Tabla 3.2 Distintas Representaciones de los Enteros del 0 al 15.
Decimal
Binario
Hexadecimal BCD
00
0000
0
0000 0000
01
0001
1
0000 0001
02
0010
2
0000 0010
03
0011
3
0000 0011
04
0100
4
0000 0100
05
0101
5
0000 0101
06
0110
6
0000 0110
07
0111
7
0000 0111
08
1000
8
0000 1000
09
1001
9
0000 1001
10
1010
A
0001 0000
11
1011
B
0001 0001
12
1100
C
0001 0010
13
1101
D
0001 0011
14
1110
E
0001 0100
15
1111
F
0001 0101
3-62
Sistemas Numéricos
3.4 Decimal Codificado en Binario (BCD)
Puesto que las computadoras construidas usando el sistema binario
requieren de una menor cantidad de circuitos electrónicos y por lo tanto
son más eficientes que las máquinas que operan con otros sistemas
numéricos, el sistema binario es el sistema más natural para una
computadora y el de mayor uso actualmente. Por otro lado, el sistema
decimal es el más natural para nosotros. Todos los cálculos que
realizamos usualmente se realizan en el sistema decimal, pero deben ser
convertidos por las computadoras de decimal a binario antes de realizar
cualquier operación. Debido a esto, muchas de las primeras
computadoras usaban un sistema de codificación decimal a binaria. En
tal sistema, se usan grupos de dígitos binarios para representar cada uno
de los 10 símbolos usados en el sistema decimal. Por ejemplo, uno de los
códigos más obvios y naturales es usar un “código binario de pesos”
donde cada posición representa un “peso” tal y como se muestra en la tabla 3.3
Tabla 3.3 Código binario de pesos.
Código Binario
Dígito Decimal
Peso
8421
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
0
1
2
3
4
5
6
7
8
9
Nótese que son necesarios 4 dígitos binarios para cada símbolo decimal.
Esto es ineficiente pues las combinaciones de 4 dígitos binarios son 24 =
16 de los que sólo usamos 10 pero si usamos 3 dígitos 23 = 8 son
insuficientes.
Para simplificar la conversión de decimal a binario, es más práctico
codificar un número decimal d n-1 d n- 2 K d1 d 0 donde d i ® { 0, 1, ..., 9 }
de la siguiente forma: Reemplace cada dígito decimal con su equivalente
de 4 símbolos binarios tal como están definidos en la tabla 2 anterior. El
número resultante es llamado código binario decimal, código 8, 4, 2, 1 o
3-63
Ensamblador
simplemente BCD (Binary-coded decimal), y lo indicaremos con el
sufijo BCD. Así, tenemos que:
10001010101BCD = 45510
Hay que resaltar que el número resultante BCD y el binario son muy
distintos. A cada uno de los dígitos binarios le llamamos bit (de binary
digit). En la representación BCD, cada segmento de 4 bits representa un
sólo dígito decimal cuyo peso es una potencia de 10. Unicamente 10 de
los 16 posibles patrones de grupos de 4 bits son necesarios para la
representación BCD. Aunque el formato BCD claramente simplifica la
conversión decimal, tiene la desventaja de requerir más bits por cada
número a representar. Usando 8 bits, el mayor número representable es
10011001BCD = 9910
mientras que en binario con el mismo número de bits tenemos:
111111112 = 25510
Otras de las desventajas es en las operaciones matemáticas como la resta
que normalmente se realizan usando el complemento del número (ver
siguiente sección). Para resolver estas desventajas se diseñaron otro tipos
de códigos. Uno de los primeros es el llamado de exceso 3 (excess 3) en el
que para formar la equivalencia, primero se suma 3 al número decimal.
Por ejemplo para representar al 4, primero sumamos 3, resultando 7 y
luego usamos el BCD “normal”, que es 0111. El 0111 es el código exceso
3 para el 4.
Cambiando cada 0 por 1 y cada 1 por 0 formamos lo que se llama el
complemento del número binario. Este procedimiento es usado para
formar el complemento a 9 de un número decimal. Por ejemplo el
complemento de 0100 (1 decimal en código exceso 3) es 1011 u ocho
decimal.
El código exceso 3 no es un código de peso (de notación), esto es, cada 1
no representa una potencia de 2 que podamos sumar para formar el
número decimal. Un código de peso en el que el complemento a 9 pueda
ser formado complementando cada bit, es el código 2, 4, 2, 1
representado en la tabla 4. Este código se usa extensivamente en
instrumentos y calculadoras electrónicas.
3-64
Sistemas Numéricos
Tabla 4. Representaciones Alternas.
Decimal
Exceso 3
Complemento Código
a nueve
2, 4, 2, 1
Peso
2 4 21
00
01
02
03
04
05
06
07
08
09
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1100
1011
1010
1001
1000
0111
0110
0101
0100
0011
0000
0001
0010
0011
0100
1011
1100
1101
1110
1111
3.5 Números Negativos
Hasta el momento sólo hemos trabajado con números positivos (sin
signo) pero el signo positivo o negativo es necesario para distinguir a los
positivos de los negativos. Los números sin signo se consideran como
positivos y el signo de + es omitido. En una computadora los números se
almacenan en una memoria que tiene un número finito y fijo de
posiciones. Cada posición puede tomar un valor de 0 ó 1 y es costumbre
representar a los números negativos reservando la última posición de la
izquierda para el signo. De esta forma, en una computadora que tenga
longitudes de registro, llamadas palabras de 8 bits (8 bits equivalen
generalmente a 1 byte) sólo podran usarse 7 posiciones (128 números
distintos ó 2n-1) y la última para el signo.
Por convención se ha escogido utilizar un 1 para números negativos y 0
para los positivos. De tal forma, -104 se representa por 11101000 y 104
como 01101000. A esta notación se le llama notación con signo. Las
operaciones matemáticas pueden realizarse en la misma forma que las
operaciones manuales con números decimales.
El resultado de una operación matemática en una palabra de n bits
requiere, típicamente, que el resultado sea una palabra de n bits. Si el
resultado completo es una palabra de n+1 bits, como en el caso de una
suma, se dice que ha ocurrido un desbordamiento o saturación (overflow). Un desborde se indica con una generación extra de una señal que
modifica una memoria de un bit cambiándola de 0 a 1. En algunos casos
la saturación puede ser ignorada y considerar como resultado adecuado a
3-65
Ensamblador
los n bits de la palabra y en otros será necesario tomar alguna acción
correctiva.
3.5.1 Complementos
Aunque el código de notación con signo es la contraparte directa del
código decimal usado por los seres humanos, otra representación binaria
llamada de complementos es usada con frecuencia en computadoras
digitales, principalmente porque simplifica la construcción de ciertas
operaciones aritméticas.
Existen dos tipos de complementos usados:
· 1. Complemento a la base. Se forma restando cada dígito de la
cantidad al número de la base menos uno y luego agregando 1 al
resultado así obtenido. Para el sistema decimal llamamos a esta
forma complemento a diez, para el binario, complemento a dos.
· 2. Complemento a la base menos 1. Se forma restando a cada
dígito de la cantidad al número de la base menos uno. Para el
sistema decimal llamamos a esta forma complemento a nueve,
para el binario, complemento a uno.
Por ejemplo el complemento a diez de 8710 es 1310 (1210+110) y el de
2310 es 7710 (7610+110) mientras que el complemento a nueve de 8710 es
1210 y el de 7710 es 2210. En el sistema binario el complemento a dos de
101102 es 010102 y el de 110102 es 001102.
El método para encontrar el complemento en la base dos de cualquier
cantidad consiste en sustituir todo 0 por 1 y todo 1 por 0, dependiendo del
tipo de complemento se sumará 1 o no.
La ventaja principal de usar complementos en sistemas digitales durante la suma o resta, es que todos los bits del número son tratados de
forma uniforme y que la resta y suma son realizadas por el mismo circuito
que sólo suma y es mucho más sencillo.
Al usar la representación por complementos la resta queda simplificada
realizándose con sumas:
3-66
Sistemas Numéricos
Complemento a la base
89
-23
66
11011
-10100
00011
89
+77
66 (el último acarreo se descarta)
11011
+01100
100111 (el último acarreo se descarta)
Complemento a la base menos uno
89
-23
66
11001
-10100
00011
89
+76
1 65
+1 (el último acarreo se suma)
66
11001
+01001
1 00010
+1 (el último acarreo se suma)
00011
3.6 Código Grey y ASCII
Las secuencias de los números binarios son “naturales” y generalmente
se comprenden con facilidad pues siguen un patrón posicional tal como el
sistema decimal. Podemos, sin embargo, representar a un número por una
secuencia arbitraria de 1 y 0. Para evitar ambiguedad debemos sin embargo asignar a cada valor numérico una secuencia distintiva e individual.
Los números representados en otros sistemas que los naturales, son
llamados códigos puesto que se les debe asignar un código (regla de
asignación) para determinar el valor numérico representado por la
secuencia. Ya hemos analizado algunos códigos utilizados en
computación e introducimos dos más: el Grey reflejado y el ASCII.
El código ASCII (American Standard Code for Information Interchange, Código Americano Estandard para el Intercambio de
Información) es un esfuerzo de los diseñadores para tener compatibilidad
entre las distintas máquinas y aplicaciones. El código se forma de 7 bits y
3-67
Ensamblador
el octavo se deja disponible para que el diseñador juege con el, ya sea
para comprobar por medio de paridad (explicada en los siguientes
capítulos) si no hay error de transmisión o manipulación de datos o para
expandir el juego de caracteres disponibles elevándolo de 27 (128) a 28
(256) (esto último es lo que se realiza en las computadoras modernas).
Las primeras 32 posiciones están reservadas para carácteres de control y
usualmente no se pueden desplegar o imprimir (hay formas de dar la
vuelta a esto). Los demás se usan para las letras, los números y toda una
serie de símbolos utilizados. En Japón y otros países donde se usa otro
tipo de letras que no son las romanas, se utilizan hasta 2 bytes (16 bits o
posiciones de unos y ceros) para que el código pueda representar todas
sus palabras. El usar este código garantiza, en muchos casos,
compatibilidad entre datos de distintas aplicaciones de forma que el
resultado de un programa pueda ser leído por otro con poco esfuerzo de
nuestra parte.
3-68
Sistemas Numéricos
Figura 3.2 Código ASCII de 8 Bits.
En el código Grey, los dos primeros números son representados en
forma natural, la siguiente serie de números son encontrados de la forma
especificada en la figura 3.2. Una imagen espejo se representa por la línea
de guiones entre los primeros dos números (en la tabla de la izquierda) y
que da lugar al punto de la reflexión. De ahí en adelante un cero es
añadido a la primera serie de números y un 1 a la segunda. Repeticiones
sucesivas del proceso de reflexión nos permiten hacer cualquier serie de
números.
La característica más importante del código Grey es que los números
cambian de uno a otro sólo en un dígito. Por ejemplo notemos que del
número 7 (0100) al 8 (1100) sólo cambia el dígito en la posición cuarta
mientras que en la representación binaria (7=0111, 8=1000) cambian 4
3-69
Ensamblador
dígitos. Encuentra aplicación en un sin número de situaciones de la que
daremos un breve ejemplo.
Supongamos que tenemos una veleta que nos dará la dirección del
viento en cualquier momento. Un circuito recibe la señal y la interpreta
mandando el resultado a un computador que analizará los datos junto con
muchos más para dar un pronóstico de tiempo. Si el circuito detecta que
más de un dígito cambia a la vez, podemos estar seguros de que hay un error en la transmisión o en el sensor y podemos descartar la lectura y
esperar a la siguiente o dar la alarma para que la situación se corriga.
Figura 3.3 Desarrollo del código Grey.
3.7 Resumen
Los números son la base de todas las representaciones formales que
realizamos. Las bases distintas a la de diez han estado en uso durante
mucho tiempo pero es la base 2 la única distinta de 10 que actualmente
usamos. Dada su sencillez, la base 2 se utiliza en las computadoras
digitales actuales. Es importante entenderla y ser capaz de realizar
operaciones sencillas fácilmente.
3-70
Sistemas Numéricos
3.7.1 Puntos Importantes del Capítulo
· El sistema decimal es un sistema de posiciones en la que a cada
sitio se le da un peso que equivale a potencias de 10.
· El sistema binario se usa extensivamente en computación.
· Existen otras representaciones de números usando unos y ceros
que hacen más fácil el tratamiento de números dentro de la
computadora como son el BCD, exceso tres, 2421 y
complementación.
· Los números negativos se representan reservando un bit para el
signo.
· El código Grey y el ASCII son usados extensivamente en
computación.
3.8 Ejercicios
3.1 Convierta los siguientes números a su equivalente binario:
a) 3910, b) 1210, c) 12310
3.2 De los números binarios obtenidos en el problema 3.1 encuentre su
equivalente hexadecimal.
3.3 Convierta los números del problema 3.1 a base 8 y base 4 (recuerde
que la base 8 sólo consta de 8 símbolos distintos 0 a 7, y la base 4 del 0 al
3).
3.4 Encuentre la equivalencia binaria de los siguientes números:
a) 3.12310, b) 0.437510, c) 1.110
3.5 Sume y luego reste en forma binaria 1210 + 1010.
3.6 Divida y multiplique en forma binaria 310ÿ1510.
3.7 Convierta los siguientes números binarios a su complemento de 1 y
de 2:
a) 101112, b) 10012, c) 11112
3.8 Haga la siguiente resta usando complementos a 1 y a 2:
111012-110112
3-71
Ensamblador
3.9 Convierta los siguientes números hexadecimales a binario:
a) BCD16, b) 63516, c) FFF16
3.10 Una regla sencilla para multiplicar dos números en cualquier base
es multiplicar los dos números en forma decimal. Si el producto es menos
que la base, se toma tal cual; si es mayor que la base, divida en decimal
entre la base y tome el residuo como el dígito menos significativo y el
cociente como el más significativo. Por ejemplo 26 x 26 = 46, 38 x 28 = 68;
pero 26 x 46 = 86 y 6ÿ8 = 1 y sobran 2 por lo que 26 x 46 = 126. Usando
esta regla, multiplique:
a) 27 x 47, b) 27 x 37, c) 54 x 44
3.8 Bibliografía
Llano Emiliano Sistemas Digitales y Arquitectura de Computadoras.
Unam 1993. Capítulo 3.
3-72
Capítulo 4
4.1 Las herramientas
Existen una multitud de métodos para codificar un programa en
ensamblador. El primero y más primitivo consiste en codificar
directamente el programa por medio de una herramienta como DEBUG;
veremos como esto es posible y por qué es un método utilizado solamente
para programas pequeños.
El segundo método consiste en utilizar un código fuente en un
procesador de palabras o cualquier otro editor que nos entregue como
resultado código ASCII y a continuación ensamblar este código para
producir un código ejecutable.
Este método es el más corriente y conveniente, para llevarlo a cabo es
necesario seguir los siguientes pasos:
· 1. Preparar el programa, su estructura, sus datos, métodos de
cálculo, etc. Este paso previo se realiza generalmente en papel o
en la multitud de programas disponibles para tal fin, como son
Procesadores de Palabra, programas de Diagramas de Flujo, etc.
· 2. Codificar el programa en un editor o Procesador de Palabra que
entregue código ASCII como salida. Se puede utilizar desde el
primitivo EDLIN, el editor de programas de PASCAL o C, hasta
un sofisticado procesador de palabras basado en Windows.
· 3. Transformar el código fuente en lenguaje de máquina por
medio de un ensamblador (por ejemplo MASM). En está fase se
corrigen los posibles errores de estructura y sintaxis del programa
fuente.
4-73
Ensamblador
· 4. Ligar el programa si el ensamblador no lo hace por nosotros,
esto es, agregar todas las rutinas externas a nuestro programa y
que se requieran para su funcionamiento.
· 5. Verificar el funcionamiento del programa ejecutable entregado
en el paso anterior. Aquí será necesario corregir los errores de
lógica y funcionamiento del programa ejecutable. Se puede
utilizar el programa DEBUG o cualquier otro comercial para
ejecutar el código paso a paso, revisar los registros, etc.
· 6. Repetir la secuencia anterior a partir del paso 2 tantas veces
como sea necesaria hasta obtener un código limpio que ejecute
según las especificaciones originales.
Como se ve de los pasos anteriores esto lleva mucho tiempo y consume
muchos recursos de dinero y esfuerzo, por lo que el paso 1 es el más
importante y en el que debemos tardarnos más para evitar tardarnos en los
demás.
4.2 La Sintaxis
Como en todo lenguaje de programación, hace falta respetar ciertas
reglas. En esta sección detallamos algunas de las reglas del ensamblador.
4.2.1 Las Reglas del Juego
El lenguaje ensamblador, como todos los demás, se forma de un juego
de instrucciones separadas en líneas conteniendo cada una de ellas (como
ya se vio en capítulos precedentes) las siguientes partes:
· Un identificador opcional.
· Un mnemónico válido que forme parte del juego de instrucciones
del procesador.
· Un operando o varios, separados por comas si el mnemónico así lo
requiere.
· Un comentario en relación a la instrucción.
Dentro del archivo fuente no se requiere de ninguna organización en especial pero se recomienda utilizar cuatro columnas para mantener la
claridad del programa y hacernos la vida más fácil.
4-74
Las Herramientas
4.2.2 Los Caracteres
El ensamblador reconoce un subconjunto de la secuencia de caracteres
ASCII disponibles. Los caracteres válidos son:
· Los caracteres del alfabeto a excepción de los caracteres
acentuados (se pueden usar en el campo de los comentarios).
· Los caracteres numéricos.
· Los siguientes símbolos:
· + - * / = ( ) [ ] ; ‘ . ! , _ : @ $ espacio tabulador CR (retorno) LF
(alimentación de línea)
Los caracteres alfabéticos mayúsculas son tratados exactamente como
los minúsculos:
MOV AX,0 = MoV Ax,0
INC BX
= inc bx
pero
‘ABC’ no es igual a ‘abc’
Aún así, se recomienda seguir algún tipo de convención propia y
respetarla siempre. A partir de este momento en el libro se sigue la
siguiente convención:
· Los comentarios en español tratando de no emplear anglicismos
ni modismos donde sea posible, siempre en minúsculas y
utilizando acentos y marcas diacríticas que hagan más fácil su
comprensión.
· Las etiquetas en español y siempre en minúsculas.
· Las instrucciones y su operandos en mayúsculas siempre.
VRAI
TRUE
EQU
EQU
1
1
;Colocar flag en 1
;set flag en uno
Aunque el segmento anterior puede ser muy claro para algunos,
preferimos:
marca
EQU
verdadero (1)
1
;Colocar bandera o indicador como
4-75
Ensamblador
Que creemos, es mucho más claro para todo mundo a excepción de
algunos cuantos privilegiados que preferimos discriminar a lo largo del
libro.
4.2.3 Los Separadores
Los diferentes elementos de un programa (instrucciones,
identificadores, etc.) deben ser separados por lo menos con un espacio.
Los operandos de una instrucción se separan entre ellos por medio de una
coma y, en este caso, el agregar uno o más espacios es opcional y se hace
muchas veces sólo por claridad:
MOV AX,0
MOV Ax, 0
MOV
AX,
MOV AX, 0
0
Son correctos pero
MOVAX, 0
es incorrecto pues no hay un espacio separador entre el mnemónico y el
operador.
4.2.4 Los Identificadores
Los identificadores son los nombres definidos por el usuario para
propósitos diversos. Representan una variable o una etiqueta o marca a la
que probablemente, pero no necesariamente, hará referencia el programa
más adelante. Los identificadores están formados por una serie de
caracteres el primero de los cuales debe de ser alfabético, no puede
contener espacios y no debe de exceder de 80 caracteres en total.
4.2.5 Palabras Reservadas
El ensamblador reconoce en tre cuatro categorías de palabras
reservadas:
· Los mnemónicos del juego de instrucciones del procesador en
cuestión.
· Las directivas del ensamblador en caso de que se use.
· El nombre de los registros del procesador en cuestión.
4-76
Las Herramientas
· En caso de usar ensamblador, las palabras BYTE, WORD y
DWORD.
En ninguno de los casos pueden aparecer estas palabras como
identificadores en un programa.
4.3 Editores
Podemos escribir y discutir largo y tendido sobre las ‘grandes’ ventajas
de un editor o procesador marca X sobre el marca Y, pero no tiene sentido
la discusión cuando para lograr un programa fuente sólo requerimos lo
mínimo indispensable de un procesador:
· Que sea lo más rápido posible de ejecutar. De ser posible que
quede residente en memoria y aún más sin estorbar o robar
memoria principal a la hora de ejecutar nuestro lenguaje de
máquina.
· Que entrege como salida un archivo de código ASCII sin
caracteres extraños ni de control que el ensamblador no puede
usar.
En suma, se puede utilizar cualquier editor o procesador por sencillo o
complicado que sea. Lo importante a resaltar aquí es que nos sintamos a
gusto con él y que nos provea de herramientas que faciliten y no
dificulten nuestro trabajo, ya de por si difícil.
Habrá gente que en este renglón se sienta en casa utilizando un editor
primitivo como EDLIN o WORDSTART y gente que no pueda trabajar
con algo menos que VENTURA. La elección depende de gustos y
necesidades. Para nuestro gusto, un editor intermedio como EDIT de las
versiones nuevas del sistema operativo MS-DOS (6.2 en adelante) que se
incluye como parte de sus utilerías, es suficiente para la mayoría de los
casos.
Si se cuenta con algún editor residente en memoria como SIDEKICK
contamos con algunas ventajas adicionales como son el tener siempre
disponible el editor desde cualquier punto ya sea dentro de DEBUG,
ejecutando el programa u otros.
Seguramente con el nuevo advenimiento de sistemas operativos más
poderosos basados en ventanas e iconos, el panorama se modificará pero
4-77
Ensamblador
se debe recordar que más nuevo y complicado no necesariamente quiere
decir mejor.
4.4 Debug
En los siguientes capítulos utilizaremos el programa DEBUG debido a
que es un programa más sencillo de usar en principio que MASM y nos
pone en contacto íntimo con la máquina. Para teclear, probar y corregir
un código sólo necesitamos ejecutar DEBUG mientras que para usar
MASM se requiere de hasta 4 programas (editor, MASM, LINK y
EXE2BIN).
Al usar un programa como DEBUG nos ahorramos, por el momento,
toda una serie de directivas del ensamblador que causan que el programa
ejecutable crezca.
Por último, al usar DEBUG nos ponemos en contacto íntimo con la
máquina. Tarde o temprano si se continua programando en ensamblador
se requiere aprender a usar el DEBUG.
Tabla 4.1 DEBUG contra Ensamblador
DEBUG
Ensamblador
F á c i l d e Difícil de ejecutar
ejecutar
No
se Se agregan muchas líneas de código al
a g r e g a programa ejecutable
nada
al
programa
ejecutable
M u y Nos aleja del funcionamiento interior de
cercano a la la máquina
máquina
No es tan Muy versátil
versátil
Bueno para Bueno para programas grandes
programas
pequeños
Debug es práctico para modificar registros de la UPC, localidades de
memoria, cargar, crear e iniciar programas.
Para ejecutar DEBUG basta tenerlo en la memoria secundaria presente
e invocarlo con la instrucción:
C>DEBUG
4-78
Las Herramientas
-
El guión que aparece a continuación es el indicador de que DEBUG se
encuentra en memoria activo y listo a recibir nuestros comandos.
4.4.1 Comandos
El DEBUG acepta toda una serie de comandos sencillos de una letra que
describimos brevemente a continuación:
(Los argumentos entre paréntesis cuadrados indican parámetros
opcionales)
?. Despliega ayuda (en versiones nuevas de DEBUG).
D[dirección]. Despliega, despliega la información de cierta área de
memoria en la pantalla (en hexadecimal).
F[dirección inicial] [dirección final] [constante]
E[dirección]. Introduce un programa usando códigos hexadecimales o
modifica una dirección colocando los nuevos códigos hexadecimales.
A[dirección]. Introduce un programa usando mnemotécnicos válidos
del juegos de instrucciones.
G[dirección]. Ejecuta un programa en memoria.
U[dirección inicial][,dirección final]. Desensambla un programa en
memoria, esto es, convierte sus códigos hexadecimal a mnemotécnicos.
R[registro],. Visualiza o modifica registros de la UPC.
Nnombre.extensión. Informa a Debug el nombre del programa que
queremos recuperar o guardar.
W. Escribe programa en memoria a disco, para esto es necesario haberle
puesto nombre con el comando N e indicar su longitud (registro BX+CX)
con el comando R
Q. Salir del programa DEBUG.
L. Carga un programa en memoria. Es preciso primero indicar qué
programa queremos con el comando N. Otra alternativa es invocando el
programa DEBUG [nombre programa con extensión].
4-79
Ensamblador
Ejercicio 4.1 Introduciremos un pequeño programa utilizando DEBUG
y lo ejecutaremos:
C>DEBUG
-E100
Introduzcamos esta serie de números seguidos cada uno de la barra
espaciadora: B2 1 B4 2 CD 21 CD 20. Al introducir el último número
hexadecimal presionemos la tecla de re torno en lugar de barra
espaciadora.
Verifiquemos: -D100 y ejecutemos: -G
Ejercicio 4.2 Volvamos a introducir nuestro programa utilizando ahora
los mnemotécnicos correspondientes. Por el momento no nos importa
que significan. Termine cada línea con Retorno.
-A100
MOV DL,1
MOV AH,2
INT 21
INT 20
El la última línea presionamos Retorno para terminar con el programa y
lo ejecutamos: -G
Problema 4.1 Usando el manual de DEBUG averigüe cómo guardar su
programa utilizando los comandos R, N, W. Ejecute ahora el programa
desde el Sistema Operativo. Escoja un nombre que termine con la
extensión .COM.
4-80
Las Herramientas
4.5 MASM
Para el uso de un ensamblador el primer paso consiste, como ya se
mencionó, en diseñar el programa en papel, a continuación en pasarlo a
un editor que entrega ASCII. Como ejemplo introduzcamos nuestro
programa de la sección anterior, para lo cual se requieren agregar otros
comandos propios del ensamblador:
cara
SEGMENT
ASSUME CS:cara
MOV DL,1
MOV AH,2
INT
21h
INT
20h
cara
ENDS
END
Se guarda el programa con cualquier nombre seguido de una extensión
.ASM, por ejemplo PRUEBA.ASM. Más adelante explicaremos las
líneas adicionales. Lo siguiente es ensamblar el programa:
C>MASM prueba
El ensamblador pregunta por el nombre de tres archivos que por el
momento ignoramos, menos el segundo (LST, listado) que dirigimos a la
impresora con LPT1:. El siguiente paso es ligar el programa con las
rutinas externas al programa o con otros programas pues el ensamblador
sólo nos entrega un archivo con extensión OBJ de objeto, código que aún
no es ejecutable.
C>LINK prueba
Al final contaremos con un archivo llamado PRUEBA.EXE el ligador
nos entrega un error que por el momento ignoramos pues no se especificó
el inicio de la pila en el programa. Finalmente convertimos el programa
EXE a uno de tipo COM que explicaremos en la siguiente sección:
C>EXE2BIN prueba prueba.com
4.5.1 Las Instrucciones del Ensamblador
El ensamblador cuenta con una serie de instrucciones para hacer más
fácil la tarea del programador. Estas instrucciones, conocidas como
4-81
Ensamblador
pseudo - instrucciones, no son parte del juego de instrucciones de ningún
procesador en especial sino que sirven para indicar ciertas acciones al
ensamblador.
Analizaremos a continuación algunas de las más importantes.
4.5.1.1 Segmentos
La instrucción SEGMENT y ENDS informan al ensamblador en qué
segmento se localiza el programa y deben contar con una etiqueta que
defina el nombre.
La pseudo instrucción ASSUME CS:nombre define el segmento donde
se localiza el programa (definido con el registro CS, segmento de código)
¿Por qué el ensamblador no lo puede definir por sí mismo? La razón no es
clara pero es necesario incluir la pseudo instrucción.
4.5.1.2 END
Indica el fin del archivo de programa
4.5.1.3 Definiciones
Es necesario muchas veces definir variables, palabras, bytes, etc. para
aclarar el significado de ciertas partes del código. Entre las instrucciones
para realizar esto tenemos EQU que asigna valores a etiquetas:
si
escape
EQU
EQU
1
27
;identificador si=1
;identificador escape=27
El valor especificado puede ser también una asignación completa
evaluada al momento de ensamblar:
cinco
seis
definido)
EQU
EQU
2*2+1
cinco+1
;asigna el valor a cinco de 5
;asigna el valor de seis (cinco ya
Los números pueden estar en binario (1011b), en hexadecimal (0AF2h)
o en decimal (123 ó 123d) sólo es necesario colocar el identificador
respectivo en cada número. En el caso de decimal no es necesario y en el
caso hexadecimal, si comienza por letra, es necesario colocar un 0 para
evitar que el ensamblador se confunda con una etiqueta.
4-82
Las Herramientas
Para la definición de variables se utilizan las directivas DD, DB y DW.
El comando DB define bytes que pueden tener un valor cualquiera entre 0
y 255 decimal:
estado
xyz
tipo Byte
no_se
tabla
aún
tabla1
1,2,etc.
tabla2
0,1,0,1,etc.
DB
DB
0
1,2,3
;define estado inicial
;define una zona de tres elementos
DB
DB
?
5 DUP(?)
;un valor no definido aún
;tabla de 5 elementos no definidos
DB
5 DUP(1,2,3,4,5)
;Tabla de 25 elementos inicializados a
DB
10 DUP(0,1)
;Tabla de 20 elementos inicializados a
La pseudo - instrucción DUP es opcional en operadores como DB, DW
o DD y su propósito es hacer duplicado de sus argumentos. El número
que precede a la instrucción indica cuántas veces se quiere hacer el
duplicado.
La directiva DW, de la misma forma que DB, define variables pero en
este caso de 16 bits (una palabra o Word en inglés). El mismo caso sucede
para DD que define palabras dobles o 32 bits.
Estas directivas pueden también servir para cadenas de caracteres:
mensaje
salta
alarma
DB
DB
DB
‘Bienvenido a Ensamblador$’
‘Salta la línea’,13,10,’$’
07,’Mensaje con sonido’,’$’
Note que al final se agrega el signo de $ que indica el fin del mensaje y
sólo es necesario si se intenta desplegar la cadena en la pantalla.
4.6 Diferencia entre COM y EXE
Tanto los archivos tipo COM como EXE pueden ser cargados en la
memoria principal y ejecutados ¿Entonces por qué preferir uno sobre
otro y cuáles son sus diferencias?
Un archivo tipo COM es la forma más fácil de guardar un programa
ejecutable. Consiste únicamente de una serie de números en binario que
conforman el programa. No existe ninguna otra información además del
programa por lo que ocupa el menor espacio posible en memoria
secundaria. Los archivos tipo EXE contienen un encabezado que
contiene información variada requerida para funcionar, lo que hace que
4-83
Ensamblador
el programa sea un poco más grande. Si el programa objeto es de 100
bytes, el archivo COM será de 100 bytes, mientras que el EXE tiene una
longitud mínima de 640 bytes, aun cuando el programa se forme de 1 sólo
byte.
Puesto que son más pequeños y compactos, los programas COM son
ejecutados más rápido. Un programa del tipo COM puede utilizar una
instrucción del tipo INT 20 para terminar su ejecución, mientras que los
EXE requieren de una instrucción tipo RET.
Por otro lado existen desventajas en usar archivos tipo COM:
· No pueden ocupar más de 64K bytes de memoria (la longitud de
un segmento) lo que puede ser desventaja para un programa muy
grande.
· No pueden ser ligados con otros archivos restando flexibilidad al
lenguaje. El propósito del programa LINK es el de unir varios
programas en uno sólo ejecutable lo que acarrea toda una serie de
ventajas en sistemas grandes.
· No pueden usar con facilidad distintos segmentos de memoria,
mientras que en los tipo EXE esto se realiza sumamente fácil.
· Se requiere de un paso extra, si usamos ensamblador, para
convertir del tipo EXE a COM al tener que usar EXE2BIN.
Considerando todo esto es preferible comenzar con archivos tipo COM
y luego evolucionar a los del tipo EXE.
4.7 Otros
Existen en el mercado otras herramientas profesionales para la
programación en ensamblador. Incluimos tres herramientas de la
categoría Shareware en un disco de 3 1/2" que pueden sernos de utilidad,
en especial cuando no contamos con ninguna otra para practicar.
Recuerde que si el programa resulta útil es muy conveniente registrarse
con el autor. Recibe uno el derecho legal a usarlo, actualizaciones,
manuales, etc.
Vale la pena mencionar el programa Language Window (Shareware)
que coloca una tabla del juego de instrucciones 80286 en la memoria y
puede ser activado en cualquier momento.
4-84
Las Herramientas
4.8 Esqueleto de Programa en Ensamblador
Presentamos el esqueleto que sugerimos sea usado para ensamblar
programas.
pila
DB
pila
datos
;nombre del programa
;con descripción
;y variables utilizadas
;autor, fecha, versión
;——————————————————;definiciones con la instrucción EQU
;——————————————————SEGMENT STACK
;define la pila
20 DUP(‘pila’)
;con la palabra ‘pila’
ENDS
;——————————————————SEGMENT
;define segmento de datos
;datos aquí con DB (datos DB
‘hola$’)
datos
ENDS
;——————————————————prognom SEGMENT
;define segmento de código
;——————————————————principal PROC FAR
;parte principal del programa
ASSUME
CS:prognom, DS:datos
comienzo: ;aquí programa principal
;prepara pila para el regreso
PUSH DS
;salva el viejo segmento de datos
SUB
AX,AX
;coloca cero en AX
PUSH AX
;y guárdalo
;prepara DS con segmento actual
MOV AX,datos
;dirección del segmento de datos
MOV DS,AX
;y guarda en registro DS
;La parte principal programa va aquí
RET
;regresa al S.O.
principal ENDP
;——————————————————subr1
PROC NEAR
;define una subrutina
;Aquí va la subrutina
subr1
ENDP
;——————————————————prognom ENDS
;fin del segmento de código
;——————————————————END
comienzo
;fin del ensamble
4-85
Ensamblador
Se debe hacer notar aquí la incialización del segmento de pila en cuya
memoria reservada se coloca la palabra “pila” muchas veces, de esta
forma es muy claro donde comienza la pila para propósitos de espulgar el
programa y nos damos inmediatamente cuenta del contenido modificado
de la pila.
4.9 Resumen
Se introduce y comentan sobre las principales herramientas para
ensamblar un programa: DEBUG y MASM. Estos programas son
sencillos de usar pero a su vez constan de una serie de comandos que
deben ser aprendidos antes de poder utilizarlos en toda su capacidad. En
este capítulo se establecen las reglas que serán utilizadas a lo largo del
libro.
4.10 Bibliografía
Llano Emiliano Sistemas Digitales y Arquitectura de Computadoras.
Unam 1993.
Mercier Philippe Assembleur Facile. Marabout 1992, Alleur Bélgica.
Varios Macro 86. Columbia Data Products Inc. USA 1986.
4-86
Capítulo 5
5.1 Entrada/Salida
La transferencia de datos entre la lógica que forma parte del sistema de
computadora y aquella que está más allá de éste, se conoce en general
como entrada/salida o E/S (input/output o I/O).
Se incluye dentro del ámbito del sistema a toda la lógica que se haya
diseñado para trabajar en conjunción con la UPC. Toda aquella que no
caiga dentro de esta clasificación se le conoce como externa.
La interfase entre el sistema de computación y la lógica externa debe
estar claramente definida; debe proveer de facilidades para transferir
datos, además de las señales de control que identifiquen a todos los
eventos que ocurren.
Hay muchas formas en las que un sistema puede transferir datos hacia el
exterior; pero todos están incluídos en las siguientes tres categorías:
1) E/S Programada. En este caso todas las transferencias de
datos entre la computadora y el exterior son controladas por la
computadora o para ser más precisos por un programa corriendo
en la computadora. Existirá un protocolo bien definido en el que
la computadora hace conocer a la lógica externa que los datos
están disponibles en una localidad de memoria fija de donde ésta
los puede tomar, o bien, la computadora indicará de alguna
forma a la lógica externa que está esperando que ponga
información en ciertas localidades de memoria para poder
accesarlas. La característica clave de la Entrada/Salida
5-87
Ensamblador
Programada es que la lógica externa hace exactamente lo que se
le dice que haga.
2) E/S por Interrupción. Las interrupciones son una forma
que tiene la lógica externa para forzar a la computadora a poner
atención y suspender lo que está haciendo para atender a las
necesidades de la lógica externa.
3) Acceso Directo a Memoria. Esta es una forma de transferir
datos entre la memoria interna y los dispositivos externos sin
involucrar a la Unidad de Procesamiento Central en la lógica de
la transferencia.
5.2 Entrada/Salida Programada
Los datos son transferidos entre el sistema de computación y la lógica
externa, en cualquier dirección, por medio de un puerto de entrada y
salida. Un puerto de entrada y salida consiste en un puerto con memoria
de entrada y salida (buffer) conectado a las líneas de datos del bus del
sistema y a las interconexiones que accesan a la lógica externa.
Cuando la lógica externa transmite datos al sistema de cómputo, lo hace
presentando los datos en las conexiones del puerto de entrada y salida,
por lo que este mismo puerto almacena provisionalmente estos valores.
El puerto de entrada y salida no puede estar comunicándose
constantemente con las líneas del bus de datos, puesto que estas líneas
pueden estar llevando información de o hacia la memoria. Si las
conexiones del puerto de entrada y salida se comunicaran
permanentemente con el bus de datos, cada vez que la lógica externa
presentase datos a las conexiones de entrada y salida, los datos se
propagarían por las líneas del bus de datos con consecuencias
imprevisibles. La UPC debe, por lo tanto seleccionar un puerto de E/S y
leer el contenido del puerto en la misma forma que lo hace de una
dirección de memoria.
Como los datos se leen de los puertos de E/S tal como si se tratase de una
localidad de memoria, a esta forma de transferencia de E/S se le conoce
también como E/S de mapa de memoria (memory mapped I/O). Este
tipo de esquema permite a la computadora utilizar las mismas
instrucciones poderosas de lectura y escritura tanto para localidades de
memoria como para los puertos de Entrada/Salida. Por ejemplo, se
pueden realizar operaciones aritméticas directamente con los puertos sin
necesidad de almacenar los datos en memorias provisionales.
5-88
Lógica más allá de la UPC
Normalmente existe más de un dispositivo externo por lo que debe de
haber más de un puerto de entrada y salida. Una forma rudimentaria, pero
muy utilizada en sistemas pequeños, de obtener uno o más puertos de
entrada y salida consiste en dividir las líneas de dirección de la UPC entre
la memoria y los puertos. Por ejemplo, si tomamos una línea de dirección
cualquiera de un sistema de microcomputadora y escogemos que cuando
su valor sea 0 accese a la memoria y que cuando éste sea 1 accese a los
puertos de entrada y salida, tendremos dividida la memoria en dos áreas
bien diferenciadas, una de las cuales nos sirve para comunicarnos con los
dispositivos externos y la otra con la memoria principal.
La penalidad pagada cuando una línea de dirección es utilizada para
seleccionar el buffer de un puerto de entrada y salida, es que el mapa de
memoria se reduce automáticamente a la mitad, lo que en la mayoría de
los sistemas de medios a grandes es un precio muy severo a pagar,
solamente para seleccionar un puerto.
Un método preferido es agregar a la UPC más líneas que controlen la
transferencia de y hacia los puertos de entrada y salida.
Desafortunadamente la transferencia a ciegas de datos de un sistema de
computación y la lógica externa, no siempre provee la suficiente
capacidad de entrada y salida. Las siguientes características no existen en
este sistema:
1.- El sistema de cómputo debe decir a la lógica externa cuando los
datos han sido colocados en el buffer de entrada y salida y por lo tanto
están listos para ser leídos; de la misma forma la lógica externa debe de
indicar a la computadora cuando ha colocado datos en el buffer de
entrada y salida, y éstos pueden ser leídos.
2.- Tanto la computadora como la lógica externa deben de tener
alguna forma de informarse entre ellas la naturaleza de los datos que
van a intercambiar. Claramente los datos que se están transfiriendo
pueden ser sujetos a varias interpretaciones, por ejemplo pueden ser
datos puros binarios, algún código que identifique alguna operación a
ejecutarse; pueden ser parte de una dirección o una dirección
completa.
Cuando la computadora transmite señales a la lógica externa como un
medio para identificar eventos o datos se refiere a estas señales como
controles de entrada y salida. La misma información viajando en sentido
5-89
Ensamblador
opuesto, esto es de la lógica externa hacia la computadora, se le conoce
como estado de entrada y salida. La lógica externa no puede controlar a la
computadora, sólo puede enviar información de su estado para que la
computadora la interprete.
Los sistemas de computación usualmente tienen toda una serie de
controles de entrada y salida así como líneas de estado que son separadas
y distintas de los puertos de entrada y salida. Las computadoras
usualmente asignan uno o más puertos de entrada y salida para funcionar
como conductores del control y del estado, mientras que otros puertos de
entrada y salida son los encargados de transferir los datos. Es solamente
la forma en que la UPC interpreta los datos que hacen la diferencia entre
información de datos, control o estado de los dispositivos.
5.3 Entrada/Salida por Interrupción
Casi todas las UPC tienen una línea por la cual la lógica externa puede
solicitar atención inmediata. A ésta se le conoce como señal de petición
de interrupción puesto que por medio de ella la lógica externa pide sea
interrumpido lo que se esté haciendo y se le preste atención.
Nótese que aunque nos hemos referido a la lógica externa entregando
datos, el flujo de información bien puede ser en el sentido contrario. De
hecho, nada nos dice que una salida o entrada de datos tenga que suceder
después de una interrupción, bien pueden ser señales de control las que se
transmitan.
El propósito de la interrupción es informar a la UPC que suspenda lo
que está haciendo, procese los datos que se están transmitiendo desde el
puerto de E/S y reanude lo que suspendió.
La característica más importante de toda esta serie de eventos es que son
asíncronos e impredecibles.
5.3.1 Respuesta de la UPC a una Interrupción
En su forma más elemental, la Unidad de Proceso Central puede responder a una interrupción cargando en el registro Contador de Programa
(PC) la dirección inicial del programa que atiende a la interrupción pero
esto nos deja con dos preguntas:
5-90
Lógica más allá de la UPC
–
–
¿Qué pasa con el programa que está en ejecución?
¿De dónde obtiene la UPC la dirección del programa que la
lógica externa necesita que se ejecute?
El programa que se está ejecutando en memoria tiene información
almacenada en los registros de la UPC que puede ser importante: el
acumulador (A), el Contador de Programa (PC), el Contador de Datos
(DC) y otros registros existentes en la UPC. Si el nuevo programa a
ejecutar no tiene algún mecanismo de guardar toda esta información, se
corre el riesgo de no poder volver a ejecutar el programa que estaba en
primer lugar. Si guardamos los valores de los registros con sólo
restituirlos a los registros correspondientes, en especial el Contador de
Programa, podemos regresar al sitio en el que suspendimos la ejecución.
Una interrupción no se reconoce hasta terminar con la instrucción que
se está ejecutando, por lo que el registro de Instrucción (I) es quizá el
único que no importe su valor pues inmediatamente es substituido por la
instrucción próxima a ejecutarse.
Existen dos formas de guardar la información necesaria para poder
volver al punto donde se interrumpe un programa antes de comenzar el
que atiende a la interrupción:
–
1) La ejecución de un microprograma almacenado en la Unidad
de Control que automáticamente guarda los registros por
nosotros en un área predeterminada de memoria. A esta área de
memoria reservada se le llama Pila (Stack) (en algunos diseños
se guardan en un juego de registros paralelos).
– 2) Dejar que el programador ejecute primero una rutina de
servicio a la interrupción que se encarge de almacenar los datos
de los registros en un lugar seguro.
Al final de la ejecución del programa de interrupción, no importando
que método esté disponible, se deben realizar los pasos en orden
contrario para restaurar los registros correspondientes y volver al sitio
del programa donde éste fue interrumpido.
Ahora consideremos la forma de obtener la dirección donde se ejecutará
la rutina que atiende a la interrupción. Si se trata de un sólo caso,
5-91
Ensamblador
podemos considerar el tener una dirección de memoria fija que sepa de
antemano la UPC, pero en el caso de múltiples puertos de entrada y salida
esto resulta un poco impráctico.
Considere la forma en que la UPC responde a una interrupción:
–
–
1) Manda la señal de aceptación de la interrupción.
2) Guarda de forma automática todos los registros o permite al
usuario guardarlos de alguna manera.
– 3) Mueve el contenido del vector de interrupciones que manda
el puerto de E/S, al Contador de Programa para comenzar a
ejecutar un programa en otra localidad de memoria.
– 4) Realizar los pasos en sentido contrario para regresar al punto
del programa donde éste fue interrumpido.
Utilizar un vector de interrupciones que forme la dirección en memoria
donde se encuentra el programa que atiende a la interrupción,
proporcionado por el dispositivo que interrumpe, nos permite tener gran
flexibilidad al momento de definir nuestras rutinas.
En forma funcional podemos comparar una interrupción con una
subrutina de un lenguaje de alto nivel.
5.3.2 Código de Selección de un Dispositivo que Interrumpe
Otro de los esquemas muy utilizados consiste en exigir que el
dispositivo que interrumpe a la UPC mande un código que identifique
qué puerto de E/S es el que interrumpe.
Si se manda un código de selección del dispositivo, podemos entonces
accesar a una tabla en memoria que nos dé el vector de dirección de
interrupción que cargaremos al Contador de Programa para ejecutar la
rutina de interrupción.
Para esto se exige un poco más de la lógica externa pues debe ser capaz
de almacenar su código de selección; existen para esto en el mercado
varios tipos de circuitos programables de interfase de E/S (PIO, PIA, etc.)
La secuencia de eventos es como sigue:
–
1. La lógica del dispositivo externo crea una señal de petición de
interrupción que transmite a la UPC.
5-92
Lógica más allá de la UPC
–
2. Cuando la UPC está lista a dar servicio a la interrupción,
responde con una señal de aceptación.
– 3. Al recibir la señal del procesador, la lógica del dispositivo
externo coloca un código de identificación (de X número de bits)
en el bus de datos del sistema. La UPC recibe los datos y los
interpreta como un código de identificación que usa para armar la
dirección donde se encuentra localizado el programa que atiende
a la interrupción en cuestión.
– 4. Siguiendo un protocolo especificado por el sistema, el
dispositivo externo coloca ahora sus datos a través del puerto de
entrada y salida para que éste lo transmita al bus del sistema
cuando se le solicite.
Generalmente se pueden deshabilitar las interrupciones con alguna
instrucción del juego de instrucciones que lo permita, pero existe otro
tipo de interrupciones que exigen el máximo de atención y no pueden
esperar. A este tipo de interrupciones con tan alta prioridad se les conoce
como no mascarillables y no pueden ser deshabilitadas. Generalmente se
reserva esta línea de muy alta prioridad a eventos tales como falla
inminente de energía o un error del sistema que impide que éste pueda
seguir trabajando, tal como un error de memoria.
5.4 Acceso Directo a Memoria
Como ya se observó en las secciones precedentes, el objetivo de la
mayoría de las operaciones de entrada y salida es la transferencia de datos
entre los dispositivos externos y la UPC. Ya sea la E/S programada o la de
interrupciones, se requiere de la participación activa de la UPC, esto es,
la ejecución de instrucciones para leer o escribir tanto a los dispositivos
externos como a la memoria interna (principal). Esto puede no ser tan
rápido como se piensa para ciertos procesos que requieren de extremada
velocidad como es el caso de la transferencia del contenido de memoria a
la pantalla de visualización (CRT) o de y hacia la memoria de
almacenamiento masivo como son los discos flexibles y duros de un
sistema. Una vez más, la solución consiste en substituir los programas
por electrónica que realice la misma función pero a más velocidad.
La solución lógica consiste en diseñar una computadora de propósito
específico que se dedique exclusivamente a la transferencia de datos de y
hacia la memoria. A tal circuito se le conoce como dispositivo de Acceso
Directo a Memoria o por su siglas en inglés como DMA.
5-93
Ensamblador
Una discusión completa del tema queda fuera del rango que pretende
abarcar este libro por lo que, una vez más, referimos al lector interesado a
la bibliografía para más detalles del tema.
5.5 Realización en la Familia Intel
Los procesadores de la familia Intel, 80x86 y Px, no escapan a estas
imposiciones de servicio y requieren de un esquema complejo de
interrupciones y de acceso directo a memoria.
Una de las características interesantes de este circuito es que contiene
dentro de su juego de instrucciones una instrucción que nos permite
simular una interrupción por medio de programa (instrucción INT).
Aunque contar con una instrucción que simule las interrupciones por
programa pueda sonar extraño e innecesario en un principio, baste saber
que los diseñadores pasan muchas horas diseñando y programando la
realización de rutinas eficientes para muchas de las operaciones que se
realizan dentro de un sistema de cómputo. Tener acceso a estas rutinas
nos da gran flexibilidad y facilidad de programación como veremos en
los siguientes capítulos.
La lista de interrupciones y sus funciones es enorme y hay libros
dedicadas a ellas. Para nuestros propósitos usaremos algunas cuantas que
iremos describiendo en cada caso.
5.6 Formas de Direccionamiento
Como ya se ha mencionado, las instrucciones especifican los siguientes
elementos dentro de una UPC:
· La operación a realizar (suma, mueve datos, resta, etc.)
· Los elementos a manipular en la UPC, ya sea en la memoria o en
los registros de trabajo.
Para acceder a una información en la memoria, la UPC debe saber su
dirección y su longitud.
Existen distintas posibilidades de presentación de una localidad de
memoria. Analizaremos en detalle cada forma de direccionamiento pues
se encuentran en casi todas las instrucciones a utilizar.
5-94
Lógica más allá de la UPC
Es importante resaltar que en ninguna instrucción (generalmente)
se permite como operandos dos direcciones de memoria.
Ilustraremos nuestros ejemplos con la instrucción mover (MOV, move)
que es una instrucción que se encuentra en el juego de instrucciones de
toda UPC. Esta instrucción se encarga del movimiento de datos de la
memoria a los registros, de los registros a la memoria, de la memoria a la
memoria o entre registros dependiendo de sus argumentos y del
procesador que se trate.
La forma general de esta instrucción es:
MOV
destino, fuente
pero nada nos indica que no pueda ser exactamente al revés. El
diseñador tiene en esto la última palabra. Dentro del sistema Intel se sigue
la primera convención y la instrucción tiene cinco formas:
MOV
MOV
MOV
MOV
MOV
registro, constante
memoria, constante
registro, memoria
memoria, registro
registro, registro
Nótese que no hay forma de mover entre localidades de memoria sin
pasar antes por un registro.
Las constantes o localidades de memoria pueden estar definidas con
etiquetas si se usa un ensamblador, lo que facilita enormemente la tarea
de llevar el control de estas localidades o constantes (revise pseudo
instrucción ASSUME y EQU en capitulo anterior).
Por el funcionamiento del procesador que estamos analizando, la
dirección se divide en segmento+dirección dentro del segmento
representado como:
Segmento:Desplazamiento
Recuerde que el registro por omisión para datos es siempre DS
(segmento de datos) de forma tal que en lugar de usar
MOV
MOV
AX,DS:[BX]
AX,DS:[SI+2]
se usa
5-95
Ensamblador
MOV
MOV
AX,[BX]
AX,[SI+2]
Como siempre existen excepciones que confirman la regla:
Si se usa el registro BP (apuntador base), el segmento por omisión será
SS (segmento de pila).
Si el programador decide usar un segmento alterno. Este se debe
especificar en la instrucción:
MOV
AX,CS:[BX]
La dirección efectiva o real se forma en los sistemas 80x86 desplazando
4 bits a la izquierda el contenido del registro de segmento y luego
agregando el contenido del registro especificado. Si hablamos de 16 bits
nos da un número de 20 bits (FFFFFF hexadecimal) para la dirección ó 1
Megabyte de memoria directa. Para las aplicaciones actuales está
memoria es insuficiente por lo que se usa memoria virtual para tratar de
mantener la compatibilidad con diseños anteriores a la vez que se trata de
empujar el estandard a nuevas dimensiones, empantamiento que tarde o
temprano tendrá que romperse y crear una nueva generación
independiente.
Resumiendo:
segmento 0000 (desplazado 4 bits a la izquierda)
+
registro
dirección efectiva de 20 bits
Presentamos en la tab la 5.1 un resumen de los tipos de
direccionamiento.
5-96
Lógica más allá de la UPC
Tabla 5.1 Formas de Direccionamiento
Inmediata
registro valorîinmediato
Directa
registroîdirección de memoria
Base
registroîDS:[BX] o SS:[BP]
Indice
registroîDS:[SI] o DS:[DI]
Indice y
base
registroî[BX][SI], [BX][DI],
[BX+SI], [BP][SI], [BP][DI] o
[BP+SI]
Base y
registroîident.[BX], ident.[BP] o
Desplazam
[BX+constante]
iento
(Directo o
Inmediato)
Indice y
Desplazam
iento
(Directo o
Inmediato)
registro ident.[SI], ident.[DI] o
[SI+constante]
Base,
Indice y
Desplazam
iento
(Directo o
Inmediato)
registroîident.[BX][SI],
ident.[BX][DI], ident.[BP][SI],
ident. [BP][DI]
5.6.1 Inmediata
RegistroîValor inmediato
El contenido de un registro es inicializado por un valor inmediato o una
constante.
Ejemplos:
MOV AH,12
MOV BX,1
;AHî12, AL no es afectado,
; el movimiento es de 8 bits (1 byte)
;BXî1, tanto BL como BH se afectan,
5-97
Ensamblador
; movimiento de 16 bits (1 palabra)
El nombre del registro indica el número de bits implicados en la
operación (L o H de 8 bits=1 byte, X de 16 bits=1 palabra)
5.6.2 Directo
Registroîdirección de memoria
El contenido de un registro es sustituido por el contenido de la zona de
memoria apuntada por el identificador. La operación puede ser de 8 ó 16
bits y si los dos operandos no coinciden, se genera un error al ensamblar.
Ejemplos:
;define los apuntadores a memoria
Ident1
DB
2
Ident2
DW
5
Ident3
DB
?
;Operaciones de memoria a un registro
MOV AH,ident1
MOV BX,ident2
;Operaciones de registro a memoria
MOV ident1,CH
MOV AH,ident2
registro de 8 bits
;apuntador de 1 byte
;apuntador de 2 bytes=1 palabra
;define una zona de 1 byte
;AHî2 (8 bits)
:BXî5 (16 bits)
;ident1îCH (8 bits)
;ERROR, operación de 16 bits a un
5.6.3 Base
RegistroîDS:[BX]
RegistroîSS:[BP]
Inicializa un registro con el contenido del área de memoria situada en la
dirección DS:[BX]. En este caso [BX] representa el contenido de la zona
de memoria cuya dirección está contenida en BX. La operación puede ser
de 8 ó 16 bits. Quedará esto más claro con los siguientes ejemplos:
MOV
MOV
BX,8
AX,[BX]
;BXî8
;AXîcontenido de la zona de memoria
DB
DW
2
5
;dirección DS:0008
;Define una zona de 1 byte
:Define una zona de 2 bytes
de la
Ident1
Ident2
5-98
Lógica más allá de la UPC
MOV
MOV
BX,OFFSET ident2
AX,[BX]
;BXîDesplazamiento de ident2
;AXîde la zona de memoria a la
dirección
; DS:ident2 (ident2=5)
En las dos líneas precedentes obsérvese que la pseudo instrucción OFFSET indica un desplazamiento a partir de la variable. Como la variable
contiene 2 se indica un desplazamiento de 2 bytes, el propio de IDENT1
(2), el de IDENT2 parte alta (0) y finalmente el de IDENT2 parte baja (5).
Una forma alternativa de este direccionamiento es con el uso del
registro BP con el apuntador de segmento SS:
MOV
MOV
BP,0AFFFh
AX,[BP]
;AXîcontenido de la zona de memoria
; apuntado por SS:BP (afff)
5.6.4 Indice
RegistroîDS:[SI]
RegistroîDS:[DI]
Este modo de direccionar se utiliza generalmente para las cadenas de
caracteres o para aislar un elemento de una tabla o matriz. Generalmente
el segmento utilizado es DS salvo en las excepciones que resaltaremos en
ejemplos.
MOV SI,0
MOV AX,[SI]
dirección DS:0
MOV DI,0
MOV [DI],AH
DS:0îcontenido de AH
;AXîcontenido de la memoria en la
;Zona memoria apuntado por
5-99
Ensamblador
5.6.5 Indice y base
RegistroîDS:[BX][SI]
RegistroîDS:[BX][DI]
RegistroîDS:[BX+SI]
RegistroîSS:[BP][SI]
RegistroîSS:[BP][DI]
RegistroîSS:[BP+SI]
Este tipo de direccionamiento utiliza BX y SI o DI, la base o segmento
es DS. Es posible usar también la notación [BX+SI], [BX+DI].
MOV
MOV
MOV
BX,2
SI,5
AH,[BX][SI]
;AHîcontenido de la dirección de
;memoria DS:7 (BX+SI)
Se habla de un segmento base puesto que frecuentemente se utiliza para
referenciar los elementos de una tabla, de ahí el nombre de base (del
inicio de la tabla):
tabla
DB
MOV
MOV
20 DUP(?)
BX,OFFSET tabla
SI,2
;define tabla de 20 elementos
;BXîdirección base de la tabla
;Se va a leer el tercer elemento de la
MOV
MOV
AH,[BX][SI]
SI,8
;(siendo el primero 0)
;AHî3er elemento de la tabla
;Apuntar al noveno elemento de la tab-
MOV
AL,[BX][SI]
;ALî9no elemento de la tabla
tabla
la
La segunda forma del direccionamiento índice y base es el que utiliza a
BP y SI o DI como apuntador donde SS es el segmento base por omisión.
MOV
MOV
MOV
5-100
BP,2
SI,5
AH,[BP][SI]
;AHî contenido de la memoria a
; la dirección SS:[BP+SI] (SS:5+2)
Lógica más allá de la UPC
5.6.6 Base y Desplazamiento (Directo o Inmediato)
RegistroîDS:ident.[BX]
RegistroîSS:ident.[BP]
RegistroîDS:ident.[BX+constante]
Una combinación de los precedentes tipos de direccionamiento
(elementos dentro de una tabla).
Ident1
DW
MOV
MOV
5 DUP(?)
BX,2
AX,ident2[BX]
;Zona de 10 bytes (5 palabras)
;AXî3er elemento de la tabla Ident2
5.6.7 Indice y Desplazamiento (Directo o Inmediato)
Registroîident.[SI]
Registroîident.[DI]
Registroî[SI+constante]
Otra combinación de los precedentes (elemento dentro de un registro).
MOV
MOV
SI,4
AX,[SI+1]
;AXîcontenido de la zona de memoria
;apuntado por DS:4+1
5.6.8 Base, Indice y Desplazamiento (Directo o Inmediato)
Registroîident.[BX][SI]
Registroîident.[BX][DI]
Registroîident.[BP][SI]
Registroîident.[BP][DI]
Registroî[BX+SI+ident.]
Este tipo de direccionamiento complicado es utilizado para direccionar
a estructuras (elemento dentro de un registro que está dentro de una base
de datos).
Ident1
DB
MOV
MOV
MOV
2
;Zona de 1 byte
BX,5
SI,4
AX,ident1[BX][SI+1]
;AXîcontenido de la zona de memoria
5-101
Ensamblador
; apuntado por DS:2+5+4+1
5.6.9 Número de Bits en una Instrucción
Si una dirección de memoria como [BX+1] aparece como operando, el
circuito 80x86 debe saber si va a manipular a 1 ó 2 bytes en dicha
dirección. Hemos dicho que si el segundo operando lo forma un registro,
éste define por completo el tipo de operación con lo que respecta al
número de bytes. Pero existen ciertos casos donde los operandos de la
operación no permiten intuir el número de bytes lo que provoca un
mensaje de error al ensamblar. Por ejemplo en los siguientes casos:
MOV
INC
MOV
[BX+1],4
[DI]
[SI+2],1
Se debe de completar la instrucción como sigue:
Para el caso de 1 byte
MOV
INC
MOV
BYTE PTR [BX+1],4
BYTE PTR [DI]
BYTE PTR [SI+2],1
Para los operaciones del tipo palabra
MOV
INC
MOV
WORD PTR [BX+1],4
WORD PTR [DI]
WORD PTR [SI+2],1
Donde BYTE PTR se refiere a Apuntador de Byte y WORD PTR a
Apuntador de Palabra.
5.7 Resumen
Hemos dicho que un sistema de cómputo sólo debe de contar con la
UPC y la memoria principal. Aunque esto es estrictamente cierto, la
funcionalidad del circuito sería casi nula. Es necesario, entonces, agregar
toda una serie de circuitos y dispositivos que hagan que el diseño sea
funcional y práctico para el que lo usa.
5-102
Lógica más allá de la UPC
La interfase a estos dispositivos y circuitos se realiza por medio de los
puertos de Entrada/Salida. Es por estos puertos que la computadora se
comunica y recibe información del mundo externo.
Los diseños de las computadoras modernas toman en cuenta esta variable y tratan de integrarla dentro del circuito resultante.
5.7.1 Puntos Importantes del Capítulo
· La transferencia de datos entre el sistema de cómputo y el mundo
externo se le conoce como Entrada y Salida (E/S o I/O).
· La E/S se puede dividir en Programada, por Interrupciones y
Acceso Directo a Memoria (DMA).
· La E/S programada es conocida también como E/S de mapa de
memoria pues se divide el mapa de memoria entre memoria y
dispositivos de E/S.
· Para evitar dividir el mapa de memoria se prefiere agregar más
líneas de control a la UPC que seleccionen entre la memoria y los
dispositivos de E/S.
· La E/S por Interrupción solicita al sistema su atención por medio
de una línea la atención.
· Si la UPC es interrumpida debe de realizar una secuencia
ordenada antes de atender a la interrupción y esto incluye entre
otras cosas el preservar el contenido de los registros.
· El dispositivo externo que interrumpe debe proporcionar un
código de dispositivo o un vector de interrupciones que es la
dirección donde se encuentra el programa que atiende al
dispositivo.
· El DMA consiste en guardar o leer datos de la memoria principal
sin la intervención de la UPC. Para esto, es necesario generar
todas las señales que requiere la memoria y que en condiciones
normales genera la propia UPC.
· Existen varias formas de componer una instruccíon:directa, base,
índice y las combinaciones de las anteriores.
5-103
Ensamblador
5.8 Instrucciones Analizadas
INT, MOV
5.9 Problemas
5.1 Justifique la decisión de los diseñadores de conservar el esquema de
memoria real de sólo 1 megabyte en lugar de los 4 gigabytes accesables
por los modelos 386, 486, P5 y P6.
5.2 Ensamble todos los pedazos de código en el ensamblador agregando
las pseudo instrucciones necesarias y depure hasta no obtener ningún error. No importa que el programa no tenga sentido y que no se pueda ver el
resultado.
5.3 Intente ahora introducir los pedazos de código desde DEBUG.
Ahora sí podemos ver los resultados de las transferencias utilizando las
instrucciones adecuadas del programa DEBUG.
5.4 Investigue las mejoras de diseño de las siguientes generaciones: 486
a P5, P5 a P6.
5.10 Bibliografía
Llano Emiliano Sistemas Digitales y Arquitectura de Computadoras.
Unam 1993. Capítulo 12 y 14.
Mercier Philippe Assembleur Facile. Marabout 1992, Alleur Bélgica.
Capítulo 1.
5-104
Capítulo 6
6.1 Más sobre Interrupciones
Todos los diseños de computadoras incluyen la previsión de
interrupciones electrónicas. Lo novedoso del circuito 80x86 es la
inclusión de estas mismas interrupciones electrónicas pero dentro del
juego de instrucciones del circuito lo que permite el acceso a las utilerías
diseñadas cuidadosamente (algunas no tanto) que hacen que el
programar en ensamblador se simplifique.
La instrucción de interrupción (INT realiza dentro del la UPC todos los
pasos de una interrupción generada por un dispositivo de E/S y transfiere
la ejecución del programa principal a la subrutina de interrupción
apuntada por la dirección encontrada en el vector de interrupciones que
se pasa como argumento de la instrucción.
Los vectores de interrupción se encuentran en las localidades bajas de
memoria, de la dirección 00000h a la 00400h (1Kbyte), formando así 256
vectores de interrupción cada uno de 32 bits (segmento:dirección).
Por ejemplo si empleamos la instrucción INT 20, informamos a la UPC
que se transfiera control a la subrutina cuya dirección encontramos en la
localidad 20*4=80 decimal ó 40h. Se debe seguir la convención de byte
menos significativo primero, esto es, byte bajo de dirección, byte alto de
dirección, byte bajo de segmento, byte alto de segmento.
Una lista completa de las interrupciones se encuentra en la sección de
ROM del manual IBM Personal Computer Technical Reference.
Para pasar parámetros a la subrutina de interrupción se utilizan los
registros, cada uno de ellos con una función distinta según el caso. El
6-105
Ensamblador
llamar a una interrupción hace que el procesador guarde la dirección de
regreso (CS e IP) junto con las banderas en la pila. Afecta también a las
banderas IF (bandera de interrupción) y TF (bandera de trampa o paso a
paso).
Ejemplo 6.1. Ilustraremos aquí el punto con un breve ejemplo ya
considerado en el capítulo 4 sección 4.4.1. En ese punto no se explicó la
función del programa, pero aquí ya tenemos todos los elementos
necesarios para comprender su función y colocar los comentarios:
MOV
MOV
DL,1
AH,2
;Valor ASCII a desplegar
;Despliega un carácter ASCII en la
INT
INT
20h
21h
;Función de despliegue
;Regresa control a programa supervi-
pantalla
sor
Dando por resultado que el programa despliegue una cara feliz en un
punto x de la pantalla, sobre el cual por el momento no tenemos control, y
regrese al programa supervisor, en este caso, DEBUG. En este punto es
necesario introducir el programa de nuevo a la computadora usando DEBUG.
Hemos pues aprendido el vector de interrupciones que los diseñadores
del ROM de IBM utilizaron para definir la interrupción de la pantalla (21h)
y la de fin de programa que llama (20h).
Si consultamos la tabla del capítulo 3 de códigos ASCII, podemos ahora
variar el carácter a desplegar modificando la primera instrucción.
6.2 Saltos Incondicionales (JMP)
¿Qué pasaría si en lugar de una cara feliz queremos muchas?
Requerimos pues de introducir una nueva instrucción: JMP. Esta
instrucción salta a la localidad de memoria que se le in dica a
continuación como argumento.
Si empleamos el ensamblador podemos usar direcciones simbólicas
definidas previamente:
Aqui:
MOV
MOV
INT
JMP
6-106
DL,1
AH,2
21h
aqui
Explorando el Ensamblador
Si se emplea el DEBUG se debe de especificar la dirección:
-a100
MOV
MOV
INT
JMP
DL,1
AH,2
21
0100
Desensamble el programa para asegurarse de que lo tecleó bien:
-u100,106
1649:0100 B201
1649:0102 B402
1649:0104 CD21
1649:0106 EBF8
MOV DL,1
MOV AH,2
INT 21
JMP 0100
Al ejecutar el programa (-g) la pantalla se llena inmediatamente de caras
felices. Para interrumpir el programa presionamos la tecla de CTRL y la de
BREAK a la vez. Esto genera una interrupción de teclado que al ser
atendida reconoce la secuencia de teclas e interrumpe el programa
regresando control al programa supervisor.
Los saltos pueden ser incondicionales o condicionales y a su vez
dividirse en tres tipos:
· Cortos. La dirección está representada en 8 bits, por lo que cae
entre las direcciones comprendidas a -128 y +127 bytes de la
dirección de donde se realiza el salto.
· Cercanos. Se representa en 16 bits por lo que van dentro del
mismo segmento a -32,767 ó +32,768 bytes de distancia.
· Lejanos. A otro segmento. Se representan por 32 bits y los
registros IP y CS son modificados.
· De índice. Pueden ser cortos o cercanos y toman la forma de JMP
registro o JMP memoria. Se usan en general para salto indirecto
usando como apuntador un índice contenido en un registro o en
una localidad de memoria:
Salta
DW
DW
DW
DW
MOV
JMP
uno
dos
tres
cuatro
BX,escoge
Salta[BX]
;define tabla de etiquetas
;Coloca en BX la opción
;Salta a Uno si escoge=0
6-107
Ensamblador
;a Dos si escoge=1, etc.
;instrucciones para opción Uno
;instrucciones para opción Dos, etc.
uno:
dos:
Trataremos por el momento con saltos incondicionales. Observemos
detenidamente los códigos que nos entrega el programa DEBUG: B201,
B402, CD21, EBF8. Por deducción podemos inferir que B2 corresponde el
número hexadecimal a la instrucción MOV, CD el de INT y EB al de JMP.
¿Pero por qué en la instrucción JMP el programa coloca F8 en lugar de la
dirección 100 que es a la que estamos saltando para continuar con la
ejecución del programa? Recordemos que estamos saltando a unas
cuantas instrucciones atrás y por lo tanto es un salto corto definible en 1
byte. Si se tratase de un salto hacia adelante de 8 bytes, el código
correspondería a EB08, pero éste es un salto hacia atrás y por lo tanto
negativo de 8 bytes que corresponde a F8.
6.3 Incrementando Registros y Memoria (INC)
Podemos introducir una nueva variante al programa dejando que
despliege toda la serie de caracteres ASCII en lugar de uno sólo:
MOV
MOV
INT
INC
JMP
DL,00
AH,02
21
DL
0102
Lo que nos lleva a una nueva instrucción: INC. Esta instrucción
incrementa un registro o la memoria en una unidad. La bandera CF no es
afectada nunca por esta operación pero sí las banderas de cero y
saturación (ZF, SF) son modificadas de acuerdo al resultado.
Ejemplos:
INC
INC
INC
INC
BX
AL
variable
tabla[BX]
;Operación de 2 bytes
;Operación de 1 byte
;Incrementa una dirección
;Incrementa un elemento de una tabla
Recuerde que en nuestro programa muestra se realiza una operación de
1 byte y el número máximo que se obtiene es de FFh ó 255 la siguiente
operación de incremento regresa el contador a 0 y vuelve a comenzar la
cuenta.
6-108
Explorando el Ensamblador
Ejercicio. 5.2 Deje ejecutar el programa por un tiempo aleatorio e
interrumpa. Visualice el contenido de los registros utilizando la
instrucción r del DEBUG.
6.4 Lazos (LOOP)
Nuestro programa va mejorando pero aún falta mucho para lograr el
control deseado. Una nueva mejora sería controlar el número de veces
que el programa pasa por un conjunto de operaciones. Si modificamos el
programa de la siguiente manera podemos lograrlo:
MOV
MOV
MOV
INT
INC
LOOP
INT
CX,0100
DL,00
AH,02
21
DL
0105
20
Se introduce un nuevo elemento de programación: la instrucción LOOP.
Está instrucción realiza un salto condicional a la dirección indicada
mientras un contador, en el registro CX, no cumpla con cierta condición.
En este caso la condición es: No saltar cuando CX=0. Esta instrucción no
afecta a las banderas pero si decrementa el registro CX de forma
automática.
ATENCION: ¡Si el contador CX se encuentra en cero, la instrucción
LOOP decrementa al registro antes de realizar la comparación por lo que
recorreremos el código 256 veces! Se puede usar la instrucción de salto
condicional JCXZ (Jump if CX zero, salta si CX es cero) antes de entrar al
lazo:
JCXZ
no_entra
LOOP
repite
repite:
...
no_entra:
;prueba CX
;instrucciones del lazo
;no entrar al lazo
Note que en este caso al estar usando DEBUG, la primera instrucción introduce en CX un contador de 256 decimal.
6-109
Ensamblador
Ejercicio 5.3 Agregar las lineas necesarias para ensamblar con MASM y
ensamble. Utilice pseudo instrucciones específicas para los contadores y
direcciones.
La instrucción LOOP tiene dos variantes:
· LOOPE. Realiza el lazo mientas CX<>0 y ZF=1
· LOOPNE. Realiza el bucle mientras CX<>0 y ZF=0
Se debe tener cuidado con las banderas pues un código aparentemente
correcto a primera vista no funcionará:
MOV
SI,OFFSET cadena
;En SI lleva el apuntador a una
cadena
Busca:
CMP
AL,[SI]
almacenada en AL
INC
SI
comparar
LOOPNE busca
;Compara con una cadena
;Prepara para la siguiente cadena a
;Busca hasta encontrar cadena
La instrucción CMP compara las dos cadenas y modifica las banderas de
acuerdo al resultado (ver siguiente sección), pero la siguiente instrucción
es una operación aritmética que vuelve a modificar las banderas, en este
caso la que más nos interesa, la bandera de cero (ZF) por lo que LOOPNE
no puede salir del bucle nunca.
6.5 Saltos Condicionales y Comparaciones
Existen muchos casos en programación en los cuales no nos es
suficiente un salto incondicional a una dirección predeterminada, sino
que deseamos saltar sólo si se cumple alguna condición previa.
Para estos casos, los diseñadores del circuito proveen de los saltos
condicionales que pueden ir desde unos cuantos hasta una gran variedad
como en los diseños modernos.
Todos los saltos condicionales dependen de las banderas para hacer sus
pruebas por lo que es necesario modificarlas de alguna forma (con alguna
instrucción) antes de hacer la prueba de salto. La instrucción preferida es
CMP:
CMP
CMP
6-110
Registro,Constante
Memoria,Constante
Explorando el Ensamblador
CMP
CMP
CMP
Registro,Memoria
Memoria,Registro
Registro,Registro
La operación realiza una resta sin modificar ni el destino ni la fuente
pero sí modificando las banderas de acuerdo al resultado. Por ejemplo:
MOV
CMP
AH,5
AH,6
;-AH+6
AH=05h=0000 0101 ó 1111 1011 en complemento a 2.
1111 1011+0000 0110 = 1 0000 0001 por lo que las banderas
modificadas serán:
· OF=0 (no hay desbordamiento),
· SF=0 (último bit del byte o signo),
· ZF=0 (el resultado es diferente a 0) y
· CF=1 (puesto que hay un acarreo).
Se debe poner cuidado en operaciones indefinidas:
CMP
[BX],1
;Operación indefinida
Se define poniendo el tipo de apuntador:
CMP
CMP
BYTE PTR [BX],1 ;Del tipo Byte -[BX]+1
BYTE WORD [BX],1;Del tipo Word (2 bytes) -[BX]+1
El juego de instrucciones del circuito 80x86 nos ofrece una amplia gama
de saltos condicionales divididos en tres categorías genéricas:
· Comparación de banderas
· Comparación entre números sin signo
· Comparación entre números con signo
Para la comparación de banderas, todas las instrucciones toman la
forma J.. identificador donde J.. en el mnemónico de la instrucción e
identificador es una etiqueta o dirección localizada en un intervalo de +128
a -127 bytes de distancia.
6-111
Ensamblador
Tabla 6.1 Comparación de Banderas
Instrucción
Condición
Significado
Salta si ZF=1
(Jump if Zero)
JZ
Salta si ZF=1
(Jump if Equal)
JE
Salta
si
ZF=0
(Jump if Not Zero)
JNZ
Salta si ZF=0
(Jump if Not Equal)
JNE
Salta si CF=1
(Jump if Carry)
JC
Salta
si
CF=0
(Jump if Not Carry)
JNC
Salta si SF=1
(Jump if Sign)
JS
Salta si SF=0
(Jump if Not Sign)
JNS
Salta si OF=1
(Jump if Overflow)
JO
Salta si OF=0
(Jump if Not Overflow)
JNO
Sabemos que en las comparaciones aritméticas sin signo la bandera
CF=1 indica un desbordamiento de capacidad. Si comparamos dos
números A y B con la instrucción CMP (-A+B) y encontramos un
desbordamiento, esto nos indica que A<B puesto que el complemento a 2
de A es un número positivo muy grande que al sumarse a B nos da un
desbordamiento. De la misma forma si A>B no habrá desbordamiento y
si A=B el indicador de cero (ZF) será 1.
Tabla 6.2 Comparación de Números sin Signo
Salta si
Instrucción
Valor de la bandera
A>B
JNBE o JA
(Jump if not Below or Equal,,
Jump if Above)
CF=0 y ZF=0
A>=B
JNB, JAE o JNC
(Jump if Not Below,
Jump if Above or Equal,
Jump if Not Carry)
CF=0
A<=B
JBE o JNA
(Jump if Below or Equal,
Jump if Not Above or Equal)
(CF=1 y ZF=1)
o (CF<>ZF)
A<B
JB, JNAE o JC
(Jump if Below,
Jump if Not Above or Equal,,
Jump if Carry)
CF=1
A=B
JE o JZ
(Jump if Equal,
Jump if Zero)
ZF=1
A<>B
JNE o JNZ
(Jump if Not Equal,
Jump if Zero)
ZF=0
6-112
Explorando el Ensamblador
A diferencia del grupo anterior, los siguientes saltos condicionales
toman en cuenta operaciones con signo.
Tabla 6.3 Comparación de Números con Signo
Salta si
Instrucción
Valor de la bandera
A>B
JG o JNLE
(Jump if Greater,
Jump if Not Less or Equal)
(ZF=0 y OF=SF)
A>=B
JGE o JNL
(Jump if Greater or Equal,
Jump if Not Less)
SF=OF
A<=B
JNG o JLE
(Jump if Not Greater,
Jump if Less or Equal)
(ZF=0 y SF=OF) o
(ZF=1 y SF=OF) o
(ZF=0 y SF<>OF)
A<B
JNGE o JL
(Jump if Not Greater or Equal,
Jump if Less)
(SF<>OF)
A=B
JE o JZ
(Jump if Equal,
Jump if Zero)
ZF=1
A<>B
JNE o JNZ
(Jump if Not Equal,
Jump if Not Zero)
ZF=0
En un ejemplo notaremos la importancia de seleccionar un grupo u otro:
MOV
CMP
JB
AH,0FFh
AH,0
sigue
;Compara FF con 0
;Comparación sin signo
;Salta si menor
La instrucción JB no provocará el salto pues 255 no es inferior a 0.
MOV
CMP
JL
AH,0FFh
AH,0
sigue
;Compara FF con 0
;Comparación con signo
;Salta si menor
En este caso si hay un salto pues -1 (0FFh en aritmética con signo) es
menor que 0.
Todos los saltos condicionales dentro del juegos de instrucciones del
circuito 80x86 son saltos cortos, esto es, a -127 ó +128 bytes de distancia y
no modifican las banderas. Por lo que si se requiere un salto largo se debe
de emplear un truco, en lugar de:
6-113
Ensamblador
JL
...
...
...
sigue
;Serie de instrucciones
;que dan más de 128 bytes
;(error señalado por el ensamblador)
sigue:
Usaremos:
JGE
JMP
sigue1
sigue
sigue1:
;Instrucción contraria a JL
;Serie de instrucciones
;que dan más de 128 bytes
...
sigue:
6.6 Operaciones Lógicas (AND, OR, NOT, XOR,
TEST, NEG)
Existen cierto tipo de instrucciones muy próximas a la lógica de la
máquina, en especial a las que realiza la UAL, que permiten manipular los
bits individuales de maneras extrañas y muy prácticas. Todas tienen las
siguientes combinaciones posibles:
Mnemónico
Mnemónico
Mnemónico
Mnemónico
Mnemónico
Registro, Valor
Memoria, Valor
Registro, Memoria
Memoria, Registro
Registro, Registro
Donde Registro es un registro interno de trabajo de 8 ó 16 bits, Valor es
una constante o valor y Memoria es una dirección de memoria o
apuntador a una.
Todas las operaciones lógicas son realizadas bit a bit.
6.6.1 Y Lógico (AND)
La primera que analizaremos es la operación Y (AND) que nos entrega un
1 si los dos operadores son 1 y un 0 si cualquiera de ellos es cero. Se
utiliza muy a menudo para aislar ciertos bits de un byte o para poner en
cero ciertos bits de un byte. Una de las aplicaciones menos utilizadas de
la operación Y Lógica es obtener el residuo de una resta:
AND
Calcule el residuo de 29/8
00001 1101 (29)
0000 0111 (divisor-1=7)
6-114
Explorando el Ensamblador
0000 0101 (residuo de 5)
Cuando se trata de eliminar ciertos bits y otros no, se le nombra al
operador de Y Lógico mascarilla. Un cero en la máscara nos dará por
resultado un cero y un uno conserva el valor original del operando:
AND
AX,6
;Usa mascarilla (6) para conservar
AND
AX,0FBh
;Usa mascarilla para eliminar bits 2 y
bits 1 y 2
4
Ejercicio 6.2 Diga cuales de las siguientes instrucciones son
incorrectas y por qué.
AND
AND
6,AX
AX,BH
6.6.2 O Lógico (OR)
La operación O (OR) da por resultado un 1 si cualesquiera de los dos
operadores es un uno. La mascarilla en este caso sirve para forzar ciertos
bits a 1 lógico.
Ejercicio 6.3 Diga cuales de las siguientes instrucciones son
incorrectas y por qué.
OR
OR
OR
AX,0104h
014h,AX
BH,AX
6.6.3 No Lógico
En este caso la operación da por resultado el contrario de lo que
teníamos en un principio; esto es, si hay un 1 tendremos un 0 y si hay un
cero un 1. La operación toma la forma
NOT
NOT
Registro
Memoria
6.6.4 O Exclusivo Lógico (XOR)
Esta operación da por resultado un 1 si los bits son diferentes y un 0 si
son iguales. Un uso común de la instrucción XOR es para colocar
rápidamente un registro en cero:
XOR
AX,AX
;Coloca AX en cero
Puede utilizarse también para invertir (complementar) uno o más bits
con una mascarilla:
6-115
Ensamblador
XOR
AH,6
;Complementa bits 1 y 2 de AH
Esta instrucción es muy usada en programas gráficos donde se desea
pasear una figura por un fondo sin destruir éste.
6.6.5 Prueba Lógica (TEST)
La prueba lógica es el equivalente a la operación Y (AND) sin modificar
los operadores pero alterando las banderas de acuerdo al resultado. Es
comparable a la operación CMP para comparaciones lógicas.
6.6.6 Las Banderas
En todas las operaciones lógicas se modifican las banderas, a excepción
de la operación TEST que es una prueba no destructiva, de la siguiente
forma:
· Las banderas CF (acarreo) y OF (saturación) siempre son cero
puesto que las operaciones no son aritméticas.
· El indicador AF (auxiliar) queda indeterminado y no se usa.
· Los indicadores SF (signo), ZF (cero) y PF (paridad) se modifican
de acuerdo a los operadores.
· La operación NO (NOT) es una excepción a lo anterior pues no
modifica las banderas.
6.6.7 Complemento a 2 (NEG)
Con esta operación realizamos la negación de cada uno de los bits de un
byte o palabra y luego la suma de 1 (el complemento a 2) en un sólo paso.
Las formas permitidas de la operación son:
NEG
NEG
Registro
Memoria
Como ejemplos tenemos:
MOV
NEG
NEG
AX,5
AX
AX
;AX=-5
;AX=5
Nuevamente hay que tener cuidado con los operadores indeterminados:
6-116
Explorando el Ensamblador
NEG
NEG
NEG
[BX]
BYTE PTR [BX]
WORD PTR [BX]
;¿Es byte o palabra?
;byte
;palabra
6.7 El Mundo Externo: IN y OUT
La UPC pasa una gran parte del tiempo comunicándose con los
dispositivos externos o atendiendo sus demandas por medio de las rutinas
de interrupción. El ensamblador da el control de todos estos recursos al
programador y permite funciones que en lenguajes de alto nivel es muy
difícil o imposible realizar. Para ilustrar el punto supongamos que
queremos controlar con precisión la forma en que la bocina de nuestra
computadora personal emite sonidos (ampliaremos el concepto en
capítulos subsecuentes).
Para poder controlar el mundo externo necesitamos introducir las
instrucciones IN y OUT. Estas instrucciones son la única forma en que un
lenguaje en ensamblador puede comunicarse con un puerto externo y son
muy comunes en todos los circuitos de microcomputadoras.
6.7.1 La instrucción OUT
Esta instrucción envía un byte o una palabra a un puerto.
OUT
OUT
Puerto, AL
Puerto, AX
;envía byte a puerto
;envía palabra a puerto
El número de puerto puede ser enviado también al registro DX:
MOV
OUT
identificado por DX
DX,puertonum
DX,AL
;el número de puerto a DX
;envía contenido de AL al puerto
La instrucción no afecta ninguna bandera.
6.7.2 La instrucción IN
Al contrario de la instrucción OUT, IN recibe un byte o palabra de un
puerto externo:
IN
IN
Puerto, AL
Puerto, AX
;recibe byte de puerto y coloca en AL
;recibe palabra de puerto y coloca en
AX
El número de puerto puede ser enviado también al registro DX:
MOV
DX,puertonum
;el número de puerto a DX
6-117
Ensamblador
IN
DX,AL
por DX y coloca en AL
;recibe byte de puerto identificado
La instrucción no afecta ninguna bandera.
Introduzcamos el siguiente programa desde DEBUG:
IN
AND
XOR
OUT
MOV
LOOP
JMP
AL,61
AL,fc
AL,2
61,AL
CX,140
10b
104
Asegúrese de las instrucciones con el comando U y antes de ejecutar
salve el programa
-nsonido.com
-rbx
BX 0000
:
-rcx
CX 0000
:f
-w
Writing 000F bytes
El programa no puede interrumpirse una vez que se ejecuta pero es
ilustrativo de ciertos conceptos que redondean este capítulo. No
podemos utilizar la instrucción de interrupción puesto que los
diseñadores no pensaron que fuese importante contar con una rutina de
interrupción para la bocina.
Comentemos ahora el programa:
bocina
EQU
61h
;puerto que controla la bocina
quita
EQU
0fch
;máscara para quitar bits 0 y 1
;el bit 0 controla un oscilador que queremos apagar y el bit 1 se conecta a una
compuerta
;que genera el pulso que se envía a la bocina
alterna
EQU
02h
;máscara para alternar bit 1
;y generar sonido pulsante
retraso
EQU
140h
;retraso para poder escuchar el sonido
IN
AL,bocina
;
6-118
Explorando el Ensamblador
AND
AL,quita
;apaga oscilador y compuerta
XOR
OUT
MOV
AL,alterna
bocina,AL
CX,retraso
;apaga y prende la compuerta
LOOP
JMP
repite
sigue
;pierde el tiempo
;repite el proceso
sigue:
;marca la frecuencia
repite:
Ejercicio 6.4 Introduzca el programa anterior en MASM agregando
todas las pseudo instrucciones que vengan al caso.
Ejercicio 6.5 Varíe el retraso y comente sobre el resultado.
6.8 Resumen
Las interrupciones forman el trabajo principal y el cuello de botella de la
UPC. El circuito de INTEL nos permite tener acceso a las mismas rutinas
de interrupción diseñadas por los fabricantes del SO dandonos muchas
ventajas.
Los saltos dentro de un segmento de código de programa pueden
dividirese en incondicionales y condicionales y a su vez los
incondicionales dependiendo el signo.
Las máscaras son patrones de bits que nos permiten eliminar ciertas
secuencias de un byte que no nos interesan.
Las instrucciones de IN y OUT nos permiten tener acceso de forma
directa a los dispositivos externos.
6.9 Instrucciones Analizadas
INT, JMP, INC, LOOP, JCXZ, LOOPE, LOOPNE, CMP, JZ, JE, JNZ,
JNE, JC, JNC, JS, JNS, JO, JNO, JNBE, JA, JNB, JAE, JBE, JNA, JB,
JNAE, JG, JNLE, JGE, JNL, JNG, JLE, JNGE, JL, AND, OR, NOT,
XOR, TEST, NEG, IN, OUT.
6.9 Problemas
6.1 Averigue algunas de las interrupciones prácticas dentro del sistema
operativo MS-DOS.
6-119
Ensamblador
6.2 Utilice las interrupciones aprendidas en su ventaja modificando el
programa del ejercicio 6.1.
6.3 Modifique el programa del ejercicio 6.1 para que limpie la pantalla y
luego coloque una cara sonriente en la mitad de la pantalla.
6.10 Bibliografía
Mercier Philippe Assembleur Facile. Marabout 1992, Alleur Bélgica.
Barden William Jr. How to Program Microcomputers. Sams & Company 1987.
Lafore Robert Assembly Language Primer for the IBM PC. The Waite
Group 1992.
IBM Personal Computer Technical Reference.
6-120
Capítulo 7
7.1 El Sistema Operativo
En todo sistema de cómputo existe una relación íntima entre los
programas en ensamblador y el sistema operativo (SO) que se ejecuta en
la computadora. En los sistemas personales el sistema operativo reside
principalmente en el disco ya sea duro o flexible y recibe el nombre de
DOS(Disk Operating System).
Como ya sabemos el propósito principal del SO es administrar a otros
programas, mantendiéndolos en áreas específicas de memoria
secundaria para su almacenamiento o en memoria principal para su
ejecución. Provee las funciones básicas de impresión, despliegue,
almacenaje, etc. A todas estas funciones sólo se puede acceder en su nivel
más bajo a través del lenguaje ensamblador.
En un principio la persona que se debía entender con el SO debía tener
un íntimo conocimiento de la máquina. Por ejemplo, si se deseaba
guardar un archivo en disco flexible, se debía saber al detalle dónde se
localizaba cada byte en el disco, el tiempo que tarda en dar una
revolución, cuanto tarda el motor de pasos en llegar a una pista
determinada, etc. Como se puede imaginar, los programas se convertían
en programas muy largos y complejos. Si por cualquier causa cambiaban
los dispositivos, todo debía de ser reescrito.
Si el SO de todas formas debía de tener rutinas de manejo de todos los
dispositivos conectados a la máquina. ¿Por qué no hacer las rutinas del
SO accesibles a los otros programas? La idea detrás del SO cae dentro de
esta pregunta. De esta forma si se desea escribir un carácter a la pantalla
no se debe saber ya nada del circuito que la controla sino únicamente la
7-121
Ensamblador
información de la rutina que lo puede hacer y cómo decirle qué queremos
hacer. De esta forma, al llamar una rutina del SO, nuestra tarea se
convierte de una de extrema complejidad a una de moderada a poca
dificultad.
Todas estas rutinas de control de periféricos residen en una memoria
ROM que forma parte del BIOS de la computadora y a la que podemos
tener acceso de forma ilimitada.
Analizaremos a lo largo del capitulo diversas utilerías del BIOS que nos
serán de utilidad posteriormente.
7.2 El Teclado
La rutina de interrupción que atiende al teclado es la 21h entrando con el
registro AH en 1 y regresando en AL al carácter leído. El uso de las teclas
CTRL y BREAK interrumpe la rutina.
Ejercicio 7.1 Introduzca los siguientes ejemplos usando DEBUG. No
olvide revisar el registro AL para ver el código ASCII de la tecla que se
presionó.
MOV
INT
INT
AH,1
21
20
Se puede utilizar un salto incondicional para introducir una oración
completa:
MOV
INT
JMP
AH,1
21
100
Mientras estamos en la rutina de interrupción del teclado podemos
revisar las funciones de edición que la rutina nos proporciona como es el
borrado de caracteres, el empleo de teclas de control, etc.
Se puede obligar a la rutina del teclado a guardar la información en una
memoria intermedia. Para esto es necesario pasar como parámetro 0Ah
en el registro AH y en los registros DS:DX la dirección de la memoria
intermedia de almacenaje (buffer) en cuyo primer byte debemos guardar
el máximo número de caracteres a leer del teclado. La rutina nos regresa
en el segundo byte de la memoria de almacenaje el número de caracteres
leídos del teclado.
7-122
El Mundo Exterior
Ejercicio 7.2 Introduzca y ejecute el siguiente programa en DEBUG:
MOV
MOV
INT
INT
DB
DX,109
AH,a
21
20
20
Examine el resultado con la instrucción D.
Ejercicio 7.3 Convierta el programa del ejercicio 7.2 a MASM
añadiendo las pseudo instrucciones requeridas. No olvide inicializar el
registro de segmento de datos (DS) con la dirección apropiada usando
SEGMENT.
7.3 La Pantalla
Para desplegar una serie de caracteres podemos utilizar la rutina 21h
nuevamente pero ahora colocamos en el registro AH un 9 y en DS:DX
colocamos el inicio de la cadena a desplegar terminada por un signo de
pesos ($).
Ejercicio 7.4 Introduzca el siguiente programa en DEBUG a partir de la
dirección 0100.
MOV
MOV
INT
INT
DB
DX,109
AH,9
21
20
‘Ejemplo de cadena impresa$’
Ejercicio 7.5 Introduzca el programa del ejercicio 7.4 pero con las
pseudo instrucciones requeridas para el ensamblador MASM. No se
olvide de inicializar el segmento DS de donde se toma la dirección de la
cadena a desplegar.
Unamos las funciones aprendidas para realizar un programa que haga
eco a la pantalla de lo que se teclea. Sabemos ya que para que la función
de despliegue sepa donde termina la cadena ésta debe de finalizar con $
por lo que en este sencillo programa nosotros debemos de introducirlo:
MOV
MOV
INT
DX,116
AH,a
21
7-123
Ensamblador
MOV
MOV
MOV
MOV
INT
INT
DB
DL,a
AH,2
DX,118
AH,9
21
20
30
El programa anterior en MASM:
;Programa que acepta caracters del teclado y hace eco a la pantalla
;No olvide terminar la cadena de caracteres con $
teclado EQU
0Ah
;función del teclado
pantalla EQU
02h
;función de la pantalla
lf_cr
EQU
0Ah
;alimentación de línea
muestra EQU
09h
;función de despliegue
dos
EQU
21h
;Llamada del SO
fin
EQU
20h
;Termina y vuelve al SO
;—————————————————————————————————
————————————————
datos
SEGMENT
memoria DB
20
;20 bytes para memoria de
teclado
; a partir de esta localidad
datos
ENDS
;—————————————————————————————————
————————————————
programa
SEGMENT
ASSUME CS:programa
;informa a MASM dónde estamos
ASSUME DS:datos
;y dónde está el buffer
ORG
0100h
;fija el origen a dirección 100
inicio:
MOV DX,OFFSET memoria
;dirección de buffer
MOV AH,teclado
INT
dos
;interrupciones varias
MOV DL,lf_cr
;agrega alimentación de línea
MOV AH,pantalla
INT
dos
;interrupciones varias
MOV DX,OFFSET memoria+2
;salta primeros 2 bytes de
buffer de teclado
MOV AH,muestra
;despliegua en pantalla
INT
dos
INT
fin
;termina y regresa a SO
;—————————————————————————————————
————————————————
programa ENDS
7-124
El Mundo Exterior
;******************************************************************
END inicio
Aparentemente más complicado pero mucho más fácil de entender y
mantener.
7.4 La Impresora
Como para las funciones anteriores, esta vez usamos la interrupción
21h, pero enviamos un 5 en el registro AH y en DL el carácter a imprimir.
En el siguiente ejemplo mostramos una forma muy primitiva de enviar
una cadena a la impresora.
MOV
MOV
INT
MOV
MOV
INT
MOV
INT
MOV
MOV
INT
INT
DB
DL,[122]
AH,05
21
DL,[123]
AH,05
21
DL,[124]
21
DL,[125]
AH,05
21
20
‘si’,0d,0a
Claro que una mejor versión es inmediata usando direccionamiento
indirecto:
MOV
MOV
MOV
MOV
INT
INC
LOOP
INT
DB
CX,40
BX,111
DL,[BX]
AH,05
21
BX
0106
20
‘Un mensaje a imprimir de 40 caracteres’,0d,0a
Que para el MASM queda:
;Programa para impresión de caracteres
imprime EQU
cuenta EQU
a imprimir
05h
40
;función de impresora
;Lleva la cuenta de la cadena
7-125
Ensamblador
dos
EQU
21h
;Llamada del SO
fin
EQU
20h
;Termina y vuelve al SO
;—————————————————————————————————
————————————————
datos
SEGMENT
cadena DB
‘Mensaje de 40 caracteres’,0Dh,0Ah
datos
ENDS
;—————————————————————————————————
————————————————
programa
SEGMENT
ASSUME CS:programa
;informa a MASM dónde estamos
ASSUME DS:datos
;y dónde están los datos
ORG
0100h
;fija el origen a dirección 100
inicio:
MOV CX,cuenta
MOV BX,OFFSET cadena ;dirección de mensaje
ciclo:
MOV DL,[BX]
;dirección carácter a imprimir
MOV AH,imprime
INT
dos
;interrupciones varias
INC
BX
;apunta a siguiente carácter
LOOP ciclo
;y vé a imprimirlo
INT
fin
;termina y regresa a SO
;—————————————————————————————————
————————————————
programa ENDS
;******************************************************************
END inicio
Presentamos a continuación un programa para mandar códigos de control a la impresora:
MOV
MOV
MOV
MOV
INT
INC
LOOP
INT
DB
CX,2
BX,0111
DL,[BX]
AH,05
21
BX
0106
20
1b,’E’
Observe que en la última línea definimos el carácter de control para
modo enfatizado de una impresora EPSON o compatible y puede ser
sustituído por cualquier otro código.
7-126
El Mundo Exterior
7.5 Los Corrimientos y Operaciones Aritméticas
Comenzamos esta sección con un sencillo programa que convierte un
número en binario a dígitos hexadecimales en su equivalente ASCII y lo
despliega en la pantalla. Integraremos finalmente el programa en uno
sólo que convierte de decimal a hexadecimal:
;Programa para convertir binario a hexadecimal (ASCII)
muestra EQU
02h
;función de despliegue
cuenta EQU
4
;Lleva cuenta de # dígitos
mascara EQU
0Fh
;Máscara 4 bits extrema
derecha
ascii
EQU
30h
;Equivalente de hexadecimal
a ASCII
nueve EQU
3Ah
;9 en ASCII
a_f
EQU
7h
;Convierte dígito A-F en
ASCII
dos
EQU
21h
;Llamada del SO
fin
EQU
20h
;Termina y vuelve al SO
;—————————————————————————————————
————————————————
programa
SEGMENT
;inicio de
segmento
ASSUME CS:programa
;informa a MASM dónde estamos
ORG
0100h
;fija el origen a dirección 100
inicio:
MOV CH,cuenta
;número de dígitos
rota:
MOV CL,cuenta
;cuenta a 4 bits
ROL
BX,CL
;desplaza dígito a la izq.
MOV AL,BL
;y pasa a reg. AL
AND
AL,mascara
;quita dígitos de la izq.
ADD
AL,ascii
;convierte a ASCII
CMP
AL,nueve
;¿Es mayor que 9?
JL
despliega
;Despliega si ente 0 y 9
ADD
AL,a_f
;el dígito está entre A y F
despliega:
MOV DL,AL
;Coloca ASCII en DL
MOV AH,muestra
;Función de despliegue
INT
dos
;Llama a DOS
DEC
CH
;¿Terminamos con los 4 dígitos?
JNZ
rota
;Si aún no se termina
INT
fin
;Regresa a SO
;—————————————————————————————————
————————————————
programa ENDS
7-127
Ensamblador
;******************************************************************
END inicio
Si el programa se ejecuta desde el sistema operativo no se obtendrá nada
pues el registro BX (de donde se toman los datos) está vacío. Para ver su
utilidad y comprender los nuevos conceptos es necesario ejecutarlo
desde DEBUG y llenar el registro BX con el comando r.
Ejercicio 7.6 Ejecute el programa desde DEBUG dando varios valores a
BX y observando el resultado.
Antes de explicar el funcionamiento de este programa introduzcamos
los corrimientos, rotaciones y las operaciones aritméticas:
7.5.1 Corrimientos y Rotaciones
Este tipo de instrucciones desplazan los bits de un byte o palabra hacia la
izquierda o derecha. Estas son operaciones comunes para encuadrar un
byte con respecto a un valor o para eliminar los bits superfluos, algo similar a las máscaras en las operaciones lógicas como Y, O, etc.
Se dividen en tres grupos:
· Desplazamientos Aritméticos o corrimientos sin signo
· Desplazamientos Aritméticos o corrimientos con signo.
· Rotaciones
La UAL sólo es capaz de realizar un número básico y limitado de
operaciones:
· Complemento a unos (inversión de bits)
· Complemento a dos (negación)
· Suma
· Desplazamientos
· Rotaciones
· Operaciones Lógicas (Y, O, O Exclusiva)
Recordemos que si recorremos un registro en todos sus bits hacia la
izquierda el equivalente es multiplicar por 2:
0000 0001 = 1 decimal
7-128
El Mundo Exterior
0000 0010 = 2 decimal (corrimiento de 1 bit)
0000 0100 = 4 decimal (corrimiento de 2 bits)
principio que funciona en cualquier base: 1d con un corrimiento de 2
posiciones a la izquierda equivale a 100d o multiplicar 2 veces por la
base.
De la misma forma un corrimiento hacia la derecha equivale a dividir
entre la base, en este caso 2:
0000 1000 = 8 decimal
0000 0100 = 4 decimal (corrimiento de 1 bits)
0000 0010 = 2 decimal (corrimiento de 2 bits)
pero ocurre un problema importante con los números que tienen signo:
0000 1000 = 8d —-> 1111 1000 (complemento a 2 1111 0111+1)
realizemos -8/2
0111 1100 = 124d en lugar de -4
Note que es importante en este caso inyectar a la izquierda el signo para
obtener el resultado correcto 1111 1100 = -4 para lo cual se usan
corrimientos aritméticos con signo.
Todas las operaciones de corrimiento y rotaciones toman la forma
Mnemónico
Mnemónico
Mnemónico
Mnemónico
Registro,1
Memoria,1
Registro,CL
Memoria,CL
Donde Registro es un registro de 8 o 16 bits, Memoria es una localidad
de memoria o una dirección y 1 es el valor inmediato de uno.
7.5.1.1 Corrimientos Lógicos (Rotaciones)
ROR destino,cuenta, Rota a la derecha byte o palabra pasando por el
acarreo.
7-129
Ensamblador
RCR destino,cuenta, Rota a la derecha a través del acarreo byte o
palabra
ROL destino,cuenta, Rota a la izquierda byte o palabra pasando por
acarreo
RCL destino,cuenta, Rota a la izquierda a través del acarreo byte o
palabra
7.5.1.2 Corrimientos Aritméticos
SHR destino,cuenta, Corrimiento lógico a la derecha de byte o palabra
pasando por acarreo
SAR destino,cuenta, Corrimiento aritmético a la derecha de byte o
palabra agregando el signo y pasando por acarreo
SHL/SAL destino,cuenta, Corrimiento a la izquierda de byte o palabra
pasando por acarreo
Todos los corrimientos quedan ilustrados en la figura 7.1
7-130
El Mundo Exterior
Figura 7.1 Corrimientos y rotaciones.
7.5.2 Operaciones Aritméticas
Las operaciones aritméticas son realizadas sobre los registros y son de
8, 16, o 32 bits que nos dan números entre 0 y 65,535 en aritmética sin
signo.
El circuito es capaz de realizar sumas, restas, multiplicaciones y
divisiones pero estas dos últimas operaciones las realiza en base a sumas
por lo que son lentas, para sacar provecho de estas operaciones es
necesario tener otro circuito dedicado a las operaciones matemáticas que
se llama coprocesador pues realiza el proceso en paralelo al procesador
principal.
7.5.2.1 Suma y Resta (ADD y SUB)
7-131
Ensamblador
Las sumas y restas toman la forma de
Mnemónico
Mnemónico
Mnemónico
Mnemónico
Registro,Inmediato
Memoria,Inmediato
Registro,Memoria
Registro, Registro
Donde Registro es un registro de 8 o 16 bits, Memoria es una localidad
de memoria o una dirección e Inmediato es un valor.
Si existe un desbordamiento, el bit del registro de banderas de acarreo es
puesto en 1 lógico y las demás banderas (SF, CF, OF, ZF) se afectan de
acuerdo al resultado.
Recuerde el caso de los números negativos y de que las operaciones
pueden ser con signo o sin signo lo que puede dar por resultado sorpresas
indeseables. La resta se logra sumando el complemento a dos del
sustraendo.
Existen las operaciones complementarias de suma (ADC) y resta (SBB)
pero de doble precisión donde se agrega o quita el acarreo al resultado:
suma1
DD
?
;Número de 32 bits
suma2
DD
?
;Número de 32 bits
;Suma los primeros 16 bits
MOV AX,WORD PTR suma1
;trabaja los primeros 16 bits
bajos
ADD
AX,WORD PTR suma2
;sumando los 16 bits bajos
;trabaja ahora los 16 bits altos agregando el acarreo si existe
MOV DX,WORD PTR suma1+2
;16 bits primeros altos
ADC
DX,WORD PTR suma2+2
;suma siguientes 16 bits altos
;agregando acarreo
7.5.2.2 Decrementos
Esta instrucción decrementa en 1 un registro o localidad de memoria
dando por resultado una operación idéntica a la de la resta pero sin afectar
la bandera de acarreo (CF) por lo que se debe poner atención si queremos
realizar un salto condicional que dependa de este indicador.
DEC
DEC
Registro
Memoria
7.5.2.3 Multiplicación y División
7-132
El Mundo Exterior
En el caso de la multiplicación (MUL) y división (DIV) tenemos algo
similar a la suma y resta. Es necesario una operación para considerar el
signo en caso de requerirlo (IMUL, IDIV). Estas operaciones usan
registros específicos como se muestra en la figura 7.2.
Figura 7.2 Multiplicación y división.
En el caso específico de la división, es necesario prevenir, pues una
división entre 0 o una cantidad muy pequeña genera una interrupción
definida de antemano (al cargar el SO) que detiene el proceso general del
SO y por ende de la máquina.
La instrucción DIV no permite dividir un byte entre un byte ni una
palabra entre otra palabra por lo que hay que proceder como sigue:
;división entre dos bytes
7-133
Ensamblador
MOV AL,dividendo
CBW
MOV BL,divisor
DIV
BL
;división entre dos palabras
MOV AX,dividendo
CWD
(DX,AX)
MOV BX,divisor
DIV
BX
una palabra
;en AL el dividendo
;Convierte un byte en palabra (AH=0)
;en BL el divisor
;división de una palabra entre un byte
;en AX el dividendo
;Convierte palabra en doble palabra
;en BX el divisor
;división de una doble palabra entre
Para este punto debe de quedar claro el funcionamiento del programa
introducido en la sección an te rior e introducimos el siguiente
complemento que conviete de un número decimal tomado del teclado a
un binario en el registro BX.
;Convierte Decimal del teclado a binario, el resultado se queda en BX
decbin
SEGMENT
ASSUME CS:decbin
MOV BX,0
;Limpia registro BX
otro:
MOV AH,1
;entrada del teclado
INT
21h
;llama a DOS
SUB
AL,30h
;ASCII a binario
JL
sale
;Salir si el #<0
CMP
al,9d
;¿Es > a 9?
JG
sale
;Si es así, no es dígito
CBW
;Convierte byte en AL a palabra en
AX
;Multiplica número en BX por 10
XCHG AX,BX
;Cambia registros AX y BX
MOV CX,10d
;Coloca 10 en CX
MUL CX
;y multiplica por AX (AX*10)
XCHG AX,BX
;Regresa # a BX
;Suma número en AX por el de BX
ADD
BX,AX
;suma dígito a #
JMP
otro
; y ve por otro
sale:
INT
20h
;Regresa a SO
decbin
ENDS
END
Ejercicio 7.7 Introduzca el programa anterior en MASM y ejecute desde
DEBUG para observar el resultado en el registro BX. Use la instrucción
7-134
El Mundo Exterior
T (trace) para seguir el flujo del programa paso a paso y anotar el estado
de los registros que nos interesan tal como AX, BX y CX. Tendremos un
problema al llegar a la instrucción INT pues nunca regresaremos al
programa original, por lo que debemos sustituir la instrucción por una
nueva que no hace nada: NOP. Esta instrucción ocupa un sólo byte por lo
que en este caso debemos usar 2 para sustituir a INT que ocupa 2 bytes.
El programa DEBUG utiliza la siguiente simbología para las banderas:
Tabla 7.1 Simbología de DEBUG para las banderas
Bandera
OF, saturación
DF, dirección
IF, habilita interrupciones
SF, signo
ZF, cero
AF, acarreo auxiliar
PF, pariedad
CF, acarreo
En uno lógico
En cero lógico
OV
DN
EI
NG
ZR
AC
PE
CY
NV
UP
DI
PL
NZ
NA
PO
NC
Ejercicio 7.8 Ejecute el programa DECBIN anterior pero ahora
interrumpimos el flujo del mismo antes de ejecutar la última instrucción
para poder analizar el registro BX. Para esto utilizamos el comando de
DEBUG g pero seguido de una dirección lo que hace que se introduzca
una pausa en lugares determinados (hasta 10). En nuestro caso el punto
antes de la interrupción 20h es 11b: g 11b.
7.6 Procedimientos o Subrutinas
En muchos casos se requiere, por orden, dividir el programa en
segmentos lógicos independientes uno de otro pero a la vez que estos
segmentos estén disponibles para su uso por otros segmentos del
programa. Se resuelve esto, al igual que en los lenguajes de alto nivel, con
los procedimientos o subrutinas que pueden ser llamados desde cualquier
parte de un programa.
En el lenguaje ensamblador se define el inicio de una sección de un
programa con la pseudo instrucción PROC y su final con la pseudo
instrucción ENDP. Los procedimientos pueden ser cortos o lejanos
dependiendo de si se encuentran o no en el mismo segmento y se requiere
de especificar en la instrucción con qué tipo estamos tratando:
7-135
Ensamblador
nombre
segmento
nombre
PROC
NEAR
;si se trata de uno en el mismo
PROC
FAR
;si se trata de uno en otro segmento
El nombre es el identificador del procedimiento con el cual haremos
referencia posteriormente a esta subrutina.
Para llamar a la subrutina o procedimiento se utiliza la instrucción
CALL (llama) seguida de la dirección o identificador. La instrucción
forza a que el procesador guarde la dirección de la siguiente instrucción
que sigue a la de la llamada en un área especial de datos llamada la pila
que analizaremos en el siguiente capítulo a fondo.
Si es una llamada corta o cercana, sólo se guarda el registro IP, si se trata
de una llamada lejana, es necesario guardar tanto CS como IP. No es
afectada ninguna badera por esta instrucción.
Todo procedimiento debe de terminar con la instrucción RET para que
el ensamblador sepa que termina la subrutina y debe regresa al programa
que la llamó. Esto implica devolver el valor del apuntador de programa
anterior sacándolo de la pila. Una vez más si es una llamda corta, toma
una palabra de la pila y la restituye en IP, si es una llamada lejana, toma
dos palabras de la pila, la primera para IP y la segunda para CS.
La instrucción RET puede tener un parámetro auxiliar para ajustar el
tamaño de la pila al regresar. Analizaremos esto en detalle en el siguiente
capítulo.
Finalmente integramos los dos programas ya vistos en uno general que
convierte un número decimal a hexadecimal directamente del teclado:
;Convierte Decimal a Hexadecimal (DECHEX)
;(máximo de 65535d)
dechex SEGMENT
ASSUME
CS:dechex
;Parte principal del programa que une a los otros 2
repite: CALL
decbin ;Del teclado (base 10) a
binario
CALL
crlf
;Agrega nueva línea
CALL
binhex ;binario a hexadecimal y
despliega
CALL
crlf
;Agrega nueva línea
JMP
repite ;Vuelve a repetir
;—————————————————————————————————
————————————————
7-136
El Mundo Exterior
;Convierte Decimal del teclado a binario, el resultado se queda en BX
decbin
PROC NEAR
MOV BX,0
;Limpia registro BX
otro:
MOV AH,1
INT
21h
SUB
AL,30h
;ASCII a binario
JL
sale
;Salir si el #<0
CMP
al,9d
;¿Es > a 9?
JG
sale
;Si es así, no es dígito
CBW
;Convierte byte en AL a palabra en
AX
;Multiplica número en BX por 10
XCHG AX,BX
;Cambia registros AX y BX
MOV CX,10d
;Coloca 10 en CX
MUL CX
;y multiplica por AX (AX*10)
XCHG AX,BX
;Regresa # a BX
;Suma número en AX por el de BX
ADD
BX,AX
;suma dígito a #
JMP
otro
; y vé por otro
sale:
RET
decbin
ENDP
;—————————————————————————————————
————————————————
;Convierte número binario en BX a hexadecimal y despliega
binhex
PROC NEAR
MOV CH,4
;número de dígitos
rota:
MOV CL,4
;cuenta a 4 bits
ROL
BX,CL
;desplaza dígito a la izq.
MOV AL,BL
;y pasa a reg. AL
AND
AL,0Fh
;quita dígitos de la izq.
ADD
AL,30h
;convierte a ASCII
CMP
AL,3Ah
;¿Es mayor que 9?
JL
despliega
;Despliega si ente 0 y 9
ADD
AL,7h
;el dígito está entre A y F
despliega:
MOV DL,AL
;Coloca ASCII en DL
MOV AH,2h
;Función de despliegue
INT
21h
;Llama a DOS
DEC
CH
;¿Terminamos con los 4 dígitos?
JNZ
rota
;Si aún no se termina
RET
;Regresa
binhex
ENDP
7-137
Ensamblador
;—————————————————————————————————
————————————————
;Despliega una nueva línea
crlf
PROC NEAR
MOV DL,0Dh
;CR
MOV AH,2
;función de despliegue
INT
21h
;llamada a DOS
MOV DL,0Ah
;LF
MOV AH,2
;función de despliegue
INT
21h
;llamada a DOS
RET
;regresa de crlf
crlf
ENDP
;******************************************************************
dechex
ENDS
END
Introduzca el programa anterior en MASM y pruebe con varios
números. Para finalizar interrumpa presionando la tecla de CTRL y C al
mismo tiempo.
Ejercicio 7.9 Introduzca la pseudo instrucción PAGE como primera
instrucción del programa y ensamble pero ahora usando el comando
MASM dechex, , lpt1:, dechex
Asegúrese de tener la impresora prendida y con papel pues el resultado
se pasará directamente a la impresora. Así mismo hemos creado una
referencia cruzada en un archivo de nombre DECHEX.REF que nos indica dónde se definieron todas las variables (con el signo #) y dónde son
utilizadas todas ellas.
7.7 Resumen
Se analizan en este capítulo distintas formas de aprovecharnos de las
interrupciones de los diseñadores del SO en nuestro beneficio. Se
introducen conceptos de procedimientos y la forma de accesar dejando
para el siguiente capítulo la forma idónea de pasar parámetros a las
subrutinas.
7-138
El Mundo Exterior
7.8 Instrucciones Analizadas
INT 20 (AH=9h, AH=0Ah, AH=5h), ROR, RCR, ROL, RCL, SHR,
SAR, SHL, SAL, ADD, SUB, ADC, SBB, MUL, DIV, IMUL, IDIV,
NOP, RET, CALL y las pseudo instrucciones PROC NEAR, PROC FAR,
ENDP, PAGE.
7.9 Problemas
7.1 Analice el programa de conversión de decimal a hexadecimal en lo
referente a las instrucciones de corrimiento.
7.2 Realice un código como subrutina en el que se le pasan como
parámetro la fecha en el registro AX dividida en día, mes, año de la
siguiente forma:
El día de 1 a 31 (5 bits)
El mes de 1 a 12 (4 bits)
El año en 7 bits y al que se le suma 1995 siempre
Al recibir la llamada, la subrutina regresa el día en ASCII en el registro
BX, el mes en ASCII en el registro AX y el año en ASCII en CX y DX. El
programa principal lo despliega en la pantalla o imprime.
7.10 Bibliografía
Mercier Philippe Assembleur Facile. Marabout 1992, Alleur Bélgica.
Morgan Christopher Bluebook of Assembly Languages Routines for the
IBM. Olume Waite, New York 1984.
Lafore Robert Assembly Language Primer for the IBM PC. The Waite
Group 1992.
IBM Personal Computer Technical Reference.
7-139
Capítulo 8
8.1 La pila
En los primeros diseños de microcomputadoras surgía el problema de
que al atender las interrupciones generadas por los distintos dispositivos
se requería preservar el contenido de varios registros en algún lugar.
Algunos diseñadores tomaron el camino de dejar al programador la tarea
de reservar un área de memoria para tal fin y de asegurar que los registros
fueran restaurados antes de volver al programa principal. Otros se
aseguraron de que esto sucediera siempre al incorporar una serie de
registros paralelos donde se guardaba automáticamente el contenido de
todos los registros de la UPC.
Pronto se vio que la primera opción, con algunas variantes, era el
camino adecuado y el más conveniente para proporcionar toda la
flexibilidad al programador y dejar que él decida cuándo y cómo deben
guardarse los datos requeridos. Esto da gran libertad pero a la vez
complica las cosas al tener que ser extremadamente cuidadoso con el
manejo de esta área reservada de memoria llamada pila.
Para poder implementar el uso de pilas, es requisito indispensable
agregar nuevos registros o utilizar los ya existentes de forma que
permitan el control de dicha pila. En el circuito de Intel, el manejo de
pilas se logra a través del registro SS (stack segment o segmento de pila)
junto con SP (stack pointer o apuntador de fin de pila) y BP (base pointer o
apuntador base de pila).
El uso de subprogramas plantea también la necesidad de pasar
parámetros a estas subrutinas o procedimientos. Los tres métodos
preferidos son:
8-141
Ensamblador
· En los registros. Limitado a unas cuantas variables.
· Por medio de variables. Mejor que el anterior, pero se requiere de
la definición estricta de muchas variables o el uso de archivos de
almacenamiento externos creciendo mucho el programa. Usado
cuando se requieren un gran número de variables.
· Usando la pila. Es el método preferido y el más utilizado cuando
las variables van de pocas a regular en cantidad.
La pila es usada en las microcomputadoras modernas para guardar el
apuntador de memoria al llamar una rutina de interrupción (INT o
interrupción externa) o un procedimiento del usuario (CALL). Sabemos
ya que se necesitan de por lo menos 16 bits pues se requiere guardar IP en
caso de una llamada cercana o CS e IP en el caso de una lejana.
8.1.1 Manejo de la Pila
Para el manejo de pila se usan las instrucciones PUSH que empuja
valores de un registro o localidad de memoria a la pila y POP que saca
valores de la pila restituyéndolos en un registro o localidad de memoria.
La pila en este circuito es una pila del tipo FIFO (first in first out), es decir,
el primero que entra es el primero que sale (PEPS).
Un ejemplo de un programa completo donde se requiere utilizar la pila
para guardar el contador que a su vez es utilizado en otra subrutina
ilustrará el punto:
;Sonido de Motor de Automóvil
ruido
SEGMENT
principal PROC FAR
ASSUME
ORG
100h
inicio:
MOV CX,10d
otro:
PUSH CX
CALL disparo
CALL silencio
POP
CX
CS:ruido
;Define inicio de programa
;número de repeticiones
;guarda la cuenta en la pila
;Sonido de disparo
;Y guarda silencio
;Recupera el contador de
;disparos de la pila
LOOP otro
;repite hasta terminar
INT
21h
;Regresa a DOS
principal ENDP ;fin programa principal
;—————————————————————————————————
————————————————
8-142
La Pila
;Subrutina para hacer un sonido breve
disparo
PROC NEAR
MOV DX,140h
;valor inicial de la espera
MOV BX,3Fh
;cuenta inicial
IN
AL,61h
;lee el puerto de sonido
AND
AL,11111100b
;quita bits 0 y 1
sonido:
XOR
AL,2
;Cambia bit 1 de AL
OUT
61h,AL
;Salida a puerto de sonido
;—————————————————————————————————
——————————; Genera número aleatorio
ADD
DX,9248h
;Suma de patrón pseudo aleatorio
MOV CL,3
;Prepara a rotar 3 bits
ROR
DX,CL
;Rota 3 bits
MOV CX,DX
;Coloca en CX
espera:
LOOP espera
;ciclo de espera
;
DEC
BX
;¿fué suficiente?
JNZ
sonido
;Salta si aún no se acaba
AND
AL,11111100b
;Quita bits 0 y 1
OUT
61h,AL
;Manda al puerto de sonido
RET
;Regresa de la subrutina
disparo
ENDP
;—————————————————————————————————
————————————————
;Retraso ligero (aumentar o disminuir según modelo)
silencio
PROC NEAR
MOV CX,0FFFFh
;cuenta de silencio
repite:
LOOP repite
MOV CX,0FFFFh
;cuenta de silencio
repite1:
LOOP repite1
RET
;Regresa
silencio
ENDP
;******************************************************************
ruido
ENDS ;fin de código
END
inicio
;fin de ensamble
Para comprender el funcionamiento de la pila, ejemplificaremos
analizando la pila con una llamada a una subrutina donde se pasan los
parámetros en la pila.
;pasa a la pila los parámetros
PUSH DS
PUSH DX
CALL rutina
8-143
Ensamblador
En este punto recalquemos que la cima de la pila se encuentra en donde
esté la última dirección del último byte empujado a la pila. En el
momento que el procedimiento “rutina” recibe el control la pila se
encuentra en el siguiente estado:
100
98
96
94
92
SPÄ
valor de DS
valor de DX
valor de CS
valor de IP
îcima de la pila
El subprograma no podrá leer los datos de DS y DX que nos interesan
pues el procesador empuja automáticamente los valores de CS e IP (en
este caso de una llamada lejana) hacia la pila. Es menester entonces, el
ajustar el apuntador de la pila BP para que apunte al área de interés
siguiendo los siguientes pasos:
· 1. Salvar el valor actual de BP en la pila (PUSH BP).
· 2. Inicializar BP al mismo valor que SP (MOV BP,SP).
100
98
valor de DS
îBP+8
96
valor de DX
îBP+6
94
valor de CS
îBP+4
92
valor de IP
îBP+2
valor de BP
îBP
90
SPÄ
Los parámetros están disponibles ahora con un simple ajuste del
apuntador.
MOV
MOV
AX,[BP+6]
BX,[BP+8]
;mueve el valor de DX a AX
;mueve el valor de DS a BX
Cuando el subprograma termine, deberá informar al procesador por
medio de la instrucción RET n que se ajuste la pila de acuerdo a los
movimientos realizados de forma tal que pueda recuperar CS e IP para
volver al punto de ejecución anterior, siendo n el número de bytes a
ajustar. Ilustramos este último punto con un ejemplo completo paso a
paso.
Contamos con una pila inicial de 50 elementos (100 bytes) definidos en
la sección de pila del programa. Las cifras a la izquierda son los
8-144
La Pila
desplazamientos dentro del segmento SS definido también al inicio del
programa así como los valores de SP y BP que coinciden:
îcima de la pila
SPÄ
98
.....
BPÄ
96
94
92
.....
.....
.....
El programa requiere del uso posterior de CX por lo que se empuja a la
pila:
PUSH CX
98
valor de CX
BP,SPÄ
96
94
îcima de la pila
.....
.....
.....
Si empujamos también el valor de DS (PUSH DS) tendremos:
98
BPÄ
valor de CX
96
SPÄ
valor de DS
94
92
îcima de la pila
.....
.....
El programa x llama en este punto al subprograma y pasando los
parámetros BX y DX en la pila:
PUSH
PUSH
CALL
98
96
94
92
90
88
BX
DX
y
BPÄ
valor de CX
SPÄ
valor de DS
valor de BX
valor de DX
CS de x
IP de x
;llama a subrutina lejana y
îcima de la pila
8-145
Ensamblador
En este punto el programa y tiene el control y requiere del registro BP (es
la regla general de uso de la pila). No hay forma de saber si el programa
anterior usa o no BP por lo que también es regla general guardarlo en la
pila y comenzar a usarlo desde ese momento:
PUSH
MOV
BP
BP,SP
;salva registro BP
;BP recibe el valor de SP
Lo que nos deja con la pila en el siguiente estado:
98
96
94
92
90
88
86
BP,SPÄ
valor de CX
valor de DS
valor de BX
valor de DX
CS de x
IP de x
BP de x
(parámetro 1)
(parámetro 2)
Desde este momento podemos consultar entonces los parámetros de la
pila que se requieren y se encuentran en BP+8 (parámetro 1) y BP+6
(parámetro 2) puesto que todos los elementos de la pila requieren de 2
bytes.
La subrutina y puede usar la pila para salvaguardar los valores que
requiere más tarde para su uso. Si se requiere usar algún registro, primero
se empuja a la pila para asegurarse de que el programa que llama no
pierda sus registros de trabajo. Supongamos en este momento que se
llama a la subrutina z pasándole los parámetros AX y BX:
PUSH
PUSH
MOV
MOV
PUSH
PUSH
CALL
AX
BX
AX,1
BX,1
AX
BX
z
;guarda registro de subrutina x
;guarda registro de subrutina x
;inicializa valor para subrutina z
;inicializa valor para subrutina z
;guarda registro en la pila
;guarda registro en la pila
;llama subrutina lejana z
Las primeras instrucciones de la subrutina z serán para ajustar la pila y
asegurarse el acceso a ella:
PUSH
MOV
8-146
BP
BP,SP
;Guarda registro BP
;inicializa pila para subrutina z
La Pila
Con lo que tenemos en la pila:
98
96
94
92
90
88
86
84
82
80
78
76
74
72
BP,SPÄ
valor de CX
valor de DS
valor de BX
valor de DX
CS de x
IP de x
BP de x
valor de AX
valor de BX
Parámetro 1
Parámetro 2
CS de y
IP de y
BP de y
(parámetro 1)
(parámetro 2)
(salvado por y)
(salvado por y)
(transmitido por y)
(transmitido por y)
(salvado por z)
La subrutina z puede a su vez consultar los parámetros 1 y 2 que manda
la subrutina y (BP+6 y BP+8) y al regresar debe sacar el valor de BP de la
pila (POP BP) para asegurar el comportamiento de la misma:
98
96
94
92
90
88
86
84
82
80
78
76
74
72
BPÄ
valor de CX
valor de DS
valor de BX
valor de DX
CS de x
IP de x
BP de x
(salvado por y)
(salvado por y)
(transmitido por y)
(transmitido por y)
SPÄ
valor de AX
valor de BX
Parámetro 1
Parámetro 2
CS de y
IP de y
BP de y
(salvado por z)
(parámetro 1)
(parámetro 2)
En este momento la subrutina z informa al procesador que ya terminó su
tarea (RET) y éste toma los valores de la parte alta de la pila (en SP y SP+2)
y reinicializa CS e IP lo que regresa el control a la subrutina y. Existe otra
forma de la instrucción RET: RET n (ya mencionada) en la que se ajusta la
pila por medio del registro SP agregándole un valor n (entero). El valor de
n es conocido en el momento de la programación pues sabemos el
número de parámetros pasados a la subrutina. Podemos entonces deducir
el número de parámetros de la pila y colocar el valor correcto de n. En este
8-147
Ensamblador
caso el valor es 4 pues se pasaron 2 parámetros y cada uno ocupa 2 bytes
(2*2 bytes = 2 parámetros)
RET
98
96
94
92
90
88
86
84
82
80
78
;regresa sin ajuste de la pila
BPÄ
valor de CX
valor de DS
valor de BX
valor de DX
CS de x
IP de x
BP de x
SPÄ
valor de AX
valor de BX
Parámetro 1
Parámetro 2
(salvado por y)
(salvado por y)
(transmitido por y)
(transmitido por y)
CS de y
IP de y
BP de y
(salvado por z)
76
74
72
RET
4
(parámetro 1)
(parámetro 2)
;regresa con ajuste de la pila en 4
bytes
98
96
94
92
90
88
86
BPÄ
valor de CX
valor de DS
valor de BX
valor de DX
CS de x
IP de x
BP de x
84
82
SPÄ
valor de AX
valor de BX
(salvado por y)
(salvado por y)
Parámetro 1
Parámetro 2
CS de y
IP de y
BP de y
(transmitido por y)
(transmitido por y)
80
78
76
74
72
(parámetro 1)
(parámetro 2)
(salvado por z)
En este punto la subrutina y recupera los valores empujados en la pila en
orden inverso del almacenaje antes de volver a la rutina que llama (x).
8-148
La Pila
POP
POP
POP
BX
AX
BP
y regresa ajustando a su vez la pila en 4 unidades (2 parámetros):
RET
98
BPÄ
96
SPÄ
94
92
90
88
86
84
82
80
78
76
74
72
4
valor de CX
valor de DS
valor de BX
valor de DX
CS de x
IP de x
BP de x
valor de AX
valor de BX
Parámetro 1
Parámetro 2
CS de y
IP de y
BP de y
(parámetro 1)
(parámetro 2)
(salvado por y)
(salvado por y)
(transmitido por y)
(transmitido por y)
(salvado por z)
A su vez la rutina x recupera sus valores empujados en la pila:
POP
POP
DS
CX
y la pila vuelve a su estado inicial (en lo que respecta a los apuntadores):
SPÄ
98
96
94
92
90
88
86
84
82
80
78
76
74
72
BPÄ
valor de CX
valor de DS
valor de BX
valor de DX
CS de x
IP de x
BP de x
valor de AX
valor de BX
Parámetro 1
Parámetro 2
CS de y
IP de y
BP de y
(parámetro 1)
(parámetro 2)
(salvado por y)
(salvado por y)
(transmitido por y)
(transmitido por y)
(salvado por z)
8-149
Ensamblador
Como conclusión es un buen hábito colocar en la pila los registros o
datos que serán utilizados por algún subprograma antes de llamarlo y una
vez que termina la llamada, ajustar la pila y recuperar los valores de la
pila:
prueba
prueba
PROC NEAR
PUSH AX
PUSH BX
....
POP
BX
POP
AX
RET
ENDP
;tanto AX como
;BX serán utilizados
;instrucciones del subprograma
;recupera los valores
;de AX y BX de la pila
Se aconseja también especificar al inicio del subprograma por medio de
comentarios cuáles son los registros que se pasan como parámetros y
cuáles serán eventualmente destruidos:
Lee
PROC NEAR
;Leer un carácter del teclado
;regresa el AL el código ASCII
;destruye AL al llamarse
MOV AH,1
INT
21h
RET
lee
ENDP
;servicio de lectura de teclado
;lee un carácter
8.1.2 Guardando las Banderas
Existen dos instrucciones especiales para colocar las banderas en la
pila, una la opuesta de la otra: POPF y PUSHF. Estas instrucciones no
contienen ningún operando.
Se puede usar esta instrucción cuando se requiere de realizar
operaciones intermedias antes de utilizar un salto condicional o cualquier
otra operación que utilice las banderas para operar:
PUSHF ;Guarda las banderas
MOV AX,2
ADD
AX,3
;operación que modifica banderas
POPF
;recupera las banderas
8-150
La Pila
8.2 Manejo de Cadenas
En la mayoría de las aplicaciones informáticas, el tratamiento de
cadenas de caracteres es particularmente importante. Es tan común que
los procesadores modernos integran una serie de instrucciones para
realizar estas operaciones de forma eficiente.
Las operaciones de cadenas pueden ser utilizadas para otras
operaciones para las cuales no fueron planeadas pero son muy eficientes
como es el caso de mover páginas completas de texto o imágenes a la
memoria de donde el controlador de video toma su información para
desplegar en la pantalla.
En este tipo de operaciones intervienen dos operandos en distintas áreas
de memoria:
· 1. Cadena Fuente. Siempre en un segmento apuntado por DS y
usando como apuntador a la cadena el registro SI (Source Index).
· 2. Cadena Destino. Siempre en un segmento apuntado por ES y
usando como apuntador a la cadena el registro DI (Destination
Index).
Las operaciones se realizan carácter a carácter y es posible realizarlas
con otras operaciones que ya conocemos aunque de forma un poco torpe
e ineficiente:
Texto1:
DB
‘Un texto cualquiera’
Texto2:
DB
‘Otro texto’
Texto3:
DB
20 DUP(?)
;inicialización
PUSH DS
POP
ES
MOV SI,OFFSET texto1
MOV DI,OFFSET texto2
;Estamos listo a comparar las dos cadenas
MOV AL,[SI]
MOV AH,[DI]
CMP
AH,AL
;aquí la lógica de las comparaciones
;
;zona de 20 caracteres
;ESîDS
;SIîdirección del texto1
;DIîdirección del texto2
;ALî‘U’ de “Un text...”
;AHî‘O’ de “Otro te...”
;Compara las cadenas
8-151
Ensamblador
;Transferir de un área a otra:
MOV SI,OFFSET texto2
;SIîdirección del texto2
MOV DI,OFFSET texto3 ;DIîdirección del texto3
MOV AL,[SI]
MOV [DI],AL
;aquí la lógica que controla los contadores, el inicio, el fin, etc.
8.2.1 La Instrucción REP
El modificador REP (de REPeat, repite) causa que la instrucción
siguiente sea repetida hasta que el registro CX sea cero. REP se usa con un
grupo de tres instrucciones: MOVS (Move String, mueve cadena), SCAS
(Scan String, busca cadena) y CMPS (Compare String, compara cadena).
Las banderas afectadas dependen de la operación.
Para que este comando funcione, debemos colocar de antemano el
número de repeticiones en el registro CX para lo cual entre muchas otras
posibilidades, podemos utilizar una pseudo instrucción del ensamblador:
texto
largo
DB
EQU
MOV
‘Una cadena de longitud x’
$-texto
;calcula longitud de ‘texto’
CX,largo
;en CX la longitud de la cadena
8.2.2 La Dirección de la Operación (CLD y STD)
El resultado de las operaciones de cadenas es manipular dos de ellas de
forma que una resida en el segmento DS:SI y la otra en ES:DI. Existen dos
posibilidades para ajustar el contador de forma que comience por el
principio de la cadena o por el final. La instrucción CLD (Clear Direction
Flag, Limpia bandera de dirección) indica al procesador que los registros
SI y DI serán incrementados después de cada instrucción de manejo de
cadenas mientras que la instrucción STD (Set Direction Flag, Fija
bandera de dirección) indica que los dos registros serán decrementados.
8.2.3 Mover Cadenas (MOVSB y MOVSW)
La instrucción MOVS se usa después de la instrucción REP y mueve una
cadena de una localidad a otra de la memoria. La cadena fuente es
apuntada por SI y reside en el segmento DS, mientras que el destino es
apuntado por DI y reside en el segmento extra (ES).
8-152
La Pila
Cada vez que la instrucción MOVS se ejecuta suceden 3 cosas:
· 1. SI es incrementado (o decrementado dependiendo de la
bandera de dirección) ya sea en uno o dos bytes dependiendo si es
transferencia de palabra o byte.
· 2. DI se incrementa (o decrementa dependiendo de la bandera de
dirección) ya sea en uno o dos bytes dependiendo si es
transferencia de palabra o byte.
· 3. CX es decrementado. Si llega a cero la transferencia termina.
En esta operación no se modifica ninguna bandera.
Mostramos un pedazo de código que mueve 40 letras “x” de su área de
memoria fuente a su memoria destino. No es un programa completo pero
es ilustrativo.
;***************************************************
datos
SEGMENT
fuente DB
40 DUP(‘x’)
datos
ENDS
;***************************************************
extra
SEGMENT
destino DB
40 DUP(?)
extra
ENDS
;***************************************************
ASSUME DS:datos
ASSUME ES:extra
MOV AX,datos
;dirección del segmento fuente
MOV DS,AX
;en registro DS
MOV AX,extra
;dirección del segmento destino
MOV ES,AX
;en registro ES
MOV SI,OFFSET fuente
;dirección de la fuente
MOV DI,OFFSET
destino ;dirección del destino
CLD
;bandera de dirección hacia adelante
MOV CX,40
;cuenta en CX
REP
MOVS
;Mueve toda la cadena
Como se ve en el ejemplo anterior hay muchos registros que inicializar,
pero una vez que se logra esto, el trabajo es sencillo y lleva 2
instrucciones escritas en una sola línea (la última).
Ejercicio 8.1 Complete el programa anterior con todas las instrucciones
y pseudo instrucciones faltantes y ejecute.
8-153
Ensamblador
La instrucción REP MOVS reemplaza al siguiente grupo de instrucciones
repetidas CX veces:
MOV
MOV
INC
INC
AL,DS:[SI]
ES:[DI],AL
SI
DI
8.2.4 REPE ó REPZ y REPNE ó REPNZ
Estas instrucciones se utilizan sólo junto con las operaciones de
comparación y búsqueda de cadenas (siguientes dos secciones) y señalan
la terminación de dichas operaciones cuando el registro CX llega a cero o
se localiza la cadena.
REPE (Repite while equal, repite mientras igual) repite la operación
CMPS o SCAS mientras CX sea distinto de cero o el resultado de una
comparación cause que la bandera de cero sea limpiada (no cero) como
resultado que dos palabras o bytes no sean iguales. Las banderas
afectadas dependen de la operación y el resultado de ésta.
REPNE (Repite while not equal, repite mientras no igual) repite la
operación CMPS o SCAS mientras CX sea distinto de cero o el resultado de
una comparación cause que la bandera de cero sea fijada (cero) como
resultado de que dos palabras o bytes sean iguales. Las banderas
afectadas dependen de la operación y el resultado de ésta.
8.2.5 Compara Cadenas (CMPSB y CMPSW)
La instrucción CMPS se usa después de la instrucción REPE o REPNE y
compara una cadena de una localidad a otra de la memoria. La cadena
fuente es apuntada por SI y reside en el segmento DS, mientras que el
destino es apuntado por DI y reside en el segmento extra (ES).
Cada vez que la instrucción CMPS se ejecuta suceden 3 cosas:
· 1. SI es incrementado (o decrementado dependiendo de la
bandera de dirección) ya sea en uno o dos bytes dependiendo si es
transferencia de palabra o byte.
8-154
La Pila
· 2. DI se incrementa (o decrementa dependiendo de la bandera de
dirección) ya sea en uno o dos bytes dependiendo si es
transferencia de palabra o byte.
· 3. CX es decrementado. Si llega a cero la transferencia termina o:
A. REPE. Si la cadena fuente es distinta de la destino.
B. REPNE. Si la cadena fuente es igual a la destino.
En esta operación se modifican las banderas de acuerdo a la
comparación realizada (AF, CF, OF, PF, SF, ZF).
La instrucción REPE CMPS reemplaza al siguiente grupo de
instrucciones repetidas CX veces:
otro:
MOV
MOV
CMP
JPZ
INC
INC
JMP
AL,DS:[SI]
AH,ES:[DI]
AL,AH
salta
SI
DI
otro
salta:
8.2.6 Busca Carácter (SCASB y SCASW)
La instrucción SCAS se usa después de la instrucción REPE o REPNE y
compara una cadena de una localidad al registro AL (en caso de byte o AX
en caso de palabra). La cadena fuente es apuntada por DI y reside en el
segmento extra (ES).
Se usa esta instrucción para buscar un carácter dentro de una cadena.
Cada vez que la instrucción SCAS se ejecuta suceden 2 cosas:
· 1. DI se incrementa (o decrementa dependiendo de la bandera de
dirección) ya sea en uno o dos bytes dependiendo si es
transferencia de palabra o byte.
· 2. CX es decrementado. Si llega a cero la transferencia termina o:
– A. REPE. Si la cadena fuente es distinta al contenido de
AL (o AX).
8-155
Ensamblador
–
B. REPNE. Si la cadena fuente es igual al contenido del
registro AL (o AX).
En esta operación se modifican las banderas de acuerdo a la
comparación realizada (AF, CF, OF, PF, SF, ZF).
La instrucción REPE SCAS reemplaza al siguiente grupo de instrucciones
repetidas CX veces:
otro:
MOV
CMP
JPZ
INC
JMP
BL,ES:[DI]
AL,BL
salta
DI
otro
salta:
8.2.7 LODSB y LODSW
Este grupo de instrucciones cargan el contenido de una localidad de
memoria por DS:SI al registro AL o AX según el tipo de operación.
Después de la operación el registro SI es incrementado o decrementado
(de acuerdo a la bandera de dirección) de forma automática en 1 ó 2 bytes.
El uso de los repetidores (REP, REPE, REPNE) no tienen sentido en este
caso.
8.2.8 STOSB y STOSW
Estas instrucciones pasan el contenido del registro AL o AX, según el
tipo de operación, a la localidad de memoria apuntada por DS:SI.
Después de la operación el registro SI es incrementado o decrementado
(de acuerdo a la bandera de dirección) de forma automática en 1 ó 2 bytes.
Con el repetidor REP, nos puede servir para llenar un área de memoria
con un valor conocido. Por ejemplo:
;Inicializa una zona de 10 caracteres con 0
MOV CX,0Ah
;cuenta 10
MOV AL,0
;valor a traspasar
REP
STOSB ;repite 10 veces la operación
8-156
La Pila
Mostramos en el siguiente programa el uso de las instrucciones REPE y
CMPS para buscar una llave introducida por el usuario dentro de una frase
y decidir si ésta o no. Una cosa importante a notar en este programa es el
uso de segmentos que se traslapan en el caso del segmento de datos (DS) y
el segmento extra (ES), lo que hace un poco más sencillo el manejo de la
programación pues no es necesario acordarse de los segmentos fuentes y
destino sino hacer referencia a una misma área de memoria en este caso
llamada DATOS.
;BUSCA
;El usuario introduce una llave y a continuación
;una frase, el programa decide si la llave se encuentra en la frase
muestra
EQU
9h
;función de despliegue
teclado
EQU
0Ah
;función del teclado
dos
EQU
21h
;interrupción de DOS
;******************************************************************
pila
SEGMENT
STACK;define la pila
DB
20 DUP(‘pila ‘)
;palabra pila y 4 blancos
pila
ENDS
;******************************************************************
datos
SEGMENT
;define segmento de datos
ora_max DB
127d
;long. máxima de frase
ora_real
DB
?
;long. real introducida
oracion
DB
127d DUP(?)
;espacio para 127 caracteres
llav_max DB
20d
;long. máxima de llave
llav_real DB
?
;long. real introducida
llave
DB
20d DUP(?)
;espacio para 20 caracteres
mens1
DB
0Dh,0Ah,’Llave a buscar: $’
mens2
DB
0Dh,0Ah,’Frase: $’
mens3
DB
0Dh,0Ah,’No se encontro $’
mens4
DB
0Dh,0Ah,’Si se encontro $’
datos
ENDS
;******************************************************************
Busca
SEGMENT
;segmento del código
principal PROC FAR
;parte principal del programa
ASSUME CS:busca,DS:datos,ES:datos
;—————————————————————————————————
——————————Inicio:
;guarda la valores para el regreso
PUSH DS
;segmento de datos viejo
SUB
AX,AX
;forma rápida de poner 0 en AX
8-157
Ensamblador
PUSH AX
;guarda en pila (DS:0)
;—————————————————————————————————
——————————;DS apunta ahora a nuestro segmento de datos
MOV AX,datos
;dirección del segmento de datos
MOV DS,AX
;hacia el registro DS
;—————————————————————————————————
——————————;ES apunta ahora a nuestro segmento extra
MOV AX,datos
;dirección del segmento de datos
MOV ES,AX
;hacia registro ES
la_llave:
;pide llave y coloca en memoria
MOV DX,OFFSET mens1 ;dirección mensaje
MOV AH,muestra
;y prepara a mostrar
INT
dos
;—————————————————————————————————
——————————;Toma llave y guarda
MOV DX,OFFSET llave_max
;dirección de llave
MOV AH,teclado
;toma llave de teclado
INT
dos
;—————————————————————————————————
——————————frase:
;pide la frase y guárdala
MOV DX,OFFSET mens2 ;dirección mensaje
MOV AH,muestra
;y prepara a mostrar
INT
dos
;—————————————————————————————————
——————————;Toma frase y guarda
MOV DX,OFFSET ora_max;dirección frase
MOV AH,teclado
;toma frase de teclado
INT
dos
;—————————————————————————————————
——————————;Busca la llave en la frase
;SI apuntador a la llave
;DI apuntador a la frase
;BX apuntador a inicio de frase
;DX long. frase - long. llave + 1
;CX long. de la frase
;
CLD
;búsqueda hacia adelante
MOV AL,ora_real
;long. frase
SUB
AL,llav_real
;- long. llave
JL
no_esta
;si long. llave>long. frase
8-158
La Pila
CBW
;cambia byte a palabra
MOV DX,AX
;poner en DX
INC
DX
;+1
MOV BX,OFFSET oracion ;BX apunta a inicio frase
;—————————————————————————————————
——————————compara:
MOV DI,BX
;lugar donde comienza comparación
MOV SI,OFFSET llave
;SI inicio de llave
MOV AL,llav_real
;toma long. llave
CBW
;convierte a palabra
MOV CX,AX
;y guarda en CX
REPE
CMPSB
;repite hasta CX=0 o no se encuentre
carácter
JZ
si_esta
;no encontramos nada
INC
BX
;avanza apuntador a sig. carácter de
frase
DEC
DX
;¿Acabamos?
JZ
no_esta
;si, no está
JMP
compara
;no, continua siguiente
;—————————————————————————————————
——————————si_esta:
;encontramos llave
MOV DX,OFFSET mens4 ;dirección mensaje
MOV AH,muestra
;prepara a mostrar
INT
dos
JMP
la_llave
;Ve por otra llave y frase
;—————————————————————————————————
——————————no_esta:
;no se encontró llave
MOV DX,OFFSET mens3 ;dirección mensaje
MOV AH,muestra
;prepara a mostrar
INT
dos
JMP
la_llave
;Ve por otra llave y frase
RET
;—————————————————————————————————
——————————principal ENDP
;fin de programa principal
busca
ENDS
;fin del segmento
END
inicio
;fin del programa
Una vez que la llave y la oración han sido aceptadas, el programa
procede a las comparaciones. Usa el registro BX como apuntador a los
caracteres de la frase. La comparación comienza desde el primer carácter.
Asumamos que la llave tiene una longitud de tres caracteres, requerimos
8-159
Ensamblador
de comparar la llave con los primeros tres caracteres de la frase de la
siguiente forma:
· 1. El registro SI apunta a las letras de la llave.
· 2. El registro DI apunta a las letras de la frase.
· 3. El registro CX lleva la cuenta del número de letras de la llave.
La instrucción CMPB se repite hasta que CX es cero o la comparación
falla. Para no buscar en toda la frase y hacer que el programa sea más
rápido, sólo se busca en (longitud de la frase - longitud de la llave + 1)
caracteres sin necesidad de llegar al final. Afortunadamente la
interrupción del teclado (0Ah) regresa la longitud de lo que el usuario
tecleó en la localidad inmediatamente después a la memoria reservada
para dicha entrada, en este caso, en la localidad llav_real y ora_real.
Es necesario empujar DS a la pila pero también un cero para poder
regersar al segmento adecuado de datos y que el programa lo sepa
(segemento de datos en DS:0).
8.3 Resumen
Se introduce en este capítulo el manejo de la pila, siempre importante en
los diseños actuales de computadoras y programas. Se analizan además,
las instrucciones de manejo de cadenas particulares a este circuito y la
forma de usarlas en nuestra ventaja en tareas sencillas como la del último
programa presentado en el capítulo.
8.4 Problemas
8.1 Convierta el programa BUSCA de forma tal que no se usen las
instrucciones especiales de manejo de cadenas en la comparación.
8.2 Programe un algoritmo desde DEBUG que llene las localidades de
memoria de la 200h a la 2FFh con un 0.
8.3 Averigüe si el circuto MOTOROLA tiene instrucciones similares a las
presentadas en este capítulo para manejo de pila y de cadenas.
8-160
La Pila
8.5 Instrucciones Analizadas
PUSH, POP, RET n, POPF, PUSHF, REP, CLD, STD, MOVS, MOVSB,
MOVSW, CMPS, CMPSB, CMPSW, REPE, REPZ, REPNE, REPNZ,
SCAS, SCASB, SCASW, LODS, LODSB, LODSW, STOS, STOSB,
STOSW
8.6 Bibliografía
Revista PC Magazine EU números de 1983.
Mercier Philippe Assembleur Facile. Marabout 1992, Alleur Bélgica.
Barden William Jr. How to Program Microcomputers. Sams & Company 1987.
MASM 86 Columbia Data Products, EU 1986.
IBM Personal Computer Technical Reference.
8-161
Capítulo 9
9.1 El Vídeo
Durante el desarrollo de las microcomputadoras hubo que tomar varias
decisiones con respecto a los componentes que forman el sistema y a la
forma de interactuar entre ellos. Muchas de estas decisiones, buenas o
malas, aún nos siguen persiguiendo con el fantasma de la compatibilidad.
Los caracteres que se despliegan en la pantalla funcionaban en un
principio como una serie de códigos ASCII que se mandaban al manejador
de la pantalla uno a uno y éste se encargaba de todo lo demás. Esto
funciona muy bien en los confines del despliegue de letras, pero una vez
que el usuario y los diseñadores exigen más de la pantalla, este esquema
es inadmisible.
El siguiente paso consiste en reservar un área de memoria para cada
localidad gráfica de la pantalla llamada PIXEL (Picture Element,
Elemento de Pantalla).
Utilicemos de inmediato el programa DEBUG para averiguar a qué nos
referimos:
C>CLS
C>DEBUG
-d b800:0 fa
Notaremos que en la columna de la derecha (caracteres ASCII) están
localizadas las letras C>DEBUG, ¡Hemos localizado el área de memoria
reservada para la pantalla de video! Esta pantalla va de la dirección
B800:0000 a la B800:0F9F (4000 bytes) y se forma por dos bytes por
localidad.
9-163
Ensamblador
¿Pero qué significa el 07h que aparece a la izquierda de cada carácter?
Hemos también localizado el byte de atributo de cada pixel de la pantalla.
Desde DEBUG juguemos con los atributos:
C>DEBUG
-rds
DS 08F1
:b800
-e0
Introduciendo la secuencia 41, F0, 42, 87, 43, F (Recuerde introducir
usando la barra espaciadora y terminar usando la tecla de RETORNO) que
da por resultado ABC (códigos 41,42 y 43) pero en distintos atributos en la
parte superior del video (códigos F0, 87 y 0F).
El manejo del video se puede hacer también utilizando las rutinas de la
interrupción 10h que el fabricante del BIOS (IBM, Phoenix, etc.) diseñó:
;Limpia la pantalla: Servicio 06h de la interrupción 10h del BIOS
cls
PROC NEAR
PUSH AX
PUSH BX
PUSH CX
PUSH DX
MOV AH,6
;Función de movimiento de pantalla
MOV AL,0
;Código para limpiar pantalla
MOV CH,0
;renglón izq. sup.
MOV CL,0
;columna izq. sup.
MOV DH,24
;renglón inf. derecho
MOV DL,79
;columna inf. derecha
MOV BH,7
;Byte de atributo del carácter
INT
10
;llamada al ROM
POP
DX
POP
CX
POP
BX
POP
AX
RET
cls
ENDP
El siguiente programa usa de la misma forma la interrupción 10h:
;Posiciona cursor en columna AL, renglón AH
;Servicio 02h de la interrupción 10h
;Parámetros de entrada: AL y AH
;Parámetros de salida : Ninguno
9-164
El Video
;Registros destruidos : Ninguno
IR_A_XY PROC NEAR
PUSH BX
PUSH DX
MOV DX,AX
MOV AH,2
MOV BH,0
INT
10h
POP
DX
POP
BX
RET
ir_a_XY ENDP
;en DH el renglón, en DL la columna
;servicio 2 de la
;página cero de la
;interrupción 10h
9.2 Ventanas
Las rutinas del ROM de una computadora IBM o compatible permiten el
manejo de ventanas dentro de la pantalla, así como de varias páginas de
video para animaciones rápidas y distintos trucos programáticos.
Esta característica es muy utilizada en los programas modernos
integrados y ambientes en el que el manejo de las ventanas es la base.
Mostraremos a continuación cómo colocar una ventana sencilla en la
pantalla de 20 columnas de ancho y 9 renglones de alto colocada en la
mitad de la pantalla:
;Ventana: demostración del uso de ventanas
;NO utilice el ENTER para terminar una línea
ventana
SEGMENT
ASSUME CS:ventana
;—————————————————————————————————
———————————
;limpia pantalla
MOV AH,6
;Función de movimiento de pantalla
MOV AL,0
;Código para limpiar pantalla
MOV CH,0
;renglón izq. sup.
MOV CL,0
;columna izq. sup.
MOV DH,24
;renglón inf. derecho
MOV DL,79
;columna inf. derecha
MOV BH,7
;Byte de atributo del carácter
INT
10h
;llamada al ROM
;—————————————————————————————————
———————————
9-165
Ensamblador
, colocar el cursor en las coordenadas adecuadas a la ventana
coloca:
MOV DH,16
;renglón
MOV DL,30
;columna
MOV AH,2
;servicio 2 de la
MOV BH,0
;página cero de la
INT
10h
;interrupción 10h
MOV CX,20d
;cuenta a 20
;—————————————————————————————————
———————————
; acepta la entrada del teclado 20 veces
tecla:
MOV AH,1
;función del teclado
INT
21h
CMP
AL,3
;Si CTRL C
JZ
salida
;termina
LOOP tecla
;sigue hasta agotar CX
;—————————————————————————————————
———————————
;mueve pantalla hacia arriba
MOV AH,6
;función de mover
MOV AL,1
;Número de líneas
MOV CH,8
;Renglón sup. izq.
MOV CL,30
;Columna sup. izq.
MOV DH,16
;renglón inf. derecho
MOV DL,50
;Columna inf. derecha
MOV BH,7
;Atributo normal
INT
10h
;llamada a int. video
JMP
coloca
salida:
INT
20h
;—————————————————————————————————
———————————
ventana
ENDS
END
Ejercicio 9.1 Convierta el programa anterior en uno modular usando
llamadas a subrutinas (CALL) en lugar de las secuencas de código
insertadas para: 1. limpiar pantalla, 2. colocar cursor y 3. mover renglón
hacia arriba (Scroll).
El siguiente código llena la pantalla con cierto carácter predeterminado
y aprovecha que ya sabemos la dirección del área de memoria donde se
guardan los datos de la pantalla.
;Llena: llena la pantalla con un carácter
;Define el segmento de video
9-166
El Video
video
SEGMENT
AT
0B800h
DB
800h DUP(?)
;tamaño de la pantalla (2048 bytes)
video
ENDS
;**********************************************************
codigo
SEGMENT
llena
PROC FAR
ASSUME CS:codigo
ASSUME DS:video
PUSH DS
;guarda el segmento viejo
SUB
AX,AX
;AX en cero
PUSH AX
;segmento DS:0
;—————————————————————————————————
———————————
;apunta a segmento de video
MOV AX,video
MOV DS,AX
;—————————————————————————————————
———————————
;coloca carácter con atributo normal
MOV AX,0701h
MOV BX,0
;inicio de pantalla
MOV CX,0F9Fh
;cuenta
;—————————————————————————————————
———————————
;llena la pantalla
otro:
MOV [BX],AX
;inserta carácter
INC
BX
;incrementa contador en
INC
BX
;2 bytes
LOOP otro
RET
;—————————————————————————————————
———————————
llena
ENDP
codigo
ENDS
END
En el programa anterior remarquemos que no es posible regresar al SO
usando una interrupción (20h) puesto que el programa modifica el
segmento de datos por lo que primero se debe salvar este segmento y por
último regresar con la instrucción RET.
Ejercicio 9.2 Modifique el programa anterior (llena) para usar la
instrucción MOVS en lugar del lazo utilizado.
9-167
Ensamblador
Ejercicio 9.3 Utilizando el ejemplo del programa LLENA, modifique
ahora el programa VENTANA para agregar un recuadro a la ventana.
Utilice las instrucciones de movimientos de cadenas.
9.3 Pantallas de Graficación
Poco a poco ha habido una evolución en los dispositivos que conforman
los sistemas de cómputo, mientras más avanza la tecnología y los
productos se vuelven populares por demanda o por baja de costos los
diseñadores están presionados a mejorar día a día. De la sencilla pantalla
de despliegue de caracteres en modo monocromático, se pasa a mejores
resoluciones, más detalle (número de pixels), y más colores. De 320 x
200 pixels a color se evoluciona pasando por 640 x 200 16 colores hasta
el estandar actual de 1200 x 480 en 16 millones de colores en los cuales se
obtiene resolución casi fotográfica.
Todo lo anterior desemboca en requerimientos más y más grandes de
memoria pues el manejo de cada pixel requiere entonces de más bytes
para especificar sus atributos y colores de los posibles de seleccionar,
siendo común hasta 2M bytes y más en los controladores modernos
Al ejecutarse el sistema operativo, éste “despierta” en modo de texto y
es la obligación del programa que utiliza los servicios del sistema
operativo de cambiar este modo. Una posibilidad es el comando MODE
del SO o cualquier utilería proporcionada por los fabricantes de los
dispositivos controladores de video, pero la operación debe realizarse
manualmente. La alternativa es entonces automatizar el proceso dentro
del programa que así lo requiera.
Para lograr esto se requiere de dos pasos:
· 1. Alterar algunos bits de un byte especial que reside en memoria
llamado bandera del equipo.
· 2. Informar al BIOS del ROM que se ha modificado dicha bandera.
Al prender el equipo, el SO reconoce por medio de los interruptores de la
máquina, qué tipo de equipo está conectado en ese momento y guarda la
información en un byte localizado en la dirección absoluta 00410h. Si se
9-168
El Video
quiere engañar al SO es necesario modificar los bits de esa localidad, en
nuestro caso especial nos interesan los bits 4 y 5:
· 00 tarjeta de color 40 x 25 caracteres
· 10 tarjeta de color 80 x 25 caracteres
· 11 tarjeta monocromática 80 x 25 caracteres
La interrupción 10h maneja esto por nosotros con distintas alternativas
de las que sólo investigaremos 3:
· 80 x 25 caracteres monocromáticoal llamar con AL=2, AH=0.
· 640 x 200 pixel monocromático llamar con AL=6, AH=0.
· 320 x 200 pixels de color llamar con AL=4, AH=0.
Presentamos un corto programa que realiza esto precisamente:
;MODO: cambia el modo de la pantalla a
;M=monocromático
;A=Alta resolución 640 x 200 monocromático
;C=Baja resolución 320 x 200 color
tecla
EQU
1h
;entrada del teclado
muestra
EQU
9h
;despliegue en pantalla
dos
EQU
21h
;llamada a DOS
;—————————————————————————————————
———————————
;Define el segmento de la bandera de video
rom
SEGMENT AT 040h
ORG
10h
;dirección del byte
bandera
DW
?
rom
ENDS
;************************************************
;Define segmento de código
modo
SEGMENT
ASSUME CS:modo
ASSUME DS:modo
ORG
100h
inicio:
MOV DX,OFFSET mens ;dirección del mensaje
MOV AH,muestra
;prepara a desplegar
INT
dos
;—————————————————————————————————
———————————
;escoge la bandera del equipo
9-169
Ensamblador
ASSUME DS:rom
MOV AX,rom
MOV DS,AX
;—————————————————————————————————
———————————
;Toma la letra del usuario y escoge la opción
MOV AH,tecla
;entrada del teclado
INT
dos
;llama a DOS
CMP
AL,’m’
;monocromático
JE
mono
CMP
AL,’a’
;alta resolución
JE
alta_res
CMP
AL,’c’
;color
JE
color
JMP
inicio
;—————————————————————————————————
———————————
;coloca en monocromático
mono:
MOV AX,bandera
;bandera del equipo
AND
AX,11001111b
;quita los bits del video
OR
AX,00110000b
;y coloca bits de monocromático
MOV bandera,AX
;regersa a su lugar
MOV AL,2
;80 columnas en Blanco y Negro
MOV AH,0
;función de modifica equipo
INT
10h
;llama a rutina de video
JMP
salida
;finaliza
;—————————————————————————————————
———————————
;coloca en alta resolución
alta_res:
MOV AX,bandera
;bandera del equipo
AND
AX,11001111b
;quita los bits del video
OR
AX,00100000b
;y coloca bits de 640 x 200
MOV bandera,AX
;regersa a su lugar
MOV AL,6
;640 x 200 en Blanco y Negro
MOV AH,0
;función de modifica equipo
INT
10h
;llama a rutina de video
JMP
salida
;finaliza
;—————————————————————————————————
———————————
;coloca en color 320 x 200
color:
MOV AX,bandera
;bandera del equipo
AND
AX,11001111b
;quita los bits del video
MOV bandera,AX
;regresa a su lugar
MOV AL,4
;320 x 200 color
MOV AH,0
;función de modifica equipo
9-170
El Video
INT
10h
;llama a rutina de video
;—————————————————————————————————
———————————
salida:
INT
20h
;salir
;—————————————————————————————————
———————————
mens
db
13,10
db
‘Teclee “m” para 80 x 25 monocromático’,13,10
db
‘
“a” para 640 x 200 monocromático’,13,10
db
‘
“c” para 320 x 200 color’,13,10
db
‘Selección: $’
;—————————————————————————————————
———————————
modo
ENDS
END
inicio
El programa anterior debe ser ensamblado en un archivo del tipo COM
(usando EXE2BIN) y sólo puede ser usado en equipos con tarjeta CGA,
EGA, VGA, SVGA o UVGA. El program hace uso de la pseudo instrucción
SEGMENT AT que forza al segmento en una dirección explicita conocida
de antemano. Otra cosa importante es que como deseamos un programa
tipo COM, hemos forzado los datos en un sólo segmento y no en
segmentos separados como en ejemplos anteriores, la única excepción a
esto es con el uso de la pseudo instrucción SEGMENT AT explicada en el
parráfo anterior.
Establecemos en este momento la siguiente regla:
REGLA. Se puede tener más de un segmento en un archivo COM si este segmento
es definido de antemano con la pseudo instrucción SEGMENT AT.
La forma de accesar la pantalla cambia también pues ahora se requiere
de 320 x 200 (en baja resolución color) ó 64,000 bytes localidades de
memoria para guardar la información de los pixels por lo que se escogió
asignar 2 bits de un byte para cada localidad lo que nos da una posibilidad
de 4 combinaciones para cada pixel y 4 pixel por byte (16,000
localidades) en las siguientes combinaciones:
· 00 negro
· 01 amarillo
· 10 azul
· 11 blanco
9-171
Ensamblador
Experimentaremos un poco con el color y DEBUG una vez que ya
tenemos el programa MODO ensamblado. Como primer paso necesitamos
cambiar el modo de la pantalla a color resolución baja (320 x 200) usando
nuestro programa MODO escogiendo la opción C y de ahí entrar a DEBUG
y cambiar el segmento al de la pantalla:
C>DEBUG
-rds
DS 08F1
:b800
En este punto intentemos lo siguiente:
-f0 400 55
Con lo que logramos que aparezcan una docena de rayas amarillas en la
parte superior del monitor. El color depende del monitor que estemos
usando y del ajuste del mismo. Lo que hemos logrado con el comando f es
llenar las primeras 400h localidades del segmento de memoria con la
constante 55h ó 01010101b, el código amarillo para cada uno de los 4
pixeles de cada byte.
¿Por qué si llenamos las primeras 400h localidades de memoria de video
obtenemos entonces áreas en blanco de la pantalla? La memoria de video
para color se divide en dos partes:
· De las localidades 0000h a la 1F3Fh (7,999 bytes) para las líneas
pares (0,2,4, etc.)
· De las localidades 2000h a la 3F3Fh (7,999 bytes) para las líneas
impares (1,3,5, etc.)
Probemos otros colores:
-f 400 800 aa
-f0 800 c00 ff
Y llenemos ahora las líneas impares:
-f 2000 2400 55
-f 2400 2800 aa
-f 2800 2c00 ff
Ejercicio 9.4
Experimente en este punto con distintos códigos y vea el
resultado.
9-172
El Video
Como se puede suponer, es un poco difícil tratar de mantener el control
de las dos áreas de memoria para gráficar un punto o línea de un lugar a
otro. Para evitarnos este problema podemos usar una rutina del ROM que
hace todo el trabajo:
–
–
–
–
AH = 12h
DX = número de renglón del punto a desplegar
CX = número de la columna del punto a desplegar
AL = color del punto
En el siguiente programa evitaremos el uso de esta interrupción y
diseñaremos el programa de forma tal que deba resolver todo lo
relacionado a los colores, bancos y pixels de cada byte. El programa opera usando las teclas 0,1,2 y 3 para definir el color del pixel y las flechas
para definir la dirección de dibujo del mismo. A su vez, hace uso de la
interrupción 16h del teclado que nos regresa el código de cada tecla
presionada. Desgraciadamente las teclas como mayúsculas, bloqueo de
números, etc. no tienen código, para eso se requiere del llamado a la
interrupción 16h con AH=2 lo que regresa una bandera con los distintos
estados de las teclas presionadas en el registro AX de acuerdo a la
siguiente tabla:
· bit 0 Tecla de mayúsculas derecha presionada
· bit 1 Tecla de mayúsculas izquierda presionada
· bit 2 Tecla de control presionada
· bit 3 Tecla de alterno presionada
· bit 4 Tecla de bloque de desplazamiento activada
· bit 5 Tecla de bloque numérico activada
· bit 6 Tecla de bloqueo de mayúsculas activada
· bit 7 Tecla de insertar activa
;LINEAS Rutina para dibujo de linea en la pantalla utilizando
;las flechas para la direccion
;Se requiere de monitor de color y de usar antes
;el programa MODO de este mismo capítulo (opción c)
arriba
EQU
48H
; flecha hacia arriba
9-173
Ensamblador
abajo
EQU
50H
; flecha hacia abajo
derecha
EQU
4dH
; izquierda EQU
4bH
;
escape
EQU
1bH
; tecla de ESC
;************************************************
video
SEGMENT AT 0B800H
; define segmento extra apuntando a
pantalla
wd_buff
LABEL WORD
v_buff
LABEL BYTE
video
ENDS
;************************************************
linea
SEGMENT
; define el segmento de codigo
;—————————————————————————————————
—————————————————
principal PROC FAR
; parte principal del programa
ASSUME
cs:linea,es:video
inicio:
; direccion inicial de ejecución
; prepara stack para regreso
PUSH ds
; guarda segmento de datos viejos
SUB
AX,AX
; cero en AX
PUSH AX
; y guarda en pila(segmento DS:0)
; prepara ES con el segmento de video
MOV AX,video
; direccion del segmento de video
MOV ES,AX
; pasarlo a ES
; limpia pantalla usando el ROM
MOV AH,6
; usa corrimiento hacia arriba
MOV AL,0
; código para limpiar pantalla
MOV CX,0
; punto superior izq.=0,0
MOV DL,79
; punto inf izq en
MOV DH,24
; 79,24
INT
10H
; llama a rutina de ROM
; el apuntador a la pantalla está en CX, DX
; renglón de 0 a 200d en DX
; columna de 0 a 320d en CX
;
; posiciona en centro de pantalla para comenzar
MOV DX,100d
; número de renglones/2
MOV CX,160d
; columnas/2
;
; lee el teclado para obtener el dígito que se está presionando
lee_tecla:
MOV AH,0
; función del teclado sin eco
INT
16H
; llama a dos
CMP
AL,escape
; si es ESC
JZ
salida
; termina
9-174
El Video
CMP
AL,33H
; si es mayor que 3 (ASCII)
JG
grafica
; no es color
CMP
AL,30H
; si es menor que cero (ASCII)
JL
grafica
; no es color
; número entre 0 y 3, es un color, defínelo
AND
AL,3
; quita 5 bits superiores
MOV BL,AL
; guarda en BL
JMP
lee_tecla
; toma siguiente caracter
; encuentra hacia a donde ir y grafica siguiente punto
grafica:
MOV AL,AH
; poner código de búsqueda en AL
CMP
AL, arriba
JNZ
no_arriba
; salta si no es hacia arriba
DEC
DX
; si es hacia arriba resta línea
no_arriba:
CMP
AL,abajo
JNZ
no_abajo
; salta si no es hacia abajo
INC
DX
; si hacia abajo, resta línea
no_abajo:
CMP
AL,derecha
JNZ
no_derecha
INC
CX
no_derecha:
CMP
AL,izquierda
JNZ
prendelo
DEC
CX
; llama a subrutina de gráfica que requiere color en AL,
; renglón en DX y columna en CX
prendelo:
MOV AL,BL
;retrasa
CALL graf_punto
; lee puerto B en AL
JMP
lee_tecla
; mascarilla los 2 bits más bajos
salida:
RET
; regresa a dos
principal ENDP
; final de parte principal de programa
;—————————————————————————————————
———————————————graf_punto PROC NEAR
; subrutina de graficación en pantalla
; resolución media, 320 x 200 en color
; entra con :
; coordenada x en CX, coordenada y en DX
; color en AL(0=apagado, 1,2,3=colores)
PUSH BX
; salva BX,columna,renglon y color
PUSH CX
9-175
Ensamblador
PUSH DX
PUSH AX
; multiplica renglón x # de bytes por renglón
; (80 pero como ya fue mult. por 2 usar 40)
PUSH DX
; salva reng. para par/non
MOV AL,40
; bytes/reng./2
AND
DX,0FEh
; quita bit de non/par
MUL DL
; AX tiene ahora la dir. del reng.
; ver si hay que sumar 2000H para segundo banco de memoria
; si el renglón es impar
POP
DX
; trae número original de reng.
TEST DL,1
; probar bit 0 para par/non
JZ
par
; salta a pares
ADD
AX,2000H
; suma para apuntar a 2do banco de
mem.
par:
MOV BX,AX
; salvar reng. en BX
; sumar dir de columna a renglón
PUSH CX
; salvar columna
SHR
CX,1
; correr a izq. para
SHR
CX,1
;no matar bits de color
ADD
BX,CX
; sumar a dir. en BX
; usar los bits de pos. para poner color y mascara en pos. correcta
POP
CX
; recupera columna
AND
CX,3
; guarda bits de pos.
INC
CX
; corrimiento gratis
POP
AX
; trae color
MOV DL,0FCh
; DL mascara 11,11,11,00
corre:
; AL color 00,00,00,cc
ROR
AL,1
; corre color
ROR
AL,1
; 2 bits a la derecha
ROR
DL,1
; corre mascarilla 2 bits
ROR
DL,1
; a la derecha
LOOP corre
; hacerlo el # de veces de pos. de bits
; contenido del byte, mascarilla todo menos color y OR con bits de color
AND
[v_buff + BX],DL
; mascarilla
OR
[v_buff + BX],AL
;OR con el color
POP
DX
; restaura renglón
POP
CX
; restaura columna
POP
BX
; restaura BX y
RET
; regresa
graf_punto ENDP
;—————————————————————————————————
————————————————linea
ENDS ; fin de segmento
9-176
El Video
;*****************************************************
;
END
inicio
; fin de ensamble
Se introduce la pseudo instrucción LABEL BYTE, LABEL WORD y LABEL DWORD que define o redefine una localidad como tipo byte, palabra
o palabra doble. Recordemos que las pseudo instrucciones DB y DW
reservan y apartan las localidades de memoria necesarias según la
definición realizada, a diferencia de éstas, LABEL sólo informa al
ensamblador qué tipo de localidad es la que sigue o precede sin reservar
localidades de memoria y es sumamemte usada cuando se necesita
definir una misma área como de dos o más tipos diferentes. Se requiere de
este tipo de instrucciones pues el ensamblador es muy rígido con la
definición de tipos y no es posible cambiar “al vuelo” el tipo de datos.
El dibujar líneas horizontales y verticales es muy sencillo pero en diagonal se vuelve un poco más complejo requiriendo de más pasos para
definir qué pixeles encender y cuáles no. Presentamos un programa en el
que al introducir una secuencia de 5 números (x1, y1, x2, y2, color)
realiza la línea utilizando el algoritmo de Bresenham.
Ejercicio 9.5 Investigar en qué consiste el algoritmo de Bresenham para
dibujar una línea entre dos puntos en una pantalla.
El programa que sigue se basa en este algoritmo y en las interrupciones
del ROM para graficar una línea recta entre dos puntos. El programa debe
de ser ejecutado como tipo EXE (no usar EXE2BIN) y se debe cambiar la
pantalla a color 320 x 200 antes de usarse. Una vez en el programa, éste
espera 5 parámetros desde el teclado introducidos con ENTER:
· x1,y1: coordenadas del punto inicial
· x2,y2: coordenadas del punto final
· color: el color para la línea
al introducir el úlitimo dato el programa grafica la línea y espera otros 5
datos.
;BRESENHAM: dibujo de una línea recta entre dos puntos de la pantalla
;use el programa MODO para cambiar a pantalla de color 320 x 200
;Acepta 5 parámetros (x1,y1), (x2,y2) y color (1,2,3,4,0)
datos
SEGMENT
delta_x
DW
?
;|x2-x1|
delta_y
DW
?
;|y2-y1|
9-177
Ensamblador
mitad_y
LABEL WORD
;|y2-y1|/2
mitad_x
DW
?
;|x2-x1|/2
cuenta
DW
?
;eje largo
x1
DW
?
;coordenada 1
y1
DW
?
;coordenada 1
x2
DW
?
;coordenada 2
y2
DW
?
;coordenada 2
color
DW
?
;color de 0 a 4 ó de 0 a 1
datos
ENDS ;fin de datos
;—————————————————————————————————
—————————————————bresen
SEGMENT
;segmento de código
prog
PROC FAR
;parte principal del programa
ASSUME
CS:bresen,DS:datos
inicio:
;dirección inicial
;guarda la pila para el regreso
PUSH DS
SUB
AX,AX
PUSH AX
; limpia pantalla usando el ROM
MOV AH,6
; usa corrimiento hacia arriba
MOV AL,0
; código para limpiar pantalla
MOV CX,0
; punto superior izq.=0,0
MOV DL,79
; punto inf izq en
MOV DH,24
; 79,24
INT
10H
; llama a rutina de ROM
n_linea:
CALL decbin
;pide x1
MOV x1,BX
CALL decbin
;pide y1
MOV y1,BX
CALL decbin
;pide x2
MOV x2,BX
CALL decbin
;pide y2
MOV y2,BX
CALL decbin
;pide el color
MOV color,BX
CALL linea
;dibuja la línea
JMP
n_linea
;vuelve por otros datos
prog
ENDP ;
;—————————————————————————————————
—————————————————linea
PROC NEAR
;rutina para dibuhar la línea
;encuentra |y2-y1| resultado en delta_y
MOV AX,y2
;toma y2
9-178
El Video
SUB
AX,y1
;resta y1, resultado en AX
;Si delta_y es + SI=1, si - SI=-1
MOV SI,1
;bandera positiva
JGE
guarda_y
;manten si positivo
MOV SI,-1
;poner bandera en negativo
NEG
AX
;saca valor absoluto
guarda_y:
MOV delta_y,AX
;guarda valor de |y2-y1|
;encuentra |x2-x1| resultado en delta_x
MOV AX,x2
;toma x2
SUB
AX,x1
;resta x1, resultado en AX
;Si delta_x es + DI=1, si - DI=-1
MOV DI,1
;bandera positiva
JGE
guarda_x
;si positivo manten
MOV DI,-1
;si negativo pon bandera
NEG
AX
;y saca valor absoluto
guarda_x:
MOV delta_x,AX
;guarda valor de |x2-x1|
;Encuentra si pendiente > o < que 1
CMP
AX,delta_y
;compara las deltas
JL
c_pend
;pendiente>1
CALL pend_1
;pendiente<=1
JMP
fin
c_pend:
CALL pend
;pendiente>1
fin:
RET
linea
ENDP
;—————————————————————————————————
—————————————————pend_1
PROC NEAR
;pendiente < 1
;Calcula |x2-x2|/2
MOV AX,delta_x
;toma |x2-x2|
SHR
AX,1
;divide entre 2
MOV mitad_x,AX
;y guarda
;Inicia valores
MOV CX,x1
;toma x1
MOV DX,y1
;toma y1
MOV BX,0
;inicializa BX
MOV AX,delta_x
;fija la cuenta a
MOV cuenta,AX
;|x2-x1|
punto:
CALL graf_punto
;grafica un punto
ADD
CX,DI
;inc/dec de X
ADD
BX,delta_y
;+|y2-y1| en BX
CMP
BX,mitad_x
;compara con |x2-x1|/2
9-179
Ensamblador
JLE
SUB
ADD
dcuenta
BX,delta_x
DX,SI
;no modificar inc/dec de Y
;BX-|x2-x1|
;incrementa o decrementa Y
dcuenta:
DEC
cuenta
;¿Terminamos?
JGE
punto
;aún no
RET
pend_1
ENDP
;—————————————————————————————————
—————————————————pend
PROC NEAR
;pendiente > 1
MOV AX,delta_y
;toma |y2-y1|
SHR
AX,1
;divide entre 2
MOV mitad_y,AX
;y guarda
;Inicia valores
MOV CX,x1
;toma x1
MOV DX,y1
;toma y1
MOV BX,0
;inicializa registro BX
MOV AX,delta_y
;toma |x2-x1|
MOV cuenta,AX
;y guarda en cuenta
punto2:
CALL graf_punto
;grafica punto
ADD
DX,SI
;incrementa o decrementa Y
ADD
BX,delta_x
;BX+|x2-x1|
CMP
BX,mitad_y
;compara con |y2-y1|/2
JLE
dcuenta2
;no incrementar o decrementar X
SUB
BX,delta_y
;BX-|y2-y1|
ADD
CX,DI
;incrementa o decrementa X
dcuenta2:
DEC
cuenta
;¿Se terminó la línea?
JGE
punto2
;aún no
RET
pend
ENDP
;—————————————————————————————————
—————————————————graf_punto PROC NEAR
PUSH BX
;guarda registros
PUSH CX
PUSH DX
PUSH AX
PUSH SI
PUSH DI
;Rutina de ROM para graficar punto requiere de:
;DX=renglón, CX=columna, AL=color
MOV AX,color
9-180
El Video
MOV AH,12d
;función de graficar punto
INT
10h
;Rutina del BIOS
POP
DI
;Regresa registros
POP
SI
POP
AX
POP
DX
POP
CX
POP
BX
RET
graf_punto ENDP
;—————————————————————————————————
—————————————————key_in
EQU
1h
doscall
EQU
21H
;—————————————————————————————————
—————————————————decbin
PROC NEAR
;Ver capítulo 6
;Acepta decimal de teclado, regresa
;binario en registro BX
MOV BX,0
;Limpia registro BX
otro:
MOV AH,key_in
;entrada del teclado
INT
doscall
;llama a DOS
SUB
AL,30h
;ASCII a binario
JL
sale
;Salir si el #<0
CMP
al,9d
;¿Es > 9?
JG
sale
;Si es así, no es dígito
CBW
;Convierte byte en AL a palabra en
AX
;Multiplica número en BX por 10
XCHG AX,BX
;Cambia registros AX y BX
MOV CX,10d
;Coloca 10 en CX
MUL CX
;y multiplica por AX (AX*10)
XCHG AX,BX
;Regresa # a BX
;Suma número en AX por el de BX
ADD
BX,AX
;suma dígito a #
JMP
otro
; y ve por otro
sale:
RET
decbin
ENDP
;—————————————————————————————————
—————————————————bresen
ENDS ;fin de segmento principal
;—————————————————————————————————
—————————————————-
9-181
Ensamblador
END
inicio
;fin de programa
9.4 Resumen
9.5 Instrucciones Analizadas
SEGMENT AT, LABEL BYTE, LABEL WORD
9.6 Problemas
9.1 Modifique el programa BRESENH de forma que salte un renglón
después de aceptar cada parámetro.
9.2 Modifique el programa BRESENH de forma que acepte alguna tecla
que usted defina para terminar la ejecución.
9.3 Modifique el programa BRESENH para que limpie la pantalla
despues de la graficación de cada línea.
9.7 Bibliografía
Revista PC Magazine EU números de 1993.
Mercier Philippe Assembleur Facile. Marabout 1992, Alleur Bélgica.
Barden William Jr. How to Program Microcomputers. Sams & Company 1987.
MASM 86 Columbia Data Products, EU 1986.
IBM Personal Computer Technical Reference. Apéndice A
9-182
Capítulo 10
10.1 Archivos
Por razones históricas y de compatibilidad con sistemas anteriores y con
versiones viejas, el MS-DOS proporciona acceso a lectoras de disco de
varias formas:
· Acceso secuencial a registros
· Acceso aleatorio a registros
· Acceso aleatorio a bloques
· Manejadores de archivo (file handles)
En el último esquema se especifica el nombre del archivo y la
interrupción regresa un número de manejador que a partir de ese
momento sirve para accesar los datos del archivo.
Analizaremos brevemente en las siguientes secciones cada uno de los
métodos e ilustraremos con breves programas.
10.2 Acceso Secuencial
El primer paso para el acceso a los datos de un archivo es guardar el
nombre junto con otra información en un área de memoria preestablecida
llamada Bloque de Control de Datos (File Control Block o FCB). El
segundo paso consiste en especificar un lugar en memoria donde se
guardan los datos de las lecturas y escrituras al disco y esta área se llama
Area de Transferencia de Datos (Data Transfer Area o DTA). El tercer
paso es abrir el archivo y el cuarto y último sera cerrar el archivo una vez
que los datos han sido leídos o escritos a él.
10-183
Ensamblador
La interrupción utilizada para el manejo de archivo es la 21h con
distintos servicios solicitados por medio de datos en el registro AH. El
primer servicio que veremos es el de abrir un archivo y corresponde al
servicio 0Fh. Este servicio exige que el archivo ya exista por lo que lo
creamos primero ya sea desde el sistema operativo copiando desde el
teclado (consola) o usando un procesador de palabras.
Función de abrir un archivo, Servicio 0Fh de la interrupción
21h
Registros:
AH=0Fh
DS=Dirección del Segmento del FCB
DX=Desplazamiento de la dirección del FCB
(El nombre del archivo con su extensión deben estar ya en el FCB)
Regresa:
AL=00
el archivo se encontró y abrió
AL=FF
El archivo no existe
FCB
# de disco,tamaño de registro,fecha,tamaño archivo,# bolque,etc.
Practiquemos a abrir un archivo desde DEBUG:
Primero creamos el archivo copiándolo desde el teclado:
C>COPY CON prueba.txt
ejemplo de información^Z
1 files(s) copied
C>DEBUG
-a100
MOV
DX,5C
MOV
AX,0F
INT
21
INT
20
Pero aún no hemos especificado el nombre del archivo, sin embargo
colocamos un número (5C) que corresponde al lugar donde estará el
nombre del archivo. DEBUG comienza siempre en la dirección 100h del
segmento correspondiente. ¿Qué hay en las localidades del 00h al FFh?
Estas localidades forman parte del Prefijo del Segmento del Programa y
dentro de éste, el FCB ocupa las localidades del 5Ch al 7Ch (en los
accesos aleatorios es hasta 80h) seguido del DTA de la dirección 80h a la
FFh.
10-184
El Acceso a Disco
El prefijo del segmento del programa de DEBUG consiste, en este caso,
mayoritariamente en información que el SO y el programa requieren saber de un archivo en particular, por ejemplo los 2 primeros bytes a partir
de la dirección 0 son la INT 20h, los siguientes 2 el tamaño de la memoria
disponible, los 6 siguientes en la dirección del despachador de DOS, los 4
siguientes en la dirreción donde los programas terminan, los siguientes
dos en la dirección de salida para una interrupción por teclado (IP), etc.
Esta estructura se muestra en detalle en el apéndice E del libro IBM Personal Computing Disk Operating System.
El siguiente paso consiste en especificar el nombre del archivo que
queremos abrir. Dejemos que DEBUG nos ayude y usemos el comando N,
pero primero llenemos algunas localidades con un valor conocido como
por ejemplo 22.
-f 40 7f 22
-nprueba.txt
y comprobemos con D.
-d0
Veremos que los bytes a partir del 5Ch han sido modificados y contienen
ahora el nombre del archivo PRUEBA.TXT que DEBUG colocó ahí.
Ejecutemos ahora nuestro programa con G. Si desplegamos de nuevo la
dirección 0 (D0) nos encontramos que todo el panorama del FCB ha
cambiado y muestra nueva información de acuerdo a la siguiente tabla:
10-185
Ensamblador
Tabla 10.1 El FCB
SO=datos llenados por el SO al abrir el archivo
usuario=datos proporcionados por el usuario
0
5C
Número de disco
SO
1
5D
2
5E
3
5F
nombre del archivo
usuario
4
60
5
61
6
62
7
63
8
64
9
65
10
66
extensión archivo
usuario
11
67
12
68
bloque actual
SO
13
69
14
6A
tamaño registro
SO
15
6B
16
6C
tamaño archivo (bajo)
SO
17
6D
18
6E
tamaño archivo (alto)
SO
19
6F
20
70
fecha
SO
21
71
22
72
23
73
24
74
25
75
Uso del SO
SO
26
76
27
77
28
78
29
79
30
7A
31
7B
32
7C
registro actual
usuario
33
7D
# reg. aleatorio (bajo)
usuario
34
7E
35
7F
# reg. aleatorio (alto)
usuario
36
80
Expliquemos brevemente las localidades más relevantes:
–
En la dirección 5C encontramos ahora un 02 que corresponde al
disco C, en la 6A encontramos ahora 80h (128d) que es el tamaño
10-186
El Acceso a Disco
–
–
–
del registro que el sistema operativo toma por omisión pues es el
tamaño estandard para archivos secuenciales.
En las localidades 6C hasta la 6F se encuentra el tamaño del
archivo y en este caso es de 16h (22d) bytes. Recuerde que se
especifica primero byte alto, byte bajo, palabra alta, palabra baja.
La fecha se encuentra almacenada en las localidades 70h y 71h y
en nuestro caso encontramos el número 09h 1Fh nuevamente en
el orden invertido (1Fh 09h ó 0001 1111 0000 1001) con la
siguiente convención:
A. Los primeros 5 bits corresponden al día. Por ejemplo 01001
ó día 9.
B. Los siguientes 4 bits al mes. Por ejemplo 1000 ó mes 8.
C. Los últimos 7 bits al año que se deben sumar a 1980. Por
ejemplo 0001111, 15d que corresponde a 1995.
De las localidades 72h a 7Bh se encuentra la dirección donde está
almacenado físicamente el archivo, es un poco complicado y no
lo tocaremos en este momento.
Una vez que el archivo fue abierto, podemos proceder a leer su
información. El servicio 14h de la interrupción 21h nos permite leer
secuencialmente de un archivo:
Función de lectura secuencial,INT 21h servicio 14h
Registros: AH=14h
DS=Dirección del Segmento del FCB
DX=Desplazamiento de la dirección del FCB
(El nombre del archivo con su extensión, bloque actual, registro
actual y el tamaño del registro deben estar ya en el FCB)
Regresa:
AL=00
registro del archivo leido exitosamente
AL=01
fin de archivo,no hay datos en el registro
AL=02
DTA muy pequeño,fin de transferencia
AL=03
fin de archivo
Desde DEBUG metamos ahora el siguiente programa:
10-187
Ensamblador
MOV
MOV
INT
MOV
INT
INT
DX,5C
AH,0F
21
AH,14
21
20
;dirección del FCB
;abrir archivo
;llama a DOS
;servicio de lectura
;llama a DOS
;vuelve a DEBUG
Antes de ejecutar el programa llenemos con la instrucción f las
localidades 80h a FFh con un valor conocido. Estas localidades forman el
Area de Transferenmcia de Datos (Data Transfer Area o DTA) y es el
lugar en memoria de donde se toman los datos para escribir en un archivo
o se toman los datos del archivo y se colocan en ese sitio. Esto es:
El área de transferencia de datos (DTA) es el lugar provisional en
memoria donde un registro de archivo es almacenado en su camino
de o hacia el disco duro o flexible.
En la localidad 7Ch debemos colocar de antemano el número de registro
a leer pero en este caso el archivo es tan pequeño que con el primer
registro basta. Usando el comado E7C pongamos un cero en esa localidad.
Ejecutemos ahora el programa y revisemos con el comando D las
localidades de la 80h a la FFh; encontramos el texto que contienen
nuestro archivo muestra. Al terminar el programa, esta localidad se
incrementa en 1 pues ahora debe apuntar al siguiente registro por lo que
una segunda ejecución del programa nos daría el siguiente registro, esto
es, una lectura secuencial.
Pasemos ahora a un programa más sofisticado de lectura secuencial.
Para que este programa funcione, el archivo debe terminar con un
símbolo de fin de archivo (EOF, 1Ah) que la mayoría de los procesadores
colocan de forma automática:
;—————————————————————————————————
————————————————
;SECUENCI: Lee un archivo ASCII y preséntalo en la pantalla
;ejecutar junto con el nombre del archivo
;—————————————————————————————————
————————————————
dos
EQU
21h
;llamada al SO
abre
EQU
0Fh
;llamada al SO
lee
EQU
14h
;llamada al SO
muestra EQU 2h
;llamada al SO
fcb
EQU
5Ch
;llamada al SO
eof
EQU
1Ah
;llamada al SO
10-188
El Acceso a Disco
;—————————————————————————————————
————————————————
datos
SEGMENT
ORG
7Ch
regnum
DB
?
;número de registro
ORG
80h
dato
DB
80h DUP(?)
;área DTA
datos
ENDS
;—————————————————————————————————
————————————————
secuencial SEGMENT
;programa principal
;—————————————————————————————————
————————————————
prog
PROC FAR
ASSUME CS:secuencial,DS:datos
inicio:
;Salva segmentos para el regreso
PUSH DS
;salva DS
SUB
AX,AX
;0 en AX
PUSH AX
;coloca 0 en la pila
;Abre el archivo y coloca registrro en 0
MOV DX,fcb
;poner dirección de FCB dentro del
segmento
MOV AH,abre
;servicio de apertura
INT
dos
;de la interrupción 21h
MOV regnum,0
;primer registro en 7Ch
;lee registro en forma secuencial
lee_reg:
MOV DX,fcb
;apunta al FCB
MOV AH,lee
;servicio de lectura de
INT
dos
;la interrupción 21h
;muestra el registro que se encuentra en DTA
MOV CX,80h
;número de caracteres
MOV BX,0
;inicializa el apuntador
imprime:
MOV DL,[dato+BX]
;toma carácter
CMP
DL,eof
;¿Llegamos al final de archivo?
JE
salir
;si, salir
MOV AH,muestra
;no, muestra
INT
dos
INC
BX
;incrementa el contador
LOOP imprime
;realiza 80h veces
JMP
lee_reg
;y ve por otro registro del archivo
salir:
RET
;termina y regresa a DOS
prog
ENDP
10-189
Ensamblador
secuencial ENDS
END
inicio
El programa requiere del nombre del archivo que queremos leer y ver en
la pantalla como parámetro del programa: SECUENCI LEEME.TXT, el SO
se encarga entonces de llenar el FCB con los datos del archivo (en este
caso LEEME.TXT), algo similar al comando N de DEBUG.
Otra cosa a notar es el manejo del registro DS y ES que el sistema
operativo, por tratarse de un archivo tipo EXE, inicializa con los datos del
mismo programa apuntando a los segmentos adecuados.
Existe también un traslape de 1 byte entre el FCB y el DTA en la dirección
80h. En el caso de acceso secuencial no hay problema pues el byte no es
usado. Pero en el caso de acceso aleatorio se debe tener cuidado de no
perderlo por lo que muchas veces se prefiere “mudar” el área de DTA a
otro lado.
En nuestro siguiente programa introducimos las funciones de crear un
archivo y de escribir a él conjuntamente:
;—————————————————————————————————
————————————————
;GUARDA: toma datos del teclado y guarda en un archivo
;—————————————————————————————————
————————————————
dos
EQU
21h
;llamada al SO
crear
EQU
16h
;crear archivo
escribe
EQU
15h
;despliega carácter
cierra
EQU 10h
;cierra archivo
mem_prov EQU
0Ah
;memoria para el teclado
fcb
EQU
5Ch
;área de control de archivo
cr
EQU
0Dh
;ASCII de RETORNO
lf
EQU
0Ah
;ASCII de ALIMENTA LINEA
;—————————————————————————————————
—————————————————
datos
SEGMENT
ORG
7Ch
regnum
DB
?
ORG
80h
dato
DB
80h DUP(?)
;área DTA
datos
ENDS
;—————————————————————————————————
————————————————guarda
SEGMENT
10-190
El Acceso a Disco
prog
PROC FAR
ASSUME CS:guarda,DS:datos
inicio:
;Guarda segmentos para regresar en paz
PUSH DS
;salva DS
SUB
AX,AX
;colcoa 0 en AX
PUSH AX
;y empuja a la pila
;crea archivo, coloca contador de registro en 0
MOV DX,fcb
;FCB en DX
MOV AH,crear
;servicio de Crea archivo
INT
dos
;de DOS
MOV regnum,0
;0 en dirección 7Ch (número de
registro)
;limpia memoria llenando con retorno de línea
otra_linea:
MOV BX,0
;apuntador de caracteres
MOV CX,80h
;cuenta de caracteres
borra:
MOV [BX+dato],cr
;CR en zona de datos
INC
BX
;incrementa apuntador
LOOP borra
;repite 80h veces
;Toma línea del teclado
MOV dato-2,78
;long. máxima de línea
MOV DX,OFFSET dato-2 ;dirección de DTA
MOV AH,mem_prov
;acepta del teclado una línea
INT
dos
CMP
dato-1,1
;si no hay caracteres
JLE
salir
;salir del programa
;inserta alimentación de línea
MOV BL,dato-1
;coloca cuenta de caracteres
aceptados
MOV BH,0
;en registro BH
MOV [dato+BX+1],lf
;inserta alimentación de línea
;escribe registro al archivo en modo secuencial
MOV DX,fcb
;FCB en DX
MOV AH,escribe
;escribe en secuencia
INT
dos
JMP
otra_linea
;y acepta otra línea
;Cierra el archivo y sal a DOS
salir:
MOV DX,fcb
;FCB en DX
MOV AH,cierra
;función de cerra archivo
INT
dos
RET
prog
ENDP
10-191
Ensamblador
;—————————————————————————————————
—————————————————
guarda
ENDS
END
inicio
Los datos se introducen al programa línea a línea terminando con RECuando introducimos un RETORNO unicamente el programa
cierra el archivo y regresa el controla al SO. Se han introducido dos
nuevas funciones en el programa como se resumen en las siguientes tablas:
TORNO.
10-192
El Acceso a Disco
Función de Crear un Archivo,INT 21h servicio 16h
Registros: AH=16h
DS=Dirección del Segmento del FCB
DX=Desplazamiento de la dirección del FCB
(El nombre del archivo con su extensión deben estar en el FCB)
Regresa:
AL=00
archivo creado exitosamente
AL=FF
no hay espacio para crear archivo
Se debe tener cuidado con la función pues si el archivo ya existía se
borra, dejándolo con longitud 0.
Función de Escritura Secuencial,INT 21h servicio 15h
Registros: AH=15h
DS=Dirección del Segmento del FCB
DX=Desplazamiento de la dirección del FCB
(El nombre del archivo con su extensión, bloque actual, registro
actual y el tamaño del registro deben estar en el FCB)
Regresa:
AL=00
registro del archivo escrito exitosamente
AL=01
el disco está lleno
AL=02
DTA muy pequeño, fin de transferencia
En la escritura secuencial, la interrupción se encarga de incrementar el
número de registro por nosotros dejándolo apuntando al siguiente. Si el
registro no es lo suficientemente grande como para llenar un área del
disco llamada sector, es colocado en una memoria temporal por el SO. De
ahí es tomada cuando la longitud del sector es excedida o hasta que se
cierra el archivo guardando la información en ese momento presente en el
archivo. Un sector tiene una longitud de 512 bytes y es la unidad básica de
almacenamiento en archivos.
Ejercicio 10.2 Agregue al programa GUARDA una rutina que mande a la
pantalla un CR y LF por cada línea introducida.
10-193
Ensamblador
Ejercicio 10.3 Agrege las instrucciones necesarias para que el programa
GUARDA nos advierta con un mensaje el nombre del archivo y nos solicite
que comencemos a teclear. Agrege otra rutina para dar instrucciones
específicas para el uso del programa.
Ejercicio 10.4 Si el
programa GUARDA,
usuario no proporcionó un nombre de archivo al
que el programa despliegue un mensaje de error y
salga de inmediato.
En los ejercicios anteriores use definiciones de mensajes en el área de
datos de forma similara a:
mensaj1
DB
‘Comience a introducir datos:$’
para posteriormente llamar:
MOV DX,OFFSET mensaj1
CALL despliega
10.3 Acceso Aleatorio
Una segunda forma de leer los registros de un archivo es de forma
aleatoria, esto es, sin ningún orden en específico. Una razón por la que los
accesos aleatorios son más fáciles de manejar es debido a que en los
accesos secuenciales se requieren de tres bytes:
· El primero en la dirección 7Ch para especificar el número de
registro a leer o escribir.
· El segundo y tervero en las direcciones 68h y 69h para especificar
el número de bloque.
Cuando 127d registros han sido escritos al archivo, el bloque debe
entonces incrementarse y el contador de registros colocarse en 0. Aunque
es posible en un programa de ensamblador, cuesta también bastante
trabajo.
En una lectura/escritura aleatoria, se controla sólo el número de registro
formado por 4 bytes que nos da un total muy grande de registros dentro de
un archivo. El manejo de una palabra doble es directo con las instruccions
10-194
El Acceso a Disco
de ensamblador y el control se torna entonces sumamente sencillo y
directo.
La función de lectura de un registro aleatorio es muy similar a la de uno
secuencial con la diferencia de que el contador no es incrementado de
forma automática. El programa debe encargarse de insertar el número
adecuado de registro en la zona del FCB del archivo antes de cada lectura
o escritura.
Función de Lectura Aleatoria,INT 21h servicio 21h
Registros: AH=21h
DS=Dirección del Segmento del FCB
DX=Desplazamiento de la dirección del FCB
(El nombre del archivo con su extensión, el tamaño del registro y
el número de registro deben estar en el FCB)
Regresa:
AL=00
registro del archivo leido exitosamente
AL=01
fin de archivo,no hay datos en el registro
AL=02
DTA muy pequeño,fin de transferencia
AL=03
fin de archivo
El programa que presentamos a continuación lee un sólo registro de
cualquier parte de un archivo. El nombre del archivo se introduce como
parte del comando para ejecutar el programa y el número de registro
como primer dato del programa.
;—————————————————————————————————
————————————————
;LEEALEA: Lee registro en forma aleatoria de un archivo
;—————————————————————————————————
————————————————
dos
EQU
21h
;llamada al SO
abre
EQU
0Fh
;abre un archivo
leer
EQU
21h
;leer de un archivo aleatorio
muestra
EQU
2h
;despliega carácter
tecla
EQU 1h ;lee un carácter del teclado
fcb
EQU
5Ch
;área de control de archivo
eof
EQU
1Ah
;ASCII de fin de archivo
;—————————————————————————————————
—————————————————
datos
SEGMENT
10-195
Ensamblador
ORG
7Dh
;registro aleatorio
aleatorio1 DW
?
;registro parte baja
aleatorio2 DW
?
;registro parte alta
ORG
80h
dato
DB
80h DUP(?)
;área de transferencia de datos
datos
ENDS
;—————————————————————————————————
————————————————leealea
SEGMENT
prog
PROC FAR
ASSUME CS:leealea,DS:datos
inicio:
;Guarda segmentos para regresar en paz
PUSH DS
;salva DS
SUB
AX,AX
;colcoa 0 en AX
PUSH AX
;y empuja a la pila
;abre el archivo
MOV DX,fcb
;FCB en DX
MOV AH,abre
;servicio de Abre archivo
INT
dos
;de DOS
;Toma # registro del teclado y coloca en FCB
MOV ah,tecla
;función del teclado
INT
dos
SUB
AL,30h
;convierte de ASCII a binario
CBW
;convierte a palabra
MOV aleatorio1,AX
;coloca en FCB
MOV aleatorio2,0
;y 0 en parte alta
;Lee registro del archivo
MOV DX,fcb
;en DX el FCB
MOV AH,leer
;lee de forma aleatoria
INT
dos
;Muestra el registro del área de transferencia (DTA)
MOV CX,80h
;# de caracteres en CX (contador)
MOV BX,0
;Inicializa apuntador
a_pantalla:
MOV DL,[dato+BX]
;toma carácter
CMP
DL,eof
;¿Se llegó al final?
JE
salir
;si, salir
MOV AH,a_pantalla
;no, despliega en pantalla
INT
DOS
INC
BX
;ajusta el apuntador
LOOP muestra
;lazo de 80h veces
salir:
RET
;regresa a DOS
prog
ENDP
10-196
El Acceso a Disco
;—————————————————————————————————
—————————————————
leealea
ENDS
END
inicio
Para conservar el programa sencillo sólo se aceptan números del 0 al 9
que se convierten de ASCII a binario restando 30h y la región del DTA se
deja en su lugar (dirección del 7Dh al 80h) aunque existen posibles
conflictos en la localidad 7Dh que resolvemos moviendo un cero despues
de llenar la DTA y antes de leer un registro.
El escribir a un registro secuencial sigue la misma mecánica que la
lectura por lo que no nos entretendremos con ello. La única
recomendación es no tratar de leer un registro que no existe y escribir sólo
sobre registros que ya existen.
10.4 Manejadores de Archivos
A partir de la versión 2.0 de MS-DOS se prefiere que el manejo de
archivos, sus FCB y DTA sean realizados por el mismo sistema
operativo. Al usar estos servicios de las interrupciones proporcionadas
por los diseñadores del SO, nos encontramos con tres diferencias
principales:
· 1. No hay FCB. Los parámetros varios del FCB han sido
“absorbidos” por el SO y ya no son visibles al usuario, por lo que
ya no es necesario preocuparse de ellos. El nombre del archivo se
reemplaza por nombre + extensión + camino completo seguido de
un byte en cero y se le denomina ASCIIZ por el cero adicional.
· 2. No hay Registros. Al igual que el FCB, los registros
desaparecen y son sustituidos por el número de bytes que
queremos leer/escribir/accesar de un archivo.
· 3. Manejo por medio de manijas. Las manijas son números de
control de 16 bits que nos permite manejar el archivo por una
referencia simbólica en lugar de por nombre.
Otra mejora sustancial en el método de acceso a archivos es el manejo
uniforme de errores, siempre que una función genere un error, la bandera
de acarreo se fija (podemos probar la condición con un salto condicional
10-197
Ensamblador
JC) y el registro AX contiene el número de error de acuerdo a la siguiente
tabla:
Decimal Hexadecimal
Error
1
01
Número de Función Inválida
2
02
Archivo no encontrado
3
03
Camino no encontrado
4
04
Demasiados archivos abiertos
5
05
Acceso denegado (atributo o código de acceso equivocados)
6
06
Manija inválida
7
07
Bloques de control de memoria destruídos
8
08
Memoria insuficiente
9
09
Bloque de dirección de memoria inválido
10
0A
Ambiente inválido
11
0B
Formato inválido
12
0C
Código de acceso inválido
13
0D
Datos inválidos
14
0E
no se usa
15
0F
Disco inválido
16
10
Intento de remover el directorio actual
17
11
No es el mismo dispositivo
18
12
No hay más archivos
Al abrirse el archivo se le asigna un código de acceso que determina si el
archivo es de sólo lectura (código 0), de sólo escritura (código 1) o de
lectura y escritura (código 2). De la misma manera se le asigna un
atributo en un byte:
· bit 0. Sólo lectura.
· bit 1. Archivo oculto.
· bit 2. Archivo de sistema.
· bit 3. No es archivo sino etiqueta de volumen.
10-198
El Acceso a Disco
· bit 4. No es archivo sino subdirectorio.
· bit 5. Indica que es un archivo.
El atributo de un archivo se puede cambiar usando el servicio 43h de la
función 21h y se usa el registro CX para fijar el atributo.
Presentamos a continuación las distintas funciones para leer, escribir,
crear, abrir, cerrar, etc. un archivo con manijas y un programa que integra
algunas de estas funciones como ejemplo:
Función de Abrir Archivo,INT 21h servicio 3Dh
Registros:
AH=3Dh
DS=Dirección del Segmento de cadena ASCII+0
DX=Desplazamiento de la dirección de cadena ASCII+0
AL=Códig o
de
(0=lectura,1=escritura,2=lectura/escritura)
Acceso
Regresa:
Bandera de acarreo 0 si no hay error, 1 si hay error
(CF)
AX
manejador de archivo o código de error
AL=02
DTA muy pequeño,fin de transferencia
(los errores posibles son 2,,4,,5 y 12)
10-199
Ensamblador
Función Lee de Dispositivo o Archivo,INT 21h
servicio 3Fh
Registros: AH=3Fh
BX=Manejador de archivo
CX=Número de bytes a leer
DS=Dirección del Segmento de la memoria de datos
DX=Desplazamiento dentro de la memoria de datos
Regresa:
Bandera 0 si no hay error
de
acarreo
(CF)
AX
# de bytes leidos, 0 si fin de archivo o código de error
(posiblemente 5 ó 6)
Función Crear un Archivo,INT 21h servicio 3Ch
Registros: AH=3Ch
DS=Dirección del Segmento de la cadena ASCII+0
DX=Desplazamiento dentro del segmento de la cadena
ASCII+0
CX=atributo del archivo
Regresa:
Bandera 0 si no hay error
de
acarreo
(CF)
AX
manejador de archivo si no hay error o código de error
(posiblemente 3,4 ó 6)
10-200
El Acceso a Disco
Función Escribe a Dispositivo o Archivo,INT 21h
servicio 40h
Registros: AH=40h
BX=Manejador de archivo
CX=Número de bytes a escribir
DS=Dirección del Segmento de la memoria de datos
DX=Desplazamiento dentro de la memoria de datos
Regresa:
Bandera 0 si no hay error
de
acarreo
(CF)
AX
# de bytes escritos o código de error (posiblemente 5 ó 6)
Función Cierra Archivo,INT 21h servicio 3Eh
Registros: AH=3Eh
BX=Manejador de archivo
Regresa:
Bandera 0 si no hay error
de
acarreo
(CF)
AX
código de error si es que lo hay (posiblemente 6)
10-201
Ensamblador
Función Posiciona Apuntador de
Lectura/Escritura,INT 21h servicio 42h
Registros: AH=42h
AL=00 Mover desde el inicio los bytes que señale CX:DX
AL=01 Mover apuntador desde la localidad actual+bytes
señalados por CX:DX
AL=02 Mover apuntador a fin de archivo+bytes señalados
por CX:DX, si 0 regresa tamaño del archivo
BX=Manejador de archivo
CX=Número de bytes de desplazamiento dentro del
archivo (parte alta)
DX=Número de bytes de desplazamiento dentro del
archivo (parte baja)
Regresa:
Bandera 0 si no hay error
de
acarreo
(CF)
DX
apuntador a la nueva posición (parte alta)
AX
apuntador a la nueva posición (parte baja) o código de error (posiblemente 1 ó 6)
;—————————————————————————————————
————————————————
;CREAARCH: Crea un archivo con manijas y escribe en él
;—————————————————————————————————
————————————————
llama_dos EQU
21h
;servicios varios de DOS
datos
SEGMENT
;Define segmento de datos
nombre
DB
50
;nombre de archivo
DB
?
;bytes leidos
DB
50
DUP(?) ;mem. prov. del nombre
registro
DB
80
;long. del registro
DB
?
;bytes leidos
DB
80
DUP(80)
;mem. prov. del
registro
manija
DW
?
;almacenamiento de manija
;zona de mensajes del programa
mensj1
DB
0Dh,0Ah,’Nombre del archivo con todo y
camino:’,0Dh,0Ah,’$’
mensj2
DB
0Dh,0Ah,0Dh,0Ah,’Datos: (termine con
ENTER)’,0Dh,0Ah,’$’
10-202
El Acceso a Disco
mensje
DB
‘Error $’
crlf
DB
0Dh,0Ah,’$’
;nueva línea
datos
ENDS ;fin del segmento de datos
;—————————————————————————————————
————————————————
creaarch
SEGMENT
;segmento de código
;—————————————————————————————————
————————————————
principio PROC FAR
;proceso principal
ASSUME
CS:creaarch,DS:datos
inicio:
;Guarda segmentos para regresar en paz
PUSH DS
;salva DS
SUB
AX,AX
;coloca 0 en AX
PUSH AX
;y empuja a la pila
MOV AX,datos
;apunta a nuestros datos
MOV DS,AX
;y coloca en segmento
MOV DX,OFFSET mensj1 ;primer mensaje
MOV AH,09h
;función de despliegue
INT
llama_dos
MOV DX,OFFSET nombre ;dirección nombre
MOV AH,0Ah
;toma del teclado
INT
llama_dos
MOV DX,OFFSET crlf
;agraga nueva línea
MOV AH,09h
;función de despliegue
INT
llama_dos
MOV BL,nombre+1
;toma # bytes leidos
MOV BH,0
;ajusta al palabra
MOV [nombre+BX+2],0
;y agrega un cero (ASCIIZ)
;Crea el archivo con los datos proporcionados
MOV DX,OFFSET nombre+2
;dirección del nombre
MOV CX,0
;atributo de archivo normal
MOV AH,3Ch
;función crear archivo
INT
llama_dos
MOV manija,AX
;toma la manija de AX
JC
error
;si hay un error, salir
;toma el contenido del archivo del teclado
MOV DX,OFFSET mensj2 ;primero un mensaje
MOV AH,09h
;función de despliegue
INT
llama_dos
otra_linea:
MOV DX,OFFSET registro ;dirección del registro
MOV AH,0Ah
;función de lectura del teclado
INT
llama_dos
CMP
registro+1,1
;ver si hay caracteres
10-203
Ensamblador
JLE
salir
;y si no salir
;Inserta salto de línea al registro
MOV BL,registro+1
;coloca cuenta
MOV BH,0
;y ajusta BX
MOV [registro+BX+2],0Dh ;agrega salto de línea
MOV [registro+BX+3],0Ah ;y nueva línea
ADD
registro+1,2
;ajusta al nuevo final
MOV DX,OFFSET crlf
;encargarse de la pantalla
MOV AH,09h
;función de despliegue
INT
llama_dos
;Escribe al archivo el registro
MOV BX,manija
;coloca manija en BX
MOV DX,OFFSET registro+2
;dirección de mem. prov.
MOV CL,registro+1
;# de bytes a escribir
MOV CH,0
;y ajusta CX
MOV AH,40h
;función de escritura a archivo
INT
llama_dos
JC
error
;si hay un error salir
CMP
AL,registro+1
;Compara bytes escritos con los que
solicitamos escribir
JNE
error
;si no son iguales hay un error
JMP
otra_linea
;vuelve por más registros
salir:
;Cierre el archivo y termina
MOV BX,manija
;toma la manija
MOV AH,3Eh
;función de cierre de archivo
INT
llama_dos
JC
error
;si hay un error, despliega
RET
;termina normalmente
error:
;maneja los errores
MOV DX,OFFSET mensje ;el mensaje de error
MOV AH,09h
;función de despliegue
INT
llama_dos
MOV BX,manija
;toma el # de error
CALL binhex
;convierte a hexadecimal y despliega
JMP
salir
;termina cerrando
principio ENDP
;—————————————————————————————————
————————————————
;Subrutina para convertir de binario a hexadecimal y desplegar en pantalla
;# a convertir en BX
binhex
PROC NEAR
MOV CH,4
;número de dígitos
rota:
MOV CL,4
;cuenta a 4 bits
ROL
BX,CL
;desplaza dígito a la izq.
MOV AL,BL
;y pasa a reg. AL
10-204
El Acceso a Disco
AND
AL,0Fh
;quita dígitos de la izq.
ADD
AL,30h
;convierte a ASCII
CMP
AL,3Ah
;¿Es mayor que 9?
JL
despliega
;Despliega si ente 0 y 9
ADD
AL,7h
;el dígito está entre A y F
despliega:
MOV DL,AL
;Coloca ASCII en DL
MOV AH,2h
;Función de despliegue
INT
llama_dos
;Llama a DOS
DEC
CH
;¿Terminamos con los 4 dígitos?
JNZ
rota
;Si aún no se termina
RET
;Regresa
binhex
ENDP
;—————————————————————————————————
————————————————
creaarch
ENDS
;—————————————————————————————————
————————————————
END
inicio
Aunque en el manejo de archivos con manija no se requiere de registros
sino de bytes, algunas veces es conveniente dividir la longitud total de un
archivo en registros de longitud fija que nos faciliten la tarea de encontrar
información en cierto punto.
Como funciones auxiliares de la interrupción 21h no analizadas
incluimos las siguientes
· Servicio 39h. Crear un directorio
· Servicio 56h. Renombre un archivo
· Servicio 41h. Borra un archivo
· Servicio 3Ah. Remueve un directorio
· Servicio 3Bh. Cambia a otro directorio
· Servicio 17h. Renombra directorio
Todas ellas documentadas en el apéndice D del manual IBM Personal
Computer Disk Operating System
10-205
Ensamblador
10. Resumen
10. Problemas
10. Bibliografía
Revista PC Magazine EU números de 1980-93.
Barden William Jr. How to Program Microcomputers. Sams & Company 1987.
MASM 86 Columbia Data Products, EU 1986.
IBM Personal Computer Technical Reference. Apéndice D
Morgan L. Christopher Bluebook of Assembly Routines for the IBM PC
The Waite Group USA 1987
10-206
Capítulo 11
11.1 Interfase a otros Lenguajes
Como ya hemos visto a lo largo del libro, el ensamblador es ideal para
problemas en los que se requiere de rapidez o de el acceso a todos los
componentes del sistema sin restricciones. Pero por otro lado esa es su
debilidad pues cuesta mucho trabajo y tiempo hacer programas en
ensamblador. En el otro extremo del espectro se encuentran los lenguajes
de alto nivel como PASCAL o C donde es fácil realizar los programas
pero muchas veces no obtienen la rapidez necesaria o el acceso a los
dispositivos requerido.
La interfase se realiza con mucho mayor facilidad en los lenguajes
compilados que en los interpretes pues el ligador se encarga de reunir los
distintos archivos OBJ en un sólo ejecutable uniendo las librerías y
demás elementos necesarios para el funcionamiento del programa.
El reunir dos objetos de distintos lenguajes tiene problemas específicos
que analizaremos por separado en las siguientes secciones.
11.1.1 La Asignación de Memoria
Usualmente cuando se utiliza un ejecutable realizado en lenguaje de
alto nivel no existe el problema de donde poner en memoria el programa.
El interprete o el compilador se encarga de asignar las localidades. Las
cosas cambian cuando se unen con uno de ensamblador pues es necesario
tomar en cuenta más módulos independientes.
En el caso de un lenguaje compilado, el ligador desentraña las
localidades requeridas y el problema se simplifica. En el caso de un
11-207
Ensamblador
interprete se requieren de ciertos trucos para encontrar como localizar el
programa en memoria sin conflictos.
11.1.2 Transfiriendo el Control a Ensamblador
Una vez localizado en memoria o ligado en nuestro programa ejecutable
surge el problema de transferir el control a nuestro programa en
ensamblador para que realice su función y regrese su resultado al
programa de lenguaje de alto nivel. El traspaso se realiza generalmente
con una llamada lejana (FAR CALL) por lo que nuestros procedimientos
en ensamblador deben de ser definidos como lejanos (PROC FAR). Otra
diferencia importante es que no se requiere de guardar el segmento de
datos en la pila pues es ahora el lenguaje de alto nivel el que se encarga de
estos menesteres.
11.1.3 Pasando Argumentos
En la gran mayoría de los casos se requieren de parámetros para que el
programa de ensamblador haga su trabajo y a la vez regrese resultados al
programa que lo llama. La información se pasa usualmente en forma de
apuntadores (direcciones a las variables) que deben de ser resueltos en el
programa de ensamblador.
Se debe estar de antemano de acuerdo en los argumentos que se pasan
entre los programas y se debe también establecer un protocolo estricto
que se debe de seguir al pie de la letra para la interfase. En los interpretes
debe de conocerse a fondo el área de la pila donde el interprete almacena
su variables de forma de poder pasar estas direcciones como argumentos.
En el caso de los compiladores el paso es sencillo pues además de colocar
las direcciones de las variables en la pila, coloca el valor de estas variables también en la pila simplificando nuestra labor en ensamblador.
En resumen si se planea un programa con muchas referencias a
ensamblador es casi indispensable usar un lenguaje compilado que nos
facilite la tarea de interfase y transmisión de argumentos entre uno y otro
lenguaje.
11.1.4 Pascal y Ensamblador
El sistema de interfase de todos los lenguajes de alto nivel compilados a
ensamblador siguen más o menos los mismos lineamientos que consisten
en los siguientes pasos:
11-208
Interfase a otros Lenguajes
11.1.4.1 Definir la función.
El ligador no puede unir módulos incompatibles y que se declaren de
forma incorrecta. Las funciones y datos en ensamblador deben de
definirse como públicas con la directiva PUBLIC. El modelo de
PASCAL debe de definirse como grande (.MODEL LARGE)
11.1.4.2 Entrar a la función.
Se debe de comenzar la función con dos instrucciones:
PUSH
MOV
BP
BP,SP
En esta secuencia se establece BP como el apuntador a la pila
semipermanente para facilitar la referencia a los argumentos. Inicializar
de esta forma el apuntador asegura que BP permanezca constante durante
el proceso y se pueda hacer referencia a cada parámetro como un
desplazamiento fijo de BP.
11.1.4.3 Apartar espacio para los datos locales
(opcional)
Las funciones en ensamblador pueden usar la misma técnica que las de
alto nivel para apartar espacio en la pila para sus datos provisionales. Al
decrementar SP en una cantidad par, aseguramos un espacio en la pila:
PUSH
MOV
SUB
.
.
.
MOV
MOV
BP
BP,SP
SP,4
;reserva 4 bytes (2 palabras) en la pila
WORD PTR [BP-2],0 ;primera palabra a 0
WORD PTR [BP-4],0 ;segunda palabra a 0
No se debe olvidar de incrementar SP antes de salir de la función para
restaurar los apuntadores.
11.1.4.4 Conservar los valores de los registros
Se deben de conservar los valores de los registros SI, DI, SS y DS así
como BP (ya guardado):
11-209
Ensamblador
PUSH
MOV
SUB
PUSH
PUSH
PUSH
PUSH
.
.
.
BP
BP,SP
SP,4
SI
DI
SS
DS
;reserva 4 bytes (2 palabras) en la pila
No se debe de olvidar de sacar los valores de la pila antes de regresar de
la función.
11.1.4.5 Acceder parámetros
Considere la pila una vez realizado los pasos anteriores (x es el
programa de alto nivel):
d
d-2
d-4
d-6
d-8
d-10
d-12
BPÄ
Parámetro
Parámetro
...
...
CS de x
IP de x
BP de x
SPÄ
espacio para datos
locales
espacio para datos
locales
SI de x
DI de x
SS de x
DS de x
d-14
d-16
d-18
d-20
d-22
d-24
Dirección alta
(salvado por y)
(reservado por y)
(reservado por y)
(salvado por y)
(salvado por y)
(salvado por y)
(salvado por y)
El número de los parámetros es predefinido y podemos asignarlo de la
siguiente manera:
para1
para2
para3
EQU
EQU
EQU
[BP+6]
[BP+8]
[BP+10]
;primer parámetro
;segundo parámetro
;tercer parámetro
11.1.4.6 Regresar valores (opcional)
El valor que se regresa se almacena en un registro y en general obedece a
la siguiente convención:
11-210
Interfase a otros Lenguajes
· Si es de 1 byte en AL
· Si es de 2 bytes en AX
· Si es de 4 bytes en DX y AX
11.1.4.7 Salir de la función
Al terminar nuestra función y regresar al programa que llama se deben
de realizar varios pasos:
· 1. Si se salvaron los registros SS, DS, SI o DI estos deben de
restaurarse de la pila en orden inverso en el que fueron guardados
· 2. Si se asigno espacio para variables locales en la pila, el
apuntador SP se debe de restaurar con la instrucción MOV SP,BP
· 3. Recuperar BP de la pila
· 4. Salir de la función con RET. Si se regresa a BASIC, FORTRAN
o PASCAL agregar n a la instrucción RET para ajustar la pila al
número de parámetros. Si se regresa a C, la función se encarga de
ajustar la pila.
Mostramos el siguiente ejemplo de PASCAL para llamar a una
subrutina en la que se calcula A x 2B
PROGNAM
prueba(entrada,salida);
FUNCTION
eleva(a,b:INTEGER):INTEGER; EXTERNAL;
BEGIN
WRITELN(‘5 x 2^5 es’,eleva(5,5));
END.
Y la rutina en ensamblador es
codigo
eleva
eleva
codigo
SEGMENT BYTE PUBLIC ;define el segmento como público
ASSUME
CS:codigo
PROC FAR
PUSH BP
MOV BP,SP
MOV AX,[BP+8]
;argumento 1 en AX
MOV CX,[BP+6]
;argumento 2 en CX
SHL
AX,CL
;AX=AX x 2CX
POP
BP
RET
4
;Ajusta la pila al salir
ENDP
ENDS
11-211
Ensamblador
END
11.1.5 C y Ensamblador
Un programa en C puede llamar a uno en ensamblador tal como si se
tratase de otro modulo o función escrita en C. Para tal propósito se den de
seguir los siguientes pasos:
· 1. Declare los procedimientos en C como lejanos (FAR)
· 2. Observe las siguientes convenciones:
– a) Regrese con un simple RET sin tratar de ajustar la pila
– b) Los parámetros se colocan en la pila en el orden inverso en el
que aparecen en la lista de la llamada. El primer parámetro se
encuentra en la parte baja de memoria.
– c) Los parámetros se pasan por valor a excepción de los arreglos
que se pasan por referencia
· Incluya un signo de subrayado en frente de cada variable que se va
a compartir con otros módulos. Recuerde que C solo reconoce los
primeros 8 caracteres y que a menos de que se especifique lo
contrario, si importan las mayúsculas y minúsculas.
EXTERN INT eleva(int,int);
MAIN()
{
PRINTF(‘5 x 2^5 es %d\n’,eleva(5,5));
}
Y la rutina en ensamblador es
codigo
_eleva
_eleva
codigo
END
SEGMENT BYTE PUBLIC ;define el segmento como público
ASSUME
CS:codigo
PROC FAR
PUSH BP
MOV BP,SP
MOV AX,[BP+6]
;argumento 1 en AX
MOV CX,[BP+8]
;argumento 2 en CX
SHL
AX,CL
;AX=AX x 2CX
POP
BP
RET
;Salir sin ajustar la pila
ENDP
ENDS
11-212
Interfase a otros Lenguajes
11.2 Programas Residentes
El uso de programas que terminan y quedan residentes en memoria (Terminate and Stay Resident, TSR) es una idea tan vieja como el del uso del
sistema operativo. El primer programa que queda residente en memoria
esperando nuestras ordenes es el interprete de comandos del mismo
sistema operativo. ¿Por qué no ir un poco más allá y extender el concepto
a cualquier programa del usuario?
La idea suena atrayente y es un método efectivo de extender las
funciones de un sistema operativo que no es multi-tareas ni re-entrante
sin necesidad de modificarlo. El concepto básico detrás de los programas
residentes es el de crear una rutina (usualmente un programa corto de
utilería), ejecutarla de forma que intercepte las rutinas de interrupción
que nos interesen y que ahora apunten a nuestro programa y de alguna
forma ‘pegarlo’ al SO para que este se entere de que ahora tiene una
dirección distinta de terminación y a partir de la cual la memoria principal queda libre para el usuario o los programas de aplicación del usuario.
El programa de esta forma residente puede entonces realizar su función
ya sea de forma automática (Interrupt Service Rutine, ISR) o a petición
nuestra (TSR).
Existen varios problemas técnicos para poder lograr que un programa
residente ejecute bajo las ordenes del SO pero todos ellos pueden ser
resueltos de una u otra forma.
Los pasos básicos de todo sistema que debe de quedar residente son:
· Conocer el tamaño del programa que debe de quedar residente.
· Interceptar el vector de servicio que atiende a una interrupción en
particular por medio de la cual nuestro programa ‘despertará’ y
realizará la función para la cual fue diseñado. Las más comunes
son las del teclado o las del reloj interno del sistema (tomadas de la
Tabla de Vectores de Interrupción).
· Al comenzar nuestro programa guardar todos los registros,
banderas y segmentos para poder recuperarlos al salir.
· Al terminar nuestro programa, recuperar de la pila los registros,
banderas y segmentos.
· Finalizar el proceso dejando el control a la rutina original de
atención a la interrupción.
11-213
Ensamblador
En base a los lineamientos anteriores analizaremos algunos casos de
programas residentes en las siguientes secciones.
Cada día es menos la necesidad de programas residentes con los nuevos
sistemas operativos que permiten las multi-tareas y que son re-entrantes
pudiendo dejar en un área de memoria el programa ejecutandose en el
trasfondo y de libre acceso en cualquier momento. Sin embargo, el
estudio de este caso es interesante y nos permite conocer un poco más del
desarrollo de los sistemas operativos.
Todo programa que queda residente, por su propia naturaleza, viola
algunas o todas las reglas de un programa que se comporta
‘adecuadamente’ ante un sistema operativo. El programa intercepta los
comandos dirigidos a otras rutinas y las modifica de acuerdo a su
propósito por lo que puede causar ciertos conflictos si no es diseñado con
cuidado. Una regla para no violar los preceptos del sistema operativo y
evitarse conflictos posteriores en el caso de cambio de versiones es no
usar rutinas de interrupciones no documentadas y siempre usar los
recursos del sistema a través de las rutinas de interrupción que el propio
sistema operativo proporciona.
11.2.1 Ensamblador Residente
El SO utiliza una serie de apuntadores, llamados bloques de
almacenamiento, que llevan la pista de la memoria que se asigna o quita
de los programas que se ejecutan. Para cada programa que se ejecuta,
estos apuntadores indican la dirección del prefijo del segmento del
programa (Program Segment Prefix o PSP) y la longitud del programa en
segmentos. De la misma forma, una bandera indica si se uso la memoria
apuntada por el segmento de almacenamiento asignado al programa.
El proceso se torna entonces sencillo, se requiere de averiguar la
longitud del programa en párrafos, guardarla en el registro DX y llamar a
la función 31h de la interrupción 21h del SO (manten proceso) o a la
interrupción 27h (termina pero queda residente).
Estas funciones se aseguran de que el programa quede residente
conservando el bloque asignado de almacenamiento, el PSP del
programa y el código del programa en memoria.
11-214
Interfase a otros Lenguajes
Presentamos los siguientes programa en ensamblador que explota estas
técnicas, es menester estudiarlo con cuidado para entender el
funcionamiento de una rutina que termina y queda residente.
; utileria para redireccionar impresora a pantalla
;***************************************************
dos_printer_io
EQU
17h
dos_video_io
EQU
10h
dos_function
EQU
21h
dos_terminate_resident
EQU 27h
get_vector EQU
35h
set_vector EQU
25h
cseg
SEGMENT
ASSUME
CS:cseg,DS:cseg
ORG
100h
start:
JMP
initialize
;inicializa codigo
video_io DD
printer_interceptor PROC FAR
ASSUME
STI
OR
AH,AH
JNZ
pi2
MOV AH,14h
PUSHF
ASSUME
interrupcion a pantalla
CALL video_io
pi2:
MOV AH,128
desocupada
IRET
printer_interceptor ENDP
; guarda vector de video
CS:cseg, DS:cseg
; pon interrupciones
; es una llamada a impresora?
;no, regresa
; atiENDe a pantalla
;empuja banderas para
DS:NOTHING ;simular
;poner status de imp. en
;salta servicio a impresora
initialize:
MOV
MOV
MOV
MOV
INT
MOV
MOV
BX,CS
DS,BX
AL,dos_video_io ;toma interrup de video
AH,get_vector
dos_function
video_io,BX
; y guardalo para uso posterior
video_io[2],ES
11-215
Ensamblador
MOV BX,CS
;reemplaza vector de
impresora viejo
MOV DS,BX
;con el que apunta a esta
rutina
MOV DX,OFFSET printer_interceptor
MOV AL,dos_printer_io
MOV AH,set_vector
INT
dos_function
MOV BX,CS
MOV DS,BX
MOV DX,OFFSET initialize
INT
dos_terminate_resident ;termina quedando residente
CSEG ENDS
END
START
; es necesario que este instalado el ANSI.SYS
; en CONFIG.SYS con DEVICE=ANSI.SYS
TITLE Anti CTRL-PRINTSCREEN
code_seg SEGMENT PARA PUBLIC ‘code’
ASSUME CS:code_seg, DS:code_seg, ES:code_seg,
SS:code_seg
ORG
100h
start:
JMP
screen
;limpia pantalla,
;definir prtscr como “A” y ^P como “NO”
message
screen
screen
code_seg
END
DB
27,’[2J’
DB
27,’[0;114;65p’
DB
27,’[16;66p’
DB
‘$’
PROC NEAR
MOV DX,OFFSET message
MOV AH,09h
INT 21h
INT 27h
ENDP
ENDS
start
Los programas del tipo TSR son una respuesta a la estructura de tarea
única del sistema operativo para intentar que mas de un programa
funcione a la vez.
En los programas anteriores debemos de diferenciar entre los
programas del tipo residentes (TSR) y los del tipo ISR (Interrupt Service
11-216
Interfase a otros Lenguajes
Rutine). Estos últimos no interactuan con el usuario, su labor es
silenciosa pero de todas maneras útil y muchas veces necesaria.
Dentro de la categoría de programas del tipo TSR encontramos a
calculadoras, agendas, alarmas, macro conversores, etc. En general todo
aquel programa que se active por alguna combinacion de teclas para
ejecutarse.
Dentro del ti po ISR encontramos relojes, pr ogramas de
redireccionamieto, memorias cache, colas de impresion (Spooler), virus,
etc.
Recordemos que aunque los programas TSR e ISR redireccionan las
interrupciones a sus propias rutinas, en general, no sustituyen estas
rutinas de interrupción por lo que al finalizar su tarea pasan el control a
estas y envian la información sin cambio a su sucesor.
En el caso de que la rutina si reemplace a la de interrupción es necesario
utilizar como final de la rutina la instrucción IRET en lugar de RET que
señala el fin de una rutina de interrupción.
11.3 Resumen
11.4 Problemas
11.5 Bibliografía
Revista PC Magazine EU números de 1980-93.
MASM 86 Columbia Data Products, EU 1986.
IBM Personal Computer Technical Reference. Apéndice D
Morgan L. Christopher Bluebook of Assembly Routines for the IBM PC
The Waite Group USA 1987
Tischer Turbo Pascal Internals Abacus USA 1990
11-217
Apéndice A.
A.1 Las Instrucciones del 80x86
Tabla A.1 Registros
Registros generales
AX
acumulador
BX
base
CX
cuenta
DX
datos
Grupo de apuntadores e índices
SP
apuntador del stack
BP
apuntador base
SI
apuntador fuente
DI
apuntador datos
IP
apuntador de instrucción
F
banderas del sistema
CS
segmento de código
DS
segmento datos
ES
segmento extra
SS
segmento pilas
Registros segmentos
A-219
Ensamblador
Tabla A.2 Registros y su uso
REGISTRO
OPERACION
AX
Multiplica o divide palabras
AL
Multiplica o divide bytes
AH
Multiplica o divide bytes
BX
Traduce
CX
Operaciones con cadenas o contador en
lazos
CL
Rota o corre variable
DX
Multiplica o divide palabras
SP
Operaciones con la pila
SI
Operaciones con cadenas
DI
Operaciones con cadenas
IP
No es accesible más que con saltos
A-220
Apéndice A. Las Instrucciones
Tabla A.3 Banderas Principales
Bandera
Función
OF
saturación
DF
dirección (operación con cadenas)
IF
habilita interrupciones
TF
trampa (ejecución paso a paso)
SF
signo (resultado negativo)
ZF
cero
AF
acarreo auxiliar
PF
pariedad (par)
CF
acarreo
IOPL
nivel de privilegio para E/S (2 bits)
NT
tarea anidada
RF
continua operación
VM
modo virtual
A-221
Ensamblador
Figura A.1 Segmentos y su uso.
A-222
Apéndice A. Las Instrucciones
Tabla A.4 Uso del Desplazamiento
Tipo de referencia
Segmento base por omisión
Segmento base alterno
Desplazamiento
Traer instrucción
CS
Ninguno
IP
Operación con pila
SS
Ninguno
SP
Vari ables (excepto los que DS
siguen)
CS ES SS
dirección efectiva
Fuente cadena
DS
CS ES SS
SI
Destino cadena
ES
Ninguno
DI
BP usado como registro base
SS
CS ES SS
dirección efectiva
Tabla A.5 Formas de Direccionamiento
Forma
Localización de los datos
Inmediato
En la misma instrucción
Registro
En el registro
Directo
En la localidad de memoria apuntada por el desplazamiento
contenido en la instrucción
Indirecto de Registro En la localidad de memoria apuntada por el desplazamiento
contenido en el registro
Indexado o base
En la localidad de memoria apuntada por la suma del índice o
registro base y el desplazamiento contenido en la instrucción
De base e índice con La dirección de memoria se forma por la suma del contenido del
registro base el contenido del registro índice y un
desplazamiento
desplazamiento
A-223
Ensamblador
Figura A.2 Formas de direccionamiento.
Tabla A.6 Ventajas de la Memoria Segmentada
Lineal
Segmentada
Se debe proporcionar toda la dirección
Se proporcionan las direcciones en dos o más partes
Se limita el desarrollo futuro de memorias más amplias
No hay relocalización dinámica de programas
No hay programación modular
No hay manejo de memoria por lo que la protección de ésta no es
fácil
A-224
Sí hay relocalización dinámica de programas lo que permite
multitareas
Apéndice A. Las Instrucciones
Tabla A.7 Formas de Direccionamiento y Localización de los Datos
MODO
LOCALIZACION
EJEMPLO
Inmediato
En la instrucción
ADD CH,5F
Registro
En el registro
ADD BX,DX
Directo
En la localidad de memoria apuntada por el ADD variable,BX
desplazamiento contenido en la instrucción
Registro
En la localidad de memoria apuntada por el ADD CX,[BX]
desplazamiento contenido en el registro
Indice o base
En la localidad de memoria apuntada por la ADD [SI+6],AL
suma del contenido del registro índice o base
y el desplazamiento contenido en la
instrucción
Base e índice con desplazamiento La dirección de memoria es la suma del ADD [BX+DI+5],DX
contenido del registro base, más el contenido
del registro índice y un desplazamiento
Dividimos las instrucciones en los siguientes grupos genéricos:
1.- Movimientos de registros y datos
2.- Multiplicación y división entre números decimales
3.- Suma, resta
4.- Corrimientos
5.- Operaciones con cadenas
6.- Traducción de bytes por tablas
7.- Interrupciones programables
8.- Lazos de programas
9.- Coordinar multiproceso y coproceso
10.- Prueba no destructiva de bits
A-225
Ensamblador
Tabla A.8 Instrucciones de Transferencia
Mnemónico
Descripción de su operación
MOV destino,fuente
Mueve byte o palabra
PUSH fuente
Empuja palabra a la pila
POP destino
Saca palabra de la pila
PUSHF
empuja banderas a pila
POPF
saca banderas de la pila
XCHG op1,op2
intercambia el operador 1 con el operador 2
LAHF
carga registro AH con las banderas
SAHF
guarda en las banderas lo que hay en el registro AH
IN acc,puerto
Carga en el acumulador con un byte o palabra del puerto
OUT puerto,acc
Traspasa del contenido del acumulador hacia el puerto
LEA destino,fuente
Carga dirección efectiva
LDS destino,fuente
Carga apuntador en destino y registro DS
LES destino,fuente
Carga apuntador en destino y registro ES
XLAT
traduce byte
A-226
Apéndice A. Las Instrucciones
Figura A.3 Multiplicación y división.
A-227
Ensamblador
Figura A.4 Corrimientos y rotaciones.
A-228
Apéndice A. Las Instrucciones
Figura A.5 Diagrama interno de una 80486.
A-229
Ensamblador
Tabla A.9 Instrucciones Aritméticas
Mnemónico
Descripción de la Operación
ADD destino,fuente
Suma byte o palabra
ADC destino,fuente
Suma byte o palabra con acarreo
SUB destino,fuente
Resta byte o palabra
SBB destino,fuente
Resta byte o palabra con préstamo
INC destino
Incrementa byte o palabra en 1
DEC destino
Decrementa byte o palabra en 1
NEG destino
Niega byte o palabra (complemento a 2)
CMP destino,fuente
Compara byte o palabra
MUL fuente
Multiplica byte o palabra sin signo
IMUL fuente
Multiplica enteros (byte o palabra)
DIV fuente
Divide byte o palabra sin signo
IDIV fuente
Divide enteros (byte o palabra)
CBW
cambia byte a palabra
CWD
Cambia palabra a doble palabra
DAA
Ajuste decimal para la suma
DAS
Ajuste decimal para la resta
AAA
Ajuste de ASCII para la suma
AAS
Ajuste de ASCII para la resta
AAM
Ajuste de ASCII para la multiplicación
AAD
Ajuste de ASCII para la división
A-230
Apéndice A. Las Instrucciones
Tabla A.10 Operaciones Lógicas
Mnemónico
Descripción de la Operación
NOT destino
Niega byte o palabra (complemento a 1)
AND destino,fuente
Operación Y con byte o palabra
OR destino,fuente
Operación O con byte o palabra
XOR destino,fuente
Operación O EXCLUSIVA con byte o palabra
TEST destino,fuente
Prueba byte o palabra
SHR destino,cuenta
Corrimiento lógico a la derecha de byte o palabra
SAR destino,cuenta
Corrimiento aritmético a la derecha de byte o palabra
SHL/SAL destino,cuenta
Corrimiento a la izquierda de byte o palabra
ROR destino,cuenta
Rota a la derecha byte o palabra
RCR destino,cuenta
Rota a través del acarreo byte o palabra
ROL destino,cuenta
Rota a la izquierda byte o palabra
RCL destino,cuenta
Rota a la izquierda a través del acarreo byte o palabra
Tabla A.11 Instrucciones de Transferencia
Mnemónico
Descripción de la Operación
JMP destino
Salta a una localidad de memoria indicada en destino
Jcond destino
Vea tabla A.12
LOOP destino
Lazo
LOOPE/LOOPZ destino
Lazo mientras Sea igual/Sea cero
LOOPNE/LOOPNZ destino
Lazo mientras No sea igual/No sea cero
JCXZ destino
Salta si CX=0
CALL destino
Llama a una subrutina
RET (opciónalmente un valor)
Regresa de una subrutina
INT tipo
Interrupción
INTO
Interrupción en caso de saturación
IRET
Regresa de una interrupción
Tabla A.12 Saltos Condicionales
A-231
Ensamblador
Con signo
JG/JNLE destino
Mayor/no menor no igual
JGE/JNL destino
Mayor o igual/No menor
JL/JNGE destino
Menor/No mayor no igual
JLE/JNG destino
Menor o igual/No mayor
JO destino
Saturación
JS destino
Signo
JNO destino
No saturación
JNS destino
No signo
Sin signo
JA/JNBE destino
Arriba/No abajo o igual
JAE/JNB destino
Arriba o igual/No abajo
JB/JNAE destino
Abajo/No arriba o igual
JBE/JNA destino
Abajo o igual/No arriba
No importa el signo
JC destino
Acarreo
JE/JZ destino
Igual/Cero
JP/JPE destino
Pariedad/Pariedad par
JNC destino
No acarreo
JNE/JNZ destino
No igual/No cero
JNP/JPO destino
No pariedad/Pariedad non
Tabla A.13 Instrucciones para Manipular Cadenas
Mnemónico
Descripción de la Operación
MOVSB/MOVSW
Mueve cadena de byte o palabra
CMPSB/CMPSW
Compara cadena de byte o palabra
SCASB/SCASW
Busca cadena de byte o palabra
LODSB/LODSW
Carga cadena de byte o palabra
STOSB/STOSW
Guarda una cadena de byte o palabra
Las instrucciones anteriores se utilizan con los siguientes prefijos:
REP
Repite
REPE/REPZ
Repite mientras sea Igual/Cero
REPNE/REPNZ
Repite mientras no sea Igual/Cero
A-232
Apéndice A. Las Instrucciones
Tabla A.14 Registros Dedicados para Operaciones con Cadenas (Tabla
A.13)
Registro
Operación
SI
Indice (desplazamiento) de cadena fuente
DI
Indice (desplazamiento) de cadena destino
ES
segmento de la cadena destino
CX
contador de repetición
AL/AX
valor de búsqueda, destino para LODS, fuente para STOS
DF
0=autoincrementa DI y SI, 1=autodecrementa DI y SI
Indica fin de búsqueda/comparación
ZF
Tabla A.15 Instrucciones de Control de Proceso
Instrucción
Acción
STC
Fija bandera de acarreo
CLC
Limpia bandera de acarreo
CMC
Limpia bandera de complemento
STD
Fija bandera de dirección
CLD
Limpia bandera de dirección
STI
Habilita interrupciones
CLI
Deshabilita interrupciones
HLT
Detener procesamiento hasta que se inicialice el circuito o
haya una interrupción externa
WAIT
Espera a que test (pata externa física del circuito) esté en 1
ESC
Escapa a procesador externo (coproceso)
LOCK
Bloquea el bus durante la siguiente instrucción
NOP
No hacer nada (para rellenar tiempos)
A-233
Ensamblador
A.2 Errores Comunes al Ensamblar un Programa
1.- Lógica invertida Ej. JC (Salta si está el acarreo) cuando se desea
saltar cuando acarreo=0.
2.- Uso equivocado de direccionamiento Ej. MOV AX,var cuando
queremos MOV AX,offset var.
3.- Empujar y no sacar de la pila.
4.- Usar un registro para valor temporal y llamar una subrutina que lo
usa.
5.- Olvidar poner en CX el número de repeticiones cuando se entra a un
lazo.
6.- Creer que una instrucción afecta a una bandera.
7.- Olvidar inicializar un registro.
8.- Olvidar el orden destino fuente.
9.- Contar mal los ciclos Ej. del area 100 a 103 debe haber 4 ciclos, no 3.
10.- Incrementar contadores de lazos en lugares equivocados, o no
incrementarlos.
11.- Olvidar salvar registros y banderas en programas de manejo de
interrupciones, lo que da por resultado bloqueos aleatorios.
12.- No poner espacio de pila suficiente en un programa, pues en el
curso de este pueden ocurrir interrupciones y saturar la pila.
A-234
Apendice B
Interrupciones
S e r v i c i o Interrupción Descripción
(AH=)
Parámetros
Regresa
02h
21h
Despliega carácter en pantalla
DL=carácter (ASCII)
-
-
20h
Regresa a programa supervisor
-
-
01h
21h
Lee un carácter del teclado
-
AL=carácter leido del teclado
09h
21h
Muestra una cadena en pantalla
DS:DX=dirección de la cadena terminada con $
0Ah
21h
Lee cadena del teclado
DS:DX=dirección de memoria 1er
byte
memoria
pro vi sional para contener la prov.=máximo # de carácters a
cadena
ser leidos
2do byte memoria prov.=#
carácteres leídos
05h
21h
Imprime carácter
DL=carácter a imprimir
-
0Bh
21h
Verifica entrada estandard
-
AL=FF hay un carácter
esperando
AL=00 no hay carácter
esperando
00h
10h
Tipo
de
(color,mono,etc.)
d e s p l ie g u e
01h
10h
Tipo de cursor
02h
10h
Posición del cursor
03h
10h
Lee posición del cursor
04h
10h
Lee posición de la pluma de luz
05h
10h
Selecciona página de video activa
B-235
Ensamblador
06h
10h
Desplaza pantalla hacia arriba
07h
10h
Desplaza pantalla hacia abajo
08h
10h
Lee atributo/carácter de pantalla
09h
10h
Escribe atributo /carácter a
pantalla
0Ah
10h
Escribe carácter a pantalla
0Bh
10h
Escoge paleta de color
0Ch
10h
Dibuja un punto a pantalla
0Dh
10h
Lee punto de pantalla
0Fh
10h
Regresa estado de video
10h
10h
Generador de carácteres
-
11h
Información del equipo
-
bit 0 coprocesador
bit 4,5 tipo de video
bit 7,6 # lectoras de disco
bit 9,10,11 #puertos RS-232
bit 13 joystick
bit 14,15 #puertos paralelos
-
12h
Tamaño de la memoria
-
AX=# bytes
2Ah
21h
Pide fecha del sistema
-
CX=año (1980 a 2099)
DH=mes (1 al 12)
DL=día (1 al 31)
2Bh
21h
Fija fecha del sistema
mismos que para la 2Ah
-
2Ch
21h
Pide hora del sistema
-
CH=hora, CL=minuto
D H = se g u n d o s , DL = 1 /1 0 0
segundos
2Dh
21h
Fija hora del sistema
mismos que para la 2Ch
-
30h
21h
Pide versión de DOS
-
AX=versión
38h
21h
Información del país
DX=Dirección de buffer de 34 País en buffer
bytes
0Fh
21h
Abrir archivo con manija
DS=Dirección del Segmento del AL=00, el archivo se encontró y
FCB
abrió
DX=Desplazamiento de la AL=FF, El archivo no existe
dirección del FCB
B-236
Apéndice B. Las Interrupciones
FCB, # de disco,tamaño de
registro,fecha,tamaño archivo,#
bolque,etc.
(El nombre del archivo con su extensión deben de estar ya en el FCB)
4Eh
21h
Busca primer archivo
CS:DI=archivo a buscar
DTA 0B0h
4Fh
21h
Busca siguiente archivo
CS:DI=archivo a buscar
DTA 0B0h si AL=18 no se
encontro
57h
21h
Fija/toma datos de fecha y hora de
archivo
B-237
Introducción
Índice
!
804861-27
A
acceso aleatorio1-23, 10-194
acceso directo a memoria5-88
acceso secuencial1-23
acceso secuncial10-183
acumulador1-9
ADC7-132
ADD7-131
AND6-114
ASCII3-67
ASCIIZ10-197
asignación de memoria11-207
ASSUME4-82
B
BCD3-63
BIOS2-34
bit1-2, 3-64
Bresenham9-177
buffer5-88
bus1-4
239
Ensamblador
byte1-3, 3-65
BYTE PTR5-102
C
C y ensamblador11-212
CALL7-136
carácteres de control3-68
CLD8-152
CMP6-110
CMPS8-152
código fuente2-35
código Grey3-67
código objeto2-35
códigos3-67
COM4-83
compilador2-45
complemento3-64
complemento a 13-66
complemento a 23-66
complemento a 93-64
conjunto de instrucciones2-38
contador de programa1-10
conversión binario-decimal3-58
conversión decimal-binario3-59
240
Introducción
corrimientos7-128
CPU1-2 - 1-3, 1-19
D
DB4-83
DD4-83
debug4-78
DEC7-132
desbordamiento3-65
dirección1-3, 1-25
dirección efectiva2-42
direccionamiento
directo a memoria2-42
Implícito2-41
indirecto2-42
memoria2-41
relativo2-42
directivas de ensamblador2-39
DIV7-133
división en otros sistemas3-60
DMA5-88
DOS7-121
DTA10-183
DUP4-83
241
Ensamblador
DW4-83
E
E/S1-2, 5-87
mapa de memoria5-88
E/S por interrupción5-88
E/S programada5-87
END4-82
ENDP7-135
ENDS4-82
ensamblador2-36
entrada/salida5-87
EPROM1-27
EQU4-82
exceso 33-64
EXE4-83
F
FCB10-183
H
hardware2-33
hexadecimal3-61
I
I/O1-2
IBM1-27
242
Introducción
IDIV7-133
IMUL7-133
IN6-117
INC6-108
input/output5-87
instrucción1-8
INT5-94, 6-105
interfase a otros lenguajes11-207
intérprete2-45
interrupción
impresora7-125
no mascarillable5-93
pantalla7-123
teclado7-122
ISR11-213, 11-216
J
JMP6-106
juego de instrucciones1-3
L
LABEL BYTE9-177
LABEL DWORD9-177
LABEL WORD9-177
Leibnitz3-56
243
Ensamblador
lenguaje de máquina2-36
lenguajes de alto nivel2-44
LOOP6-109
LOOPE6-110
LOOPNE6-110
M
macroinstrucción1-18
macroprograma1-19
manijas10-197
masm4-81
memoria1-2
memoria auxiliar1-22
memoria de alta velocidad1-21
memoria de lectura escritura1-25
memoria de respaldo1-22
memoria de sólo lectura1-25 - 1-26
memoria de sólo lectura programable y borrable1-27
memoria interna1-21
memoria no volátil1-27
memoria principal1-21
memoria secundaria1-22
memoria volátil1-27
microinstrucción1-18
244
Introducción
microprograma1-18
microprogramación1-18
mnemónico1-12
MOV5-95
MOVS8-152
MOVSB8-152
MOVSW8-152
MUL7-133
multiplexión1-17
multiplicación en otros sistemas3-60
N
NEG6-114
NOP7-135
NOT6-114
O
OR6-114
OUT6-117
P
PAGE7-138
paginación2-42
palabra3-65
palabra de dirección1-25
paridad1-7
245
Ensamblador
paridad impar1-7
paridad par1-7
párrafo1-31
Pascal y ensamblador11-208
pila5-91, 8-141
PIXEL9-163
POP8-142
POPF8-150
PROC7-135
procesador de comandos2-34
programas2-33
programas residentes11-213
PROM1-27
PSP11-214
PUSH8-142
PUSHF8-150
R
RAM1-25
RCL7-130
RCR7-130
registro1-9
registro contador de datos1-9
registro de estado1-15
246
Introducción
registro de instrucción1-10
REP8-152
REPE8-154
REPNE8-154
REPNZ8-154
representación con signo3-65
REPZ8-154
resta en otros sistemas3-60
RET7-136
ROL7-130
ROM1-25 - 1-26
ROR7-129
rotaciones7-128
rutina de servicio de interrupción5-91
S
SAL7-130
saltos cercanos6-107
saltos condicionales6-110
saltos cortos6-107
saltos de índice6-107
saltos lejanos6-107
SAR7-130
saturación3-65
247
Ensamblador
SBB7-132
SCAS8-152
SEGMENT4-82
SEGMENT AT9-171
SHL7-130
SHR7-130
sistema base dos3-56
sistema binario3-56
sistema decimal3-55
sistema notacional3-56
sistema operativo2-33
sistemas numéricos3-53
software2-33
STD8-152
SUB7-131
suma en otros sistemas3-60
T
TEST6-114
tiempo de acceso1-23
TSR11-213
U
UAL1-19
unidad aritmética y lógica1-19
248
Introducción
unidad de control1-17, 1-20
unidad de memoria1-20
unidad de procesamiento central1-3, 1-19
UPC1-2 - 1-3, 1-19
V
vector de interrupciones5-92
video9-163
W
WORD PTR5-102
X
XOR6-114
249
Descargar