Download Apuntes prog. sist. empotrados. - ¡Bienvenido a paloalto.unileon.es!

Document related concepts
no text concepts found
Transcript
Usad esta numeración:
1 de 357
Apuntes de
Máquinas
Electrónicas
(2º parcial)
© 1994-2005, José María Foces Morán.
ƒ Arquitectura de Intel
ƒ Programación estructurada
ƒ El bus del microprocesador empotrado
i386EX
ƒ Tecnologías de memorias y dispositivos
ƒ Diseño de sistemas empotrados
ƒ Prácticas 10-15.
Usad esta numeración:
2 de 357
Apuntes de Máquinas
Electrónicas
© 1994, 2004 José María Foces Morán.
Capítulo 1
Registros y Modos de
direccionamiento del procesador
i386ex.
Usad esta numeración:
3 de 357
OBJETI VOS DEL TEMA.
En este tema desarrollaremos una descripción general de la arquitectura de los
microprocesadores x86 y más en concreto el conjunto de registros de propósito
general y los modos de direccionamiento de memoria. Esta arquitectura,
concebida y desarrollada por ingenieros de la compañía Intel, recibe el nombre
“Intel Architecture”, abreviadamente IA.
El estudio del microprocesador MIPS llevado a cabo en la parte DOC de esta
asignatura nos permitió estudiara un gran número de conceptos sobre
organización y diseño de computadores. La arquitectura IA es notablemente
diferente de la arquitectura del microprocesador MIPS, sin embargo, los
conceptos estudiados en DOC ofrecen un alto grado de generalidad que nos va
a servir para afrontar esta búsqueda de una forma confiadamente progresiva y
directa. Del mismo modo que en DOC, a lo largo de capítulos siguientes e este
estudiaremos las arquitecturas correspondientes a las operaciones con enteros y
con reales.
INTRODUCCI ÓN.
IA es una arquitectura muy extendida, usada por un amplísimo abanico de
computadores y un número infinitamente mayor de aplicaciones. IA es la
arquitectura de los microprocesadores que se usan en los ordenadores
personales así que el número de microprocesadores con arquitectura IA se
cuenta por cientos de millones. Estos microprocesadores se incorporan en
ordenadores de sobremesa, ordenadores portátiles y servidores de red y
además, existe un gran número de sistemas empotrados basados en
microprocesadores con IA.
El microprocesador 8086 fue anunciado por Intel en 1978 como extensión
compatible del entonces famoso microprocesador 8080. Mientras que el 8080 era
una máquina del tipo acumulador, en el 8086 se extendió la arquitectura con
otros registros adicionales, siendo esta arquitectura el núcleo básico de la IA.
Después del 8086 vinieron el 80286 y más tarde, hacia 1985, Intel presentó el
80386. Es este último el microprocesador que constituye la primera realización
de la IA de 32 bits– el énfasis principal de SBM va a proyectarse sobre la versión
llamada i386SX.
IA contiene dos sub-arquitecturas: Una arquitectura básica y una arquitectura
que podríamos llamar extendida. La arquitectura “básica” se la conoce como
arquitectura del modo real. A la arquitectura completa o extendida se la conoce
como arquitectura del modo protegido. En este capítulo, vamos a centrarnos en la
arquitectura del modo real ya que esta arquitectura del modo real define los
elementos computacionales básicos necesarios para el diseño de aplicaciones. El
modo protegido está pensado para el diseño de sistemas operativos que
incorporan gestión de memoria segmentada y paginada, multitarea, protección
entre tareas y otras que, de algún modo, están fuera del temario cubierto por
esta asignatura. En un tema posterior realizaremos una aproximación
Usad esta numeración:
4 de 357
descriptiva del modo protegido que nos provea de una visión completa de la
IA.
Por defecto, el ancho de palabra de la arquitectura IA del modo real es de 16 bits,
todos los registros visibles por el programador son de un ancho de 16 bits. Tal
como estudiamos en DOC, es muy frecuente el uso de registros para albergar
direcciones de memoria de datos -y de instrucciones. Puesto que los registros
arquitecturados son de ancho de 16 bits ¿Las direcciones de memoria son
también de 16 bits? Si los registros son de ancho 16 bits, eso significa que el
procesador sólo podrá acceder a 216 direcciones de memoria, pero, esta
capacidad de direccionamiento de memoria es notoriamente escasa. Los
diseñadores del 8086 idearon un método para aumentar la capacidad de
direccionamiento de la IA por encima de los 64Kb, la técnica se llamó
segmentación.
Como veremos en breve, la segmentación es una técnica de particionamiento
del espacio completo de direccionamiento del procesador. A cada partición de
tamaño 64 Kb se le llama segmento y su dirección base se conserva en alguno de
los registros habilitados a este efecto: Los registros de segmento. El
programador maneja dos partes para representar la dirección de un dato: La
dirección base de segmento (Guardada previamente en un registro de segmento) y
un offset que representa el desplazamiento que se suma a la dirección base para
formar la dirección final. En este proceso, el registro de segmento es desplazado
4 bits a la izquierda con lo que su alcance llega a ser de 20 bits, esto es,
aproximadamente 1 Mbyte: El uso de la segmentación y su conjunto especial de
registros llamados registros de segmento, permitió lograr un espacio de
direcciones superior a 220 bits dividido en fragmentos de 64Kb llamados
segmentos.
Por último, el término segmentación, se emplea para diferentes usos y no
debemos confundir tales usos. Es recomendable consultar la versión traducida
del libro Organización y Diseño de Computadores: La interfaz
hardware/software, capítulo 6, allí tendremos ocasión de observar otro uso del
término segmentación distinto del uso empleado en IA.
Cuando el microprocesador i386SX funciona en modo real, hemos mencionado
que constituye una arquitectura de 16 bits, por defecto. Cuando funciona en
modo protegido, su arquitectura es de 32 bits, por defecto. En modo protegido,
los ocho registros de propósito general pueden ser considerados como
verdaderos registros de propósito general, muy similares a los del
microprocesador MIPS -si no fuese por su número tan reducido, 8 vs. 32. Sin
embargo, en modo real los ocho registros son de 16 bits de ancho y su carácter
es notablemente menos general: Existen dependiencias entre el uso de un
determinado registro y las instrucciones que pueden operar sobre él, o los
modos de direccionamiento que pueden formarse usando el registro elegido.
Empleando la terminología de los profesores Hennessy y Patterson, el conjunto
de registros y los modos de direccionamiento presentan una baja ortogonalidad.
Usad esta numeración:
5 de 357
(UN M ODELO ESTRUCTURAL SIM PLE DEL I386SX)
Esta sección aparecerá en el Web site de MEL.
v RESUMEN DE LOS MODOS REAL Y PROTEGIDO.
El µP i386SX arranca en modo real. Cuando el sistema en el que está incluido el
µP recibe potencia eléctrica, el µP lee la primera instrucción del programa de
arranque desde una posición de memoria prefijada y ejecuta esa primera
instrucción –en modo real. Esta instrucción suele ser un salto largo (jmp far) y
debe estar ubicada 8 bytes antes del final del espacio de direccionamiento
obtenible con el microprocesador. El programa que se ejecuta en tiempo de
arranque, se ejecuta pues en modo real. En este modo, el µP presenta un modelo
de programación muy parecido al del µP 8086, sin embargo, los programas se
ejecutan con un rendimiento muy superior. Esto es debido a que la
organización interna del µP i386SX incorpora mejoras que elevan el
rendimiento notablemente y además, las frecuencias de reloj aceptadas por este
µP son hasta 10 veces superiores a las frecuencias disponibles con el µP 8086.
El modo protegido es el modo nativo del i386SX en el que la arquitectura nos
ofrece todas sus capacidades. Estas capacidades son requeridas para el diseño
de sistemas operativos modernos, sistemas de tiempo real, etc. El espacio físico
de direcciones es mucho más grande que en el modo real, los registros de
propósito general presentan ya su ancho de 32 bits completo, los registros de
segmento apuntan ahora a unas estructuras en memoria llamada selectores de
segmento, estos selectores de segmento apuntan a segmentos físicos de memoria
cuyo tamaño es variable y poseen otros atributos relacionados con el esquema
de protección entre tareas implementado por este µP, los offsets se extienden
hasta 32 bits (Los offsets disponibles en modo real son de 16 bits). En modo
protegido el micro nos ofrece todas las posibilidades de funcionamiento en
multitarea con protección, memoria virtual segmentada y paginada, y otras.
El cambio de modo real a modo protegido se produce por programa al escribir un
1 en el bit PE del registro de CR0. El registro CR0 forma parte de un conjunto de
registros del µP llamados control registers. En la figura 3 está ilustrado cómo se
usa el registro CR0 para conmutar a modo protegido.
El sistema operativo MS-DOS se ejecuta en modo real. Por tanto este sistema
operativo no tiene disponibles todos los recursos de la IA, es una situación muy
frecuente que bajo MS-DOS, ciertos programas necesiten algunos de los
recursos que sólo están disponibles en modo protegido. En estas situaciones se
instalan unos programas llamados extensores cuya tarea consiste en conmutar a
modo protegido, realizar ciertas operaciones a petición del programa de
aplicación usando la arquitectura del modo protegido y al terminar esas
Usad esta numeración:
6 de 357
operaciones, regresar a modo real, donde vuelve otra vez a reactivarse el
programa de aplicación que solicitó los servicios. Estos programas llamados
extensores, solían usarse para dar la impresión a un cierto programa de
aplicación, de que la memoria del computador es más extensa que el Mb típico
al que el µP está limitado en modo real.
Por supuesto, los sistemas operativos multitarea que empleamos en nuestros
ordenadores de sobremesa, se ejecutan en modo protegido. Windows NT,
Linux, Solaris para Intel, iRMX, Windows 98 y otros sistemas, se ejecutan en
modo protegido, sólo funcionan en modo real durante un corto periodo de
tiempo, en el inicio del proceso de arranque del sistema.
v LOS REGISTROS DEL I386SX VISIBLES AL PROGRAMADOR.
Cuando estudiamos los fundamentos de arquitectura de computadores
establecimos que los registros visibles al programador presentes en el
microprocesador MIPS, se empleaban para albergar variables y constantes
usadas frecuentemente por parte de los programas que se estuvieran
ejecutando. En la programación del µP i386SX ocurre lo mismo, los registros de
propósito general del procesador se usan para efectuar operaciones sobre datos
procedentes de la memoria, almacenar datos que se van a usar con frecuencia,
efectuar el paso de parámetros a subrutinas, y otras actividades que iremos
descubriendo en un tema posterior dedicado a la programación.
La memoria es un espacio de almacenamiento de datos y programas muy
amplio donde el procesador almacena grandes cantidades de información. Los
registros de propósito general actúan como elementos de almacenamiento
transitorio o temporal de información, cuyas velocidades de acceso son muy
rápidas. El número de registros de propósito general incluidos en el µP i386SX
es pequeño, ocho tan sólo, un número muy inferior a la capacidad de la
memoria. Son estos ocho registros los que se emplean para realizar cálculos con
variables y constantes, con datos y con direcciones. El µP contiene otros
registros que se emplean para otros usos distintos de los mencionados como
puede ser la generación del flujo secuencial de ejecución, obtención del status
de las operaciones aritméticas, gestión de colas de procesos, gestión de memoria
virtual, control de la memoria cache, verificación de programas, autoexamen
del procesador en el arranque, análisis de trazas de rendimiento de programas
y otros usos. Veamos la clasificación completa de los registros ahora.
El conjunto completo de registros del i386SX se puede dividir en las categorías
que siguen:
1. Registros de propósito general -GPRs.
2. Registros de segmento.
3. Puntero de instrucción -'Instruction Pointer'- y registro de FLAGS.
4. Registros de control.
5. Registros de direcciones del sistema.
Usad esta numeración:
7 de 357
6. Registros de depuración.
7. Registros de test.
Las categorías 1 a 3, contienen los 16 registros con más interés para nuestro
estudio del modo real de ejecución. Comencemos pues describiendo los GPRs.
v LOS REGISTROS DE PROPÓSITO GENERAL.
Los nombres de estos registros de 32 bits son EAX, EBX, ECX, EDX, ESI, EDI,
EBP y por último ESP. Los ocho registros de propósito general del i386SX
pueden almacenar tanto datos como direcciones. Los operandos - datos o
direcciones- pueden ser de los siguientes tamaños:
Datos: Operandos de 1, 8, 16, 32 y 64 bits.
Direcciones de memoria: Operandos de 16 o 32 bits.
Los 16 bits de menos peso de cada uno de los registros pueden ser accedidos
separadamente del resto del registro. Para lograr acceder a estos 16 bits de
menos peso, se suprime la E que es el prefijo de cada nombre de registro: AX,
BX, CX, DX, SI, DI, BP y SP. Cuando se usan estos nombres de registro, la parte
superior del registro - los 16 bits de mayor peso- ni se usan ni se alteran.
Los registros AX, BX, CX y DX permiten el acceso a los bytes bajo y alto de
forma independiente del resto del registro. Los nombres de estos bytes son AL,
BL, CL y DL en caso de que el acceso sea al byte bajo. En caso de que el acceso
sea al byte alto, los nombres de los bytes son AH, BH, CH y DH. En la figura 4
se ilustra la estructura de los GPRs: sus nombres, los anchos de los registros
expresados en bits en bits y la accesibilidad de las sub-words y sub-bytes.
v EL REGISTRO PUNTERO DE INSTRUCCIÓN: IP.
En la IA, el equivalente del registro PC del µP MIPS es el registro Puntero de
Instrucción, un registro de 32 bits llamado EIP -Extended Instruction Pointer. EIP
mantiene en todo momento el offset de la siguiente instrucción a ejecutar.
Sumando este offset a la dirección base del segmento de código (Code Segment,
CS), se obtiene la dirección física de memoria donde se encuentra la instrucción
que se va a ejecutar en el siguiente ciclo de instrucción.
Las direcciones de memoria física, direcciones de memoria completas, capaces
de referenciar algún objeto en memoria cuando son decodificadas, se expresan
con dos componentes en el “interior” del chip: segmento y offset. La notación
empleada para indicar una dirección de memoria es segmento:offset. Una
determinada dirección de memoria física se compone de una dirección base de
segmento y un offset que se añade a esa dirección base de segmento. En modo
real los segmentos tienen un tamaño de 64Kb.
Cuando el procesador efectúa un ciclo de bus de búsqueda instrucción, la
dirección de memoria de la instrucción aparece en el bus de direcciones, pero,
internamente el procesador representa esa dirección física mediante un
Usad esta numeración:
8 de 357
segmento y un offset, tal como hemos explicado en el párrafo anterior: La
dirección de memoria de la instrucción se forma con el registro de segmento CS
y el offset se toma del registro EIP, por lo que la expresión que representa la
dirección de memoria de una instrucción que va a ser recogida por el µP, es
siempre:
CS:EIP = CS x 10H + EIP = Dirección física de la instrucción
Como hemos dicho anteriormente, EIP es un registro de 32 bits, por tanto el
offset de la anterior fórmula es de 32 bits. Sin embargo, en modo real, el
procesador sólo usa la parte baja del registro de EIP. En la primera fase de
ejecución de una instrucción, el procesador accede a la memoria y la dirección
de la instrucción que va a ser recogida se forma según la fórmula siguiente:
CS:IP = CS x 10H + IP
El offset de la dirección de memoria física tiene un ancho de 16 bits, o de otro
modo, su rango de direcciones a partir de la dirección base de segmento es de
64Kb.
El registro donde se alberga la instrucción recién referenciada está constituido
por una estructura más compleja que un simple registro de instrucción.
Ciertamente, las instrucciones de la IA presentan una longitud variable, así que
no existe un registro de instrucción de 32 bits como ocurría en la
implementación simple del capítulo 5 del libro de DOC. El microprocesador
i386SX contiene una cola llamada cola de instrucciones no decodificadas. Esta cola
tiene una capacidad de 16 bytes, esta cola podría considerarse como el
equivalente del registro IR de MIPS.
La cola de instrucciones no codificadas, permite al procesador, seguir
recogiendo instrucciones desde la memoria mientras la ruta de datos prosigue
ejecutando la instrucción en curso, de este modo, solapando ciclos de bus que
recogen instrucciones y la ejecución de la instrucción actual, se gana tiempo y
por tanto se reduce el CPI. Obviamente, la complejidad de la microarquitectura
se incrementa de forma muy ostensible, pero, ese es el trabajo de los ingenieros
que diseñan procesadores cada vez más rápidos y versátiles.
Para terminar esta breve reseña de características sorprendentes del µP i386SX,
es necesario mencionar que, en realidad, el µP contiene una pipa de
instrucciones que no sólo permite que el microprocesador solape la recogida de
la instrucción siguiente con la ejecución de la presente si no que se solapan
varias instrucciones en fases sucesivas de ejecución. Esta técnica llamada
ejecución encauzada, reduce el CPI en una magnitud proporcional al número
de estadíos o fases de la pipa. Puedes ampliar tus conocimientos acerca de esta
importante técnica de paralelismo a nivel de instrucción, en el capítulo 6 de
P&H.
v EL REGISTRO DE STATUS O BANDERAS (EFLAGS).
El registro de status es un registro de 32 bits llamado EFLAGS. Este registro no
se emplea para albergar ni datos ni instrucciones. De este registro,nos interesan
Usad esta numeración:
9 de 357
ciertos bits particulares. Cuando la ALU del µP efectúa una operación
aritmética, automáticamente altera ciertos bits del registro FLAGS los cuales nos
indican si el resultado de la operación fue nulo, o fue no-nulo, si fue positivo o
negativo, si hubo desbordamiento o si no lo hubo. Cada uno de estos bits recibe
un nombre y ocupa una posición fija dentro del registro. La porción de 16 bits
mas baja de este registro se la llama FLAGS y contiene la parte más importante
de este registro cuando el micro se usa en modo real.
Veremos solamente aquellos bits de EFLAGS que nos interesan en este
momento del curso. Puedes explorar el resto en el manual Programmer’s
manual de intel. Antes de listar estos bits tan importantes mencionaremos que
el uso de estos bits, después de haber sido afectados por una operación
aritmética, suele consistir en ejecutar una instrucción de salto condicional. En
este procesador, estas instrucciones de bifurcación exploran algún bit particular
de FLAGS y dependiendo de su valor, 0 o 1, se toma el salto, o no se toma. Al
final del capítulo, realizaremos algún ejemplo, en el momento de describir los
saltos condicionales.
Los bits más importantes del registro FLAGS
OF: Overflow (Desbordamiento). Bit 11. Este bit representa el
desbordamiento en aritmética entera con signo, se activa pues, cuando el
resultado de una operación aritmética llevada a cabo con números enteros,
supera la precisión usada. Para operaciones de 8/16/32 bits, OF se pone a valor
1 en aquellos casos en donde la precisión del resultado, sin el bit de signo,
necesita mas de 7/15/31 bits.
DF: Direction (Dirección). Bit 10. DF determina sí los registros ESI y/o
EDI se postincrementan o se postdecrementan durante las instrucciones de
procesamiento de cadenas de caracteres -strings. Si DF = 0 entonces ESI y/o
EDI se postincrementan, sino se postdecrementan.
IF: INTR Enable (Habilitación de interrupciones por el pin INTR).
Bit 10. Este bit es de control, no de status, esto significa que el programador
establece un valor para este bit de acuerdo con las necesidades de su proyecto.
Este bit se emplea para indicar al procesador que acepte interrupciones del tipo
intr o que no acepte esas interrupciones. Un valor 1 indica que el procesador sí
aceptará estas interrupciones en caso de que alguna tuviese lugar, un valor 0 en
este bit indica que el procesador no aceptará estas interrupciones, esto es, en
caso de que alguna tuviese lugar, el procesador no alterará su estado actual de
ejecución de programa. Esta posibilidad de evitar que el µP atienda a una
interrupción es imprescindible para la realización de sistemas multitarea donde
en determinadas circunstancias, dos programas, o dos instancias de un mismo
Usad esta numeración:
10 de 357
programa, pueden compartir ciertas variables y por tanto el sistema debe poder
garantizar que los accesos a estas variables compartidas están secuenciados de
forma fiable. En esos casos, el código de programa que actualiza el valor de
alguna de esas variables, no debe ser interrumpido de ninguna forma: debe
constituir una sección crítica.
SF: Sign flag (Flag de signo). Bit 7. SF se pone a valor 1 si el bit de mayor
peso del resultado de la última operación aritmético/lógica es precisamente 1
(Como veremos en el tema de representación de la información, este bit de
mayor peso de un operando es el que codifica el signo, el cual cuando es 1,
convenimos en que es negativo).
ZF: Zero flag (Flag de cero). Bit 6. ZF se pone a uno -1- cuando todos los
bits del resultado de la última operación aritmética/lógica son cero -0-.
AF: Auxiliary Carry Flag (Flag de acarréo auxiliar). Bit 4. El flag de
acarreo auxiliar se usa para simplificar la suma y la substracción de números
representados en código BCD empaquetado. El bit AF se pone a uno cuando en
la última operación aritmética se genera acarreo procedente del bit 3 (Caso de la
adición) o un toma sobre el bit 3 (Caso de la substracción). En el resto de los
casos el bit AF se queda en valor cero. Lo anterior es válido en cualquiera de los
tres tamaños de operando: 8, 16 o 32 bits.
PF: Parity flag (Flag de paridad). Bit 2. El flag PF se pone a uno si los ocho
bits de menos peso del resultado contienen un número par de unos (paridad
par). En otro caso, este bit toma valor cero (Lo que indica que los ocho bits de
menos peso del resultado tienen paridad impar). PF es pues solo función de los
ocho bits más bajos del resultado, independientemente de su tamaño: 8, 16 o 32
bits.
CF: Carry flag (Flag de acarréo). Bit 0. En el caso de una suma el bit CF se
pone a uno si la operación produjo acarreo procedente del bit de mayor peso del
resultado. En el caso de la sustracción el bit CF se pone a uno cuando se ha
generado un toma sobre el bit de mayor peso del resultado. En el resto de casos,
el bit CF se encuentra en valor cero. Para operaciones que trabajan con
operandos de 8, 16 o 32 bits el carry se genera en el bit 7, 15 o 31
respectivamente (caso de la suma). Para operaciones con operandos de 8, 16 o
32 bits el toma se genera sobre el bit 7, 15 o 31 respectivamente.
Tamaño del
resultado.
SF coincide con bit
núm:
8 bits
7
16 bits
15
32 bits
31
Usad esta numeración:
11 de 357
DI RECCI ONA MI ENTO D E OPERA NDOS EN MEMORI A: SEGMENTA C IÓN.
Los microprocesadores de la familia x86 emplean un procedimiento bastante
elaborado para direccionar la memoria. Asumen que la memoria se encuentra
particionada en zonas llamadas segmentos, cada una de las cuales engloba a
una zona de memoria de 64K posiciones con una determinada dirección base, a
partir de la cual se calculan las 64K posiciones. A esa dirección base se la
denomina dirección base del segmento. Para direccionar un byte particular
perteneciente al segmento, añadimos a la dirección base del segmento una
cantidad llamada offset, de esa suma resulta una dirección física. Esta dirección
física es la que aparece en el bus de direcciones.
Los registros de segmento son los encargados de albergar las direcciones base de
los segmentos donde residen los datos que van a ser manejados por los
programas. El ancho de estos registros es de 16 bits. Sin embargo, se nos
evidencia un problema inmediatamente: los 16 bits de ancho de los registros de
segmento, limitan su uso a los primeros 64Kb de memoria, es decir, con 16 bits
de ancho, solamente podremos establecer direcciones base de segmento en los
primeros 64Kb aproximadamente.
Los ingenieros de Intel decidieron añadir algunos bits a esos registros sin
aumentar su tamaño ¿Sorprendente? Veamos cómo lo lograron. Si desplazamos
el contenido de un registro de segmento, cuatro lugares a la izquierda,
transformamos los 16 bits en 20 bits ¿Ok? Además, los cuatro bits de menos
peso del resultado serán ceros. Esto es justamente lo que hace la unidad de
segmentación del i386SX en modo real: suma el offset de nuestro dato con el
registros de segmento desplazado cuatro lugares a la derecha. De este modo, la
dirección física obtenida es de un ancho superior a 20 bits. Calcularemos
exactamente cuantos en un momento posterior.
El rango de direcciones para los segmentos es de 220 posiciones, 1Mb de
memoria. Es decir podemos poner la dirección base de un segmento en
posiciones de memoria comprendidas entre la 0 y la 220 - 1.
El párrafo anterior requiere una lectura muy atenta. ¿Qué hemos perdido al
multiplicar por 10H? En realidad no demasiado: El rango en el que podemos
establecer una dirección de segmento es correcto, sin embargo no podemos
ponerla en cualquier dirección comprendida dentro del mismo. La razón es que
nosotros escribimos los 16 bits mas altos de la dirección, pero los cuatro mas
bajos siempre son cero. Dicho de otra forma, tenemos 5 dígitos hexadecimales,
el último siempre es cero (0H), ciertamente esas direcciones están en el rango,
pero no son todas las del rango. En este proceso, hemos perdido granularidad,
las direcciones base de segmento no pueden contarse de uno-en-uno si no de
16-en-16 (Recuerda, 4 bits a la izquierda: 24 =16). Finalmente, el desplazamiento
automático del registro de segmento nos permite usar 20 líneas del bus de
direcciones del i386SX.
Usad esta numeración:
12 de 357
¿Qué alineación presentan pues los segmentos? Decimos que presentan una
alineación de 16 bytes a lo que nos referimos con el término llamado párrafo: Los
segmentos se encuentran alineados en frontera de párrafo.
'Dirección de segmento' mod 16 = 0
v LOS REGISTROS DE SEGMENTO DEL I386SX.
El i386SX contiene seis registros de segmento. Cada uno de los estos registros se
emplea para la formación de direcciones de memoria real, las que venimos
denominando direcciones de memoria física.
Los programas están formados por segmentos lógicos cuyo tamaño no puede
ser superior al tamaño de un segmento físico de memoria, i.e. 64Kb. Los
segmentos lógicos típicos contenidos en un programa son:
Segmento de código (CODE)
Segmento de datos (DATA)
Segmento de pila (STACK)
Un solo programa puede contener uno más de los segmentos lógicos que
aparecen en la lista. Cuando un programa con una estructura de segmentos
lógicos como la que acabamos de mencionar se carga en memoria para su
ejecución, el sistema operativo se encarga de construir en memoria principal los
segmentos lógicos incluidos en el programa, de este modo, el segmento lógico
de código pasa a residir en un cierto rango de direcciones de memoria donde el
módulo cargador del sistema operativo va a transferir todas y cada una de las
representaciones binarias de todas las instrucciones pertenecientes al segmento
de código. El sistema operativo es también el encargado de reservar espacio
para los otros segmentos, los de datos y pila, y de construirlos adecuadamente
en memoria.
Cuando el proceso de carga y construcción de los segmentos termina, entonces,
el sistema operativo, para poder “saltar” al programa recién cargado, tiene que
preparar el contexto del nuevo programa: las direcciones base de cada uno de
los tres segmentos que forman parte del programa tienen que ser cargadas en
los registros de segmento correspondientes. Posteriormente, cuando se inicie la
ejecución del programa, éste será capaz de formar las direcciones físicas de los
datos e instrucciones que habrá de procesar. El registro CS siempre contiene la
dirección base del segmento físico donde el sistema ha ubicado las instrucciones
del programa. El registro DS contiene la dirección base de segmento
correspondiente a la dirección de comienzo del segmento de datos del
programa. El registro empleado para la pila es el registro SS, el cual, también
será inicializado adecuadamente antes de iniciarse la ejecución del programa.
Usad esta numeración:
13 de 357
Existen programas que pueden necesitar hacer uso frecuente de varios
segmentos de datos. En esos casos resultará conveniente usar otros registros de
segmento de datos como los registros ES, FS y GS.
Los nombres de los seis registros de segmento son CS, SS, DS, ES, FS y GS. En
modo real, el ancho de cada uno de estos registros es 16 bits. La figura 7 ilustra
el uso explicado de cada uno de estos segmentos.
En el capítulo 5 del libro de los profesores Patterson y Hennessy estudiamos el
diseño de la ruta de datos del microprocesador MIPS. A pesar de que la
arquitectura del microprocesador MIPS y la arquitectura IA son muy distintas,
los conceptos necesarios para comprender el funcionamiento del
microprocesador, pueden considerarse los mismos. Según vimos, la CPU
repetía continuamente el proceso de recogida y ejecución de instrucciones que
residen en la memoria principal. En el caso de IA y en el caso particular de un
microprocesador i386SX, las instrucciones que ejecuta el microprocesador se
encuentran en una zona de memoria llamada segmento de código y estas instrucciones
operan sobre datos que residen también en la memoria. Pues bien, los datos que
maneja un programa se encuentran en la memoria, en un segmento de datos. Los
programas que se ejecutan sobre un procesador con arquitectura IA forman las
direcciones físicas de los datos que manejan con la ayuda de un registro de
segmento, típicamente el registro DS.
El segmento de stack constituye un estructura de datos imprescindible para
implementar las subrutinas y poder efectuar mecanismos de interrupción del
microprocesador, como veremos en un tema posterior. La dirección base del
segmento físico de memoria donde reside el stack se guarda en el registro SS.
BYTES, W ORDS Y ENDIA NISMO EN LA A RQUITEC TURA IA.
El sistema de memoria en un sistema microprocesador suele diseñarse de forma
que en cada dirección de memoria se pueda almacenar como mínimo un byte,
esto es, 8 bits de información, 8 símbolos 0 o 1. Esto significa que cuando el
procesador efectúa un acceso a la memoria, el número de bits transferido, debe
ser como mínimo de 8. El ancho del dato puede ser, sin embargo, superior a 8.
Anchos de 16, 32 y 64 bits son típicos en el momento presente. Cuando en un
programa especificamos la dirección de memoria de un dato,
independientemente de su ancho, el microprocesador asume que el dato está
formado por 1, 2, 4 o más bytes y que para efectuar la transferencia tendrá que
transfereir cada uno de estos bytes cuyas direcciones de memoria siguen a la
dirección base del primer byte del dato.
Las words de la arquitectura IA se almacenan en dos bytes que corresponden a
posiciones de memoria consecutivas según hemos explicado en el párrafo
anterior: El byte bajo se encuentra en la dirección de memoria especificada y el
byte alto se encuentra en la dirección de memoria siguiente. El microprocesador
i386SX es una máquina que usa un ordenamiento de datos multibyte del tipo
extremista inferior -little endian. En la figura tenemos un ejemplo que explica
Usad esta numeración:
14 de 357
cómo se almacena en memoria una word según los convenios que aplican al
micro i386SX.
Las dwords se almacenan siguiendo el mismo esquema anterior; en este caso
tenemos cuatro bytes en direcciones también consecutivas con el byte bajo
almacenado en la dirección especificada y los bytes siguientes en direcciones
consecutivas.
La direcciones de memoria física que maneja el i386SX están formadas por una
dirección base de segmento y un offset. La dirección base de segmento se suele
cargar en un registro de segmento y nos permite alcanzar 1 Mb de memoria,
después, al añadir el offset se puede alcanzar casi otros 64Kb por encima del 1
Mb.
La dirección base del segmento de memoria física donde se encuentra el dato
que se va a transferir, se encuentra almacenada normalmente en el registro de
segmento que se esté usando en ese momento y el offset es la cantidad que se
suma al anterior después de multiplicarlo por 10H. Vamos a estudiar las diferentes
maneras de formar offsets. A estas formas distintas del offset en un programa se
les denomina modos de direccionamiento y este procesador acepta un elevado
número de ellos. Recordemos que el microprocesador MIPS sólo aceptaba un
modo de direccionamiento de memoria.
Los diferentes modos de direccionamiento del procesador i386SX, sirven pues
para generar el offset de un operando residente en memoria. Este operando
puede ser operando fuente o destino y puede, en principio, encontrarse en
memoria o en un registro de propósito general del procesador.
La microarquitectura del microprocesador i386SX incluye una unidad de
segmentación (Segmentation Unit). La unidad de segmentación multiplica por
10H el contenido del selector de segmento que se esté usando y le suma la
dirección efectiva (offset). La figura ilustra cómo se efectúa el de la dirección
física o dirección lineal de un dato residente en memoria.
Quizás sea este un buen momento para volver a leer acerca de los modos de
direccionamiento de MIPS y de otros microprocesadores en el capítulo 3 de
P&H. Esta lectura nos da idea de lo importante que resulta el contar con un
abanico de modos de direccionamiento lo más amplio posible: Eso va a facilitar
los desarrollos de sistemas y aplicaciones. Ya sabemos que también afectará
negativamente al rendimiento del microprocesador puesto que la generación de
direcciones será mas compleja, la unidad de bus será mas compleja también,
más costosa y tendrá un impacto mayor en el rendimiento global del sistema.
Ejemplo: La dirección de memoria física más alta que puede hacer aparecer un
i386SX en su bus de direcciones en modo real. En un microprocesador como el
MC68000, basta con conocer el ancho en bits del bus de direcciones para
calcular la dirección de memoria más alta posible. En la arquitectura IA
tendremos que formar la dirección calculando la expresión segmento:offset que
contenga el segmento más alto posible y el offset más alto posible también.
Usad esta numeración:
15 de 357
¿Cuanta memoria puede direccionar pues un i386SX? El segmento más alto es
aquel que tiene todos sus bits puestos a 1, esto es, FFFFH y el offset también
más alto, i.e. FFFFH - recordemos que los offsets en modo real son de 16 bits.
Operemos igual que lo hace la unidad de segmentación del i386SX y efectuemos
el cálculo que aparece en la figura.
LOS MODOS DE DIR EC C IONA MIENTO DEL I386S X: UNA VISIÓN G EN ERA L.
Los operandos usados en los programas de ordenador, sabemos que pueden
estar almacenados en la memoria principal o en los registros del
microprocesador. La arquitectura del conjunto de instrucciones incluye las
sintaxis y las semánticas necesarias para que los programas puedan hacer uso
de los operandos. A estas diferentes formas de acceso a la memoria y a los
registros se las conoce como modos de direccionamiento. En el capítulo 3 de
P&H se desarrolla la noción de modo de direccionamiento de una forma
general, en este apartado, estamos interesados solamente en estudiar el
conjunto de modos de direccionamiento de la arquitectura IA.
Los modos de direccionamiento presentes en la IA, pueden hacer referencia a
operandos residentes en registros, en memoria y a operandos inmediatos. El
uso de valores inmediatos es considerado como modo de direccionamiento en
esta arquitectura -como ya sabemos acerca de MIPS, el dato accedido se
encuentra inmerso dentro de los bytes que forman parte de la representación
binaria de la instrucción.
Antes de continuar con la explicación de cada uno de los modos disponibles en
el IA, recordemos que la inclusión de un conjunto amplio de modos de
direccionamiento avanzados facilita grandemente la labor de desarrollo de
software y reduce el número de instrucciones empleadas en un programa dado.
Al mismo tiempo sin embargo, incrementa la complejidad de la máquina y por
tanto el diseñador ha de establecer un punto de equilibrio entre ambos
aspectos: complejidad de la ruta de datos y flexibilidad de los modos.
La arquitectura IA contiene un conjunto de modos de direccionamiento muy
amplio y notablemente ortogonal: no son muchas las restricciones existentes a la
hora de elegir un modo de direccionamiento que va a ser usado con una
determinada instrucción. La inmensa mayoría de los modos usados mas
frecuentemente están implementados directamente en el hardware, por tanto su
decodificación y resolución serán muy rápidas. Aquellos modos que pudieran
faltar han de ser implementados a base grupos de dos o tres instrucciones que
hagan uso de los modos disponibles. Estos modos de direccionamiento no
disponibles se usarán con tan poca frecuencia que, probablemente, su
implementación en el hardware no sería eficiente. Si examinásemos los tiempos
de ejecución de algunos modos de direccionamiento descubriríamos que los
modos complejos requieren poco mas tiempo cuando se implementan a base de
instrucciones que cuando se incorporan directamente por hardware, por lo que
no es interesante consumir área de chip, lógica, y otros recursos para
implementarlos.
Usad esta numeración:
16 de 357
Por último, debemos mencionar que todos los modos de direccionamiento de la
IA pueden ejecutarse en un único ciclo de reloj, independientemente de lo
complejos que sean. Vamos a continuar con una breve explicación de cada uno
de ellos y vamos a realizar también algunos ejemplos aclaratorios.
v MODO DIRECTO REGISTRO –ALMACENAR DATOS TEMPORALMENTE.
Cuando los programas acceden frecuentemente a ciertos datos, podemos
acelerar la velocidad de ejecución, ubicando esos datos en registros del
microprocesador, de forma que los accesos resulten más rápidos.
Evidentemente, el número de registros es muy reducido si lo comparamos con
el número de bytes de memoria, por tanto los datos o variables pueden ubicarse
en registros, pero durante periodos relativamente cortos de tiempo: Justo el
tiempo en el que estos datos jueguen un papel relevante en los procesos de
información que se están llevando a cabo por parte del programa .
En este modo de direccionamiento, el registro seleccionado se especifica
mediante su nombre, como en los siguientes ejemplos:
MOV ES, CX ; Transferir el contenido del registro CX al registro ES. CX es un
registro de propósito general, ES es un registro de segmento llamado extra segment
ES.
ADD AX, BX; sumar el contenido de AX con el de BX y cargar el resultado en AX.
En los dos ejemplos precedentes, ambos operandos, fuente y destino, se han
especificado usando el modo directo registro, es decir, ambos operandos son
registros. En el futuro, será frecuente que hagamos uso de modos distintos para
cada uno de los dos operandos, fuente y destino. La excepción más notable a
esta práctica, será que en una gran mayoría de casos, la arquitectura IA no
permite que los dos operandos de una instrucción residan ambos en memoria.
En el modo directo registro se pueden emplear cualesquiera registros de
propósito general y también cualesquiera registros segmento. El registro
EFLAGS es el registro de estado. Este registro contiene 32 bits que representan,
de forma independiente entre sí, el carácter del resultado obtenido de las
operaciones aritméticas y lógicas. EFLAGS contiene bits como zero y carry los
cuales representan, respectivamente, que el resultado obtenido en la ejecución
de la última instrucción aritmética/lógica fue cero y que hubo desbordamiento
en aritmética sin signo. En ciertas ocasiones, será necesario transferir el
contenido completo del registro EFLAGS a un registros de propósito general.
Una vez allí, se puede operar con el contenido de EFLAGS como si se tratase de
un numero. ¿Cómo se transfiere el contenido del registro EFLAGS a un registro
de propósito general? No se puede usar una instrucción MOV; existen dos
instrucciones para transferir de/desde EFLAGS al registro EAX. EFLAGS ens n
registro de 32 bits, en modo real, la mitad baja de 16 bits es la que se emplea con
mayor frecuencia, a esa mitad nos referimos con el nombre FLAGS. Veamos el
siguiente ejemplo:
MOV EAX, FLAGS;Esta instrucción no transfiere el contenido de FLAGS a EAX.(Es
ilegal).
Usad esta numeración:
17 de 357
LAHF;"Load AH with Flags". Esta instrucción sí lo hace.
SAHF; Transferir la parte alta del registro AX a la parte baja de FLAGS.
Estos ejemplos ilustran lo importante que es comprender las especificaciones
tanto de los modos de direccionamiento como de las instrucciones. Recordemos
la línea inicial del párrafo anterior : "Este modo de direccionamiento se puede
emplear con cualquiera de los registros de propósito general o selectores de
segmento". Por tanto, si se nos plantea la necesidad de transferir el contenido de
EFLAGS, el cual como sabemos, no es un GPR ni un selector de segmento,
debemos hacerlo utilizando un recurso especial: una instrucción - si tal
instrucción existe- o un pequeño programa. Por último, haremos un
comentario en relación con el ejemplo sobre FLAGS: existe una forma de
transferir FLAGS a memoria y después cargar FLAGS desde memoria. La
instrucción que transfiere FLAGS a memoria se llama PUSHF. Esta instrucción
transfiere FLAGS a la dirección más alta de la pila. Del mismo modo, existe otra
instrucción llamada POPF que lee la word que se encuentra en la posición más
alta de la pila y la carga en el registro FLAGS.
v MODO INMEDIATO- UNA FORMA DIRECTA DE REPRESENTACION DE CONSTANTES.
Frecuentemente los cálculos que efectuamos usan constantes: Operandos cuyo
valor no cambia nunca a lo largo de la ejecución del programa del cual forman
parte.
Estos valores constantes podrían ser almacenados en celdas de memoria, o en
registros de propósito general. Sin embargo, esta costumbre de uso no resulta
conveniente puesto que, sobre todo, los registros constituyen un recurso muy
escaso. Así que los diseñadores pensaron que habría que idear algún otro
medio de almacenar las constantes. Tal como vimos en MIPS, pueden incluirse
constantes embebidas en la representación binaria de las instrucciones. Se
denomina inmediato estas constantes incluidas en la representación binaria de
una instrucción.
¿Cómo se lleva a cabo esta inclusión de constantes "dentro" de las propias
instrucciones? El ensamblador, una vez resueltos los valores de cada uno de los
inmediatos empleados en un programa, procede a ensamblar cada una de las
instrucciones que forman parte del programa. Cuando el ensamblador
encuentra una instrucción que incluye un inmediato, genera el código de
operación de la instrucción y otros campos de ésta y, en alguno de los campos
de bits siguientes al op-code, almacenará el inmediato (Exactamente igual que en
el microprocesador MIPS). Veamos algunos ejemplos:
XOR AH,11001100b; Efectuar el OR-exclusivo del contenido de AH (en tiempo de
ejecución) con el valor binario 11001100b. Ver la figura 4.
Usad esta numeración:
18 de 357
v MODO DIRECTO-MEMORIA. PARA VARIABLES ESTÁTICAS.
Hay muchas ocasiones en que las variables -operandos cuyo valor puede
alterado a lo largo de la ejecución de un programa- no están localizadas en
registros, sino en direcciones de memoria conocidas. En esas circunstancias, la
dirección de memoria se especifica dentro de la propia instrucción siguiendo al
mnemónico.
La dirección de memoria donde reside el operando que se desea usar se incluye
dentro de la propia instrucción en forma de offset. Este offset, en este caso
particular, es una constante, porque su valor no varía a lo largo de la ejecución
del programa. Pero, esta constante, no representa en sí misma un valor que
pueda ser usado en la computación de un resultado. Esta constante representa
parte de una dirección de memoria. Es decir, esta constante, interviene en el
cálculo de una dirección de memoria y, por supuesto, en esa dirección de
memoria, residirá un dato que el programa usará en un instante posterior. A
esta constante que representa parte de la dirección de memoria de un dato, no
se le llama inmediato.
En lenguaje Macroensamblador, estas constantes que representan direcciones
de memoria, están sujetas a las misma reglas sintácticas de formación que los
inmediatos. Pueden especificarse como constantes literales en cualquier base
legal del lenguaje o como constantes simbólicas. Sin embargo, a diferencia de
los inmediatos, se encierran entre corchetes. Los corchetes significan que la
expresión debe evaluarse como offset de una dirección de memoria. La dirección
de memoria efectiva, o sea, la dirección de memoria real que aparecerá en el bus
de direcciones, se formará con el offset mencionado y con un registro de
segmento, normalmente el registros DS.
Los offsets en modo real pueden ser de 8 o de 16 bits y la dirección efectiva se
forma sumando el offset a la dirección base de segmento. También, si se desea,
se puede especificar la dirección lógica completa, es decir un registro de
segmento o un valor de segmento constante y después de los 'dos puntos' (:)
especificamos el modo de direccionamiento memoria-directo. Veamos dos
ejemplos:
MOV AX, [500H];Cargar el registro AX con el contenido de la posición de memoria
500H. 500H es el offset de la posición de memoria cuyo contenido se desea cargar en
AX. Esta posición se encuentra en el segmento de datos que esté en uso en el momento
de ejecutarse la instrucción. El selector de segmento normalmente será DS, sin
embargo, podría ser otro registro de segmento. La dirección lineal del operando
fuente en este caso sería DS:0500H.
ROL [MASCARA] ;Rotar a la izquierda el contenido de la posición de memoria
MASCARA la cual se encuentra dentro del segmento de datos en uso, por ejemplo DS. La
dirección lineal del operando fuente -y en este caso también destino- sería
DS:MASCARA = DS x 10H + MASCARA.
MOV GS:[mi_offset5], SI; Transferir el contenido del registro SI a la dirección
de memoria formada con la siguiente fórmula: GS: mi_offset5 = GS x 10H +
mi_offset5. mi_offset5 es una constante simbólica que representa un offset.
Usad esta numeración:
19 de 357
MOV 1200H:[mi_offset6], FF00H; Transferir la constante FF00H, un inmediato
representado por una constante literal, a la dirección de memoria formada mediante
la siguiente fórmula: 1200H: mi_offset6 = 1200H x 10H + offset5. mi_offset6 es una
constante simbólica que representa un offset.
v MODO REGISTRO-INDIRECTO: PARA ACCEDER A LOS ELEMENTOS DE UN ARRAY.
Este modo de direccionamiento es probablemente el más usado. En un gran
número de estudios estadísticos sobre el uso de los modos de direccionamiento
este modo destaca como el más usado. ¿Cuál es la razón? En la introducción al
lenguaje C que hicimos en el principio de curso, estudiamos una estructura de
datos ampliamente usada en programación llamada array. Un array es una
matriz formada por celdas de memoria contiguas que contienen datos de algún
tipo (cadenas, punteros, words, dwords, enteros, etc.).
Para acceder a los elementos de un array necesitamos conocer la dirección
inicial o dirección base del array y el tipo de datos que contiene el array.
Con esta información, ya podemos acceder a cada elemento del array.
Supongamos que deseamos acceder al elemento n del array, el offset de la
dirección de memoria donde reside este elemento se calcula sumando a la
dirección base del array el producto del índice del elemento menos uno
multiplicado por el tamaño de elemento:
Offset del elemento n = Dirección base del array + (n-1)*Tamaño en bytes del tipo de
dato contenido en el array
Se ha de usar n-1 y no n porque el primer elemento del array está representado
por un índice 0 y no 1.
Para formar offsets siguiendo el esquema explicado, podemos hacer uso de los
diferentes modos registro-indirecto que nos ofrece IA. IA contiene varios modos
registro-indirecto, el primero de ellos, recibe este nombre precisamente.
v MODO REGISTRO-INDIRECTO.
El offset del operando residente en memoria, se encuentra en un registro BASE.
En el siguiente ejemplo transferimos al registro CX el contenido de la dirección
de memoria que está almacenada en BX:
MOV CX, [BX]; Llevar al registro CX el contenido de la dirección de memoria
almacenada en BX. BX contiene el offset de esa dirección de memoria real, el
segmento será probablemente DS.
Los modos de direccionamiento que siguen, son todos ellos variaciones sobre el
modo registro-indirecto.
v MODO BASE CON DESPLAZAMIENTO.
En este modo de direccionamiento el offset del operando residente en memoria
se forma añadiendo un DESPLAZAMIENTO al contenido del registro BASE
especificado en la instrucción. El desplazamiento es una constante que se emplea
para formar el offset del operando. Un desplazamiento es en todo igual que un
inmediato, la diferencia radica en que los inmediatos intervienen en cálculos
Usad esta numeración:
20 de 357
efectuados con datos, mientras que los desplazamientos intervienen en los
cálculos con direcciones de memoria. Ciertamente, son frecuentes las
situaciones en que tenemos que efectuar cálculos para formar la dirección de
memoria donde reside un objeto de un determinado tipo. El offset en esta caso
viene dado por la siguiente fórmula:
Offset = [Registro Base] + desplazamiento
Veamos dos ejemplos para ilustrar cada una de las dos formas de especificar
un desplazamiento en el texto fuente de un programa escrito en lenguaje
Macroensamblador:
MOV CX, VECTOR[BX]; DESPLAZAMIENTO es VECTOR. Registro BASE es BX.La
dirección efectiva del operando fuente se forma sumando el valor de la
constante simbólica VECTOR al contenido del registro BASE, BX (en tiempo de
ejecución del programa). Esa dirección contiene el dato que se va a
transferir al registro CX.
INC Byte PTR 24h[BX]; DESPLAZAMIENTO = 24h. Registro BASE es BX. Incrementar
en una unidad el BYTE residente en la dirección de memoria [BX] + 24h. El
tipo de datos allí presente se asume que es un byte. Puesto que esta
instrucción sólo usa un operando el cual es al mismo tiempo operando fuente
y operando destino, resulta necesario romper la ambigüedad en cuanto al
ancho en bytes del operando de memoria. Esta ambigüedad se rompe mediante el
modificador de ancho de acceso llamado PTR: BYTE PTR significa que en este
acceso a memoria, el dato que se desea acceder es de 1 byte de ancho.
v MODO ÍNDICE MAS DESPLAZAMIENTO.
Este modo es muy similar al anterior. La diferencia entre ambos viene dada por
el registro empleado que en este caso es un registro Indice, es decir SI o DI.
El offset se forma así: Offset = [Registro Indice] + desplazamiento
Ejemplos:
MOV AX, [SI + 40H]; Llevar al registro AX el contenido de la dirección de
memoria DS:[SI] + 40h.
MOV DX, TABLA[SI]; Transferir al registro DX el contenido de la dirección de
memoria DS:[SI]+TABLA. TABLA es una constante simbólica.
v MODO ÍNDICE ESCALADO CON DESPLAZAMIENTO.
Para formar el offset, el contenido de un registro índice se multiplica por un
factor de escala y el resultado se suma al desplazamiento especificado. El factor de
escala es una constante de valor 1, 2, 4 u 8.
Este modo de direccionamiento se emplea en el acceso a arrays con datos
estructurados, datos multibyte y estructuras.
Ejemplos:
MOV BX, TABLA[SI * 4]; La dirección efectiva para el operando fuente se
forma usando el registro DS como registro de segmento y el offset se forma
Usad esta numeración:
21 de 357
multiplicando por 4 el contenido de SI y sumándole el valor de la constante
simbólica TABLA (En tiempo de ejecución).
IMUL EBX, TABLE[ESI*4], 7; La dirección efectiva del operando fuente 1 es:
DS:Offset = DS:[ESI]*4 + TABLE.
v MODO BASE INDEXADO.
En este modo de direccionamiento simplemente sumamos los contenidos de
dos registros (uno INDICE y el otro BASE) para formar la D.E. Por tanto:
Offset = [Registro Base] + [Registro Indice]
Este modo resulta muy conveniente en la construcción de subrutinas que
manejan arrays cuya dirección base puede cambiar a lo largo de la ejecución de
un programa: en un registro albergamos la dirección base y el otro registro lo
empleamos para efectuar la indexación de cada uno de los elementos del array.
Veamos dos ejemplos:
MOV AX, [SI] [BX];La dirección efectiva del operando fuente es DS:Offset =
DS:[SI] + [BX].
El ejemplo siguiente es una forma completamente equivalente al anterior:
MOV AX, [SI+BX]; Offset = [SI] + [BX]
DEC WORD PTR [SI][BX]; Decrementar en una unidad la WORD que reside en la
dirección de memoria cuyo offset se forma sumando los contenidos de los
registros SI y BX y cuyo segmento es DS.
v MODO BASE CON ÍNDICE ESCALADO.
En el conjunto de modos vistos hasta aquí, podemos observar una gran
regularidad en la formación de los offsets. Ciertamente, tenemos cuatro
elementos en la fórmula que nos da el offset y son:
DESPLAZAMIENTO: Una cantidad constante de 8 o 16 bits- Un valor
constante que, como ya sabemos, es constante y está inmerso en la
representación binaria de la instrucción.
BASE: En modo real son registros base los registros BX y BP. En modo
protegido o en modo real con datos de 32 bits, un registro base puede ser
cualquier registro de propósito general (GPR).
INDICE: En modo real son registros base los registros SI y DI. . En modo
protegido o en modo real con datos de 32 bits, un registro índice puede ser
cualquier registro de propósito general (GPR) excepto ESP. La diferencia
primordial entre BASE e INDICE consiste en que el modo índice sí puede
ser escalado, como veremos a continuación.
ESCALA: El valor del registro índice especificado en el modo de
direccionamiento, puede ser multiplicado por un factor de escala cuyo valor
puede ser 1, 2, 4 u 8. Este producto se evalúa en tiempo de ejecución.
Usad esta numeración:
22 de 357
En el modo base con índice escalado el offset de la dirección de memoria efectiva
se forma multiplicando el factor de escala por el registro índice y sumándole el
contenido del registro base:
MOV CX, [DX * 8 + AX]; Offset del operando fuente = [DX * 8] + [AX]
DEC DWORD PTR [EDX*8] [EAX]; Decrementar la DWORD que reside en la dirección
cuyo offset es = [EDX*8] + [EAX]
v MODO BASE INDEXADO CON DESPLAZAMIENTO.
La fórmula que nos da el offset en este caso es:
Offset = [Registro base] + [Registro Indice] + Desplazamiento.
Ejemplos:
MOV DX, [SI] [BX + FFF0h]; Transferir el dato que se encuentra en la
dirección de memoria: Offset = [SI] + [BX] + FFF0h al registro DX.
v MODO ÍNDICE ESCALADO CON DESPLAZAMIENTO.
Este modo de direcionamiento es el que usa los cuatro elementos estudiados:
Desplazamiento, registro base, registro índice y factor de escala. La fórmula en
este caso, es la generalización de los casos anteriores:
MOV AX, [DI*4 + BX + 0F0h]; Offset = [DI*4]+[BX]+ 0F0h
v COMENTARIO FINAL.
Los modos de direccionamiento son las diferentes formas de construir un offset,
sólo el offset del dato que se desea direccionar. Una vez calculado el offset, el
microprocesador usa uno de los registros de segmento para formar la dirección
física necesaria para alcanzar el dato en memoria principal. Normalmente, en el
caso de datos en memoria, el registro que se emplea como registro de segmento
es DS, pero el programador puede haber alterado el registro de segmento a usar
por defecto. En el capítulo dedicado a la descripción del entorno del
Macroensambador de Microsoft, veremos cómo se le indica al procesador cuál
es el registro de segmento a usar por defecto. Las direcciones de memoria física
de las cuales se recoge el código que se está ejecutando, se forman con el
registro CS, siempre.
COM PATI BILIDAD EN LA FAM ILI A DE MI CROPROCESADORES INTEL 80X86.
En la pregunta anterior hemos visto una exposición general del conjunto de
modos de direccionamiento del i386SX. Hemos seguido la terminología de Intel
para definir los diferentes elementos que forman parte de cada uno de los
modos. Dicha terminología surgió en su mayor parte en el 8088 y siguientes.
Como sabemos el 8086 era un micro con muchas limitaciones comparado con el
i386SX: El ancho de palabra natural, capacidad de direccionamiento, velocidad,
tecnologías, etc. A pesar de esas limitaciones y porque la cantidad de código
Usad esta numeración:
23 de 357
escrito para el 8086 es inmensa, Intel se vio forzada a garantizar la
compatibilidad de la IA con los micros anteriores.
La compatibilidad entre IA y los microprocesadores anteriores en lo que
respecta a los modos de direccionamiento, queda resumida en la tabla
siguiente:
CONCEPTO
MODO REAL: Offsets de 16 Modo protegido: Offsets de
bits.
32 bits.
Base
BX, BP
GPR
Indice
SI, DI
GPR
Factor de escala
Uso restringido.
1,2,4 u 8.
Desplazamiento
0, 8 o 16 bits.
0, 8 o 32 bits.
Vamos a interpretar la información provista por la tabla . Fundamentalmente,
en modo real, tenemos ciertas limitaciones en lo que respecta al uso de registros
en la formación de modos de direccionamiento. Por ejemplo, al no tener Factor
de escala, todos los modos "escalados" mencionados, están restringidos en modo
real. También tenemos que tener presente que en todos los modos indirectos
tipo BASE o INDEXADOS no podemos utilizar cualquier GPR, estamos
obligados a hacer uso de los BX/BP o SI/DI, respectivamente. En cuanto a los
desplazamientos la única restricción es que han de ser de 16 bits como máximo.
En este curso usaremos el modo real en su mayor parte, por tanto será necesario
conocer estas restricciones, sobre todo en los ejercicios de laboratorio.
Recordemos que cuando el i386SX está funcionando en modo real, ejecuta
código de 16 bits, los desplazamientos son de 8 o de 16 bits y los registros BASE
e INDICE son exactamente iguales a los del 80286, es decir son registros de 16
bits si los empleamos para formar direcciones; si los empleamos para albergar
datos sí pueden utilizar su ancho completo de 32 bits, lo cual constituye una de
las grandes ventajas del i386SX sobre el 286 en modo real: en modo real
podemos acceder a los registros de 32 bits, tanto para almacenar datos como
direcciones, aunque en este último caso, el valor contenido en el registro, está
limitado a su mitad inferior de 16 bits. En un capítulo posterior, dedicado a la
descripción del lenguaje Macroensamblador, estudiaremos con un mayor nivel
de detalle estos aspectos de compatibilidad, costumbres y convenios de uso y
otros temas complementarios.
Usad esta numeración:
24 de 357
Objetivos del tema. _____________________________________________________ 1
Introducción. _________________________________________________________ 1
Un modelo estructural simple del i386SX. __________________________________ 3
v
Resumen de los modos real y protegido. _______________________________ 3
v
Los registros del i386SX visibles al programador. ________________________ 4
v
Los registros de propósito general. ___________________________________ 5
v
El registro puntero de instrucción: IP._________________________________ 5
v
El registro de status o banderas (EFLAGS). ____________________________ 6
Direccionamiento de operandos en memoria: Segmentación. _____________________ 9
v
Los registros de segmento del i386SX. ________________________________ 10
Bytes, words y endianismo en la arquitectura IA. ___________________________ 11
Los modos de direccionamiento del i386SX: una visión general._______________ 13
v
Modo directo registro –Almacenar datos temporalmente._________________ 14
v
Modo inmediato- Una forma directa de representacion de constantes. _______ 15
v
Modo directo-memoria. Para variables estáticas. _______________________ 16
v
Modo registro-indirecto: Para acceder a los elementos de un array. _________ 17
v
Modo registro-indirecto. __________________________________________ 17
v
Modo base con desplazamiento._____________________________________ 17
v
Modo índice mas desplazamiento. ___________________________________ 18
v
Modo índice escalado con desplazamiento. ____________________________ 18
v
Modo base indexado. _____________________________________________ 19
v
Modo base con índice escalado. _____________________________________ 19
v
Modo base indexado con desplazamiento. _____________________________ 20
v
Modo índice escalado con desplazamiento. ____________________________ 20
v
Comentario final.________________________________________________ 20
Compatibilidad en la familia de microprocesadores intel 80x86. _______________ 20
Usad esta numeración:
25 de 357
Apuntes de Máquinas
Electrónicas
© 1994, 2004 José María Foces Morán.
Capítulo 2
El set de instrucciones IA
Usad esta numeración:
26 de 357
Arquitectura del
i386SX: El set de
instrucciones.
Introducción.
En el libro Advances in Computer Architecture, el profesor Myers define Arquitectura de
ordenadores como una abstracción del ordenador tal como la ve un programador al
más bajo nivel -lenguaje de ensamblaje. La arquitectura engloba, pues, aspectos como
los registros del microprocesador, el conjunto de instrucciones, los modos de
direccionamiento y los tipos de datos. En este tema nos proponemos describir el juego
de instrucciones de la arquitectura IA y estudiar con detalle las instrucciones más
importantes, sobre todo aquellas que son el legado del microprocesador 8086.
El juego de instrucciones de IA.
El juego de instrucciones de la arquitectura IA es del tipo CISC, esto es, básicamente se
trata un juego de instrucciones muy extenso. IA contiene un gran número de
instrucciones en cualquiera de sus tipos. Esto facilita el desarrollo de programas
porque en la resolución de ciertos problemas, se usarán directamente instrucciones de
la arquitectura en vez de escribir programas que lleven a cabo la función deseada.
También, el desarrollo de compiladores se facilita notablemente.
El juego de instrucciones de IA, sobre todo la parte correspondiente al modo real de
ejecución, está universalmente extendido y es, por tanto, muy bien conocido por
millones de desarrolladores en todo el mundo. Existe, también, una cantidad masiva
de software para esta arquitectura: sistemas operativos, aplicaciones y software de
desarrollo. La documentación provista por la compañía Intel está excelentemente
organizada y es muy completa. Todos estos aspectos, junto con el hecho de que esta
arquitectura se está extendiendo también al desarrollo de sistemas empotrados, hacen
que el estudio de la IA resulte muy conveniente y también muy directo en esta
segunda parte de Máquinas Electrónicas donde nuestro objetivo principal es el
desarrollo de habilidades orientadas al diseño de soluciones.
1
Usad esta numeración:
27 de 357
Cla sifica ción funciona l de la s instrucciones.
En el capítulo titulado Modos de Direccionamiento y Registros, describimos el
desarrollo de la IA a lo largo del tiempo. Como vimos allí, el primer microprocesador
que incorporaba esta arquitectura completa fue el i386. El i386 incluye, pues, los dos
modos ya conocidos: el modo real y el modo protegido. El modo protegido es una
extensión del modo real que permite el desarrollo de sistemas operativos modernos.
Esta extensión incluye nuevas instruc c iones y registros además de otros conceptos
arquitecturales nuevos también. Pero en el año 1999, la IA, no sólo incluye las
extensiones del modo protegido. La compañía Intel, ha añadido a la IA, otras
capacidades que reflejan, por ejemplo, la importancia de las tecnologías multimedia. A
estas extensiones se las conoce con el acrónimo MMX y, en este último año, las
extensiones MMX han sido mejoradas aún más con la incorporación de técnicas SIMD
en la organización interna de los microprocesadores Pentium III y Pentium III Xeon.
Seguidamente, vamos a desarrollar una clasificación del juego de instrucciones IA
completo, teniendo en cuenta que nuestro interés fundamental se centra sobre las
instrucciones del modo real.
Instrucciones disponibles en modo real y protegido.
Transferencia de datos.
Aritméticas.
Lógicas, de rotación y de desplazamiento.
Manejo de cadenas de caracteres.
Manipulación de bits individuales.
Transferencia de control -Instrucciones de salto.
Soporte de lenguajes de alto nivel.
Punto flotante.
Control de punto flotante.
Extensiones MMX.
Extensiones SIMD.
2
Usad esta numeración:
28 de 357
Instrucciones disponibles en modo protegido.
Soporte del modo protegido.
Control del procesador.
Instrucciones de tra nsferencia de da tos.
En general, las instrucciones de transferencia de datos de la arquitetcura IA, pueden
llevar a cabo los siguientes tipos de transferencias de datos:
1. Registro →Registro.
2. Memoria →Registro.
3. Registro →Memoria.
4. Inmediato →Registro.
5. Inmediato →Memoria.
El tamaño de los operandos puede ser de 8, 16 o 32 bits y las instrucciones más
importantes pertenecientes al modo real suelen ser instrucciones de dos operandos, es
decir, la arquitectura IA es, típicamente, una arquitectura de dos direcciones.
La arquitectura permite transferir datos de los tres tamaños antes mencionados
respetando las reglas dadas en cuanto a origen y destino de la transferencia. Existe un
tipo de transferencia que no es posible en la arquitectura IA, se trata de la transferencia
Memoria-Memoria: no existe ninguna instrucción de transferencia de datos que acepte
dos operandos en memoria, por tanto para efectuar este tipo de transferencia,
tendremos que cargar un registro de propósito general con el dato fuente e,
inmediatamente después, salvar ese dato en la dirección de memoria de destino –
mediante otra instrucción mov. Sin embargo, existe una instrucción de procesamiento
de cadenas, movs(move string), que sí permite transferencias de memoria a memoria.
La primera instrucción de transferencia de datos que vamos a estudiar es la
instrucción MOV (move). Esta instrucción transfiere el dato presente en su operando
fuente al operando destino. ¿Cómo se concreta esto en forma escrita? Se escribe el
mnemónico de la instrucción y luego después se escribe el operando destino y
mov destino, fuente
separado por una coma escribimos el operando fuente:
3
Usad esta numeración:
29 de 357
Tanto fuente como destino pueden ser de los tamaños ya mencionados y las fuentes y
destinos de las transferencias, son las vistas en el apartado anterior. Veamos algunos
ejemplos:
mov cl, ah ;Transferir el contenido del registro AH al registro CL. El
operando fuente es AH, el operando destino es CL. El modo de
direccionamiento para el operando fuente es registro y el modo de
direccionamiento para el operando destino es registro también.
mov ax, 0078h ;Transferiir la constante expresada en hex 0078 (Un
inmediato) al registro AX. El operando fuente es el inmediato 0078h,
el operando destino es el registro AX. El modo de direccionamiento
para el operando fuente es inmediato y el modo de direccionamiento
para el operando destino es registro.
mov [2000H], eax ; Transferimos el contenido del registro EAX a la
dirección de memoria cuyo offset es 2000H. El registro de segmento que
el procesador usará para formar la dirección de memoria efectiva será,
probablemente, el registro DS. El operando fuente es EAX y el operando
destino es la dirección de memoria cuyo offset es 2000h. Modo registro
para el operando fuente, modo directo memoria para el operando
destino. La constante 2000H no se conoce como inmediato porque
interviene en la formación de una dirección de memoria y se le llama
desplazamiento.
mov ax, es ;Transferir el contenido del registro de segmento es al
registro ax.
mov gs, bx; Transferir el contenido del registro de segmento bx al
registro gs.
mov fs, bloque1[bx+si]; Esta instrucción carga el registro ds.
Asumiendo que la dirección efectiva del operando fuente se forme con
el registro DS, esta instrucción transfiere el contenido de la
dirección de memoria que tiene offset bloque1+bx+si, calculado en
tiempo de ejecución, al registro fs.
mov datos_de_entrada[si], es; Esta instrucción salva el contenido del
registro de segmento es en la dirección de memoria
datos_de_entrada+si, calculada en tiempo de ejecución.
mov [filtro_notch + ebx + esi * 4], eax; Transferir el contenido del
registro eax a la dirección de memoria cuyo offset se calcula en
tiempo de ejecución sumando el desplazamiento filtro_notch con el
registro ebx con el producto 4 * esi. La constante 4 se le llama
4
Usad esta numeración:
30 de 357
factor de escala y sirve para formar offsets de elementos de arrays
conociendo el índice correspondiente a cada elemento.
Existen otras formas de la instrucción mov en las que se efectúan transferencias de o
desde otros registros del procesador, distintos de los registros de propósito general o
de los registros de segmento. Las transferencias de registros de control o de
depuración a registros de propósito general, han de ser siempre de 32 bits- ocurre lo
mismo con las transferencias de registros de propósito general a registros de control o
a registros de depuración. Veamos algunos ejemplos:
mov edx, cr0; Transferir el contenido del registro de control del
procesador CR0 al registro edx. El registro CR0 contiene 8 flags
(banderas o bits de estado) que determinan el modo de funcionamiento
actual del procesador y las características del proceso (Un programa
en ejecución bajo un sistema operativo multitarea) que está siendo
ejecutado en este instante.
mov cr2, ecx; Transferir el contenido del registro de propósito
general ecx al registro de control CR2. El regsitro CR2 contiene la
dirección lineal que ha provocado el último fallo de página
inexistente. Tiene que ver con el sistema de gestión de memoria
virtual por páginas.
mov dr2, ebx; Transferir el contenido del registro de propósito
general ebx al registro de depuración número 2, cr2.
El juego de instrucciones de IA, contiene otros tipos de transferencias más sofisticadas
desde el punto de vista semántico: Instrucciones que al efectuar la transferencia,
intercambian datos, que extienden la precisión de los datos fuente sobre el operando de
destino o que manipulan la pila. Incluso, existen instrucciones que son capaces de
efectuar una transferencia condicionalmente. Vamos a ver algunos ejemplos sobre
CMOVcc Transferencia condicional (cc es la
condición)
estas últimas.
Las instrucciones de este grupo son muy similares a las instrucciones de salto
condicional: examinan uno o más bits del registro FLAGS y si la condición evaluada
resulta verdadera, efectúan la transferencia indicada mediante sus operandos. Estas
instrucciones sólo se encuentran disponibles en el microprocesador Pentium Pro,
Pentium II y III.
5
Usad esta numeración:
31 de 357
CMOVE/CMOVZ
Transferencia condicional si ha sido igual/cero
cmovz ecx, dword ptr [array + esi * 4 + ebx]; Transferir el contenido
de la dword que reside en la dirección de memoria cuyo offset se
calcula en tiempo de ejecución usando la fórmula dada en el modo de
direccionamiento SISS el flag de ZERO está puesto, i.e., si el
resultado calculado anteriormente fue cero.
CMOVNE/CMOVNZ
CMOVA/
Transferencia condicional si no ha sido igual/zero
Transferencia condicional si no ha sido inferior o igual
CMOVAE/CMOVNB
Transferencia condicional si
or equal/Conditional move if not below
ha estado por encimConditional move if above
cmovae cx, dx; Transferir el contenido de dx a cx SI la última
comparación dio como resultado que su primer operando era mayor o
igual que su segundo operando, considerados ambos sin signo.
CMOVB/CMOVNAE
Conditional move if below/Conditional move if not above or equal
CMOVBE/CMOVNA
Conditional move if below or equal/Conditional move if not above
CMOVG/CMOVNLE
Conditional move if greater/Conditional move if not less or equal
CMOVGE/CMOVNL
Conditional move if greater or equal/Conditional move if not less
CMOVL/CMOVNGE
Conditional move if less/Conditional move if not greater or equal
CMOVLE/CMOVNG
Conditional move if less or equal/Conditional move if not greater
CMOVC
Conditional move if carry
CMOVNC
CMOVO
Conditional move if overflow
CMOVNO
CMOVS
Conditional move if not carry
Conditional move if not overflow
Conditional move if sign (negative)
CMOVNS
Conditional move if not sign (non-negative)
CMOVP/CMOVPE
Conditional move if parity/Conditional move if parity even
CMOVNP/CMOVPO
6
Conditional move if not parity/Conditional move if parity odd
Usad esta numeración:
32 de 357
La pila es una estructura de datos muy conveniente en la programación de
microprocesadores. En la arquitectura del microprocesador MIPS, la pila apenas está
implementada, és el programador el encargado de respetar los convenios de inserción
y de extracción, el uso de un registro reservado como puntero de pila, y el resto de
push Insertar en la pila
pop
Extraer de la pila
aspectos pertinentes. Podría decirse que en la arquitectura del microprocesador MIPS,
la pila se encuentra implementada en un nivel de abstracción superior al de la
arquitectura del set de instrucciones microprocesador y al de su ensamblador. Sin
embargo, en la arquitectura IA, la pila forma parte de la arquitectura del
microprocesador e incluye instrucciones específicas para llevar a cabo inserciones y
extracciones y también incluye el uso transparente de los registros ESP y SS como
marcadores de la parte alta de la pila.
Las instrucciones más básicas de transferencia de datos con la pila son PUSH y POP.
PUSH almacena su operando fuente en el stack (pila) y POP efectúa una extracción del
stack y transfiere el dato extraído a su operando destino. Estas instrucciones sólo
aceptan un operando, que, en el caso de PUSH será operando fuente y que, en el caso
de POP, será operando de destino. El hecho de que sólo acepten un operando explícito
es porque el otro operando involucrado en la transferencia está implícito, es la parte
alta de la pila. Por último, las instrucciones PUSH y POP aceptan como operando
explícito un registro de propósito general, un registro de segmento, el contenido de
una dirección de memoria o un inmediato.
PUSH BX; Transfiere el contenido de BX a la posición más alta de la
pila. El registro de segmento empleado en el direccionamiento de la
pila es SS y el registro de propósito general empleado para formar el
offset de la pila es SP.
POP CX; Extraer la word que se encuentra en la parte más alta de la
pila y cargarla en el registro CX. El registro de segmento empleado en
el direccionamiento de la pila es SS y el registro de propósito
general empleado para formar el offset de la pila es SP.
PUSH EDX; Transfiere el contenido de EDX a la posición más alta de la
pila. El registro de segmento empleado en el direccionamiento de la
pila es SS y el registro de propósito general empleado para formar el
offset de la pila es SP.
7
Usad esta numeración:
33 de 357
POP EAX; Extraer la dword que se encuentra en la parte más alta de la
pila y cargarla en el registro EAX. El registro de segmento empleado
en el direccionamiento de la pila es SS y el registro de propósito
general empleado para formar el offset de la pila es SP.
PUSH [Pares+SI]; Si asumimos que el programa emplea datos de 16 bits,
que es el caso más común, esta instrucción transfiere la WORD que se
encuentra en la dirección de memoria DS:Pares+SI, a la posición más
alta de la pila. El registro de segmento empleado en el
direccionamiento de la pila es SS y el registro de propósito general
empleado para formar el offset de la pila es SP.
PUSH 1f90h; Transferir la WORD 1f90h a la posición más alta de la
pila. El registro de segmento empleado en el direccionamiento de la
pila es SS y el registro de propósito general empleado para formar el
offset de la pila es SP.
PUSH DS; Salvar el contenido del registro de segmento DS en la
posición más alta de la pila. El registro de segmento empleado en el
direccionamiento de la pila es SS y el registro de propósito general
empleado para formar el offset de la pila es SP.
POP EAX; Extraer la dword que se encuentra en la parte más alta de la
pila y cargarla en el registro EAX. El registro de segmento empleado
en el direccionamiento de la pila es SS y el registro de propósito
general empleado para formar el offset de la pila es SP.
pusha
Salvar en la pila los 8 registros de propósito general de 16 bits
popad
Extraer de la pila los 8 registros de propósito general de 16 bits
pushad
Salvar en la pila los 8 registros de propósito general de 32 bits
popad
Extraer de la pila los 8 registros de propósito general de 32 bits
Las instrucciones PUSHA y POPA transfieren el conjunto de registros de propósito
general en una sola operación. De forma similar a PUSH y a POP, PUSHA inserta todos
8
Usad esta numeración:
34 de 357
los registros de propósito general en la pila y, POPA transfiere las ocho WORDS que se
encuentran en la parte más alta de la pila, a los ocho registros de propósito general. Las
instrucciones PUSHAD y POPAD, efectúan las mismas transferencias que las
anteriores, pero usando los registros de propósito general de ancho completo de 32
bits.
PUSHA; Salva en la pila los ocho registros de propósito general de 16
bits. El registro de segmento empleado en el direccionamiento de la
pila es SS y el registro de propósito general empleado para formar el
offset de la pila es SP.
PUSHAD; Salva en la pila los ocho registros de propósito general de 32
bits. El registro de segmento empleado en el direccionamiento de la
pila es SS y el registro de propósito general empleado para formar el
offset de la pila es SP.
POPA; Carga los ocho registros de propósito general con las ocho WORDS
que se encuentran en la parte más alta de la pila. El registro de
segmento empleado en el direccionamiento de la pila es SS y el
registro de propósito general empleado para formar el offset de la
pila es SP.
POPAD; Carga los ocho registros de propósito general con las
DWORDS que se encuentran en la parte más alta de la pila. El
de segmento empleado en el direccionamiento de la pila es SS
registro de propósito general empleado para formar el offset
pila es SP.
ocho
registro
y el
de la
Las instrucciones MOVZX y MOVSX transfieren el contenido de un registro o memoria
a su operando de destino, el cual, en este caso, sólo podrá ser un registro. El operando
de destino debe ser de tamaño superior al operando fuente. La instrucción efectúa el
relleno de los bits que faltan, en el caso de MOVZX, el relleno se hace con ceros y, en el
caso de MOVSX, el relleno se hace con el bit de signo del operando fuente.
movsx ax, cl; Transferir cl a al y rellenar la parte alta de ax, es
movsx Transferir y extender en signo
movzx Transferir y extender con ceros
decir ah, con el bit de signo de cl. Si este bit de signo es un 0, ah
se rellenará con todo ceros (0); si el bit de signo de cl es un 1, ah
9
Usad esta numeración:
35 de 357
se rellenará con todos unos (1). El dato contenido en cl, representado
con una precisión de 8 bits, se ha extendido a 16 bits y, el
resultado, se ha almacenado en ax.
movzx edx, byte ptr es:[muestras+di]; Transferir el byte que está
almacenado en la dirección de memoria es:muestras+si a la parte baja
de edx, los tres bytes de peso superior del registro edx, se
rellenarán con ceros. En este ejemplo hemos formado la dirección de
memoria del operando fuente indicando explícitamente el registro de
segmento a emplear (es), si no lo hubiéramos indicado así,
probablemente la dirección efectiva se habría formado usando el
registro DS.
movsx eax, word ptr 1280h:[coeficientes+bp]; Transferir la word que
reside en la dirección de memoria efectiva 1280h:coeficientes+bp, al
registro eax, la parte alta de este registro se extiende en signo. En
este ejemplo hemos formado la dirección de memoria del operando fuente
in Transferir desde un puerto de e/s
out Transferir a un puerto de e/s
indicando explícitamente la dirección base de segmento a emplear
(1280h, 16 bits). La dirección efectiva, evaluada en tiempo de
ejecución, sería: 1280hx10h+coeficientes+bp.
Las instrucciones IN y OUT sirven para leer y escribir en puertos -direcciones de
memoria que están asociadas a dispositivos de entrada/salida. Estas direcciones
pertenecen a un espacio de direccionamiento que, en la arquitectura IA, está separado
del espacio de memoria. Los registros pertenecientes a dispositivos de e/s, suelen
proyectarse en este espacio de direcciones de e/s. La arquitectura IA, incorpora dos
instrucciones que transfieren datos desde una dirección de puerto de e/s a un registro
de propósito general y viceversa. Las dos instrucciones de transferencias de e/s
básicas son in y out.
El espacio de direcciones, como estudiaremos en el capítulo dedicado a las entradas y
salidas, se extiende a lo largo de 64Kb. Ocupa, por tanto, un solo segmento físico de
memoria, que, en el espacio de entradas y salidas tiene por dirección base de segmento
0000h. Es decir, las direcciones de e/s no están segmentadas, por tanto, en la formación
de las direcciones físicas de e/s no interviene ningún registro de segmento. Por último,
debemos establecer, que las direcciones de e/s se suelen denominar puertos de e/s o
10
Usad esta numeración:
36 de 357
simplemente puertos y que en esos puertos suelen proyectarse registros de algún
dispositivo o posiciones de memoria privada de algún dispositivo.
in al, 67h; Transferir el byte que reside en el puerto 67h al registro
al. La dirección de puerto puede expresarse como constante literal si
se puede representar con 8 bits.
in ax, 0ffh; Transferir la word que reside en el puerto ffh al
registro ax. La dirección de puerto puede expresarse como constante
literal si se puede representar con 8 bits.
in eax, 10h; Transferir la dword que reside en el puerto 10h al
registro eax. La dirección de puerto puede expresarse como constante
literal si se puede representar con 8 bits.
in ecx, dx; Transferir la dword que reside en el puerto cuyo número
está albergado en dx, al registro ecx. La dirección de puerto en este
caso puede acceder a cualquiera de las 64K direcciones de puerto
posibles en el espacio de e/s ya que dx tiene un ancho de 16 bits.
xchg Intercambiar los operandos entre sí
atómicamente.
La instrucción XCHG intercambia sus operandos. Los operandos pueden ser registros
o memoria aunque ambos no pueden ser memoria. En el caso de que uno de los
operandos sea memoria, el bus se bloquea para impedir que otros bus masters puedan
solicitar el bus al procesador, de este modo, esta instrucción puede emplearse para
implementar semáforos y producir procesos atómicos de lectura-modificaciónescritura que garanticen la actualización de variables compartidas entre varios hilos de
ejecución o entre varios procesos, de forma fiable.
xchg bx, fs:[locks4+si]; Intercambiar entre sí el contenido del
registro bx con la posición de memoria fsx10h+locks4+si. Puesto que el
registro involucrado es de 16 bits, implícitamente, los datos
intercambiados son de ancho 16 bits, words.
xchg eax, ecx; Intercambiar los contenidos de los registros de
propósito general eax y ecx.
bs11wap Byte swap
Usad esta numeración:
37 de 357
Esta instrucción produce un intercambio de bytes intra-registro. Los intercambios
producidos por xchg eran inter-registro. La utilidad principal de esta instrucción es el
cambio de ordenamiento de bytes en datos multi-byte, esto es, el cambio de
ordenamiento extremista inferior (little endian) a extremista superior (big endian) y
viceversa.
bswap ecx; Cambiar el orden de los 4 bytes del registro ecx
xadd Intercambiar y sumar
Esta instrucción en primer lugar evalúa la suma de sus dos operandos, después salva
el operando destino en el fuente y por último salva la suma en el operando de fuente.
Se emplea en sistemas multiprocesador para permitir que varios procesadores puedan
ejecutar un mismo bucle.
xadd [semaf+di], ax; Sumar el contenido de la dirección de memoria
dsx10h+semaf+di con el contenido del registro ax, salvar el resultado
en el contenido de la dirección de memoria dsx10h+semaf+di en ax y por
último, salvar la suma en la contenido de la dirección de memoria
dsx10h+semaf+di.
xadd edx, eax; Sumar edx+eax, salvar edx en eax y por último salvar la
suma edx+eax en eax.
CMPXCHG
Comparar e intercambiar condicionalmente
CMPXCHG8B
Comparar e intercambiar condicionalmente 8 bytes
Al igual que las anteriores, estas instrucciones se emplean en sistemas multiprocesador
para implementar primitivas de sincronización. La instrucción cmpxchg compara al, ax
o eax con su operando de destino (dependiendo del ancho indicado para éste último),
si resultan iguales, el operando fuente es copiado al operando de destino; en otro caso,
12
Usad esta numeración:
38 de 357
el registro al, ax o eax (según el caso) se carga con el valor contenido en el operando de
destino.
cmpxchg [sincro3+si], cx; Comparar el contenido de la word residente
en la dirección de memoria dsx10h+sincro3+si con ax, si son iguales,
se copia cx a la dirección de memoria (word) dsx10h+sincro3+si, en
otro caso se copia contenido de la dirección de memoria
dsx10h+sincro3+si al registro ax.
cmpxchg [shared_pool+bp], edx; Comparar la dword residente en la
dirección de memoria ssx10h+sincro3+si con eax, si son iguales, se
copia cx a la dirección de memoria (word) dsx10h+sincro3+si, en otro
caso se copia contenido de la dirección de memoria dsx10h+sincro3+si
al registro eax. Hagamos notar que la dirección de memoria efectiva
correspondiente al operando de destino se forma con el registro ss
puesto que para la formación del offset se ha usado bp.
CWD/CDQ Convertir word en dword, dword en quad-word CBW/CWDE
Convertir byte en word, word en dword en EAX
Estas instrucciones transfieren y convierten en la misma operación. Se emplean en
sistema multiprocesador.
cwd; Extender ax en signo y, el resultado de 32 bits, almacenarlo en
dx:ax.
cdq; Extender eax en signo y, el resultado de 64 bits, almacenarlo en
edx:eax.
cbw; Extender al en signo y cargar el resultado de 16 bits en ax.
cwde; Extender ax en signo y cargar el resultado de 32 bits en eax.
Instrucciones de a ritmética bina ria .
A diferencia del microprocesador MIPS cuya arquitectura es del tipo RISC en la cual
los operandos de la unidad aritmético-lógica han de cargarse -todos ellos- en registros
del fichero de registros (Máquinas load/store), los operandos de la ALU en la
arquitectura IA, pueden residir en un registro de propósito general, inmersos en la
13
Usad esta numeración:
39 de 357
representación binaria de la instrucción (inmediato) o en memoria. Las combinaciones
de operandos aceptables en este procesador son las siguientes:
1. Registro, Registro.
2. Memoria, Registro.
3. Registro, Memoria.
4. Registro, Inmediato.
5. Memoria, Inmediato.
add/sub
Sumar/restar
adc/sbb
Sumar operandos mas carry/Restar operandos menos carry
La arquitectura IA emplea un a lógica de dos direcciones para las instrucciones
aritmético-lógicas. Esto significa que, en este tipo de instrucciones, solamente pueden
especificarse dos operandos explícitos. Uno de ellos será operando fuente-1 y el otro
será, al mismo tiempo, operando fuente-2 y operando de destino. En la arquitectura
del microprocesador MIPS era más simple puesto que los tres operandos naturales de
una operación aritmética simple, podían especificarse los tres junto con el mnemónico
de la instrucción. Veamos algunos ejemplos:
add al, 8
almacenar
emplea el
se emplea
; al <-- al + 8. Sumar el inmediato 8 al contenido de al y
el resultado en el registro al. Para el operando fuente-1 se
modo inmediato, para el operando fuente-2 y operando destino
el modo registro.
sub eax, ecx ; eax <-- eax - ecx. Efectuar la eax-ecx y almacenar el
resultado en eax. Los modos de direccionamiento empleados son modo
registro en todos los casos.
add es:[nodos4+di], 1000 ;Sumar la constante 1000 al entero (WORD) que
reside en la dirección efectiva es:nodos4+di.
14
Usad esta numeración:
40 de 357
addc dx, [si+di];Sumar el valor del bit de carry (0 o 1) al contenido
de la dirección de memoria efectiva ds:si+di y al registro dx y cargar
el resultado de la suma en dx. Adc se emplea para sumar números
enteros, con o sin signo, cuya precisión excede el ancho de los
registros empleados. La suma se hace por “etapas” y el carry obtenido
en cada una de ellas se añade en la siguiente gracias a adc.
inc
Incrementar en 1.
dec
Restar 1.
La instrucción inc suma 1 a su operando sin signo. La instrucción dec, resta 1 a su
operando sin signo. Ambas instrucciones se suelen emplear para implementar
contadores. Sólo aceptan un operando, registro o memoria, que es al mismo tiempo
operando fuente y operando de destino.
inc word ptr [0ff00h] ; Incrementar en una unidad el dato de 16 bits
(word) que se encuentra en la dirección de memoria cuyo offset es
ff00h en el segmento físico cuya dirección base está cargada en el
registro de segmento ds -probablemente. En este caso, el operando
fuente y destino está especificado con modo de direccionamiento
directo memoria.
dec ax ; Restar 1 al registro ax. Cargar ax con el resultado de la
cmp
Comparar y reflejar el resultado en los flags
resta.
Esta instrucción se emplea para comparar dos operandos aritméticamente mediante su
diferencia. Las reglas para la especificación de operandos son las mismas que afectan
al resto de instrucciones aritméticas. El resultado obtenido en esta operación, no se
almacena en ningún registro, por tanto, ninguno de los dos operandos es operando
destino. La utilidad de la instrucción reside en el hecho de que, una vez realizada la
diferencia, la instrucción afecta a ciertos bits del registro FLAGS, como por ejemplo, el
bit zero. Si este bit presenta un 1 después de ejecutar una instrucción cmp, eso significa
que la diferencia de los dos operandos fue cero, o sea, que ambos operandos eran
iguales. Del mismo modo, la activación de otros bits como el bit de carry y de
15
Usad esta numeración:
41 de 357
overflow, pueden servir para efectuar comparaciones de magnitud, con signo y sin
signo.
El set de instrucciones de IA, contiene instrucciones que nos permiten explorar el
estado de los bits de FLAGS mencionados en el párrafo anterior. Por tanto,
examinando ciertos bits de FLAGS podemos establecer programáticamente si un
entero con signo es mayor o gual que otro. Existen, no obstante, otras instrucciones que
son capaces de explorar varios bits de FLAGS simultáneamente y establecer
condiciones mixtas (mayor o igual, menor o igual) en aritmética con signo y sin signo,
con lo cual, el programador no tiene necesariamente que decidir qué bits explorar en
cada circunstancia concreta. El programador, conociendo la lógica del programa, sólo
ha de seleccionar las instrucciones adecuadas que efectúen un salto condicional
dependiendo del estado de uno o más bits de FLAGS. Estudiaremos estas
instrucciones en un apartado posterior.
cmp word ptr 6623h:[bx], 1 ; Restar 1 de la word que reside en la
dirección de memoria cuya dirección base de segmento físico es 66230h
y cuyo offset es bx. Después de efectuar la resta, la instrucción va a
afectar a los bits de signo, cero, overflow y carry de FLAGS, de esta
forma el programador puede seleccionar el salto condicional adecuado.
Éste podrá ser salto con signo o sin signo, de magnitud, de igualdad o
de ambas cosas.
cmp eax, ebx; Hacer eax-ebx. Actualizar los flags zero, sign, overflow
y carry dependiendo de la naturaleza del resultado de la resta.
Esta instrucción se usa para cambiar el signo de un entero con signo. Acepta un único
operando que es, al mismo tiempo, operando fuente y operando destino. Este
operando puede ser registro o memoria.
mul/imul
Multiplicación entera sin signo/con signo
División entera sin signo/con signo
div/idiv
neg word ptr [bx] ; Obtener el complemento a 2 del operando entero con
signo (word) que reside en la dirección de memoria cuyo segmento es –
probablemente- ds y cuyo offset está almacenado en el registro bx.
Las instrucciones de multipliación forman resultados que son el doble de amplios que
los operandos fuente. Si, por ejemplo, se multiplican dos words, el resultado
16
Usad esta numeración:
42 de 357
producido será del tipo dword. Las instrucciones de división forman el cociente y el
resto de la misma.
imul eax, dword ptr [di]; Efectuar la multiplicación de los enteros con
signo residentes en eax y en la dirección de memoria efectiva ds:di,
cargar el resultado en eax.
imul cx, ax, 0f5h; Extender en signo el inmediato de 8 bits 0f5h y
multiplicarlo por ax, el resultado se cargará en cx.
mul dl; Efectuar la multiplicación sin signo al*dl y cargar el
resultado en el registro ax.
mul dx; Efectuar la multiplicación sin signo ax*dx y cargar el
resultado en el par de registros dx:ax.
mul edx; Efectuar la multiplicación sin signo eax*edx y cargar el
resultado en el par de registros edx:eax.
div byte ptr [si]; Efectuar la división sin signo del registro ax
entre el contenido de la dirección de memoria cuyo segmento es ds y
cuyo offset está cargado en si. El cociente se cargará en al, el resto
se cargará en al.
div bx; Efectuar la división sin signo del dato residente en los
registros dx:ax entre el dato cargado en bx. El cociente se cargará en
ax, el resto en dx.
Instrucciones de a ritmética decima l.
La arquitectura IA contiene un grupo especial de instrucciones que son capaces de
efectuar operaciones con datos en formato BCD. El formato BCD representa cada uno
los dígitos 0-9 con un solo nibble (4 bits). Este formato se emplea en aritmética entera
decimal donde su uso representa de forma natural el tipo de dato que se va a manejar
en un problema concreto como, por ejemplo, en visualización de información a través
de displays. Este formato se emplea, también, en ciertos problemas de representación
de información en los que se requiere un cierto grado de redundancia, como por
ejemplo en informática comercial.
La arquitectura IA es capaz de usar las mismas instrucciones de suma, resta,
multiplicación y división binarias con datos en BCD gracias a que IA contiene otras
instrucciones que son capaces de ajustar los resultados al formato BCD. Por ejemplo,
después de efectuar la suma de dos dígitos BCD, el posible carry, se producirá cuando
la suma sea superior a 9, no a 15 como sería el caso si la suma fuese binaria, no
decimal.
17
Usad esta numeración:
43 de 357
Los computadores se suelen diseñar de forma que se permita el acceso a bytes
individuales en memoria, de forma que, en realidad cada byte en memoria posee su
propia dirección de byte y cuando es necesario acceder a un dato multi-byte, es el
sistema el encargado de generar los ciclos de bus necesarios, los cuales, debidamente
concatenados, producirán el efecto deseado. El nibble que representa un dígito BCD
puede salvarse en una sola dirección de memoria pero, también, pueden salvarse dos
nibbles en cada byte, en cada dirección de memoria. A este último formato en que se
salvan dos dígitos BCD en cada dirección de memoria, se le conoce como formato BCD
daa/das
Ajustes decimales
aaa/aas/aam/aad/
Ajustes ASCII
empaquetado y la arquitectura IA contiene instrucciones que aceptan este formato,
también.
DAA; Ajuste decimal de AL después de una suma.
DAS; Ajuste decimal de AL después de una resta.
AAA; Ajuste ASCII de AL después de una suma. Se supone BCD no empaquetado.
AAS; Ajuste ASCII después de una resta. El operando fuente y destino implícito es AL.
AAM; Ajuste ASCII de AL después de una multiplicación. Se supone BCD no
empaquetado.
AAD; Ajuste ASCII de AL después de una división. Se supone BCD no empaquetado.
Instrucciones lógica s, de despla za miento y rota ción.
En este grupo se engloban las instrucciones AND (AND lógico bit a bit de sus
operandos), OR, XOR, NOT (negación o complemento a 1). Estas instrucciones
someten a cada par de bits de sus operandos con el mismo índice, a la operación lógica
que correspondiente. La formación de los operandos sigue las mismas reglas generales
And/xor/not/or
de la arquitectura IA.
18
Operaciones lógicas bit-a-bit
Usad esta numeración:
44 de 357
or cl, dl ; Efectuar un OR lógico del contenido de los registros CL y
DL y almacenar el resultado en el registro CL. Operando fuente-1 es
DL, operando fuente-2 es CL (El cual también es según la notación
estudiada operando destino).
xor dx, [bx]; Efectuar un XOR lógico del dato contenido en la
dirección de memoria cuyo segmento es ds y cuyo offset es bx, con el
dato contenido en el registro dx. Operando fuente-1 es la dirección de
memoria almacenada en el registro bx, operando fuente-2 es el registro
DX (también es operando destino). Los modos de direccionamiento usados
en este caso son el modo indirecto-registro para el operando fuente y
modo registro para el operando fuente-2 (destino también).
and byte ptr [di+bx], 0a8h; Efectuar el and bit a bit del contenido
(byte) de la dirección de memoria cuyo segmento es –probablemente- ds
y cuyo offset es di+si, con el inmediato de ocho bits a8h.
not dword ptr [mascara3]; Cambiar 0’s por 1’s y 1’s por 0’s de la
doble word (dword) residente en la dirección de memoria ds:mascara3.
Instrucciones de despla za miento y rota ción de bits.
Este grupo de instrucciones efectúa desplazamientos y rotaciones de los bits de su
operando. Ambos, desplazamientos y rotaciones, pueden ser a la izquierda o a la
derecha. También, en ambos casos, el bit que abandona el operando puede capturarse
en el bit de carry o no. Por último, el bit que entra en el operando puede obtenerse del
bit de carry, también.
A las instrucciones que desplazan a la izquierda se les llama SAL y SHL. Ambas
desplazan su operando de destino un cierto número de lugares a la izquierda. Sirven,
sal,shl
Desplazamientos aritmético y lógico a la izquierda
pues, para multiplicar un dato por un potencia entera de 2.
sal word ptr [mascara2], cl; Desplazar a la izquierda la word que
reside en la dirección de memoria ds:mascara2 el número de lugares
indicado en cl, salvar el resultado en la misma dirección de memoria.
El bit saliente se almacena en el bit cy de FLAGS- carry. El bit
entrante es un cero.
19
sar,shr
Desplazamientos aritmético y lógico a la derecha
Usad esta numeración:
45 de 357
shr edx, cl; Desplazar el número de lugares indicado por cl la dword
que reside en edx a la derecha, cargar el resultado en edx. El bit
saliente se almacena en el bit cy de FLAGS- carry. El bit entrante es
un cero.
sar bx, 3; Desplazar 3 lugares a la derecha la word que reside en el
registro bx. Nótese que al efectuar el desplazamiento a la derecha, en
este caso, por ser un desplazamiento aritmético, el bit de signo
aparecerá en el propio bit de signo del resultado y en los dos lugares
anteriores. Es decir, el resultado, representa la división por 23 del
operando fuente y el resultado mantiene el signo del dividendo, puesto
que el divisor es positivo. El bit entrante es siempre el bit de signo
del operando fuente.
ror, rol
Rotaciones a la derecha y a la izquierda
ror edx, cl; Rotar el registro edx a la derecha el número de lugares
indicado por cl y cargar el resultado en edx. El bit saliente se
almacena en el bit cy de FLAGS- carry y és además el bit entrante.
rol bx, 3; Rotar el registro bx a la izquierda 3, cargar el resultado
en bx. El bit saliente se almacena en el bit cy de FLAGS- carry y és,
además, el bit entrante.
rcr, rcl
Rotaciones a la derecha y a la izquierda a través del carry
rcr dh, cl; Rotar el registro dh a la derecha el número de lugares
indicado por cl y cargar el resultado en edx. El bit entrante es el
bit de carry.El bit saliente se almacena en el bit cy de FLAGS- carry.
rcl bx, 3; Rotar el registro bx a la izquierda 3 lugares, cargar el
resultado en bx. El bit entrante es el bit de carry.El bit saliente se
almacena en el bit cy de FLAGS.
20
Usad esta numeración:
46 de 357
shld, shrd
Desplazamientos dobles.
shld [string1], ax, 4; Desplazar la word que reside en ds:[string1] 4
lugares a la izquierda, a medida que se efectúa el desplazamiento,
introducir por la derecha bits que salen por la izquierda de ax.
shrd eax, ebx, cl; Rotar eax cl lugares a la derecha, rellenando por
la izquierda con bits que salen de ebx por la derecha.
Instrucciones de ma nipula ción de bytes y bits.
Estas instrucciones pueden leer cualquier bit particular de cualquier registro o de
cualquier dirección de memoria y examinar su valor almacenándolo en el bit CF de
EFLAGS o incluso modificarlo, poniéndolo (1) o borrándolo (0) o complementándolo.
Otras instrucciones que se encuentran en este grupo pueden buscar el bit mas
significativo o menos significativo de un dato de 16 o 32 bits (una dirección de
memoria o un registro), esas instrucciones son BSF (Bit scan forward) y BSR (Bit scan
reverse).
bsf, bsr
Determinar el índice del primer bit puesto a 1.
bsf esi, eax; Buscar de derecha a izquierda un 1 en eax, salvar el
índice de ese primer 1 en el registro esi.
bsr di, word ptr[bx]; Buscar de izquierda a derecha un 1 en la word
que reside en la dirección de memoria ds:bx, salvar el índice de ese
primer 1 en el registro di.
setcc
falsa.
21
Poner a 1 un byte dependiendo de que la condición expresada por
cc
sea verdadera o
Usad esta numeración:
47 de 357
setnge ch; Poner el byte cargado en ch a 1 si la condición “not
greater or equal” es verdadera. Es decir, poner el byte a 1 si el
estado actual de los flags indica que la condición no mayor o igual,
evaluada con signo, es verdadera. En otro caso forzar un 0.
setnle [bp]; Poner el byte presente en la dirección de memoria ss:bp
a 1 si el estado actual de los flags indica que la condición “no menor
o igual”, evaluada sin signo, es verdadera. En toro caso forzar un 0.
test
Evaluar la función AND y afectar flags.
test ax, bx; Esta instrucción se emplea de forma similar a la
instrucción cmp con la diferencia de que en este caso la operación
efectuada es un and lógico, no una resta. Al igual que cmp, el
resultado no se salva, sólo se ven afectados los bits de zero, sign y
parity de FLAGS. Se suele emplear en conjunción con instrucciones de
salto condicional, de puesta a 1 condicional o de transferencia
condicional.
Instrucciones de tra nsferencia de control o instrucciones de sa lto.
Las instrucciones de salto se emplean para romper el flujo secuencial de ejecución. Un
programa está formado básicamente por un conjunto de instrucciones que el
procesador ejecuta una a una. Las instrucciones se ejecutan en el mismo orden
temporal en el que están dispuestas en memoria. Esto es cierto excepto cuando el
procesador encuentra una instrucción de salto, en ese caso es posible que no se ejecute
la siguiente instrucción si no otra instrucción cuya dirección de memoria se especifica
jmp
Salto incondicional, relativo o absoluto, intra o inter-segmento.
en la propia instrucción de salto.
JMP 0100h ; Saltar incondicionalmente a la dirección de memoria cuyo
offset es 0100h calculado a partir del valor actual del registro IP
dentro del segmento actual. El segmento actual es aquel cuya dirección
base está cargada en el registro de segmento de código, CS.
22
Usad esta numeración:
48 de 357
jmp bx ; Saltar incondicionalmente a la dirección de memoria cuyo
offset está almacenado en el registro bx. La dirección de destino se
encontrará en el mismo segmento en el que se encuentra la propia
instrucción.
En la arquitectura IA, los saltos incondicionales pueden ser relativos o absolutos. Los
saltos relativos son aquellos en los que la dirección de destino se calcula sumando un
desplazamiento al valor actual contenido en el registro ip. Este desplazamiento es la
constante que se especifica junto con la instrucción y, por supuesto, estos saltos son
intra-segmento. Recordemos, por último, que la dirección base del segmento físico de
memoria donde tiene lugar el salto, está siempre cargada en el registro de segmento
CS.
Los saltos incondicionales absolutos son aquellos en los que la dirección de destino
viene incluida dentro de la representación binaria de la instrucción. En la IA los saltos
absolutos toman dos formas:
La dirección de salto está dentro del segmento actual (cs), por tanto, se representa sólo
mediante un offset, el cual, se carga en un registro. La instrucción incluye este nombre
de registro.
Una dirección física de destino completa especificada usando los modos de
direccionamiento normales de la arquitectura. La dirección de destino, en este caso,
puede ser near (intra segmento, sólo el offset) o far (inter-segmento, se especifica el
segmento y el offset de la dirección de destino). En el último caso, el efecto producido
por el salto incondicional consiste en que el registro CS y el registro IP se cargan con
los nuevos valores correspondientes a la dirección de destino. En los saltos intrasegmento, sólo se carg el IP y, por tanto, se mantiene el valor del CS, por lo que
decimos que el salto tiene lugar dentro del mismo segmento físico de memoria donde
estaba teniendo lugar la ejecución del programa.
jmp word ptr
memoria cuyo
La dirección
se encuentra
[bx] ; Saltar incondicionalmente a la dirección de
offset está almacenado en la dirección de memoria ds:bx.
de destino se encontrará en el mismo segmento en el que
la propia instrucción jmp.
jmp dword ptr [bx] ; Saltar incondicionalmente a la dirección de
memoria cuyo offset y segmento están almacenados a partir de la
dirección de memoria ds:bx. La dirección de destino en este caso, no
se encontrará, en general, en el mismo segmento en el que se encuentra
la instrucción jmp.
jmp 2000h:0550h; Saltar incondicionalmente a la dirección de memoria
2000x10h+0550h = 25500h. Un salto inter-segmento.
23
Usad esta numeración:
49 de 357
jcc
Salto condicional relativo, intra-segmento.
Las instrucciones de salto condicional se emplean para producir saltos sólo en caso de
que ciertos bits de EFLAGS estén puestos a 1 o, en su caso, a 0. Por tanto, el estado de
algún o algunos bits del registro EFLAGS es el criterio que emplea el procesador para
decidir si se toma el salto o no. Cuando el procesador ejecuta una de estas
instrucciones, examina el/los bits asociados con el mnemónico usado en la instrucción
y, si el estado de ese o esos bits, hace que la condición expresada por el mnemónico
sea verdadera, entonces tiene lugar el salto. En otro caso, la ejecución continúa en la
instrucción que ocupa la dirección de memoria siguiente.
En esta arquitectura, los saltos condicionales son siempre relativos al registro IP, son
por tanto, saltos intra-segmento. Si, en algún caso, se necesita producir un salto
condicional inter-segmento, será necesario enviar condicionalmente la ejecución a una
dirección de memoria intra-segmento donde se habrá albergado previamente una
instrucción de salto inter-segmento.
El ejemplo siguiente es una secuencia de instrucciones, un programa: Si la resta ax-bx
resulta distinta de cero, entonces la ejecución de JNZ produce un salto a la dirección
1000h relativa al contador de programa (ip), si no, el procesador continúa ejecutando la
siguiente instrucción: add cx, dx.
sub ax, bx; Efectuar axßax-bx.
jnz 1000h ; Saltar a la dirección 1000h (calculada a partir del estado
actual de IP) si el resultado de la operación anterior NO fue CERO
(jump non-zero). En caso contrario, continuar ejecutando la
instrucción siguiente(En el ejemplo add cx, dx). Presuponemos que a
partir de la dirección 1000h, dentro del segmento actual (cs), hay un
programa.
add cx, dx; Una instrucción cualquiera.
... ; Otras instrucciones de nuestro programa
... ; ...
24
Usad esta numeración:
50 de 357
... ; ...
Rango de las instrucciones de salto condicional.
Los saltos condicionales son relativos, esto significa que la instrucción viene
acompañada de un desplazamiento con signo que se suma al valor del IP en el momento
de la ejecución de la instrucción. Si el valor del desplazamiento no es superior a +127 o
menor que –128, puede representarse con 8 bits y, entonces, la representación binaria
de la instrucción de salto condicional queda más compacta. Si la dirección de destino a
la que se desea saltar en caso de que la condición sea verdadera en tiempo de
ejecución está más allá de –128/+127 bytes desde la instrucción, será necesario
codificar el desplazamiento empleando 32 bits. Los 16 bits superiores de los
desplazamientos de 32 bits están puestos a cero, por tanto, pueden llegar a –
32768/+32767 bytes contados a partir de la dirección de memoria donde se encuentra
la instrucción actual.
Concluyendo, con desplazamientos de 32 bits, donde el rango eficaz es de 16 bits,
podemos alcanzar cualquier dirección de memoria perteneciente al segmento actual.
Implementación de los saltos condicionales.
Para ejecutar un salto condicional el procesador explora el estado de uno o mas bits
del registro EFLAGS, dependiendo estos bits concretos del mnemónico de la
instrucción concreta usada. Si las condiciones del salto (expresadas como combinación
de uno o mas bits de EFLAGS) son verdaderas en el momento en el que se ejecuta la
instrucción, el procesador carga el registro EIP (Extended Instruction Pointer) con el
valor de la dirección de salto. En ese caso, la instrucción que se ejecutaría después del
salto sería la que se encuentre en la dirección de salto. Si por el contrario, la condición
de salto resulta falsa, no se altera EIP, i.e. la ejecución continúa en la siguiente
instrucción a la instrucción de salto.
Las condiciones que se pueden formar sobre la base del estado de los bits de EFLAGS,
pueden ser de naturaleza muy diversa. En principio clasificaremos los saltos
condicionales del siguiente modo:1
1. Saltos con signo. En este caso estamos suponiendo que los datos que manejamos
tienen signo y por tanto las comparaciones deben hacerse de acuerdo con lo
anterior.
2. Saltos sin signo. En este caso los datos que estamos manejando suponemos que no
tienen signo y por tanto las comparaciones solamente tendrán en cuenta la
magnitud de los datos.
25
Usad esta numeración:
51 de 357
3. Saltos que dependen de UN bit particular de EFLAGS.
Una pregunta que surge por sí sola es ¿Qué instrucciones afectan al registro EFLAGS?
Es decir, si tenemos instrucciones que tienen en cuenta algún bit o combinaciones de
bits de EFLAGS para efectuar saltos ¿Cuales son las instrucciones que en vez de
explorar EFLAGS lo alteran? En principio nos quedaremos con las siguientes ideas:
1. Ninguna instrucción de transferencia de datos afecta a EFLAGS.
2. La s instrucciones de sa lto NO a ltera n los bits de EFLAGS.
3. En general las instrucciones aritméticas SI afectan a EFLAGS( ADD, SUB, etc.)
4. Las instrucciones CMP y TEST sí afectan a EFLAGS. De hecho la mayoría de las
veces CMP será la instrucción anterior a un salto condicional.
La instrucción CMP - Compare.
La condición de salto muy frecuentemente se establece mediante la ejecución previa de
una instrucción CMP: Comparamos dos operandos restándolos y entonces
establecemos un salto condicional a una determinada dirección, dependiendo de la
naturaleza del resultado.
El formato de la instrucción es:
CMP dest, fuente; La operación que se lleva a cabo es: dest-fuente y
se actualizan los bits zero, sign, carry y overflow de acuerdo con la
naturaleza del resultado y los operandos. No se produce resultado
alguno salvo la actualización de FLAGS.
CMP dest, fuente
comparar dest con fuente haciendo dest-fuente.
Debemos insistir en que el resultado de la operación no se almacena en ningún registro.
Por ejemplo, si el resultado ha sido cero entonces se activa el bit ZF de EFLAGS. Si
hubo TOMA en la diferencia se activa el bit CF, etc. Es decir CMP resta los operandos y
en base al carácter del resultado activa unos bits u otros del registro EFLAGS. Veamos un
ejemplo:
CMP AX, BX
JG 10F0h
26
Usad esta numeración:
52 de 357
Supongamos que AX=7FFFh, BX=0001. El resultado de CMP AX, BX es 7FFFh - 0001h
= 7FFEh. Este resultado ¿Cómo afecta a EFLAGS? Vayamos bit por bit:
* ZF = 0, OF =0 y SF =0.
* CF = 0. Este bit en este caso se le llama TOMA no CARRY según veremos mas
adelante. La razón es que la instrucción CMP efectúa una resta en cuyo caso al bit de
CARRY le llamamos toma y se cumple que CARRY = NOT(TOMA).
La instrucción JG (Jump if Greater) es un salto con signo. Es decir se supone que los
operandos usados en la comparación tienen signo. ¿AX > BX? En este caso sí, por tanto,
el salto a la dirección 10F0h sí tendría lugar en este caso. ¿Cómo ha determinado el
procesador este hecho? Simplemente ha comprobado que ZF=0 y que SF = OF.
Clasificación de los saltos condicionales.
En la tabla adjunta aparece la clasificación de los saltos condicionales de la
arquitectura IA.
SALTOS CON SIGNO
Nemónico Descripción.
Condiciones para
el salto.
JG
Salta si es mayor.
ZF=0 y SF = OF.
JGE
Salta si es mayor o igual
SF=OF
JL
Salta si es menor.
SF<>OF
JLE
Salta si es menor o igual
ZF =1 o SF <>OF
SALTOS SIN SIGNO
Nemónico Descripción.
Condiciones para
el salto.
JA
Salta si es superior.
CF = 0 y ZF =0
JAE
Salta si es superior o igual.
CF = 0
JB
Salta si es inferior.
CF = 1
JBE
Salta si es inferior o igual.
CF = 1 o ZF = 1
SALTOS SOBRE BITS PARTICULARES DE
EFLAGS
JE/JZ
27
Salta si son iguales.
ZF = 1
JNE/JNZ Salta si no son iguales.
ZF = 0
JC
Salta si CF=1
CF=1.
JNC
Salta si no hubo carry.
CF=0
Usad esta numeración:
53 de 357
JO
Salta si hubo overflow en aritmética con signo.
OF = 1
JNO
Salta si no hubo overflow en aritmética con signo. OF = 0
JS
Salta si el signo fue negativo.
SF = 1
JNS
Salta si el signo no fue negativo.
SF = 0
JP
Salta si la paridad fue par.
PF = 1
JNP
Salta si la paridad fue impar.
PF = 0.
Saltos condicionales con signo y sin signo.
En la clasificación de las instrucciones de salto condicional podemos observar que cada
uno de los saltos con signo tiene un salto sin signo análogo; por ejemplo, el salto con
signo JG y el salto sin signo JA. ¿De qué depende que usemos uno u otro tipo?
Depende de la interpretación que hayamos asignado a los datos que está manejando nuestro
programa. Si estos datos los consideramos con signo, entonces los saltos condicionales
que tenemos que emplear tienen que ser con signo. Del mismo modo, si los datos que
estamos manejando los consideramos sin signo, los saltos condicionales han de ser
también sin signo.
Ejemplo. Supongamos que los registros AX y BX contienen enteros con signo de 16
bits. Escribamos un programa que encuentre el mayor de los dos y lo transfiera al
registro CX.
CMP AX, BX ; Comparemos AX con BX "¿AX es > BX?".
JG NEXT
; Sí, AX es mayor que BX(con signo!): Salta a NEXT.
MOV CX, BX ;
JMP FIN
NEXT : MOV CX, AX
FIN:
La instrucción de salto incondicional jmp: comentarios finales.
La instrucción JMP (Del Inglés jump) produce un salto incondicional. La sintaxis de esta
instrucción es así:
JMP Destino
donde destino es una etiqueta o una dirección efectiva que normalmente se encuentra
en el mismo segmento de código que la instrucción JMP. El salto a destino se produce
incondicionalmente, es decir no depende del estado de ningún bit de EFLAGS ni de
ninguna otra condición: se produce inevitablemente.
28
Usad esta numeración:
54 de 357
El salto se puede producir a cualquier dirección efectiva sin encontrarnos en este caso con
las restricciones de las instrucciones de salto condicional en cuanto al rango del salto.
Ejemplo: Escribir el código correspondiente a un bucle de espera de interrupción. En el
capítulo dedicado a la organización del i386SX y del PC estudiaremos el concepto de
interrupción y tendremos ocasión de ver la utilidad de esta rutina la cual consiste
simplemente en un bucle infinito(En Inglés se dice “Tight loop”). La realizaremos usando la
instrucción JMP.
WAIT_INTR: JMP WAIT_INTR
Los saltos a subrutina y sus retornos: call y ret.
En la arquitectura IA, al igual que en la arquitectura del microprocesador MIPS, existe
un mecanismo por el cual un programa puede saltar a otro y garantizar que el
programa de destino, puede retornar al programa principal de forma automática. Al
programa de destino se le conoce como subrutina o procedimiento.
La instrucción que produce el salto a la subrutina es una instrucción de salto
incondicional un tanto especial. Esta instrucción, cuyo mnemónico es call, salta
incondicionalmente a una subrutina y, antes de producir el salto, guarda la dirección
de retorno en la pila. De esta forma, cuando la subrutina “decida” que ha terminado su
labor, puede recuperar la ejecución del programa principal en el punto donde lo dejó,
simplemente extrayendo de la pila la dirección de retorno, la cual fue salvada en el
momento de preparar el salto.
call target
ret
Llamada al procedimiento target
Retorno de subrrutina
La instrucción de salto a subrutina es call y, la instrucción de retorno de subrutina es
ret. Como ya hemos explicado, call guarda en la pila la dirección de retorno y ret extrae
esa dirección de la pila y carga el registro ip con el valor extraído. ¿Qué ocurre si la
dirección de destino está fuera del segmento de código actual? En ese caso, call
guardará en la pila no sólo el offset de la instrucción donde se deja pendiente la
ejecución, si no también su segmento. En este caso, cuando ret retorne, no sólo extraerá
el offset de la dirección de retorno, extraerá también el segmento correspondiente a esa
29
Usad esta numeración:
55 de 357
dirección de retorno. La instrucción ret cargará ip con el offset y cs con el segmento,
ambos extraídos de la pila.
La instrucción call, por sí sola, no puede decidir si el salto es intra o inter segmento, es
decir, salto near o far. Es el programador el que decide acerca de tal distinción y, en el
propio programa, en la línea de código donde escribe la instrucción call, incluye un
modificador de rango llamado NEAR o FAR. Cuando el ensamblador encuentra una
instrucción call con un modificador near, genera la representación binaria de una
instrucción call que sólo guarda el offset de la dirección de destino. Del mismo modo,
todas las instrucciones ret incluidas en esa subrutina serán ensambladas con el código
de operación que sólo extrae de la pila el offset de la dirección de retorno. Si el
programador especifica FAR, entonces, la forma binaria de la instrucción call será la
que corresponde a un salto a subrutina lejano, se salvará el offset y el segmento de la
dirección de retorno. En este último caso, la forma de las instrucciones ret incluidas en
la subrutina, será aquella que corresponde a retornos de subrutina lejanos en los cuales
se extrae de la pila tanto el offset como el segmento de la dirección de memoria de
retorno.
Las formas de especificación de la dirección de destino en una instrucción call, son las
mismas que estaban disponible con la instrucción de salto incondicional.
La instrucción de retorno de interrupción.
Los microprocesadores son capaces de producir un salto a una subrutina como
respuesta a una indicación externa de que ha ocurrido un determinado evento. Un chip
periférico, llamado controlador de interrupciones, es el encargado de recoger estas
interrupciones procedentes de otros chips de e/s y, siguiendo un esquema de
priorización, pasar estas interrupciones al procesador para que éste, de forma fijada de
antemano, produzca un salto incondicional a la dirección de memoria del programa
que ha sido diseñado para manejar la situación creada. A este último programa se le
conoce con el término rutina de servicio de interrupción.
iret
Retorno de interrupción
Cuando tiene lugar el evento externo, el controlador de interrupciones envía una
indicación de interrupción al procesador y éste, de forma automática, abandona la
ejecución del programa actual y salta a la rutina de servicio de la interrupción. Una vez
allí, se ejecuta esta rutina y cuando termina, retorna al programa cuya ejecución se
abandonó. En el momento de saltar a la rutina de servicio, el procesador no sólo
guarda la dirección de retorno en la pila, guarda también el registro FLAGS. De este
modo, cuando se produzca el retorno, el procesador recuperará la ejecución del
30
Usad esta numeración:
56 de 357
programa que se abandonó, justo en el mismo punto donde se abandonó y ¡con el
mismo contexto! Si por ejemplo el programa abandonado estuviera a punto de efectuar
un salto condicional, tenemos que garantizar que la rutina de servicio no altere los
FLAGS porque si hiciéramos esto, el evento externo, habría cambiado el estado
algorítmico del programa y esto afectaría a su comportamiento.
Resumiendo: cuando se produce una interrupción, el procesador salta a la rutina de
servicio y guarda en la pila la dirección de retorno y los FLAGS. Cuando la rutina de
servicio retorna, no sólo extrae de la pila la dirección de retorno con su offset y su
segmento, si no que extrae también los FLAGS pertenecientes al programa
abandonado. La instrucción que se emplea en una rutina de servicio para retornar
restaurando el contexto se llama iret, interrupt return.
Instrucciones específicas para la programación de estructuras repetitivas
Las instrucciones LOOP, LOOPE, LOOPZ y otras que aparecen listadas a
continuación, usan el valor cargado en ECX como cuenta de cuantas veces se ha de
repetir un bucle. Todas ellas decrementan el registro ECX cada vez que se ejecutan y,
después, dependiendo del mnemónico concreto empleado, efectúan una comparación
con cero, con no-cero, u otras.
mov cx, 45; El bucle se ha de repetir 45 veces
bucle: ...
...
...
loop bucle ; Decrementar cx, si el nuevo valor de cx es
distinto de 0, saltar a bucle. En otro caso, continuar en la
instrucción siguiente. bucle es un desplazamiento relativo.
mov cx, 32; El bucle se ha de repetir 32 veces
bucle: ...
...
...
31
Usad esta numeración:
57 de 357
loopz bucle ; Decrementar cx, si el nuevo valor de cx es
distinto de 0 y el flag ZF está puesto a 1, continuar en la
instrucción siguiente, en otro caso saltar a bucle.
loop bucle
cx--;Repetir el bucle mientras cx<> 0
loopz bucle
cx--;Repetir el bucle mientras cx<> 0 y ZF=1
loopcc bucle
jcxz label
cx--;Repetir un bucle mientras se cumpla cc
Saltar a Label si cx=0.
Si en alguno de los programas de los ejemplos anteriores el registro cx no se
inicializase explícitamente con un valor antes de comenzar el bucle, si no que
estuviéramos suponiendo que otro programa se encargase de hacerlo, podríamos
encontrarnos que, por error, el registro cx contuviera un 0 al principio del bucle, esto
es, la primera vez que se ejecutase. La instrucción LOOP, en primer lugar, decremento
y, después, compara con cero. Pero, si el registro inicialmente estuviese a cero, el
decremento produciría un valor ffffh cuyo valor en decimal es –1. En estas
circunstancias, el bucle que no tendría que ejecutarse ninguna vez, se ejecutaría 65536
veces: un grave error. Para evitar esta situación y explícitamente examinar el valor de
cx antes de comenzar un bucle, l arquitectura IA contiene una instrucción llamada jcxz.
Esta instrucción salta a una dirección relativa especificada en la instrucción si cx
contiene un cero.
; El bucle se ha de repetir un cierto número de veces
establecido en otro contexto del programa.
; El valor de cx podría ser cero y en ese caso deseamos que el
bucle no se ejecute ninguna vez:
jcxz fuera
bucle: ...
...
32
Usad esta numeración:
58 de 357
...
loop bucle ; Decrementar cx, si el nuevo valor de cx es
distinto de 0, saltar a bucle. En otro caso, continuar en la
instrucción siguiente. bucle es un desplazamiento relativo.
fuera: ...
...
Interrupciones software.
En ciertas circunstancias deseamos saltar a una subrutina en unas condiciones
similares a las que tienen lugar con las interrupciones externas. La instrucción que nos
permite saltar a una subrutina como si se tratase de una interrupción es la instrucción
int numero_de_vector. Esta instrucción transfiere el control a una rutina de
servicio cuya dirección se obtiene a partir del inmediato que acompaña a la instrucción
usando una tabla llamada tabla de vectores de interrupción.
int numero_de_int
Llamar a la interrupción software numero_de_int
La instrucción int constituye un medio de producir una interrupción “programada”,
inmersa en el código del programa. A diferencia de ésta, las interrupciones externas
tienen lugar de forma aleatoria, en cualquier instante y, además, ocurren de forma
completamente asíncrona en relación con el sistema computador. La instrucción int
produce una interrupción síncrona que además, el programador sabe en qué
circuntancias va a a tener lugar en el flujo de ejecución del programa.
Cuando un programa ejecuta una instrucción int, el procesador salva en la pila el
registro FLAGS y, por supuesto, salva también la dirección de retorno. De este modo,
el programador consigue ejecutar la subrutina de destino con los mismo privilegios
que si fuese una rutina de servicio. De hecho, es una rutina de servicio y, como tal, el
programador debe asegurarse que los puntos de retorno de esta rutina están hechos a
base de instrucciones iret, no ret.
Los sistemas operativos suelen usar las interrupciones software como medio de acceso
al conjunto de servicios que éstos ofrecen. Por ejemplo, en el sistema MS-DOS®, el
acceso a las funciones del sistema se lleva a cabo a través de la interrupción software
número 21h. El programador precarga en un registro de propósito general, el número
33
Usad esta numeración:
59 de 357
de servicio concreto que desea usar y, en cargará el resto de parámetros necesarios en
otros registros establecidos por la función que se desee llamar. Veamos un ejemplo:
;Deseamos usar el servicio “Is drive remote” el cual nos permite
averiguar si una unidad lógica de este sistema es una unidad de red,
exportada por otro ordenador como recurso compartido, o no.
mov ax 4409h;El número de servicio es 4409h, este número ha de
cargarse en ax.
mov bl, drive;En este servicio concreto, tenemos que indicar el
número de unidad de disco que queremos describir en el regsitro bl.
Esto es algo impuesto por las características de acceso de este
servicio concreto.
int 21h ;Llamamos al servicio de MS-DOS®(21h) número 4409h.
Instrucciones de ma nipula ción de ca dena s.
Una cadena es una serie de caracteres ASCII almacenados uno después de otro en
memoria. Las cadenas se emplean para almacenar texto: nombres, direcciones, etc. Las
cadenas, formadas por cualesquiera tipos de datos básicos, constituyen un elemento
importante, presente en cualquier lenguaje de programación.
Las instrucciones que explicamos en este epígrafe permiten mover y procesar grandes
estructuras de datos en memoria. La arquitectura IA contiene instrucciones que pueden
manejar cadenas de caracteres (bytes) y también cadenas de words (16 bits) y de
doublewords (32 bits). Los elementos sobre los cuales van a trabajar las instrucciones,
se identifican mediante los registros esi (extended source index, índice origen
extendido) y edi(extended destination index, índice destino extendido). Ambos
registros contendrán el offset de los elementos de cadenas que van a ser procesados.
Cuando se usan los registros esi y edi para formar offsets de elementos de una cadena
es necesario entender las reglas de formación de las direcciones efectivas, en el caso de
esi el registro empleado por defecto es ds, en el caso de edi el registro de segmento
empleado por defecto es es. Si las circunstancias de un determinado problema hacen
necesario el uso de otro registro de segmentación distinto, como siempre, es posible el
uso de un prefijo de modificación de segmento por defecto (segment override). El uso de
dos segmentos físicos de memoria y, por tanto, el uso de dos registros de segmento
distintos, nos va a permitir manejar dos cadenas diferentes ubicadas en dos segmentos
distintos. La asociación por defecto de los registros esi y edi con ds y es
respectivamente y su posible modificación, nos da un grado de libertad muy amplio
en cuanto a la ubicación de las cadenas.
34
Usad esta numeración:
60 de 357
movs
Mover cadena
cmps
Restar elemento de cadena destino menos fuente
scas
Restar elemento de cadena destino de EAX,AX o AL
lods
Carga el elemento de acdena fuente (esi) en EAX,AX o AL
stos
Salva el elemento de cadena presente en EAX,AX o AL en
memoria (edi)
movsb; Mover el byte que se encuentra en la dirección de memoria ds:si
a la dirección de memoria es:di
cmpsd; Comparar la doubleword que reside en ds:si con la doubleword
que reside en es:di. Actualizar los flags de acuerdo con el resultado
de la comparación.
scasw; Comparar ax con la word que reside en es:di y actualizar los
flags de acuerdo con el resultado.
lodsb; Cargar el registro al con el byte que reside en ds:si.
stosw; Salvar ax en la dirección de memoria es:di.
Aplicación repetida de las instrucciones de procesamiento de cadenas
Las instrucciones de procesamiento de cadenas realizan una acción cada vez que se
ejecutan, sin embargo, la arquitectura IA contiene un prefijo específico que permite
aplicar repetidamente una determinada transformación sobre una cadena. Este prefijo
se aplica a una instrucción de procesamiento de cadenas y la acción tiene lugar
repetidamente.
Cada vez que se usa una instrucción de procesamiento de cadenas, los registros esi y
edi son automáticamente incrementados o decrementados después de cada iteración
de una instrucción. De este modo, logramos que los dos registros apunten al siguiente
35
Usad esta numeración:
61 de 357
elemento de la cadena involucrada (byte, word o doubleword). Las instrucciones de
procesamiento de cadenas pueden funcionar con direcciones decrecientes o crecientes.
El flag DF del registro FLAGS controla si ambos registros son incrementados o
decrementados cada vez que se termina el proceso de cada elemento de la cadena. Las
instrucciones que fuerzan este flag son std y cld.
cld, std
rep
poner a 0/1 el flag df
Prefijo: repetir mientras ecx no sea cero.
repe/repz
Prefijo: repetir mientras ecx no sea cero y ZF esté puesto.
repne/repnz
Prefijo: repetir mientras ecx no sea cero y ZF esté borrado.
Cuando una instrucción de procesamiento de cadenas viene acompañada de un prefijo
de repetición, la operación se ejecuta hasta que tenga lugar una de las condiciones de
terminación especificadas por el prefijo. Una instrucción rep stos constituye la forma
más rápida de borrar un gran bloque de direcciones de memoria.
Instrucciones de e/s
Las arquitecturas de microprocesador llevan a cabo las entradas y salidas de dos
formas fundamentales: usando instrucciones especiales de acceso a los puertos de e/s
o, usando las mismas instrucciones de acceso a memoria. En el primer caso, el bus
estará dividido en dos buses: el bus de memoria y el bus de e/s y, será el estado de
una señal de control de bus, la que indicará en cada caso cuál de los dos buses va a
usarse en cada transacción.
La arquitectura IA implementa las e/s usando instrucciones especiales de transferencia
de datos con puertos de e/s. Cuando el procesador ejecuta una de estas instrucciones,
fuerza un 0 en la señal m/#io del bus interfaz externo, esta señal indica al sistema que
la transacción de bus actual está dirigida al bus de e/s, es decir, la dirección que
aparece en el bus de direcciones, representa la dirección de un puerto de e/s, no una
dirección de memoria. A la otra técnica de e/s mencionada se la conoce como e/s
proyectadas sobre memoria y la emplean microprocesadores como la familia 68K de
Motorola o el microprocesador MIPS.
36
Usad esta numeración:
62 de 357
Las instrucciones in e ins transfieren un dato procedente de un puerto de e/s. Las
instrucciones out y outs transfieren un dato a un puerto de e/s.
in
Leer un puerto de e/s y transferir el dato al registro eax, ax o al
in ax, 0f4h; Leer una word del puerto 0f4h y transferirlo a ax
in al, dx ;Leer un byte from del puerto cuyo número está almacenado en
dx, cargar el byte en al. Dx puede contener cualquier número de puerto
entre 0 y ffffh.
in eax, 25h ;Leer un byte del puerto 25h y cargarlo en eax
out
Escribir en un puerto de e/s
out 0b2h, ax ;Escribir la word contenida en ax en el puerto 0b2h.
out dx, eax ;Transferir la dword contenida en eax al puerto de e/s
cuya dirección se encuentra en el registro dx.
out dx, al ;Escribir el byte que se encuentra en al al puerto de e/s
cuyo número (dirección) está albergado en dx.
ins, outs
Leer dato de un puerto de e/s, escribir dato en puerto de e/s
insb, insw, insd
Leer byte, word, dword de puerto de e/s
outsb, outsw, outsd Escribir byte, word, dword en puerto de e/s
Las instrucciones ins y outs se emplean para efectuar transferencias de datos entre
puertos de e/s y cadenas en memoria, son instrucciones de e/s en bloque y funcionan
de forma similar a las instrucciones de procesamiento de cadenas. Los registros esi y
37
Usad esta numeración:
63 de 357
edi se emplean para especificar los elementos de cadena en memoria y los prefijos de
repetición, rep, se usan para repetir las acciones simples de ins y outs y de este modo,
obtener las transferencias de datos de e/s a memoria y de memoria a e/s, en bloques
formados por múltiples elementos de cadenas (byte, word, dword).
outsb; Transferir el byte almacenado en la dirección de memoria
ds:(e)si al número de puerto de e/s especificado en dx.
outsw; Transferir la word almacenada en la dirección de memoria
ds:(e)si al número de puerto de e/s especificado en dx.
outsd; Transferir la dword almacenada en la dirección de memoria
ds:(e)si al número de puerto de e/s especificado en dx.
insb; Leer un byte del puerto de e/s cuyo número está cargado en dx a
la dirección de memoria es:(e)di.
insw; Leer una word del puerto de e/s cuyo número está cargado en dx a
la dirección de memoria es:(e)di.
insd; Leer una dword del puerto de e/s cuyo número está cargado en dx
a la dirección de memoria es:(e)di.
Instrucciones de soporte de lengua jes de a lto nivel.
Los lenguajes de alto nivel como Pascal o C incorporan una característica llamada
estructura en bloques, que permite organizar el acceso que tienen las diferentes partes
del programa a cada una de las variables que éste maneja. La estructura en bloques
confina las partes de un programa que tienen acceso a una determinada variable, de
este modo, el mismo identificador puede emplearse dentro del mismo programa, en
diferentes contextos de éste, sin interferir entre sí, significando cada uno de ellos “cosas
diferentes”. Ciertamente, nos resultaría difícil escribir un programa de 50000 líneas sin
usar más de una vez los nombres i, j, k, u, v, t, temp y otros similares.
Un bloque que está contenido estáticamente dentro de otro es un bloque que ha sido
declarado dentro del alcance del bloque actual. Las variables declaradas dentro de un
bloque dado pueden ser accedidas dentro del bloque en el que se ha n declarado y en
cualquier otro bloque estáticamente contenido dentro de éste. La excepción a esta regla
es que si un bloque contenido declara una variable del mismo nombre, entonces, esa
38
Usad esta numeración:
64 de 357
definición prevalece la declaración hecha dentro del bloque superior. Las variables
declaradas en un bloque dado, no pueden ser accedidas desde bloques exteriores –son
privadas y no pueden ser accedidas tampoco por parte de bloques contenidos.
La estructura de anidamiento de bloques, bloques contenidos estáticamente dentro de
otros de un programa puede ser cuantificada numerando el bloque más exterior con u
nivel lexicográfico 1. El término nivel lexicográfico se refiere a este nivel de
anidamiento de bloques. Cualesquiera bloques inmediatamente contenidos en un
bloque de nivel i, decimos que pertenecen a un nivel lexicográfico i+1.
La arquitectura IA contiene instrucciones y estructuras que permiten relacionar de
forma directa el nivel lexicográfico de cada bloque, las variables accesibles y privadas
de cada bloque con el flujo de control generado por la ejecución del programa. Por
ejemplo, cada llamada a subrutina viene acompañada de una estructura de datos
creada en la pila, la cual, contiene las variables locales de la subrutina y su lista de
parámetros. A esta estructura se le llamada registro de activación, el nombre proviene
de la arquitectura del computador Burroughs B6700, una máquina orientada a stacks.
Al conjunto de registros que, al comenzar la ejecución de la subrutina, contienen
información sobre el emplazamiento y extensión del registro de activación, se le
denomina el display. La arquitectura IA, no siendo una arquitectura orientada a stacks,
contiene, sin embargo, una serie de estructuras e instrucciones que le posibilitan a los
escritores de compiladores a que usen las nociones de display, registro de activación,
nivel lexicográfico, y otros.
La arquitectura IA contiene dos instrucciones que permiten efectuar una llamada a
procedimiento y su retorno distintas del par call/ret. Son las instrucciones enter y
leave. Estas instrucciones no sólo producen el salto a la subrutina, además, preparan
un registro de activación adecuado y actualizan el display. La instrucción enter crea un
marco de pila (stack frame) al entrar en un procedimiento. La instrucción leave destruye
el marco de pila recién creado y retorna, además restaura el display adecuadamente.
Los marcos de pila, de forma similar a los registros de activación, tienen un espacio
predefinido donde se albergan las variables locales y los punteros necesarios para que
el procedimiento al que pertenecen pueda producir accesos a esas variables y asegurar
retornos coherentes a los procedimientos de nivel lexicográfico anterior. De este modo,
las instrucciones enter/leave, aseguran métodos fiables de provisión de acceso del
procedimiento actual a sus variables locales y a las variables locales de otros bloques
con un nivel lexicográfico inferior.
enter
39
Crear un marco de pila en un determinado nivel lexicográfico
Usad esta numeración:
65 de 357
leave
Destruir el marco de pila actual
enter 512, 4; Crear un marco de pila de 512 bytes de espacio de
almacenamiento dinámico (para variables automáticas). El nivel
lexicográfico es 4. Este nivel representa la
profundidad de la subrutina llamada con respecto a la cadena de
llamadas que han llevado la ejecución hasta el procedimiento actual.
En este caso preciso, enter creará 4-1 = 3 punteros a los marcos de
pila anteriores, correspondientes a subrutinas contenedoras de la
actual subrutina. Estos punteros se copiarán en el marco de pila que
se va a crear desde el marco de pila actual más, un puntero a marco de
pila correspondiente al procedimiento desde el que tiene lugar la
llamada que se está preparando.
leave; Destruir el marco de pila actual. El espacio reservado en el
anterior enter es recuperado y el display es adecuadmente actualizado.
Instrucciones de ma nejo del registro de ba ndera s de esta do, FLAGS.
Las siguientes instrucciones permiten alterar y explorar el estado de ciertos bits del
registro FLAGS.
40
Usad esta numeración:
66 de 357
clc
Borrar (0) el bit de carry, cf.
stc
Poner a (1) el bit de carry, cf.
cmc
Complementar el bit de carry, cf (0->1, 1->0)
std
Poner a (1) el flag de dirección, df. (Se usa con las instrucciones de
procesamiento de cadenas)
cld
Borrar (0) el flag de dirección, df. (Se usa con las instrucciones de
procesamiento de cadenas)
cti/cli
sti
Borrar (0) el bit de habilitación de interrupciones mascarables.
Poner a (1) el bit de habilitación de interrupciones mascarables.
Instrucciones de transferencias del registro EFLAGS.
Es posible copiar o cargar varios bits de EFLAGS simultáneamente mediante el uso de
las instrucciones de este grupo. Una rutina puede calcular un mapa de bits completo
de EFLAGS o parte de él y cargarlo en EFLAGS en una sola operación.
lahf
sahf
Cargar en ah los bits sf, zf, af, pf y cf de FLAGS.
Cargar los bits sf, zf, af, pf y cf de FLAGS con ah
pushf/pushfd
Salvar flags/eflags en la pila
41
popf/popfd
Cargar flags/eflags con la word/dword presente en la pila
Usad esta numeración:
67 de 357
Instrucciones de ma nejo de los registros de segmento.
En este apartado describiremos las versiones far de las instrucciones jmp y call. En
apartados anteriores ya hemos descrito cómo se pueden cargar los registros de
segmento a través de la instrucción mov y a través de la pila. Pero, las instrucciones
mov y las de transferencia a la pila, no pueden cargar un valor en el registro cs, eso está
reservado a las instrucciones de salto largo.
Las instrucciones jmp y call, ambas, aceptan un puntero largo como operando fuente,
de este modo es posible dirigir la ejecución del programa hacia un segmento distinto
de aquel al que apunta el registro cs en el momento del salto. Cuando se ejecuta una
instrucción call, se salva en la pila la dirección de retorno, de la cual, en este caso,
tendrán que especificarse tanto el offset como el segmento. Es decir, la instrucción far
call salvará en la pila el offset y el segmento de la dirección de retorno.
Cuando termine de ejecutarse el procedimiento far, se producirá el retorno via la
instrucción ret, la cual, extraerá tanto el offset como el segmento guardados. Hay pues
dos versiones de ret, near y far, cada una con su propio código de operación y, es el
programa Macro Ensamblador el que decide qué versión utilizar en cada instante,
dependiendo del formato de declaración empleado para el procedimiento. Es, por
tanto, la declaración del procedimiento la que establece la versión de las instrucciones
call y ret usadas en las llamadas y en los retornos de ese procedimiento.
getdata proc far; El comienzo del procedimiento getdata.
...
; Es un procedimiento lejano
...
; Otras instrucciones del procedimiento
ret
; ret restaura offset y segmento desde la pila
...
; Otras instrucciones del procedimiento
ret
; ret restaura offset y segmento desde la pila
getdata endp
; Fin del procedimiento getdata.
Desde un cierto programa (principal) se producirían llamadas al procedimiento
getdata, cada una de las instrucciones call salvaría en la pila tanto el offset como el
segmento de la siguiente instrucción en memoria.
...
;Parte de un programa que llama a getdata call
getdata ;Este call salvaría offset y seg de la direcciónde memoria donde reside
la siguiente instrucción: la dirección de la instrucción mov gs, ax .
mov gs, ax
42
Usad esta numeración:
68 de 357
Instrucciones de ca rga de punteros leja nos.
Las siguientes instrucciones cargan una dirección completa segmento:offset desde
memoria, el segmento lo cargan en el registro de segmento cuyo nombre aparece en el
mnemónico de la instrucción y el offset lo cargan en el registro de propósito general
indicado como operando.
lds, les, lfs, lgs, lss
Cargar puntero residente en memoria en
RSeg:R
lds bx, procesos; Cargar en bx el offset de procesos y en ds el
segmento de procesos.
lss si, pilas; Cargar en si el offset de pilas y en ss el segmento de
pilas.
Instrucciones de soporte de interrupciones softwa re.
Ya hemos visto una pequeña introducción a las interrupciones software en un párrafo
anterior. Int también pertenece a este grupo de instrucciones que nos permiten
producir interrupciones software. El set de instrucciones de IA ofrece además las
instrucciones bound e into. Antes de describir estas dos nuevas instrucciones, vamos a
puntualizar un aspecto de la instrucción int que tiene relación con la discusión anterior
acerca de punteros lejanos. Cuando el procesador ejecuta una instrucción int en modo
real, usa el operando inmediato de 8 bits que acompaña a ésta, para formar la dirección
de salto completa. En realidad, lee un puntero far procedente de una zona de memoria
llamada tabla de vectores de interrupción, de este modo, carga en cs:ip el segmento y
el offset de la dirección de memoria que contiene el manejador de la interrupción
int, into, iret, bound
Llamadas a interrupciones software y
retorno de interrupción.
software que se desea usar.
int 15h; Llamar a la interrupción software 15h
int 3; Una forma particular de int: llamar al debugger, un trap que
conduce al debugger
43
Usad esta numeración:
69 de 357
iret; Este programa es un manejador de interrupciones y en este punto
se desea retornar al contexto en el que se produjo la última
interrupción.
bound bx, complexSalidas ; Si el índice bx hace que el acceso a su
elemento exceda el tamaño del array complexSalidas, producir una
excepción del tipo desbordamiento de array y permitir que esta
instrucción sea re-iniciada cuando el manejador de excepciones haya
organizado el sistema de memoria convenientemente.
Instrucciones miscela nea s.
lea bx, producto ; Cargar el offset de producto en bx
lea
Cargar el offset de una dirección de memoria en un registro
xlat/xlatb
Cargar un byte en al, sumarlo con ebx y formar la dirección de
memoria del un elemento de una tabla en memoria
cpuid
Obtener información acerca del procesador que está ejecutando esta
instrucción.
nop
Esta instrucción no lleva a cabo ninguna operación.
ud2
Generar una excepción del tipo instrucción indefinida.
Instrucciones de soporte del modo protegido.
Se emplean en el desarrollo de sistemas operativos avanzados.
Instrucciones de control del procesador.
Estas instrucciones se emplean en la implementación de esquemas avanzados de
control de los buses del i386SX.
Instrucciones de punto flotante y control de punto flotante.
Estas instrucciones se emplean para efectuar operaciones matemáticas sobre operandos
de tipo REAL (no enteros). Afectan al coprocesador matemático si éste está instalado 44
Usad esta numeración:
70 de 357
de hecho es tal coprocesador el que las ejecuta. En el tema de aritmética computacional
estudiaremos algunas de ellas, los formatos de sus operandos y el estándar IEEE-754.
Instrucciones de procesa miento de medios strea m: MMX.
Implementa ción de la pila en la a rquitectura IA.
La pila, al igual que en la arquitectura del microprocesador MIPS, es un array de
posiciones de memoria contiguas que se acceden a través del uso de registros y modos
de direccionamiento especiales que le confieren el carácter de estructura lifo (lastin/first-out). La disciplina de acceso consiste en que el dato introducido en último lugar
es aquel que será devuelto por la estructura en caso de ser solicitado algún dato.
La arquitectura IA incluye un implementación detallada y completa de la pila en la
que los programas normalmente acceden a la misma usando el conjunto de
instrucciones específicas de acceso a la pila. Este aspecto es claramente distinto de la
arquitectura del microprocesador MIPS donde la implementación de la pila podría
considerarse débil, dejando al programador decidir la disciplina de inserción y
extracción e, incluso, la elección de los registros puntero de pila (sp) y base del marco
de pila (bp), de acuerdo con los sistemas lingüístico y operativo que forman parte de la
plataforma base. En los sistemas basados en la arquitectura IA también pueden
producirse accesos a la pila mediante el uso de otras instrucciones de transferencia de
datos, distintas de las incorporadas para acceder a la pila, pero, existe un conjunto de
instrucciones específicamente incorporadas en la arquitectura para el acceso a la pila –
una implementación fuerte.
Los registros usados en la implementación fuerte de la pila.
La pila está formada por un rango de direcciones de memoria contiguas, por tanto,
desde el punto de vista del sistema, las direcciones físicas que apuntan a ese rango
tienen que estár formadas por una dirección base de segmento y por un offset y, éstos
han de estar cargados en un registro de segmento y un registro de direcciones. El
registro de segmento de pila es el registro ss y, el registro de “offset de pila” es el
registro esp. En todo momento, el registro esp apunta a la dirección más alta de la pila,
de forma que, si se produce una inserción en la pila a través del uso de una instrucción
de la familia push, el registro esp, es decrementado un cierto número que será el ancho
en bytes del dato que se pretende introducir en la pila.
La instrucción que permite efectuar lecturas de datos procedentes de la pila está
formada por una serie de instrucciones de la familia pop. Las diferentes formas de pop
están explicadas en el apartado correspondiente dentro de la descripción del set de
instrucciones vista anteriormente. Esta instrucción, básicamente, extrae un dato de la
pila, lo transfiere a su operando de destino y, posteriormente, incrementa el contenido
45
Usad esta numeración:
71 de 357
del regsitro esp en un número de unidades igual al número de bytes que es el ancho
del dato transferido. La pila, según podemos deducir de la descripción de push y pop,
crece hacia direcciones de memoria decrecientes y viceversa, la pila decrece en
direcciones de memoria crecientes.
Puesto que la pila está formada por las direcciones de memoria incluidas en el
segmento cuya base está contenida en ss, en esta arquitectura es directo cambiar de
pila, cambiar la zona de memoria perteneciente a la pila, si eso es necesario. Por
ejemplo, en los sistemas operativos multitarea, cuando se produce un cambio de
contexto, el kernel del sistema operativo, al despachar una tarea nueva, habilita una
pila local donde esa tarea puede almacenar parámetros y variables locales
pertenecientes a su flujo de control, a su secuencia específica de llamadas a
procedimiento. Cuando un sistema habilita un stack, una pila, el resto de ellas
permanecen “latentes” en memoria: sus contenidos no han sido borrados pero, en este
preciso instante, no son accesibles empleando la disciplina impuesta por las
instrucciones push y pop. La pila actual es aquella zona de memoria cuyo dirección
base de segmento está contenida en ss y cuyo offset se forma usando (implícitamente)
el registro esp.
Cuando el microprocesador ejecuta una instrucción push o pop, emplea el par de
registros ss:esp para formar las direcciones físicas de memoria donde se encuentran los
datos guardados en la pila.
Cómo se activa una pila.
Cuando un programa necesita memoria, para cualquier uso concebible de ésta, efectúa
una solicitud al sistema operativo utilizando llamadas del sistema específicas. Por
ejemplo, en el sistema operativo UNIX los procesos piden n bytes memoria al kernel
usando la llamada del sistema brk() o, de forma más ortodoxa, usando las llamadas de
librería pertenecientes a la familia malloc(). El subsistema de gestión de memoria
satisface la solicitud efectuando un recorrido por sus tablas de asignación de regiones
de memoria y responde devolviendo un puntero a la dirección base de una zona de
memoria que contiene n bytes en direcciones contiguas. Este puntero, en la
arquitectura IA, suponiendo que la ejecución tiene lugar en el modo 16 de bits, estará
descompuesto en las dos partes de que consta una dirección física, esto es, dirección
base de segmento y su offset.
Como conclusión, podemos decir que cuando se realiza la programación de
aplicaciones bajo un sistema operativo, para activar una pila, sólo es necesario solicitar
memoria al sistema. Si la aplicación es una aplicación integrada (empotrada) que se
ejecuta bajo un sistema que no ofrece servicios de gestión de memoria, es el
programador el que decide en que zona de memoria va a implementar el stack,
dependiendo del diseño del mapa de memoria de su sistema.
Los pasos concretos para activar una pila son los siguientes:
46
Usad esta numeración:
72 de 357
•
Decidir la dirección base de la pila, a través de una solicitud al sistema operativo o
tomando la decisión según el diseño del mapa de memoria del sistema
microprocesador base y la ocupación de memoria actual.
•
Cargar el registro selector ss usando una de las siguientes instrucciones: mov, pop
o lss.
•
Cargar el puntero de pila en el registro esp calculado como offset dentro del
segmento de pila mencionado en los puntos anteriores. Pueden usarse las
instrucciones mov, pop o lss. El registro esp debe apuntar a una dirección alineada
en frontera de palabra (word) si el ancho de la pila está fijado en 16 bits. Cuando los
programas generan direcciones de pila desalineadas, puede producirse una notable
reducción de rendimiento y, en algunos casos, pueden producirse fallos en la
ejecución de ciertas instrucciones.
•
En modo protegido sería necesario, también, establecer la granularidad de
direccionamiento del segmento de stack, su límite de capacidad y sus privilegios
de acceso.
Ejercicios propuestos.
1. Determinar el efecto de cada una de las siguientes
instrucciones del i386.
a) CDQ
b) BTC BX, AX
c) MOVSX EAX, f2h
2. Escribir un programa en lenguaje de ensamblaje del i386 que
multiplique un entero de 8 bits por un entero de 32 con
signo almacenado en eax. El resultado se almacenará en eax.
3. Escribir un programa en lenguaje de ensamblaje del i386 que
copie 1000 datos de 32 bits desde 9000h:0000h a
8000h:2000h. Después de inicializar los registros ds/esi y
es/edi convenientemente, se ha de utilizar la instrucción
repmovsd.
4. Identificar qué modos de direccionamiento están en uso en
las siguientes instrucciones así como el tamaño de los
datos intervinientes en las operaciones:
i)
mov ecx, [esi*4]
47
Usad esta numeración:
73 de 357
ii) sub bl, [ecx+35+esi]
iii) add al, [input+40+ebp]
5. En cada una de las siguientes instrucciones, calcular la
dirección efectiva de cada uno de los operandos. Asumir que
ds=1000h, ss=4300h, di=4500h, bx=2000.
i)
mov al, [di]
ii) add [bx+350h], ax
iii)
movsx eax, byte ptr [bp]
48
Usad esta numeración:
74 de 357
Apuntes de Máquinas
Electrónicas
© 1994, 2004 José María Foces Morán.
Capítulo 3
Fundamentos de programación
en lenguaje de ensamblaje
(Assembly language)
Usad esta numeración:
75 de 357
Fundamentos de programación.
© 2003, José Mª Foces Morán.
©2003, José María Foces Morán.
1/59
Usad esta numeración:
76 de 357
1. Grafos que representan las acciones de un programa a lo largo del tiempo.
La ejecución ordenada y rápida de las instrucciones que forman parte del programa es
lo que entendemos por ejecución de un programa. Pero ¿Qué significa cada grupo de
instrucciones del programa? ¿Cómo podemos expresar las acciones llevadas a cabo por
un programa en cada instante? O mejor aún, dado un algoritmo, ¿Cómo se pueden
expresar las instrucciones del algoritmo para que su traducción a un programa de
ordenador sea directa?
Una forma eficaz de realizar la representación de un programa consiste en emplear un
tipo de grafo dirigido llamado flujograma. Los segmentos dirigidos expresan el curso
que sigue el flujo de control del programa a lo largo del tiempo; los rectángulos
representan las acciones que lleva a cabo el programa; los rombos representan los
instantes en los que el programa puede efectuar una bifurcación sujeta a una
determinada condición lógica que se evalúa en el momento en el que se ejecuta el
programa y, por último, los círculos representan las uniones de dos flujos o nodos de
reagrupamiento, donde el programa fusiona dos flujos en unos solo de salida.
En la figura siguiente se representan los cuatro tipos de elementos presentes en un
flujograma.
reagrupamiento
f
condición
FIGURA 1
Definiremos también un programa limpio como aquel cuyo grafo tiene una única
entrada, la cual, está conectada con cualquier otro nodo, y que posée una única salida,
con la cual, cualquier nodo está conectado tambiñen. Esta definición resulta
conveniente para establecer la equivalencia entre flujogramas y los programas a los
que representan. El resultado que más nos interesa es que existe un pequeño conjunto
de estructuras de nodos que, convenientemente dispuestas, pueden representar
cualquier algoritmo concebible. Dijkstra estudió estos conjuntos de diagramas básicos
con los que se puede construir el grafo de cualquier programa. Se les llama diagramas
D (de Dijkstra) y son los siguientes, aunque no constituyen el único conjunto mínimo
de estructuras:
©2003, José María Foces Morán.
2/59
Usad esta numeración:
77 de 357
f
V
g
Secuencia
f
if-then-else
condición
g
F
f
condición
V
do-while
V
F
if-then
f
condición
F
V
f
condición
F
do-until
FIGURA 2
©2003, José María Foces Morán.
3/59
Usad esta numeración:
78 de 357
Definición de programa estructurado.
Se dice que un programa es estructurado si su diagrama de flujo contiene
exclusivamente estructuras como las cinco anteriores. Además, cuando se utilizan
lenguajes estructurados para la codificación, un programa estructurado no podrá
contener palabras reservadas goto.
Además de emplear la programación estructurada es muy conveniente también
emplear un enfoque de resolución en el que el nivel de detalle sea progresivamente
creciente: se toma el problema en su expresión más general y se va particionando en
sub-problemas mas sencillos hasta que se llega a un punto en el cual la forma del
problema permite ya su codificación -programación- de forma más fácil, directa y
quizás fiable.
2. Estructuras selectivas.
La estructura selectiva representa los momentos en los que un programa sigue un
camino de ejecución u otro dependiendo de que una determinada condición resulte
verdadera o falsa. En el instante preciso en el que se evalúa esa condición y, así, resulta
verdadera o falsa, el computador toma un camino o el alternativo. Tres de estas
estructuras están presentes en los lenguajes de programación de alto nivel; revisaremos
cada una de ellas.
Selectiva simple: if-then.
Esta estructura significa abandonar el curso secuencial de ejecución para ejecutar un
cierto grupo de instrucciones si la condición evaluada en tiempo de ejecución del
programa resulta verdadera. Si la condición resulta ser falsa, el programa continúa su
flujo secuencial de ejecución.
IF condición es verdadera
THEN
Ejecutar este BLOQUE de instrucciones
END_IF
//Continuación del programa
Vamos a construir ahora el flujograma que representa a esta estructura. En el
flujograma, condición es una expresión que puede ser verdadera o falsa. Si es
verdadera, se ejecutan las instrucciones correspondientes al rectángulo y después
continúa la ejecución del programa según la flecha; si la condición es falsa, el programa
simplemente continúa y, en ese caso, no se ejecutará el bloque de instrucciones
incluido en el rectángulo.
©2003, José María Foces Morán.
4/59
Usad esta numeración:
79 de 357
Bloque de
instrucciones
V
if-then
condición
F
FIGURA 3
Ejemplo. En el registro AX se ha cargado un entero de ancho 16 bits representado con
signo. Realizar un programa que calcule su valor absoluto y que la cargue en el registro AX.
IF AX < 0
THEN
Reemplazar AX por -AX
END_IF
Una vez escrita la sencilla estructura de este programa, procedamos a implementarlo en
lenguaje Macroensamblador. Vayamos por orden siguiendo el pseudocódigo que acabamos de
escribir.
1. Para evaluar ax<0, usaremos la instrucción de comparación cmp. Esta instrucción
efectúa la diferencia de sus dos operandos y actualiza los flags aritméticos de flags de
acuerdo con el resultado.
2. Una vez que se ha ejecutado cmp, usaremos un salto condicional con signo Recordemos que los datos comparados eran con signo. Si ax<0 deben ejecutarse las
instrucciones incluidas en la estructura, sin embargo, resulta más eficaz expresar
que en caso de que ax>=0 NO el programa deberá saltarse las instrucciones
incluidas en la estructura:
jge end_if ;El programa se salta las siguientes instrucciones y salta a la etiqueta
end_if
3. Las instrucciones incluidas en la estructura calcularán el valor absoluto de ax. Este
cálculo se efectuará usando la instrucción neg, el complemento a 2 puesto que, en
este instante, sabemos que el dato contenido en ax es negativo.
4. La etiqueta de salida de la estructura la vamos a llamar end_if. Esta etiqueta
constituye el nodo de salida de la estructura if-then y, en este caso, el fin del
programa.
©2003, José María Foces Morán.
5/59
Usad esta numeración:
80 de 357
El programa completo queda así:
CMP AX, 0
; ¿Es AX < 0?
JNL END_IF ; Si (AX ª 0) salta a END_IF, si no, continúa.
NEG AX
; Obtenemos el compl. a 2 de AX.
END_IF: ... ; Continúa el programa.
En el ejemplo anterior expresamos la condición "AX < 0" mediante la instrucción
CMP AX, 0. Si AX es mayor o igual que 0 o, de otro modo, si AX no es menor que 0, el
número ax es positivo, por lo cual usamos la instrucción JGE, la cual se saltará la
instrucción NEG AX. Si la condición AX < 0 es verdadera, el programa continúa
ejecutando la siguiente instrucción NEG AX, la cual complementa a 2 el registro AX, es
decir, cambia el signo de AX. Hace justo lo que buscábamos: Si AX es mayor o igual
que cero no es necesario efectuar ninguna operación para calcular su valor absoluto;
por el contrario si AX es menor que cero -negativo por tanto- tenemos que
complementarlo a 2 para obtener su valor absoluto, el cual es, por definición, siempre
positivo.
Selectiva alternativa: if-then-else.
Esta estructura significa abandonar el curso secuencial de ejecución para ejecutar un
cierto grupo de instrucciones si la condición evaluada en tiempo de ejecución del
programa resulta verdadera. Si la condición resulta ser falsa, el programa continúa su
flujo secuencial de ejecución.
IF condición es verdadera
THEN
Ejecutar este bloque de instrucciones llamadas V
ELSE
Ejecutar este bloque de instrucciones llamadas F
END_IF
En esta estructura, si la condición es verdadera se ejecuta un grupo de instrucciones
llamado "instrucciones-V" y si la condición es falsa, se ejecuta otro grupo distinto de
instrucciones, llamado "instrucciones-F". Cuando termina la ejecución de cualquiera de
los dos grupos, la ejecución prosigue a continuación del delimitador END_IF en el
texto anterior escrito en pseudocódigo. El flujograma correspondiente a esta estructura
está incluido en la figura 4.
V
bloque de
instrucciones V
if-then-else
condición
F
bloque de
instrucciones F
©2003, José María Foces Morán.
6/59
Usad esta numeración:
81 de 357
FIGURA 4
Ejemplo. Supongamos que los registros AL y BL contienen sendos caracteres ASCII de 8
bits. Realizar un programa que presente en pantalla el que contenga el valor ASCII menor de
los dos.
IF AL <= BL
THEN
Presentar el carácter que se encuentra en AL.
ELSE
Presentar el carácter que se encuentra en BL.
END_IF
Antes de codificar en ensamblador el módulo anterior escrito en pseudocódigo, recordemos de
las prácticas que la función 02h del MS-DOS (Interrupción software 21h), nos permite sacar
por pantalla el carácter que le pasemos en el registro DL.
MOV AH,2 ; Vamos a llamar a la int 21, función 2.
CMP AL, BL ; ¿AL 7 BL?
JNBE ELSE_ ; Salta a la etiq. ELSE_ si AL NO es menor o igual que BL. En
este caso, la comparación es sin signo, puesto que los caracteres ASCII
son enteros sin signo.
MOV DL, AL ;El más pequeño está en AL, lo llevamos a DL.
JMP DISPLAY ;Saltamos a DISPLAY para llamar a la int 21h puesto que dl ya
contiene el carácter ASCII que deseamos imprimir.
ELSE_: MOV DL, BL ;El más pequeño está en AL, lo llevamos a DL.
DISPLAY: INT 21h
Hemos usado la etiqueta ELSE_ porque, como veremos mas adelante, la palabra ELSE es
una palabra reservada del Macro Ensamblador. La condición AL <= BL se expresa mediante
la instrucción CMP AL, BL: si su evaluación resulta falsa, el programa se salta las
instrucciones-V saltando directamente a la etiqueta ELSE_. Usamos la instrucción JNBE
(Salta si no es menor o igual), porque estamos comparando caracteres ASCII los cuales como
sabemos son bytes -8 bits- y no tienen signo.
Si AL<=BL es verdadero, se ejecutan las instrucciones-V. Utilizamos la instrucción JMP
DISPLAY para saltar a la etiq. DISPLAY y no ejecutar las instrucciones-F.
Selectiva múltiple: case.
En esta estructura se evalúa una expresión de la que, pueden derivarse más de dos
valores y, el salto, por tanto, puede producirse también a más de dos destinos. En la
mayoría de las ocasiones, los valores producidos por la expresión, pertenecen a varios
rangos. La pertenencia a un rango específico será el criterio para decidir a qué etiqueta
se toma el salto.
©2003, José María Foces Morán.
7/59
Usad esta numeración:
82 de 357
Revisemos el pseudocódigo correspondiente a esta estructura:
CASE expresión
rango_1: Ejecutar este bloque de instrucciones(1) y saltar a la
etiqueta END_CASE.
rango_2: Ejecutar este bloque de instrucciones(2) y saltar a la
etiqueta END_CASE.
...
rango_n: Ejecutar este bloque de instrucciones(3) y saltar a la
etiqueta END_CASE.
END_CASE
En esta estructura, se evalúa la expresión y, si su valor corresponde con alguno de los
miembros llamados rango_i, entonces se ejecuta el bloque de instrucciones(i) y se
abandona la estructura saltando a la etiqueta END_CASE. Se supone que los valores
incluidos en cada uno de los rango_i forman un conjunto disjunto.
Ejemplo. Si AX contiene un número negativo, cargar un -1 en BX; si AX contiene 0,
cargar un 0 en BX; si AX contiene un número positivo, cargar un 1 en BX.
CASE AX
<0: Poner -1 en BX
=0: Poner 0 en BX.
>0: Poner 1 en BX.
END_CASE
Vamos a traducir el programa escrito en pseudocódigo a Macroensamblador:
CMP AX, 0 ;Comparamos AX con 0. La instrucción CMP afectará a los flags
zf, sf, of y cf de flags, esto nos permitirá decidir si el número es
mayor que cero (positivo), menor que cero (negativo) o es gual a cero.
JL NEGATIVO ; Jump Less, saltar a la etiqueta NEGATIVO si AX es menor que
0 considerado AX sin signo.
JE CERO ; Jump Equal, saltar a la etiqueta CERO si AX es igual a 0.
JG POSITIVO ; Jump POSITIVO, saltar a la etiqueta POSITIVO si AX es mayor
que 0 considerado AX sin signo.
NEGATIVO: MOV BX, -1 ; Cargar un –1 en bx.
JMP END_CASE ; Abandonar la estructura
CERO:
MOV BX, 0 ; Cargar un 0 en bx.
JMP END_CASE ; Abandonar la estructura
POSITIVO: MOV BX, 1; Cargar un 0 en bx. En este último caso, no es
necesario saltar explícitamente a END_CASE puesto que END_CASE es la
siguiente a ésta –el salto es implícito.
END_CASE:
©2003, José María Foces Morán.
8/59
Usad esta numeración:
83 de 357
3. Estructuras repetitivas.
En este tipo de estructuras, un bloque de instrucciones se repite un cierto número de
veces. Este número de veces puede estar prefijado de antemano o, también, puede
depender de condiciones que se reevaluan al principio o al final de cada iteración
(repetición del bloque). Estudiemos una a una estas estructuras comenzando por la
estructura conocida con el nombre FOR.
Estructura WHILE-LOOP.
Este bucle consiste en la ejecución de un bloque de instrucciones un número variable de
veces. El número total de iteraciones depende de en qué iteración precisa no se verificó
la condición lógica de mantenimiento del bucle. En una iteración cualquiera, se ejecuta
el bucle y se comprueba si se cumple la condición, en caso de que sí se cumpla, se
ejecuta otra vez y en caso de que no, se abandona la estructura.
En este tipo de bucle, el número de veces que se va a producir la ejecución del bloque
de instrucciones, no es conocido de antemano: la repetición se produce si en la
iteración actual, se evalúa como verdadera una determinada condición lógica,
significativa en el estado algorítmico del programa en ejecución. En pseudocódigo, el
bucle WHILE tomaría la forma siguiente:
WHILE condicion DO
Bloque de instrucciones
END_WHILE
Realicemos el flujograma y, a continuación un ejemplo:
do-while
bloque de
instrucciones
V
condición
F
FIGURA 5
Ejemplo: Vamos a escribir un programa en Macroensamblador que cuente el número
de caracteres que el usuario ha entrado desde el teclado. El usuario indica que no va a
introducir mas caracteres entrando un RETORNO_DE_CARRO ( cr, carácter ASCII
0Dh). En primer lugar vamos a escribir el programa en pseudocódigo:
inicializar cuenta de caracteres entrados a 0
leer un carácter
WHILE caracter <> cr DO
©2003, José María Foces Morán.
9/59
Usad esta numeración:
84 de 357
cuenta = cuenta + 1
leer un carácter
END_WHILE
Ahora vamos a transformar el programa escrito en pseudocódigo a Macroensamblador:
XOR DX, DX ; Seleccionemos el registro DX como registro de cuenta de
caracteres. Cargamos un 0 en DX.
MOV AH, 1 ; Usaremos la func. 1 de la int 21h para leer caracteres desde
el teclado.
INT 21h
; Leemos el primero de ellos.
WHILE_ : CMP AL, 0D; ¿AL contiene un valor 0Dh (ASCII cr)?
JE END_WHILE ; Sí, al contiene un cr, salimos.
INC DX
; No, al contiene un valor distinto de cr, por
tanto, incrementamos la cuenta de caracteres leídos.
INT 21h
; Leer otro carácter.
JMP WHILE_
; Saltar a a WHILE_, donde se comprueba a ver si es
el último carácter o no.
END_WHILE: ...
Estructura repetitiva FOR.
En esta estructura el número de veces que va a tener lugar la ejecución del bloque de
instrucciones, está prefijado de antemano.
FOR n veces DO
bloque de instrucciones
END_FOR
Cada vez que se termina de ejecutar el bloque de instrucciones, una variable de control
del bucle, un contador, se retrasa en una unidad y se compara con cero, si la variable ha
llegado a cero, se abandona la estructura y, si la variable no ha llegado a cero, se
reinicia el bloque otra vez. En realidad, esta estructura es un bloque DO-WHILE en el
que el valor de la variable de control se establece antes de ejecutar la primera iteración.
Actualización
de n
bloque de
instrucciones
Inicialización
de n (variable de
for
V
condición de
repetición
control)
F
FIGURA 6
©2003, José María Foces Morán.
10/59
Usad esta numeración:
85 de 357
El lenguaje Macroensamblador contiene una instrucción llamada LOOP que se emplea
para realizar bucles for en los que la variable de control se decrementa en una unidad
en cada iteración, ie., la condición de mantenimiento consiste en que la variable de
control sea distinta de cero.
La instrucción LOOP acepta una dirección de salto relativo y, cada vez que se ejecuta,
LOOP decrementa el registro CX en una unidad y si el resultado es distinto de cero,
LOOP salta a la etiqueta escrita al lado del mnemónico (la dirección de salto relativo),
si el resultado es cero, LOOP permite la ejecución secuencial de la siguiente
instrucción a ella en memoria.
Ejemplo. Escribir un programa que imprima una línea 80 asteriscos en pantalla.
Escribamos, en primer lugar, el pseudocódigo de este programa empleando la
estructura FOR.
FOR 80 veces DO
Imprimir en pantalla un * en la posición actual del cursor
END_FOR
Procedamos, ahora, a traducir el programa a Macroensamblador empleando la
instrucción LOOP de la arquitectura IA:
MOV CX, 80
;Cargamos un 80 en el registro CX. CX es el registro
empleado por LOOP como variable de control del bucle.
MOV AH,2
;Emplearemos la Función 2 de la int 21h cuya definición es
“Imprimir en pantalla el carácter ASCII contenido en DL”.
MOV DL, '*' ;Cargamos en DL el carácter a imprimir.
TOP: INT 21h;Imprimir el carácter contenido en DL.
LOOP TOP
; Ya hemos impreso un carácter, por tanto, deberemos
decrementar la variable de control del bucle:
CX <-- CX - 1; Si CX <>0
ir a TOP. Estas acciones son llevadas a cabo automáticamente por la
instrucción LOOP.
...
; Si CX ha llegado a ser cero, ya se habrán impreso los 80
caracteres en pantalla, por tanto, el programa deberá continuar su
ejecución.
Debemos fijarnos en un hecho notable. Este bucle, tal como lo hemos implementado en
el ejemplo anterior, independientemente del valor que contenga CX inicialmente, se
ejecuta al menos una vez, lo cual entra en contradicción con la especificación dada para
la estructura for. Resulta necesario, pues, añadir una instrucción que compruebe que el
valor inicial contenido en CX es distinto de cero.
©2003, José María Foces Morán.
11/59
Usad esta numeración:
86 de 357
Supongamos que por error CX valiese cero antes de llegar a la etiqueta TOP; al llegar a
LOOP TOP el registro CX se decremenataría y pasaría a valer FFFFh. Produciría un
error serio porque ¡El bucle se ejecutaría 65535 veces! Para evitar este error, contamos
con una instrucción de salto un tanto especial: JCXZ etiq_destino. Esta instrucción
salta a etiq_destino si CX vale 0. Vamos, por tanto, a mejorar el programa añadiendo
dicha instrucción aunque en este caso no sea estrictamente necesario dado que hemos
inicializado CX a valor 80 explícitamente.
La situación explicada en el párrafo anterior sí puede darse en aquellos casos en los
que la estructura FOR se encuentra en un procedimiento. En tales casos, debemos
asegurarnos de que la variable de control no tiene un valor nulo al comienzo, quizás
producido por un error en un programa ajeno que pretende usar los servicios de
nuestro procedimiento.
MOV CX, 80
;Número de asteriscos.
MOV AH,2
;Función 2 de la int 21h.
MOV DL, '*'
;El carácter a imprimir.
JCXZ FIN_FOR ; Si CX = 0 saltar a FIN_FOR.
TOP: INT 21h ;Imprimir el carácter que está cargado en DL.
LOOP TOP ; CX <-- CX - 1; Si CX <>0 saltar a TOP
FIN_FOR: ...; Si no, continuar.
Estructura REPEAT-LOOP.
En esta estructura, al revés que en la anterior, la comprobación de la condición de
repetición se lleva a cabo al final. Recordemos que en la estructura WHILE-LOOP
comprobamos si se cumple la condición inmediatamente al principio. Por tanto, en esta
estructura, la ejecución del bucle se produce explícitamente una primera vez, sin
necesidad de que la condición sea comprobada verdadera para esa primera vez. La
expresión de esta estructura en pseudocódigo es la siguiente:
REPEAT
instrucciones
UNTIL condicion
Es necesario hacer una distinción más entre esta estructura y la estructura while. En la
estructura do-while las iteraciones tienen lugar mientras la condición sea verdadera,
“mientras sea verdadera”. Sin embargo, en la estructura do-until, las iteraciones tienen
lugar mientras la condición sea falsa, “hasta que sea verdadera”.
©2003, José María Foces Morán.
12/59
Usad esta numeración:
87 de 357
Realicemos el flujograma y un ejemplo sobre la estructura REPEAT-LOOP escrito en
lenguajeMacroensamblador.
FIGURA 7
F
bloque de
instrucciones
condición
do-until
V
Ejemplo. Escribir un programa que lea caracteres del teclado hasta que el usuario entre un
carácter 'espacio'.
REPEAT
leer un caracter del teclado
UNTIL caracter es espacio
MOV AH,1 ;Para leer caracteres desde el teclado, usamos la funcion número
1 del MS-DOS® (int 21h).
REPEAT:
INT 21h
; Leer caracter desde el teclado y cargarlo en AL.
CMP AL, ' ' ; ¿Contiene al un espacio?
JNE REPEAT
; Saltar a REPEAT si al no es igual a un ‘ ‘. En otro
caso continuar con la siguiente instrucción porque al sí contiene un
espacio –el carácter que indica el final de la entrada de caracteres.
Ejercicios resueltos sobre las estructuras básicas.
En cada uno de los ejercicios que siguen, vamos a traducir las estructuras
representadas en pseudocódigo a lenguaje Macroensamblador. Se trata de programas
sin una utilidad práctica, cuyo propósito es aprender a traducir unas especificaciones
estrictas y precisas, escritas en pseudocódigo, a lenguaje Macroensamblador.
Ejercicio resuelto. Estructura if-then.
IF ( AX < -16 )
THEN Cargar -63 en BX ;Bloque “verdadero”
END_IF
©2003, José María Foces Morán.
13/59
Usad esta numeración:
88 de 357
La acción “cargar –16 en bx” no se ha de ejecutar si ax >= -16. Esta es la forma más
conveniente de interpretar esta estructura. De este modo, usaremos un salto
condicional con signo que lleve la ejecución a la salida de la estructura en el caso
mencionado. En otro caso, ese salto condicional no se tomará y entonces se ejecutará el
bloque correspondiente a la condición inicial verdadera.
cmp ax, -16 ; Comparar ax con –16 y actualizar flags
jge end_if ; Saltar el bloque “verdadero” si ax >= -16 Esta comparación
es con signo ya que el mnemónico empleado es “Jump Greater or Equal”.
mov bx, -63 ;Este es el bloque “verdadero”
end_if:
Ejercicio resuelto. Estructura if-then-else.
IF ( (AX > BX) OR (BX < CX) )
THEN
Cargar la constante binaria 11001100 en DX
;Este es el bloque “verdadero”
ELSE
Cargar la constante binaria 10101101 en DX
;Este es el bloque “falso”
END_IF
En este caso, puesto que el enunciado no rompe la ambigüedad acerca del carácter con
o sin signo de los datos contenidos en ax, bx y cx, decidiremos que son sin signo. Este
es el caso normalmente cuando los programas manejan constantes literales en base
binaria, pero, debemos insistir en que esta forma de romper la ambigüedad se deriva
exclusivamente de la costumbre y, por tanto, la opción con signo sería igualmente
aceptable.
cmp ax, bx ; Comparamos ax con bx
ja then ; Si ax es mayor que bx (sin signo) saltar a then
cmp bx, cx ; ax no es mayor que bx, comparar entonces bx con cx
ja then ; Si bx es mayor que cx (con signo) saltar a then
mov dx, 011001100b ; Este es el bloque “falso”
j end_if ;Es necesario que saltarse el bloque verdadero porque en el caso
presente, no debe ejecutarse.
then: mov dx, 010101101b ;Este es el bloque verdadero
end_if:
Ejercicio resuelto. Estructura case.
Leer caracter desde el teclado
CASE caracter
es una 'A':
Imprimir
Retornar
es una 'B':
Imprimir
Retornar
una ‘a’.
al MS-DOS®.
una ‘b’.
al MS-DOS®.
©2003, José María Foces Morán.
14/59
Usad esta numeración:
89 de 357
es una ‘C’:
Imprimir una ‘c’.
Retornar al MS-DOS®.
es cualquier otro caracter: Retornar al MS-DOS®.
END_CASE
En este ejercicio emplearemos las funciones Read Keyboard Echo, Display Character y
Terminate Program del MS-DOS®. Por lo demás, la implementación del programa
consiste casi exclusivamente en traducir línea a línea el pseudocódigo. ¿Es este
programa un programa limpio?
mov ah, 01h ; Cargar en ah el número de función correspondiente a Read
Keyboard with Echo (Leer del teclado con echo, ie., al mismo tiempo que
se lee un carácter desde el teclado, ese carácter se imprime en la
pantalla).
cmp ah, ‘A’
je es_A
cmp ah, ‘B’
je es_B
cmp ah, ‘C’
je es_C
Distinto: mov ah, 0; Si el programa ha llegado a este punto, eso
significa que el carácter recién entrado es distinto de A,B o C, por
tanto, debemos cargar en ah el número de la función Terminate Program, un
cero.
int 21h ;y...llamar a la función Terminate Program.
es_A:mov dl, ‘a’
j imprimir
es_B:mov dl, ‘b’
j imprimir
es_C:mov dl, ‘c’
imprimir: mov ah, 02h; Cargar en ah el número de la función Display
Character.
int 21h ; Y de hecho llamar a la función 02.
J distinto ;Después de imprimir el carácter en pantalla, terminamos el
programa según está indicado en el programa escrito en pseudocódigo.
Se puede comprobar fácilmente que el programa anterior sí es un programa limpio:
posee un único punto de entrada y un único punto de salida y todos sus nodos tienen
una única conexión desde la entrada y con la salida.
Ejercicio resuelto. Estructura for-do.
for 1000 veces do
Rotar ebx dos veces a la derecha
Cambiar de signo eax
Sumar ebx a eax
end_for
©2003, José María Foces Morán.
15/59
Usad esta numeración:
90 de 357
Puesto que en esta ocasión la estructura que tenemos que implementar es una
estructura for y la arquitectura IA posee una instrucción específica para realizar este
tipo de bucles, realizaremos dos implementaciones, la primera de ellas usando la
instrucción LOOP y la siguiente, utilizando comparaciones y saltos condicionales.
;Implementación con la instrucción LOOP
mov cx, 1000 ; Cargamos el 1000 en cx puesto que loop usa cx como
registro contador.
next:
ror ebx, 2 ; Rotar ebx dos veces a la derecha.
neg eax ; Cambiar eax de signo (complemento a 2).
add eax, ebx; Sumar ebx a eax
loop next; Si ( cx != 0 ) hacer cx—- y saltar a next
;Implementación usando instrucciones de comparación y salto condicional.
mov di, 1000; Cargar un 1000 en di. Elegimos di para llevar la cuenta de
veces
next:
ror ebx, 2 ; Rotar ebx dos veces a la derecha.
neg eax ; Cambiar eax de signo (complemento a 2).
add eax, ebx; Sumar ebx a eax
dec di ; Decrementar di
jnz next; Si el decremento anterior no produjo un cero,
saltar a next: di posee uno de los 1000 valores (incluido el propio 1000)
necesarios para que la estructura se repita 1000 veces.
Ejercicio resuelto. Estructura do-while.
while ( doubleword leída del puerto 5588h < -1 ) do
Escribir un –1 en el puerto 04fh
Incrementar contador de doublewords escritas
end_while
Para implementar esta estructura, será necesario hacer uso de las instrucciones in y out.
Estas instrucciones transfieren datos entre la memoria y los puertos de e/s.
En este programa vamos a habilitar un contador que cuente los caracteres que se han
transferido, sin embargo, este contador no se va a emplear para decidir en que
momento se rompe la ejecución del bucle como en los casos anteriores. Con toda
seguridad, este contador va a ser empleado por el programa en un contexto posterior,
pero, debemos insistir que el bucle en sí, no necesita ningún contador para controlar su
ejecución ya que el que se produzca o no la iteración siguiente, depende sólo y
exclusivamente del carácter del dato leído en la iteración presente.
xor edi, edi; Ponemos a 0 el registro edi
mov dx, 5588h; El inmediato que representa el número de puerto en una
instrucción in o out es de un ancho máximo de 8 bits. En caso de que
©2003, José María Foces Morán.
16/59
Usad esta numeración:
91 de 357
resulte necesario especificar un número de puerto superior a ffh, la
instrucción permite usar el registro dx como número (dirección) de
puerto.
next:
in eax, dx; Leer doubleword del puerto 5588h
cmp eax, -1; Comparar con -1
jge fin; Si ( eax >= -1 ) salir
mov eax, -1; Cargar un –1 en eax
out 4fh, eax; Transferir la doubleword cargada en eax al
puerto de e/s 4fh
inc edi; Incrementar el contador de dw’s escritas
jmp next; Repetir el bucle.
Ejercicio resuelto. Estructura repeat-until.
repeat
Dividir bx por 2
until ( bx == 0 )
El bucle contenido en esta estructura se repite al menos una vez, la primera.
Ciertamente, como la condición de iteración no se verifica al entrar en el bucle,
independientemente de cualquier circunstancia, este bucle se va a ejecutar una vez.
Para dividir bx por 2, usaremos una instrucción de desplazamiento a la derecha. Como
no sabemos el carácter del entero contenido en bx, supondremos que es sin signo, por
tanto, usaremos la instrucción de desplazamiento lógico a la derecha. Esta instrucción,
al desplazar el dato a la derecha, no mantiene el signo del dato, si fuera negativo, al
desplazarlo una vez a la derecha, se convertiría en positivo. Se trata pues de un
desplazamiento lógico(shr), no aritmético. Si quisiéramos efectuar el desplazamiento a
la derecha manteniendo el signo, utilizaríamos la instrucción de desplazamiento
aritmético a la derecha (sar).
bucle:
por 2).
shr bx, 1 ; Desplazar bx un lugar a la derecha (dividir bx
jnz bucle ; Repetir si ( bx != 0 )
Ejemplo. El cálculo del mínimo de un array. En este ejemplo nos proponemos realizar un
programa que determine cuál de los elementos de un array es el mínimo.
Al igual que en lenguaje C, en Macroensamblador podemos crear arrays de datos de
cualquier tipo. Basta con emplear la directiva de reserva de espacio adecuada y los
inicilializadores que correspondan al caso.
En este ejemplo vamos a crear un array de enteros con signo con ancho 16 bits y vamos
a inicializarlo mediante constantes incluidas en el texto del programa. La variable que
vamos a emplear para identificar la dirección base del array será, por ejemplo, array.
©2003, José María Foces Morán.
17/59
Usad esta numeración:
92 de 357
La rutina que determina el mínimo, asume que el elemento inicial del array es el
mínimo. El registro que elegimos para tener cargado el mínimo en todo momento será
el registro ax. Para referenciar cada uno de los elementos del array, utilizaremos el
modo de direccionamiento índice+desplazamiento y, cuando hayamos terminado de
usar cada elemento del array, incrementaremos el registro índice usado, si por ejemplo,
en 2 unidades ya que el tamaño de cada elemento del array es de 2 bytes. En la figura
siguiente aparece el modelo funcional de la rutina min.
min: modelo
funcional
cx: número de
elementos a ordenar
min proc near
bx: dirección base de
array
bx: dirección del
mínimo dentro de array
La rutina leerá el elemento actual del array y lo comparará con ax y si de la
comparación se deduce que hemos encontrado un elemento menor, entonces
intercambiaremos el elemento del array recién leído, con el mínimo calculado hasta el
momento. Este proceso continuará hasta que el índice del array haya tomado el valor
(Número de elementos del array+1)*2.
El flujograma incluido en la figura 8 representa una implementación estructurada de
este algoritmo, la cual se puede usar para escribir el código en lenguaje
Macroensamblador. El siguiente programa es la implementación final del mismo.
.model small; El modelo de memoria
.386;Permitir el uso de instrucciones del i386xx
.data;Comienzo del segmento de datos
;La siguiente línea de código significa que el símbolo array está
asociado con el desplazamiento de la dirección de memoria donde está
guardada la parte baja de la constante 6700 –dentro de este segmento de
datos (.data). Por tanto, nos sirve como desplazamiento base para
direccionar cada una de las words que aparece en la declaración. Además,
la propia dirección de memoria ds:[array] y las 7*2 siguientes se
inicializarán con los valores dados en la lista. Estos valores se
interpretarán como sword, ie., words de 16 bits con signo.
array sword 6700, 345, 2030, 100, 2000, -32768, 1000, 63
n equ 8;n es una constante con valor 8, NO es una dirección de memoria,
es una constante cuyo valor se va a usar en tiempo de ensamblaje: tantas
veces como aparezca el símbolo n en el texto fuente del programa será
sustituido por su valor en las expresiones en las que se haya usado. n
representa el número de elementos (del tipo sword) presentes en el array.
;En la línea siguiente se usa el valor de n, para multiplicarlo por 2. El
resultado es una nueva constante llamada fuera_del_array. fuera_del_array
©2003, José María Foces Morán.
18/59
Usad esta numeración:
93 de 357
representa el índice de la primera sword que, a partir de array, se
encuentra fuera de éste.
fuera_del_array equ n * 2
.code;Comienzo del segmento de código
.startup; Esta directiva “contiene” el código de entrada a este programa
desde MS-DOS®
xor si, si; Poner un 0 en el índice inicial del array
mov ax, [array+si]Cargar en el registro reservado para el
mínimo (ax) el primer elemento del array.
busqueda:
cmp ax, [array+si];Comparar el elemento actual del array con
el mínimo obtenido hasta este punto
jl siguiente;Si el mínimo es menor que el elemento actual,
saltar a siguiente.
mov ax, [array+si];Mediante la instrucción anterior, hemos
determinado que el elemento actual del array es menor que el mínimo, por
tanto, cargamos en ax el nuevo mínimo encontrado
siguiente:
add si, 2;Hacer que si apunte al elemento
siguiente del array
cmp si, fuera_del_array;¿si apunta a un
elemento perteneciente al array?
jb busqueda;Si si apunta a un elemento del
array, saltar a busqueda
;si apunta “fuera” del array, por
tanto el programa ha terminado de explorar el array
.EXIT 0 ;Retorno ordenado al MS-DOS®
END ; Fin del texto fuente del programa.
©2003, José María Foces Morán.
19/59
Usad esta numeración:
94 de 357
n=8
fuera_del_array = 2 * n
.startup
minimo.asm
si <-- 0
ax<-- array[si]
ax >= array[si]
F
V
ax<-- array[si]
si<--si+2
V
si<fuera_del_array
F
.exit 0
FIGURA 8
Los procedimientos en Macroensamblador.
En este apartado vamos a estudiar el concepto de subrrutina, el cual permite ejecutar
una misma pieza de código sin necesidad de duplicar su presencia en memoria cada
vez que es necesitada. En un instante dado un programa efectúa un salto a una
subrrutina y, un cierto tiempo más tarde, la subrrutina termina de ejecutarse. En ese
©2003, José María Foces Morán.
20/59
Usad esta numeración:
95 de 357
instante, la subrrutina salta al programa principal, a la dirección de memoria que
contiene la instrucción siguiente a la instrucción de salto. Si, más tarde, desde una
posición distinta dentro del programa principal, se produce un nuevo salto a la
subrrutina, se repite el proceso, pero, en esta ocasión ha de producirse el retorno a una
posición del programa principal distinta de la del caso anterior. Es decir, cada vez que
se usa la subrrutina, al terminar su ejecución, ésta salta hacia “atrás”, retorna al
programa que la llamó y continua la ejecución de éste en el mismo punto donde la
había abandonado. Ahora surge la pregunta acerca de cómo se almacena la dirección
de retorno: ¿Dónde se almacena la dirección de retorno en cada llamada?
En la arquitectura IA, la dirección de retorno de subrrutina se inserta en la pila justo
antes de efectuar el salto y, por consiguiente, la instrucción de retorno de subrrutina,
extrae esa dirección de la pila y la usa para efectuar un salto –al programa principal.
Las instrucciones de salto incondicional capaces de saltar y retornar a subrrutinas son
las instrucciones call y ret. La instrucción call salva la dirección de retorno en la pila y
salta a una subrrutina. La instrucción ret extrae de la pila la dirección de retorno y salta
a esa dirección. En este momento resulta muy conveniente regresar al capítulo
dedicado al Set de Instrucciones y leer con atención la descripción y los ejemplos sobre
las instrucciones de manejo de la pila y las instrucciones de salto a subrrutina.
Una vez introducida la noción de subrrutina, vamos a estudiar con detalle las
instrucciones de la arquitectura IA que tienen que ver con saltos a subrrutina, sus
semánticas y algunos detalles de su implementación. Antes, sin embargo, conviene
hacer algunas notas aclaratorias sobre la terminología empleada. Usaremos una
terminología que esté de acuerdo con la terminología empleada por Intel y por
Microsoft en la documentación de la arquitectura IA y del Macroensamblador,
respectivamente. De acuerdo con esto, emplearemos el término procedimiento en vez
de subrrutina y, frecuentemente, en vez de usar el término “llamada” a procedimiento,
usaremos el término “invocación”.
Las instrucciones call y ret en la arquitectura IA.
La instrucción call produce un salto a un procedimiento. call salva en la pila la
dirección de retorno. Esta dirección es una dirección de memoria real, por tanto,
debemos conocer su segmento y su offset. El procesador salva ambas componentes,
segmento y offset, en la pila. Cuando el procedimiento decide retornar al programa
principal, ejecuta una instrucción ret. Esta instrucción extrae el segmento y el offset
guardados en la pila y los carga en los registros cs y ip, respectivamente.
En ocasiones puede ocurrir que tanto el programa principal como el procedimiento
residen en el mismo segmento físico de memoria principal. En esos casos, no sería
necesario salvar el registro cs cuando se ejecuta una instrucción call, con lo cual nos
ahorraríamos un tiempo que puede ser interesante. ¿Es posible ejecutar una instrucción
call que no salve el registro cs? La respuesta es si. La arquitectura IA contiene dos
versiones de la instrucción call, cada una de ellas con su propio código de operación.
La primera versión sí salva el registro cs, la segunda versión sólo salva el registro ip. El
©2003, José María Foces Morán.
21/59
Usad esta numeración:
96 de 357
programa Macroensamblador, en tiempo de ensamblaje, decide qué versión de call que va
a emplear en cada caso, según ciertas propiedades asignadas por el programador al
procedimiento llamado. Por tanto, existen dos tipos de procedimiento, dependiendo
de la distancia que separa a un procedimiento dado de su programa principal –o de
otros procedimientos. Si la distancia es superior a un segmento físico, o sea 64Kb,
entonces decimos que le procedimiento es del tipo far, lejano. Si la distancia es inferior
a 64Kb, entonces decimos que el procedimiento es near. El programa
Macroensamblador ofrece directivas que nos permiten crear procedimientos near y far.
Existen también dos versiones de la instrucción ret. Lógicamente, si la instrucción call
empleada es del tipo lejano, far, las instrucciones ret empleadas para retornar desde
han de ser del tipo lejano y, si la instrucción call empleada es del tipo cercano, near, las
instrucciones ret empleadas para retornar desde han de ser del tipo cercano.
Funcionamiento de la versión near de call y ret.
Cuando el procesador ejecuta una instrucción near call, efectúa las siguientes
operaciones en orden temporal:
1. Inserta el valor del registro eip en la pila. Esta inserción tiene lugar
respetando el protocolo de inserciones en la pila, de forma similar a la
instrucción push, ie., se decrementa el registro sp en 2 unidades y entonces
se produce la transferencia de la word a la memoria. También, es
conveniente recordar que en el instante preciso en el que se salva eip, su
valor apunta a la instrucción siguiente en memoria, no apunta, pues, a la
instrucción que actualmente está siendo ejecutada por el procesador.
2. Carga el offset del procedimiento al que se va a saltar, en el registro eip.
3. Comienza la ejecución del procedimiento.
La ejecución de cualquiera de las instrucciones ret incluidas en el procedimiento
llamado en el párrafo anterior, sería del siguiente modo:
1. Extrae una word de la pila y la carga en el registro eip. Esta word es el offset
de la dirección de retorno salvado con la instrucción call. Como en el caso
anterior, la extracción de la word desde la pila, se lleva a cabo respetando el
protocolo de extracciones de la pila: se carga la word en el registro elegido
(en este caso eip) y se incrementa el registro sp en dos unidades.
2. La ejecución del programa se ha “reencontrado” con el programa principal,
en la dirección de memoria donde se abandonó su ejecución cuando se
produjo la llamada al procedimiento.
©2003, José María Foces Morán.
22/59
Usad esta numeración:
97 de 357
Funcionamiento de la versión far de call y ret.
En el caso de la instrucción far call, el procesador efectúa las siguientes operaciones:
1. Inserta en la pila el valor actual del registro cs.
2. Inserta en la pila el valor actual del registro eip.
3. Carga el segmento del procedimiento al que se va a saltar, en el registro cs.
4. Carga el offset del procedimiento al que se va a saltar, en el registro eip.
5. Comienza la ejecución del procedimiento.
La ejecución de cualquiera de las instrucciones ret incluidas en el procedimiento
llamado en el párrafo anterior, transcurriría del siguiente modo:
1. Extrae una word de la pila y la carga en el registro eip. Esta word es el
segmento de la dirección de retorno salvado con la instrucción call.
2. Extrae una word de la pila y la carga en el registro cs. Esta word es el offset
de la dirección de retorno salvado con la instrucción call.
3. La ejecución del programa se ha “reencontrado” con el programa principal,
en la dirección de memoria donde se abandonó su ejecución cuando se
produjo la llamada al procedimiento.
En las figuras siguientes se detalla el funcionamiento de las dos versiones de las
instrucciones call y ret.
©2003, José María Foces Morán.
23/59
Usad esta numeración:
98 de 357
Programa
.data
principal
.stack 1024
.code
...
...
1000H:2200H call promedio
1000H:2203H ...
...
...
1000H:6400H call promedio
1000H:6403H ...
...
...
.exit 0
00F7
00F8
00F9
00FA
00FB
00FC
00FD
00FE
00FF
0100
Stack
ss 6700H
sp 00FEH
03
22
promedio PROC near
1000H:9000H ... procedimiento
...
...
1000H:9010H ret
promedio ENDP
sp 0100H
ip 2203H
ip 9000H
ip 9010H
ip 2203H
cs 1000H
cs 1000H
cs 1000H
cs 1000H
Leyenda
1. call reserva espacio
en la pila, salva la
dirección de retorno y
salta a 9000H.
2. ret transfiere la
dirección de retorno
desde la pila a ip
(2203H) y libera el
espacio ocupado en la
pila.
FIGURA 9
©2003, José María Foces Morán.
24/59
Usad esta numeración:
99 de 357
.data
.stack 1024
Programa
.code
principal
...
...
1000H:2200H call promedio
1000H:2203H ...
...
...
1000H:6400H call promedio
1000H:6403H ...
...
...
.exit 0
00F7
00F8
00F9
00FA
00FB
00FC
00FD
00FE
00FF
0100
Stack
ss 6700H
sp 00FEH
03
22
promedio PROC far
7700H:9000H ...
... procedimiento
...
7700H:9010H ret
promedio ENDP
sp 0100H
ip 2203H
ip 9000H
ip 9010H
ip 2203H
cs 1000H
cs 7700H
cs 7700H
cs 1000H
Leyenda
1. call reserva espacio en la pila, salva la
dirección de retorno completa (segmento y
offset) y salta a 7700H:9000H.
2. ret transfiere la dirección de retorno
completa (segmento y offset) desde la pila a
cs:ip y libera el espacio ocupado en la pila.
FIGURA 10
Definición de procedimientos en Macroensamblador
El Macroensamblador incluye dos directivas, proc y endp, las cuales marcan el inicio y
el final de un procedimiento. La directiva proc tiene otros usos complementarios, tal
como veremos.
Al igual que las instrucciones de salto normales, la instrucción de salto a procedimiento,
call, requiere una dirección efectiva de salto. Esta dirección efectiva puede
©2003, José María Foces Morán.
25/59
Usad esta numeración:
100 de 357
especificarse empleando los modos de direccionamiento aceptados por esta
instrucción. La sección dedicada a la instrucción call en el tema sobre el Set de
Instrucciones, contiene esta información. Una de las formas más comunes de
especificación de la dirección de salto consiste en especificar la etiqueta que representa
la dirección de salto, directamente después del mnemónico de la instrucción, como en
el siguiente ejemplo:
call vectorizar
Al definir el procedimiento, es necesario incluir la etiqueta que lo representa junto con
la directiva proc. En el ejemplo que sigue, la directiva proc comienza la definición de
un nuevo procedimiento llamado vectorizar:
vectorizar proc
La directiva endp se emplea para indicar el final de un procedimiento y, al igual que
proc, requiere la etiqueta usada en el comienzo del procedimiento. En el siguiente
ejemplo, la directiva endp cierra el procedimiento vectorizar:
vectorizar endp
En el cuerpo del procedimiento tiene que incluirse al menos una instrucción de retorno
de procedimiento, ret. Esta instrucción produce el retorno automático al programa que
produjo la llamada al procedimiento. Esta instrucción, normalmente, se pone en la linea
anterior a la directiva endp.
Estudiemos ahora los aspectos acerca de la distancia del salto a procedimiento. La
directiva proc puede complementarse con el uso de ciertos modificadores, entre ellos
se encuentran los modificadores de distancia de salto, near y far. Vamos a realizar un
ejemplo con cada una de ellas, en el primero especificaremos que la distancia de salto
al procedimiento que se está iniciando es near:
; Distancia near, el llamador ha de encontrarse en el
; mismo segmento físico que el procedimiento muestreo
muestreo proc near
En el segundo ejemplo especificaremos que la distancia es far:
; Distancia far, el llamador no ha de encontrarse
; obligatoriamente en el mismo segmento físico que
; el procedimiento ajusteDelta
ajusteDelta proc far
Finalmente vamos a realizar un ejemplo completo.
Ejemplo. Realizar un procedimiento en Macroensamblador llamado modoI0 que ponga a
cero la parte alta del registro eax y que fuerce los bits pares de la palabra baja a 1 y los bits
impares de la misma palabra baja a 0. El procedimiento debe poder ser llamado desde
cualquier dirección de memoria, el resultado deberá cargarse en el registro ebx y el registro
eax no será modificado.
modoI0 proc far
push eax ;Salvar el contenido de eax
and eax, 00005555h
or eax, 00005555h
mov edx, eax ; Salvar el resultado en edx
©2003, José María Foces Morán.
26/59
Usad esta numeración:
101 de 357
pop eax ;Recuperar el contenido de eax
ret
modoI0 endp
Paso de parámetros a procedimientos a través de la pila.
En el ejemplo anterior, el registro eax contiene el dato que va a ser transformado
mediante el procedimiento modoI0. El dato residente en eax está compartido por el
programa llamador y el procedimiento, ambos acceden a él, el llamador carga eax con un
valor adecuado, el cual tiene significación en los procesos de la información llevados a
cabo por el algoritmo usado. El procedimiento toma el valor contenido en eax y lo
transforma de acuerdo con unas determinadas especificaciones funcionales y, cuando
los procesos han sido llevados a cabo con éxito, salva el resultado en ebx y el
procedimiento retorna. Cuando el control ha sido devuelto al programa llamador, éste
encuentra el resultado deseado en ebx y, el dato contenido en eax no ha sido
modificado.
En el proceso que acabamos de describir, podemos imaginar el dato contenido en eax
como un parámetro que es entregado a la rutina modoI0 y, del mismo modo, el
registro ebx podría considerarse como el parámetro devuelto por modoI0.
Ciertamente, modoI0 presenta un comportamiento, esto es, independientemente de los
procesos que tienen lugar en su interior, modoI0 tiene un modelo específico de
entrada-salida: el parámetro de entrada es eax y, el parámetro de salida es ebx.
Concluyendo, un procedimiento puede modelarse como una caja negra que acepta
unos parámetros de entrada, cada uno de ellos claramente especificado y que devuelve
unos parámetros de salida, también, claramente especificados.
El paso de parámetros en Macroensamblador puede llevarse a cabo de diversas
formas, cada uno de ellos con sus ventajas semánticas, sencillez expresiva, efectos
laterales, etc. La forma más sencilla de paso de parámetros a procedimientos consiste
en el uso de registros, pero, cuando se emplea el paso de parámetros a través de
registros, es esencial conocer ciertos detalles.
Contexto y paso de parámetros mediante los registros de propósito general.
Una práctica muy recomendable en el diseño de programas de una cierta envergadura
consiste en particionar la funcionalidad en módulos. Estos módulos pueden verificarse
de forma independiente del resto y además, una vez verificados, pueden integrarse en
proyectos diferentes sin necesidad de recompilación. Más aún, un módulo puede
instalarse permanentemente memoria y ser invocado desde distintos programas
simultáneamente. Los sistemas operativos modernos incluyen formalmente esta
capacidad en lo que se conoce como librerías compartidas o librerías de enlace
dinámico.
Uno de los problemas que presentan estos módulos instalables y las formas de uso de
los mismos, es que, cualquiera de los posibles llamadores y el propio procedimiento
incluido en el módulo, comparten el contexto dinámico del sistema. Por tanto, es
©2003, José María Foces Morán.
27/59
Usad esta numeración:
102 de 357
absolutamente necesario establecer un protocolo que permita al llamador y al
procedimiento llamado preservar aquella parte del contexto dinámico que va a ser
usada por el llamador, de forma que cuando se produzca el retorno, el llamador continúe
efectuando la computación en curso con el contexto dinámico intacto. Puede ser
interesante, ahora, leer la discusión más general acerca del contexto, incluida al final
del presente capítulo.
Concluyendo, aquella parte del contexto dinámico que va a ser modificada por un
procedimiento, debe ser preservada al iniciarse su ejecución y, debe ser restaurada
inmediatamente antes de producirse su retorno. Los dos convenios posibles para
preservar el contexto son contexto salvado por el llamador y, contexto salvado por el
llamado. El convenio más frecuentemente usado en la arquitectura IA es contexto
salvado por el llamado. Por tanto, un procedimiento, normalmente salva aquella parte
del contexto que va a modificar.
En el ejemplo anterior, el procedimiento salva el registro eax en la pila al comienzo y lo
restaura desde la pila, al final. El registro ebx, puesto que va a contener el parámetro
de retorno del procedimiento, no debe ser salvado/restaurado. En este último caso, la
interfaz funcional del procedimiento modoI0 establece este acuerdo entre el llamador y
el llamado.
Pero, el paso de parámetros que hemos explicado, no es la única forma de paso de
parámetros, pueden pasarse parámetros a procedimientos utilizando la memoria, eso
si, las posiciones de memoria empleadas, han de ser conocidas por el llamador y el
llamado. Una zona de memoria muy adecuada para este propósito es la pila, siempre
está disponible y, los elementos de gestión de la estructura, aunque forman parte del
contexto dinámico, están sujetos a unas reglas de uso que son siempre respetadas por
los programas –excepto cuando se cometen errores de programación. Vamos, pues, a estudiar
cómo se puede utilizar la pila como medio de paso de parámetros en llamadas a
procedimiento.
Paso de parámetros usando la pila.
El paso de parámetros a través de la pila puede llevarse a cabo de diferentes formas
que dependen del lenguaje de alto nivel y del sistema operativo concreto empleado.
Cuando se inicia la ejecución de un procedimiento el registro sp apunta a la dirección
de memoria que contiene la dirección de retorno. En las direcciones superiores a ésta,
residen otros datos insertados por el programa llamador. Conociendo esto, es posible,
direccionar esos datos, pero, lo que no se puede hacer, es emplear el registro ss porque
al alterar su valor provocaríamos un retorno incorrecto al programa llamador en el
momento en que el llamado ejecutase la instrucción ret. Lo que sí podemos hacer es
copiar el contenido de ss a un registro de direcciones distinto y usar éste para
direccionar los datos residentes en la pila dentro del segmento de pila. El registro más
adecuado es el registro bp, la razón es que cuando se forman modos de
direccionamiento usando el registro bp como registro base, el registro de segmento
empleado para formar la dirección de memoria real es el registro ss. Si usásemos otro
©2003, José María Foces Morán.
28/59
Usad esta numeración:
103 de 357
registro base, tendríamos que incluir explícitamente el registro ss en la expresión del
modo de direccionamiento.
Al rango de direcciones de memoria que, dentro del segmento de pila, contienen la
lista de parámetros, se las denomina marco de pila –stack frames. El programa llamador
inserta la lista de parámetros en la pila antes de producir la llamada al procedimiento
y, éste, al comenzar a ejecutarse salva el registro bp en la pila y copia sp a bp. A partir
de ese instante, el procedimiento puede acceder a los parámetros pasados en la pila
usando instrucciones mov sobre el segmento ss y formando el modo de
direccionamiento con el registro base bp+desplazamiento. La constante desplazamiento
toma valores 2,4,6, etc., que dependerán de la distancia del salto a procedimiento, near
o far y del número de registros salvados al entrar el procedimiento.
En las secciones que siguen desarrollaremos un programa de ordenación de enteros
que, en sus diversas versiones, ilustra el uso de stack frames como medio de paso de
parámetros a procedimientos. En el ejemplo siguiente, ilustraremos una forma particular
de paso de parámetros muy usada por los compiladores de lenguaje C de la compañía
Microsoft. El ejemplo que vamos a usar es el ejemplo anterior, pero usando el paso de
parámetros a través de la pila. Además, añadiremos a este procedimiento un pequeño
programa principal que nos permita ilustrar cómo este último inserta los parámetros
en la pila.
.model small
.386
.stack 1024
.data
.code
...
push eax ;Insertar el parámetro en la pila.
call modoI0
pop edx ; En este caso, el resultado lo copiamos
;a edx con pop. Así, además logramos reequilibrar
;el puntero de pila.
...
.exit 0
modoI0 proc far
push bp ;Salvar el puntero de marco de pila (bp).
push eax ;Salvar el contenido de eax
mov bp, sp ;Copiar sp en bp.
; Copiar el parámetro de entrada al registro eax
mov eax, [bp+10] ; eax ß mem(ss:[bp+10])
and eax, 00005555h
or eax, 00005555h
; Salvar el resultado en el parámetro de entrada
mov [bp+10], eax
pop eax ;Recuperar el contenido de eax
pop bp ;Restaurar bp
ret
modoI0 endp
©2003, José María Foces Morán.
29/59
Usad esta numeración:
104 de 357
procedimiento
modoI0 proc far
push bp
push eax
mov bp, sp
mov eax, [bp+10]
and eax, 00005555h
or eax, 00005555h
mov [bp+10], eax
pop eax
pop bp
ret
modoI0 endp
Evolución del stack hasta 00F0
el
interior del procedimiento00F1
00F2
salvar eax
00F4
00F5
salvar
bp
dir.retorno
Programa
.model
principal
.386
00F3
(ip)
small
dir.retorno
.stack 1024
.data
.code
...
push eax
call modoI0
pop edx
...
.exit 0
(cs)
00F6
00F7
00F8
00F9
00FA
00FB
00FC
parámetro
eax
00FD
00FE
00FF
sp inicial
sp
0100
inicial 0100h
©2003, José María Foces Morán.
30/59
Usad esta numeración:
105 de 357
procedimiento
modoI0 proc far
push bp
push eax
mov bp, sp
mov eax, [bp+10]
and eax, 00005555h
or eax, 00005555h
mov [bp+10], eax
pop eax
pop bp
ret
modoI0 endp
Evolución del stack hasta 00F0
el
interior del procedimiento00F1
00F2
salvar eax
00F4
00F5
salvar
bp
dir.retorno
Programa
.model
principal
.386
00F3
(ip)
small
dir.retorno
.stack 1024
.data
.code
...
push eax
call modoI0
pop edx
...
.exit 0
(cs)
00F6
00F7
00F8
00F9
00FA
00FB
00FC
parámetro
eax
00FD
00FE
00FF
sp inicial
sp
0100
inicial 0100h
Algoritmos de ordenación y búsqueda.
©2003, José María Foces Morán.
31/59
Usad esta numeración:
106 de 357
Método de ordenación por el
método de selección
El array a ordenar es array.
Inicializar base del sub-array no ordenado, di=0
Inicializar contador de elementos no ordenados, cx=n
Obtener el elemento mínimo, array[bx], de array usando el
procedimiento min
Intercambiar array[di] y array[bx], i.e., intercambiar el
mínimo recién calculado por el elemento base del subarray
no ordenado.
cx--; di += 2;
V
¿di apunta a un
elemento de array?
F
array
ordenado
Un ejemplo avanzado:La ordenación de un array de enteros de 32 bits con signo. En este
ejemplo vamos a desarrollar un programa de ordenación de datos enteros de 32 bits.
Llevaremos a cabo el desarrollo de forma progresiva, pasando por versiones intermedias que
aceptan una implementación excepcionalmente directa en la arquitectura IA y el
Macroensamblador. Comenzaremos por transformar en procedimiento el programa de
obtención del mínimo, de este modo, podremos usarlo reiteradamente para ordenar una
sublista de enteros. Ilustraremos varias técnicas de paso de parámetros, desde el uso de
registros hasta los marcos de pila y por fin el uso de las instrucciones enter y leave. Las
©2003, José María Foces Morán.
32/59
Usad esta numeración:
107 de 357
primeras versiones manejarán datos de 16 bits, la última versión ordenará enteros de 32 bits
con signo.
El primer objetivo de este ejercicio es ilustrar el desarrollo de un programa estructurado, el
cual está formado por varios procedimientos- también estructurados. Así mismo, nos
proponemos comprender con detalle cómo funciona el algoritmo de ordenación por selección.
Finalmente, aprovecharemos el desarrollo para comprobar en la práctica algunos conceptos
sobre macroensamblador y sobre la arquitectura IA como son, el uso de registros de segmento
por defecto, los detalles de implementación de la pila y de los procedimientos, los
modificadores de longitud de salto, las instrucciones enter y leave y algunos otros.
Comencemos recordando el funcionamiento de min0.asm.
min0.asm. El listado del código fuente de min0.asm se encuentra bajo el mismo
epígrafe anterior donde se desarrolla el programa.
Este programa busca el elemento mínimo de un array de words cuya dirección base y
número de elementos son conocidos. Vamos a comenzar el desarrollo transformando este
programa en un procedimiento de Macroensamblador que nos permita efectuar llamadas a
min cuando resulte necesario.
minproc.asm. Nuestro objetivo consiste, pues, en usar el código de min0.asm dentro
de un programa de ordenación de enteros. La forma más conveniente consiste en transformar
el programa min0 en un procedimiento y asegurarnos de que establecemos un protocolo de
paso y retorno de parámetros.
En este primer ejemplo, llevaremos a cabo el paso de parámetros a través de registros del
microprocesador. En el capítulo 3 de P&H se mencionaba que los registros pueden emplearse
como medio de paso de parámetros desde el programa llamador al programa llamado. La
conveniencia de usar los registros como medio de paso de parámetros dependerá de cada caso
concreto; las consideraciones a tener en cuenta tendrían que ver con las semánticas fijadas
en ciertos sistemas operativos, la complejidad computacional del programa concreto y otras
relativas al rendimiento. En este ejemplo emplearemos el paso de parámetros a través de
registros y, en los ejemplos que siguen, emplearemos el paso de parámetros a través de la pila
en diversas formas concretas.
Volvamos a los detalles del programa.¿Qué registro elegiremos para pasar el número de
elementos del array? El registro cx es adecuado, su nombre , además puede manifestar la
“intención” de contar,por tanto, pasaremos el número de elementos del array no ordenado
aún en cx y, en el registro bx, pasaremos su dirección base. La elección del registro bx para
pasar una dirección base es adecuada puesto que este registro puede usarse para formar
direccionamientos en memoria.
Para la dirección base usaremos sólo un registro de direcciones (base en este caso) porque
supondremos que el programa que llama a min y el propio procedimiento min, emplean
ambos, el mismo segmento de datos. Por último, el procedimiento min tiene que devolver la
©2003, José María Foces Morán.
33/59
Usad esta numeración:
108 de 357
dirección –offset- del elemento mínimo del array y, en este caso, vamos a establecer que esa
dirección será devuelta por min, en el registro di. El registro di es un registro de direcciones
y, cuando se le emplea sólo, el segmento direccionado es ds –nuestro caso.
¿Por qué no podemos emplear bp? bp no es conveniente porque el parámetro devuelto
es la dirección de memoria del elemento mayor del subarray no ordenado y, esta
dirección de memoria pertenece al segmento de datos (ds). Sin embargo, cuando en un
direccionamiento se emplea el registro bp, el segmento que se direcciona por defecto es
el segmento de pila (ss). Los dos ejemplos siguientes ilustran esta noción:
mov ax, [bx]; Transferir al registro ax el dato que se encuentra en la posición de
memoria ds:[bx]
mov ax, [bp]; Transferir al registro ax el dato que se encuentra en la posición de
memoria ss:[bp]
Una vez descrito el modelo funcional del programa, el cual nos permite conocer los
parámetros de paso y retorno del programa y los medios concretos de transferencia usados
(registros o la pila), vamos a modificar min0.asm para que sea capaz de funcionar con el
esquema de paso de parámetros que hemos ideado.
El programa min0 sólo devuelve el propio mínimo y no su dirección de memoria. En el
programa que estamos desarrollando, debemos asegurarnos, por tanto, que cada vez que la
rutina encuentra un mínimo, salva la dirección de éste en el registro di. Es decir, en este
ejemplo, min devuelve la dirección de memoria donde se encuentra el mínimo del subarray recién procesado, en un registro. El registro elegido para este propósito puede ser
cualquiera, en principio, pero, resulta provechoso elegir un registro de direcciones, por
ejemplo bx porque, de este modo, el programa principal puede usarlo para transferir el
mínimo desde la memoria a un registro de datos.
En el ejemplo precedente, min0.asm, el propio programa tenía acceso al array. En el caso
presente, al tratarse de un procedimiento y, por tanto, poder ser una rutina enlazada
independientemente del resto de rutinas que forman parte del programa, no podemos
garantizar que el array pertenezca al ámbito de acceso (scope) del procedimiento min.
Debemos, pues, efectuar el paso del offset completo del array en un registro base (bx) e,
internamente en la rutina, efectuar la indexación de cada word usando un registro índice,
en este caso, el registro si. Esta es la razón por la que usamos el modo de direccionamiento
[bx+si], bx contiene la dirección base del array no ordeando aún y, el registro si contiene el
offset adicional del elemento del array que está siendo comparado en el instante actual.
Está comprobado estadísticamente que es más efectivo efectuar la ordenación de un
array por selección de máximos que por selección de mínimos.
©2003, José María Foces Morán.
34/59
Usad esta numeración:
109 de 357
En la declaración del procedimiento, la palabra reservada uses junto con la lista de registros
que le siguen, significa que el macroensamblador debe salvar los registros ax, dx y si cada
vez que se produzca una llamada al procedimiento min y, del mismo modo, deberá restaurar
ax, dx y si, cada vez que se produzca el retorno desde este procedimiento al programa
principal. En ambos casos utilizará la pila. La forma uses es una forma sintáctica explícita
de salvar y restaurar registros antes de producir el salto a un procedimiento que los usa. En
versiones anteriores a la v. 6.11 del Macroesnamblador de Microsoft, era necesario incluir la
secuencia de instrucciones push anteriores a la llamada y la secuencia correspondiente de
instrucciones pop posteriores al retorno.
;*************
; minproc.asm
;*************
.model medium
.386
.data
array sword
6700,345,2030,100,2000,-32765,1000,63,1,2,3,4,5,6,-34
n equ 15
fuera_del_array equ n * 2
.code
.startup
; Paso de parámetros en los registros cx y bx
mov cx, n
lea bx, array
call min
;min ha devuelto la dirección del elemento mínimo en el registro bx
;por tanto usaremos bx como registro base para cargar el mínimo en
;el registro ax
mov ax, [bx]
;El nop siguiente nos ayudará en Codeview a fijar un breakpoint
;y así poder ver el contenido de ax sin que el programa termine
;y, entonces, sea borrado por Codeview.
nop
.EXIT 0
min proc near uses ax dx si di
xor si, si
;La dirección base del array no ordenado aún está en bx
;Cargamos en ax el primer elemento de este sub-array
mov ax, [bx+si]
;Cargamos en di la dirección de memoria del primer elemento
;del array
lea di, [bx+si]
©2003, José María Foces Morán.
35/59
Usad esta numeración:
110 de 357
;Multiplicar el número de elementos (words) pasado en cx por 2
;para convertirlo en número de bytes.
shl cx , 1
;Comparar con el mínimo temporal y saltar a siguiente si éste resulta
; ser menor
busqueda:
cmp ax, [bx+si]
jl siguiente
;Hemos encontrado un elemento menor que el mínimo temporal,
;lo cargaremos en ax para seguir efectuando comparaciones
mov ax, [bx+si]
;Cargaremos la dirección de memoria del nuevo mínimo temporal
;en di. El llamador necesita conocer su dirección de.
lea di, [bx+si]
;Mover si una word más a la derecha y verificar que
; no estamos fuera de los límites del array.
siguiente: add si, 2
cmp si, cx
jb busqueda
;La dirección del mínimo está en di, la transferiremos a bx para
;devolvérsela al llamador.
mov bx, di
ret
min endp
END
sel1.asm. En este ejemplo vamos a realizar la primera versión del algoritmo de
ordenación. El método empleado es el método llamado de selección. Comenzaremos
integrando minproc.asm con un programa principal que implemente este algoritmo y que use
el procedimiento min para determinar el mínimo de cada sub-array. El orden será de menor a
mayor (orden no decreciente) empleando el método de selección. Recordemos que este método
funciona calculando el mínimo de cada sub-array e intercambiándolo con el elemento más a
la izquierda. Este proceso se repite desplazando la base del array un lugar a la derecha hasta
que la base sea el último elemento, en ese instante, el array estará ordenado.
El bucle básico llama al procedimiento min mientras la base variable del array
pertenezca al array. En cada una de las llamadas a min, el programa principal le pasa los
parámetros necesarios en los regsitros cx (número de elementos en este sub-array) y bx (offset
base de este sub-array). El procedimiento usa varios registros y, por tanto, tiene que salvarlos
antes de inicializarlos y usarlos. Decimos que el procedimiento salva en la pila la parte del
©2003, José María Foces Morán.
36/59
Usad esta numeración:
111 de 357
contexto dinámico que va a emplear. La última parte del presente capítulo está dedicada al
concepto de contexto y contexto dinámico.
Inicialmente, el programa asume que el mínimo es el elemento que se encuentra en la
base del array (el elemento más a la izquierda de array). La rutina usa el registro ax para
almacenar el mínimo en cada iteración del sub-array y usa el registro di para almacenar la
posición de éste dentro del array. Al final, el procedimiento min devuelve la posición del
mínimo en el registro bx. El programa principal, sustituye el elemento base del sub-array no
ordenado por el mínimo obtenido mediante min, y, según la táctica del método de selección,
salva el elemento base del sub-array en la posición donde se encontraba el mínimo recién
encontrado. Todas estas acciones tienen lugar mientras el elemento base del sub-array
pertenezca a array, en otro caso, el algoritmo habrá terminado.
El estado del programa principal está formado por la dirección base del sub-array no
ordenado la cual se mantiene en el registro di y por el número de elementos (sword’s) que
aún no han sido ordenados. El estado de la rutina min está formado por la dirección base del
sub-array que se va a examinar (bx) y por el número de elementos no examinados aún (cx).
;*************
; sel1.asm
;*************
.model medium
.386
.data
array sword 6700,345,2030,100,2000,-32765,1000,63,6,2,3,5,4,1,-34
n equ 15
fuera_del_array equ n * 2
.code
.startup
;En cx mantenemos el número de words no ordenadas todavía
mov cx, n
;En di mantenemos el offset de la base del sub-array no ordenado
;Al principio esta base del sub-array no ordenado es la misma
;base que el propio array, por tanto, su offset es 0.
mov di, 0
next:
;La dirección de memoria de la base del nuevo sub-array
;es array+di. Esa dirección de memoria (no su contenido)
;ca cargamos en bx antes de producir la llamada a min
lea bx, array[di]
call min
;min ha retornado. En bx esperamos encontrar la dirección de
;memoria del elemento mínimo. Las siguientes cuatro instrucciones
©2003, José María Foces Morán.
37/59
Usad esta numeración:
112 de 357
;intercambian este mínimo con el elemento extremo izquierdo del array
mov ax, [bx]
mov dx, array[di]
mov [bx], dx
mov array[di], ax
;Acabamos de poner en orden una owrd más, por tanto, hay que restar
;uno al número total de words que quedan por ser ordenadas
dec cx
;Movemos el offset de la base del sub-array no ordenado dos lugares
;a la derecha.
add di, 2
;¿Nos hemos salido del array?
cmp di, fuera_del_array
;Si di es menor, ordenaremos un elemento más.
jb next
nop
.EXIT 0
min proc near
;Salvamos en la pila la parte del contexto dinámico que vamos a usar
push ax
push cx
push si
push di
;Tenemos un array de words que comienza en la dirección bx
;Para direccionar cada word de este array usaremos el registro si
;La primera de ellas está justo en la dirección bx, a una distancia
;cero de bx, esto es, si = 0.
xor si, si
;Calculemos el mínimo:
;-----------------------mov ax, [bx+si]
lea di, [bx+si]
shl cx , 1
busqueda:cmp ax, [bx+si]
jl siguiente
mov ax, [bx+si]
lea di, [bx+si]
siguiente:add si, 2
cmp si, cx
jb busqueda
mov bx, di
;------------------------;Restauramos la parte del contexto dinámico usada antes de retornar
pop di
pop si
pop cx
pop ax
ret
©2003, José María Foces Morán.
38/59
Usad esta numeración:
113 de 357
min endp
END
sel2.asm. En este paso sólo comprobaremos que el procedimiento min puede salvar el
contexto dinámico (la parte que va a usar) en la pila usando el modificador uses en vez de
efectuar varias inserciones con la instrucción push.
.model medium
.386
.data
array sword 6700,345,2030,100,2000,-32765,1000, 63,6,2,3,5,4,1,-34
n equ 15
fuera_del_array equ n * 2
.code
.startup
mov di, 0
mov cx, n
next:
lea bx, array[di]
call min
mov ax, [bx]
mov dx, array[di]
mov [bx], dx
mov array[di], ax
dec cx
add di, 2
cmp di, fuera_del_array
jb next
nop
.EXIT 0
min proc near uses ax cx si di
xor si, si
mov ax, [bx+si]
lea di, [bx+si]
shl cx , 1
busqueda:
cmp ax, [bx+si]
jl siguiente
mov ax, [bx+si]
lea di, [bx+si]
siguiente:
add si, 2
cmp si, cx
jb busqueda
mov bx, di
ret
min endp
END
©2003, José María Foces Morán.
39/59
Usad esta numeración:
114 de 357
sel3.asm. En esta implementación del algoritmo de selección en macroensamblador,
vamos a ilustrar cómo llevar a cabo el paso de parámetros a la rutina min a través de la pila.
Al igual que en el caso anterior, el programa principal debe calcular la
dirección base del sub-array que aún está sin ordenar. La expresión array[di]
representa el offset del elemento inicial de este sub-array. Cargaremos, pués, este
offset en el registro bx mediante la instrucción lea bx, array[di]. Como, en este
caso, deseamos pasar el parámetro “dirección base del sub-array” en la pila,
necesitaremos transferir el contenido del registro bx a la pila. También pasaremos
en la pila el parámetro “número de datos del sub-array no ordenado”, por tanto,
transferiremos el contenido del registro cx a la pila. La rutina min devolverá el
resultado en la pila también; recordemos que el resultado esperado por el
programa principal es la dirección de memoria (offset) del elemento mínimo del
sub-array actual. Resumiendo: necesitamos hacer sitio para tres words en la pila,
dos para los parámetros de entrada de min y otra más para el parámetro de
retorno de min.
Un marco de pila en la arquitectura IA está formado por un cierto número
de posiciones de memoria pertenecientes a la pila. En el registro bp se guarda la
dirección base del marco de pila y, por tanto, se puede acceder a la lista de
parámetros incluidos en el marco, efectuando direccionamientos con el registro
bp, dentro del segmento de pila (ss). Tal como hemos explicado, este es un
convenio ostensiblemente estricto en la arquitetcura IA.
Para entender esta expliación, seguiremos el programa linea a linea. En
primer lugar, puesto que la construcción del marco de pila supone el uso del
registro bp, será necesario salvarlo en la pila. Después, crearemos en la pila el
espacio necesario para 3 words, esto es, 6 bytes: salvaremos el tope de la pila, sp,
en bp y retrasamos sp en 6 posiciones de memoria para crear el espacio
mencionado. A continuación, de hecho, vamos a transferir los parámetros a las
posiciones reservadas para ellos en el marco: escribiremos cx y bx en el segmento
de pila usando el registro bp y un desplazamiento 2 y 4 respectivamente. Las
instrucciones mov involucradas en estas dos transferencias, no necesitan invalidar
el segmento de datos activo, el cual sigue siendo el correspondiente al registro ds
puesto que el modo de direccionamiento emplea como registro base el registro
bp y, cuando esto ocurre, el segmento por defecto es ss. Resumiendo, las
instrucciones mov mencionadas, acceden automaticamente al segmento de
memoria asociado al registro ss porque en el modo de direccionamiento
empleado especifican el uso del registro bp. Ahora se produce la llamada al
procedimiento min.
©2003, José María Foces Morán.
40/59
Usad esta numeración:
115 de 357
El procedimiento min salva en la pila la parte del contexto del procesador
que va a ser usada por la propia rutina, esto lo hace con la palabra reservada uses
que, como ya hemos mencionado en ejemplos anteriores, asegura que en cada
invocación al procedimiento en tiempo de ejecución, se producirán las
inserciones automáticas en la pila, de los registros declarados. La rutina min
transfiere ahora los parámetros desde la pila a los registros elegidos por el
programador para mantener el estado del procedimiento min. Al igual que en los
casos anteriores, usaremos el modelo funcional de min acordado al principio: el
registro bx contendrá la dirección base del sub-array no ordenado y cx contendrá
el número de words no-ordenadas.
La rutina prosigue, como en los casos anteriores, buscando el elemento
mínimo. Al terminar, la rutina transfiere la dirección de memoria del elemento
mínimo a la dirección ss:[bp-6], es decir, al espacio reservado en la pila para ser
ocupado por el parámetro de retorno de este procedimiento.
Cuando se ha producido el retorno al programa principal, éste transfiere la
dirección de memoria del mínimo al registro bx, igual que en los casos anteriores.
Seguidamente, destruirá el stack frame, para lo cual vuelve a ajustar el registro
sp, sumándole 6 –operación inversa a la resta efectuada en el momento de su
creación y, extrae de la pila bp, para deshacer la operación de inserción de bp
efectuada antes de la invocación de min. El proceso, a partir de este punto, es el
mismo que en los casos anteriores: intercambiar el mínimo actual con el elemento
inicial del sub-array no ordenado, decrementar el puntero de words pendientes
de ser ordenadas e incrementar el puntero base del sub-array y, por último,
comprobar si se ha procesar un nuevo sub-array o si, por el contrario, se ha
finalizado la ordenación del array completo.
.model medium
.386
.data
array sword 6700,345,2030,100,2000,-32765,1000,63,6,2,3,5,4,1,-34
n equ 15
fuera_del_array equ n * 2
.code
.startup
mov di, 0
mov cx, n
next:
lea bx, array[di]
;Crear stack frame para paso de parámetros
push bp
mov bp, sp
sub sp, 3*2
;----------------------------------------;Pasar parámetros a través del stack frame
mov [bp-2], cx
©2003, José María Foces Morán.
41/59
Usad esta numeración:
116 de 357
mov [bp-4], bx
;----------------------------------------call min
;Extraer el valor de retorno del procedimiento
;fuera del stack frame
mov bx, [bp-6]
;----------------------;destruir el stack frame
add sp, 3*2
pop bp
;----------------------;El algoritmo es el mismo que en los caso anteriores
mov ax, [bx]
mov dx, array[di]
mov [bx], dx
mov array[di], ax
dec cx
add di, 2
cmp di, fuera_del_array
jb next
nop
.EXIT 0
min proc near uses ax bx cx si di
xor si, si
;Referenciar los parámetros en el stack-frame
mov bx,[bp-4]
mov cx, [bp-2]
mov ax, [bx+si]
lea di, [bx+si]
;--------------------------------------------shl cx , 1
busqueda:
cmp ax, [bx+si]
jl siguiente
mov ax, [bx+si]
lea di, [bx+si]
siguiente:
add si, 2
cmp si, cx
jb busqueda
;Salvar en el stack frame el parámetro de retorno de este
;procedimiento (la dirección de memoria del mínimo recién obtenido)
mov [bp-6], di
;-----------------------------------------------------------------ret
min endp
END
©2003, José María Foces Morán.
42/59
Usad esta numeración:
117 de 357
Sel31.asm. En este paso, usaremos la instrucción ret en una forma que nos
permite especificar la cuantía del adelanto del registro sp en el instante del
retorno del procedimiento. En nuestro caso usaremos la instrucción ret 6, de esta
forma, cuando min retorna, el registro sp, después del retorno, asume el valor sp6, es decir, se ha producido el mismo ajuste que fue efectuado en el ejemplo
anterior mediante la instrucción add sp,3*2.
En este ejemplo vamos a ilustrar cómo se salvan la parte de contexto
afectada por la rutina min en la pila. En este caso, no haremos uso de la palabara
uses, en esta ocasión salvaremos los registros en la pila mediante el uso de
instrucciones push y, la restauración de esos mismos registros, la llevaremos a
cabo con instrucciones pop.
.model medium
.386
.stack 1024
.data
array sword
6700, 345, 2030, 100, 2000,-32765, 1000, 63,6,2,3,5,4,1,-34
n equ 15
fuera_del_array equ n * 2
.code
.startup
;******
SELECTION SORT ******
mov di, 0 ; Index of unordered subarray's base word
mov cx, n ; Number of unordered words
next:
;Load the base address of the unordered subarray
lea bx, array[di]
push cx ; Push number of words left unordered
push bx ; Push base address of unordered subarray
push bx ; Stack space for return value from procedure
call min
mov bx, sp ; Fetch min's return value from ss
mov bx, ss:[bx-6]; Load min's return value into bx
; bx contains the address of the min
; use it to load min into ax
mov ax, [bx]
mov dx, array[di] ;Load the leftmost word into dx
;Save it into the address of the current min
mov [bx], dx
;Save the current min into the leftmost position
mov array[di], ax
dec cx ;One unordered word less
;Move the base of the unordered subarray 2 words right
©2003, José María Foces Morán.
43/59
Usad esta numeración:
118 de 357
add di, 2
;Is this base out of the bounds of the original array?
cmp di, fuera_del_array
;If not,get a new minimum and put it into its ordered place
jb next
nop
.EXIT 0
min proc far
push bp
; Salvar bp
mov bp, sp ; Establecer stack frame local:
add bp,2+4 ; param0(bp+4),param1(bp+2),param2(bp)
push ax ; Salvar los registros que se van a usar
push si
push di
push bx
push cx
xor si, si
;****** SEARCH FOR THE MIN ******
mov bx, [bp+2];bx: array base address
mov cx, [bp+4] ;cx: size in words
mov ax, [bx+si];ax: minimum so far
lea di, [bx+si];di: mem address of minimum so far
shl cx , 1 ;si: item index
busqueda: cmp ax, [bx+si]
jl siguiente
mov ax, [bx+si];minimum so far
lea di, [bx+si];the address of the minimum
siguiente:add si, 2
cmp si, cx
jb busqueda
;Save the address of the minimum
;into the return value's slot in stack frame
mov [bp], di
pop cx ;Restore the caller's context
pop bx
pop di
pop si
pop ax
pop bp
; Liberar 6 bytes (3 words) mas después de retornar al programa
principal.
ret 6
min endp
END
©2003, José María Foces Morán.
44/59
Usad esta numeración:
119 de 357
sel311.asm. El método formal para efectuar la asignación y liberación de
marcos de pila en la arquitectura IA consiste en el uso de las instrucciones enter y
leave. Estas instrucciones sirven para crear y destruir un marco de pila,
respectivamente. La instrucción enter crea el marco de pila siguiendo estos pasos:
1. Salva en la pila el registro bp.
2. Copia el valor contenido en sp a bp, de este modo, los parámetros pueden
ser transferidos y, posteriormente, recuperados en la pila usando la
instrucción mov. El que podamos acceder a la pila con simples mov’s es
muy conveniente porque el acceso mediante el par push/pop implica la
actualización del registro sp y, por tanto, la pérdida del acceso a datos
distintos del dato residente en la parte alta de la pila.
3. Resta al registro sp el operando de la instrucción. En este paso se crea el
espacio solicitado en la pila.
La instrucción leave deshace el proceso llevado a cabo por enter:
1. Copia el contenido de bp en sp.
2. Extrae bp desde la pila.
En el programa presente, usaremos las instrucciones enter y leave para crear un
stack frame cuyo espacio reservado en la pila, puede ser usado tanto para
almacenar parámetros de entrada y de retorno, como variables dinámicas.
Recordemos que, en lenguaje C, por ejemplo, son variables dinámicas aquellas
que sólo existen mientras el procedimiento al que pertenecen, se encuentre en
ejecución –o haya producido un salto hacia adelante. En este caso particular en el
que el espacio del stack frame se va a emplear para crear variables dinámicas y
parámetros, vamos a emplazar las instrucciones enter y leave, antes y después de
la instrucción de salto a procedimiento. En el ejemplo que siguiente a éste,
haremos un uso ligeramente distinto de enter y leave.
En este ejemplo, el programa llamador obtiene el valor de retorno del procedimiento
desde el stack frame. Esto es posible gracias a que se ejecuta leave después de
transferir el valor de retorno al registro bx. Debido a esto, el procedimiento llamado no
puede usar ret n para retornar al llamador y liberar los n bytes reservados para
parámetros.
.model medium
.386
©2003, José María Foces Morán.
45/59
Usad esta numeración:
120 de 357
.stack 1024
.data
array sword
6700, 345, 2030, 100, 2000,-32765, 1000, 63,6,2,3,5,4,1,-34
n equ 15
fuera_del_array equ n * 2
.code
.startup
;****** SELECTION SORT ******
mov di, 0 ; Index of unordered subarray's base word
mov cx, n ; Number of unordered words
; Load the base address of the unordered subarray
next: lea bx, array[di]
enter 6,0
;Save parm: number of words left unordered
mov [bp-2], cx
;save parm: base address of unordered subarray
mov [bp-4], bx
call min
mov bx, [bp-6]
; Fetch min's return value from ss
leave
;bx contains address of min, use it to load min into ax
mov ax, [bx]
mov dx, array[di] ;Load the leftmost word into dx
mov [bx], dx;Save it into the address of the current min
;Save the current min into the leftmost position
mov array[di], ax
dec cx ;One unordered word less
;Move the base of the unordered subarray 2 words right
add di, 2
;Is this base out of the bounds of the original array?
cmp di, fuera_del_array
;If not, get a new minimum and put it into its ordered ;place
jb next
nop
.EXIT 0
min proc far
push ax
; Salvar los registros que se van a usar
push si
push di
push bx
push cx
xor si, si ;****** SEARCH FOR THE MIN ******
mov bx, [bp-4] ;bx: array base address
mov cx, [bp-2] ;cx: size in words
mov ax, [bx+si];ax: minimum so far
lea di, [bx+si];di: mem address of minimum so far
shl cx , 1 ;si: item index
busqueda: cmp ax, [bx+si]
jl siguiente
mov ax, [bx+si]
;minimum so far
lea di, [bx+si]
;the address of the minimum
©2003, José María Foces Morán.
46/59
Usad esta numeración:
121 de 357
siguiente: add si, 2
cmp si, cx
jb busqueda
;Save the address of the minimum
;into the return value's slot in stack frame
mov [bp-6], di
pop cx ;Restore the caller's context
pop bx
pop di
pop si
pop ax
ret
min endp
END
Sel312.asm. Las instrucciones enter y leave, pueden usarse de otro modo distinto
del explicado en el ejemplo anterior. En este ejemplo incluiremos estas
instrucciones dentro del procedimiento llamado. Esto significa que el stack frame
no contendrá espacio para parámetros, sólo para variables dinámicas, locales al
procedimiento.
Los parámetros, al estar excluidos del stack frame, se los proporcionamos al
procedimiento insertándolos en la pila antes de la llamada al procedimiento. El
procedimiento accede a las direcciones de memoria donde se encuentran los
parámetros, usando el registro base bp y un desplazamiento positivo.
AL final de la ejecución de min, en este caso, usamos la versión de la instrucción
ret que nos permite especificar el ajuste del registro sp a llevar a cabo después del
retorno al programa llamador. Esta es una forma cómoda de dejar la pila
equilibrada después de retornar del procedimiento min: puesto que antes del
salto se ejecutaron dos inserciones, después de éste se han de producir dos
extracciones(pop). Si los datos salvados en la pila, no son necesarios después del
retorno, el re-equilibrado de la pila, puede llevarse a cabo, sumando al registro sp
una cantidad equivalente al ajuste en bytes. La instrucción ret n, nos permite
llevar a cabo el re-ajuste de una forma explícita.
.model medium
.386
;Ordenar array, paso de params a min a trav‚s stack-frames
.stack 1024
.data
array sword
6700, 345, 2030, 100, 2000,-32765, 1000, 63,6,2,3,5,4,1,-34
n
equ 15
fuera_del_array equ n * 2
.code
©2003, José María Foces Morán.
47/59
Usad esta numeración:
122 de 357
.startup
;******
SELECTION SORT ******
mov di, 0 ; Index of unordered subarray's base word
mov cx, n ; Number of unordered words
next:
subarray
lea bx, array[di] ; Load the base address of the unordered
push cx
; Save parm: number of words left
push bx
; save parm: base address of unordered
unordered
subarray
call min
;Ret val is in bx
mov ax, [bx]; bx contains the address of the min, use it to load
min into ax
mov dx, array[di] ;Load the leftmost word into dx
mov [bx], dx
;Save it into the address of the current min
mov array[di], ax ;Save the current min into the leftmost
position
dec cx
;One unordered word less
add di, 2
;Move the base of the unordered subarray 2
words right
cmp di, fuera_del_array;Is this base out of the bounds of the
original array?
jb next
;If not, get a new minimum and put it into
its ordered place
nop
.EXIT 0
min proc far
enter 4,0 ;Make room for one dword dynamic variable with which
;we are going to count the number of compares peformed
;within the min procedure
push ax
; Salvar los registros que se van a usar
push si
; excepto bx (valor de retorno)
push di
push cx
mov dword ptr[bp-4],0
xor si, si
mov bx, [bp+6]
mov cx, [bp+8]
mov ax, [bx+si]
lea di, [bx+si]
shl cx , 1
busqueda:
;****** SEARCH FOR THE MIN ******
;bx: array base address
;cx: size in words
;ax: minimum so far
;di: mem address of minimum so far
;si: item index
cmp ax, [bx+si]
jl siguiente
©2003, José María Foces Morán.
48/59
Usad esta numeración:
123 de 357
siguiente:
inc dword ptr[bp-4]
mov ax, [bx+si]
lea di, [bx+si]
add si, 2
cmp si, cx
jb busqueda
mov bx, di
;minimum so far
;the address of the minimum
;Save the address of the minimum
;into the return value's slot: the
bx reg
pop
pop
pop
pop
cx
di
si
ax
leave
ret 4
;Restore the caller's context
; Retornar liberando los 4 bytes
; de los par metros cx/bx
min endp
END
sel32.asm. En este ejemplo completamos el ejercicio añadiendo a nuestro
programa la posibilidad de trabajar con datos de 32 bits, es decir, el programa
puede ordenar un array de dwords. Los cambios necesarios son los siguientes:
i)
Declaración del array como sdword en vez de word.
ii)
Multiplicar por 4 el índice de cada elemento del array en el programa
principal antes de producir la invocación de min.
iii)
La referencia al elemento siguiente del sub-array se forma sumando 4,
no 2.
iv)
El registro elegido para albergar al mínimo debe ser eax en vez de ax.
.model medium
.386
.stack 1024
.data
array sdword
6700, 345, 2030, 100, 2000,-32769, 1000, 63,6,2,3,5,4,1,34
n equ 15
fuera_del_array equ n * 4
.code
.startup
;******
SELECTION SORT ******
©2003, José María Foces Morán.
49/59
Usad esta numeración:
124 de 357
mov di, 0 ; Index of unordered subarray's base word
mov cx, n ; Number of unordered Dwords
; Load the base address of the unordered subarray
next: lea bx, array[di]
enter 6,0
; Save parm: number of words left unordered
mov [bp-2], cx
; save parm: base address of unordered subarray
mov [bp-4], bx
call min
mov bx, [bp-6] ; Fetch min's return value from ss
leave
; bx contains the address of the min, use it to load min ; into ax
mov EAX, [bx]
mov EDX, array[di] ;Load the leftmost word into dx
mov [bx], EDX;Save it into the address of the current min
;Save the current min into the leftmost position
mov array[di], EAX
dec cx ;One unordered word less
;Move the base of the unordered subarray 2 words right
add di, 4
; Is this base out of the bounds of the original array?
cmp di, fuera_del_array
;If not, get a new minimum and put it into its ordered place
jb next
nop
.EXIT 0
min proc far
push eax
; Salvar la parte de contexto usada
push si
push di
push bx
push cx
;****** SEARCH FOR THE MIN ******
xor si, si
mov bx, [bp-4] ;bx: array base address
mov cx, [bp-2] ;cx: size in words
mov EAX, [bx+si] ;EAX: minimum so far
lea di, [bx+si] ;di: mem address of minimum so far
shl cx , 2 ;si: item index (Multiply by 4, dword)
busqueda: cmp EAX, [bx+si]
jl siguiente
mov EAX, [bx+si];minimum so far
lea di, [bx+si]
;the address of the minimum
siguiente: add si, 4
cmp si, cx
jb busqueda
mov [bp-6], di ;Save the address of the minimum
;into the return value's slot in stack frame
pop cx;Restore the caller's context
pop bx
©2003, José María Foces Morán.
50/59
Usad esta numeración:
125 de 357
pop di
pop si
pop eax
ret
min endp
END
©2003, José María Foces Morán.
51/59
Usad esta numeración:
126 de 357
El contexto de un programa en ejecución.
El contexto de un programa está formado por todos los elementos de estado de un
sistema computador que son examinables por parte de un programa en ejecución. Esta
inspección puede ser llevada a cabo directamente, efectuando ciclos de lectura sobre
los elementos incluidos en el mismo o, indirectamente, efectuando alguna otra clase de
operación de cuyo resultado pueda inferirse el contexto buscado. Por ejemplo, la
representación de un programa, sus instrucciones, es una parte del contexto del
programa. La representación (binaria) del programa debe ser conocida, accesible, para
que la ejecución del programa pueda llevarse a cabo. El programa puede, también,
inspeccionar el contenido de los registros de propósito general, del registro de estado
eflags, el estado de la memoria y, quizás el estado de ciertos elementos de entrada y
salida. Normalmente, los sistemas operativos poseen un contexto más amplio que los
programas de aplicación porque pueden examinar una porción más amplia del estado
de la microprocesador o del sistema computador.
Hay elementos del estado del sistema computador que no son inspeccionables por
parte de un programa de aplicación. El contenido de zonas de memoria que no han
sido asignadas a un determinado programa debería, normalmente, no ser
inspeccionable por el programa mencionado. Los intentos de acceso a una zona de
memoria no asignada a un determinado programa dan como resultado la generación
de una excepción, una interrupción del flujo normal de ejecución del programa que
resulta en la ejecución de un programa, llamado manejador de excepción de acceso no
autorizado, el cual, al activarse, notifica al kernel del sistema operativo de este hecho
anormal y, este, a su vez, normalmente terminará el programa en ejecución. En
ocasiones, si el programa es capaz de recibir excepciones, efectuará un aviso al usuario
del programa y procederá de una forma pensada de antemano, adecuada al nivel de
gravedad adoptado por el programador, sin necesidad de ser suspendido por
completo por parte del kernel.
Una de las tareas asignadas al equipo cooperativo formado por el microprocesador y el
sistema operativo, consiste en restringir el contexto que está disponible a un programa
de usuario (aplicación). Si el sistema de memoria cache está funcionando
correctamente, ésta, constituye un elemento de redundancia en el mantenimiento de
una parte sustancial de la parte de contexto incluido en la memoria principal.
A medida que transcurre el tiempo en el que un programa se encuentra en ejecución,
éste gana y pierde partes del contexto conforme usa servicios del sistema operativo y
conforme gana y pierde recursos del sistema. En la arquitectura IA, un programa de
usuario emplea la noción de puerta de acceso de llamada (call gate), para aumentar su
nivel de privilegio. Este mecanismo permite que un programa incremente su nivel de
privilegios de acceso y, de este modo, acceder a recursos que están reservados a ese
nivel de privilegio. El acceso a los recursos, al producirse a través de una call gate, el
código que se ejecuta pertenece a un módulo del sistema operativo que accede al
©2003, José María Foces Morán.
52/59
Usad esta numeración:
127 de 357
recurso solicitado de forma ordenada, proveyendo el servicio solicitado sin permitir al
programa efectuar operaciones no permitidas sobre el recurso. En resumen: el
programa ha ejecutado el servicio solicitado, perteneciente al kernel y, por tanto ha
logrado elevar su nivel de privilegio, pero, el código ejecutado es el garante de que el
acceso al recurso está conforme a unas reglas de uso estrictas.
En las arquitecturas más antiguas, no era posible restringir partes del estado del
sistema microprocesador. Nos sirven como ejemplo las versiones iniciales de
microprocesadores de la familia x86, las cuales, no incorporaban el modo protegido,
sólo podían funcionar en modo real. Esto significa que no había forma de esconder
ninguna parte del estado del microprocesador a los programas en ejecución. Cualquier
programa podía alterar cualquier registro, tanto del procesador como del coprocesador
de punto flotante o de cualquier otro periférico o adaptador de e/s y cualquier
dirección de memoria.
La necesidad de restringir el contexto surgió con los sistemas multitarea. En estos
sistemas los recursos de la Cpu se reparten, a lo largo del tiempo, entre todos los
procesos activos y uno, o varios de ellos, son los encargados de inicializar y gestionar
el estado de la máquina en el arranque, el cual, organiza cómo, en qué instantes y
durante qué longitudes de tiempo se producirán las concesiones de la Cpu a cada
proceso. Los múltiples programas en ejecución (procesos) comparten los recursos
ofrecidos por el sistema de una forma ordenada en la que ningún proceso puede
afectar a los cómputos realizados por otros procesos.
Una parte del contexto destaca por su importancia inherente a cada proceso, es la parte
del contexto que reside en los registros de propósito general del procesador: el
contexto dinámico. Esta parte del contexto es transferida desde la memoria a los GPR’s
cuando el programa es activado por el kernel y es transferida a la memoria cuando el
proceso es suspendido. La razón de estas transferencias reside en el hecho de que los
registros de propósito general constituyen un recurso importantísmo para que el
sistema computador ofrezca un gran rendimiento. Sin embargo, replicar el conjunto de
GPR’s con cada programa en ejecución, resultaría en un coste inaceptable. En modo
real, el contexto dinámico de la arquitectura IA está formado por los GPR’s, los
registros de segmento, el registro eip y el registro flags. El contenido completo de la
memoria y de algunos otros registros pertenecientes a adaptadores y periféricos,
forman parte del estado completo del sistema microprocesador.
©2003, José María Foces Morán.
53/59
Usad esta numeración:
128 de 357
Estado de un sistema basado en un microprocesador IA
Modo real.
Contexto dinámico
- Compartido con el resto de procesos
GPR's
Registros de segmento
eip
eflags
Memoria
- En general no está compartido
- Puede haber areas memoria explícitamente compartidas
Registros de dispositivos
Figura 35
Cuando un programa se encuentra en ejecución, el flujo de control pasa del programa
principal a los procedimientos que forman parte de él. Desde un procedimiento se
puede saltar a otro procedimiento y así sucesivamente. A estas llamadas a
procedimiento que tienen lugar en sucesión, pasando el control de un procedimiento a
otro, se las conoce como llamadas anidadas. De algún modo, cuando el control pasa de
un procedimiento a otro, ambos comparten entre sí el contexto dinámico del
procesador. Las llamadas a procedimiento nos recuerdan a los cambios de contexto en
un sistema multitarea. La diferencia radica, entre otras cosas, en que el tipo de
instrucciones empleadas en los saltos a procedimiento son distintas de las empleadas
en los cambios de contexto. A pesar de ello, sí ocurre que el procedimiento llamador y
el llamado, comparten el contexto dinámico y que, si el procedimiento llamado va a
alterar este contexto de algún modo, debe existir un acuerdo por el cual llamador y
llamado, establecen quién de los dos será el encargado de salvar el contexto dinámico
o la parte de éste que va a ser usada por el procedimiento llamador.
El caso más común en los sistemas que emplean la arquitectura IA es que el
procedimiento llamado salva la parte del contexto dinámico que va a usar. El proceso
llamador, generalmente salva en memoria la lista de parámetros que van a ser
enviados al procedimiento llamado. La zona de memoria donde se producen las salvas
del contexto dinámico suele ser la pila. Esta es la razón de ser de los ejemplos en los
que ilustramos diversas técnicas de paso de parámetros y las diversas semánticas de la
instrucción ret y otros aspectos.
Por último, es necesario comentar, que además de los sistemas multitarea
tradicionales, orientados a procesos, existe hoy en día otra forma de particionar una
tarea en varias subtareas elegibles por el kernel para ser ejecutadas. Son los sistemas
llamados multithreaded, esto es, sistemas en los que un programa mantiene partes de
sí mismo en ejecución simultanea, igual que si el proceso hubiera creado procesos
hijos, pero, sin las exigencias tan costosas de los cambios de contexto entre procesos.
Los sistemas que incluyen el soporte multi-hilo están muy extendidos hoy en día y
facilitan la programación en entornos donde las aplicaciones son capaces de usar y
©2003, José María Foces Morán.
54/59
Usad esta numeración:
129 de 357
producir flujos de datos y de control a través de redes de ordenadores. Un ejemplo
notable de lenguaje de programación con soporte inherente multihilo es el lenguaje
Java. Los cambios de contexto entre threads se limitan a salvar y restaurar los contextos
dinámicos sin necesidad de producir otras salvas y restauraciones que tienen que ver
con rescursos del sistema con grandes costes de gestión: tablas de páginas de memoria
virtual, TLB’s, y otros.
©2003, José María Foces Morán.
55/59
Usad esta numeración:
130 de 357
EJERCICIOS PROPUESTOS
1. Modificar el programa de cálculo del mínimo de un array de enteros para que
calcule el máximo y el mínimo.
2. El algoritmo de cálculo del mínimo realiza n comparaciones para determinar el
mínimo ¿Crees que podrías mejorarlo? i.e. ¿Crees que podrías reducir el número de
comparaciones necesarias para determinar el mínimo? Puedes consultar el vol. 3 de
Knuth, Sorting and Searching.
3. Escribir un programa que cargue el resultado de las sumas siguientes en AX:
1 + 4 + 7 + ... + 148.
100 + 95 + 90 +...+5.
1+2+3+4+...+100
-1+2-3+4-5+6-7...-99+100
J
En los siguientes dos programas te recomiendo que emplees la instrucción LOOP.
4. Calcular la suma de los primeros 50 términos de la progresión aritmética 1, 5, 9, 13, ...
en DX.
5. Leer un carácter desde el teclado e imprimirlo 99 veces en pantalla.
6. Escribir una secuencia de instrucciones que multipliquen el contenido del registro
AX por el BX y que pongan el resultado en CX. Ignorar el posible desbordamiento.
7. Realizar un programa que lea caracteres desde el teclado hasta que el usuario
introduzca un carácter cuyo bit 5º esté puesto a 1.
8. Realizar un programa que encuentre el mayor de los números 52, 36, 45 ,10, 99, 33,
21 y 4. Se suponen almacenados en una dirección de memoria representada por la
etiqueta ARRAY. La rutina habrá de poner el mayor de ellos en el registro DX.
9. Realizar un procedimiento que inicialice 128Kb de posiciones de memoria a partir
de la dirección de memoria que se le pasa en su stack frame como puntero de 32 bits.
10. Escribir un programa en ensamblador del i386 que presente en pantalla los 128
caracteres ASCII superiores. El programa deberá presentar 10 caracteres en cada línea
separados todos ellos entre sí por un espacio en blanco.
11. Escribir un programa que imprima el carácter '?' en pantalla y que entonces lea dos
letras mayúsculas y que las presente en pantalla en orden alfabético.
12. Realizar un procedimiento en Macroensamblador que ordene de mayor a menor 100
números almacenados a partir de la dirección de memoria que se le pasa como
parámetro en su stack frame.
©2003, José María Foces Morán.
56/59
Usad esta numeración:
131 de 357
13. Realizar un procedimiento en ensamblador que presente una "o" en pantalla si AL
contiene un 1 o un 3; si AL contiene un 2 o un 4, presentará una "e".
14. Implementar la siguiente estructura en lenguaje Macroensamblador como
procedimiento.
IF ( (AX > BX) AND (BX < CX) )
THEN
Cargar la constante binaria 11001100 en DX
;Este es el bloque “verdadero”
ELSE
Cargar la constante binaria 10101101 en DX
;Este es el boque “falso”
END_IF
15. Determinar el efecto de cada una de las siguientes instrucciones del i386.
i. CDQ
ii. BTC BX, AX
iii. MOVSX EAX, f2h
16. Escribir un programa en lenguaje de ensamblaje del i386 que multiplique un entero
de 8 bits por un entero de 32 con signo almacenado en eax. El resultado se almacenará
en eax.
17. Escribir un programa en lenguaje de ensamblaje del i386 que copie 1000 datos de 32
bits desde 9000h:0000h a 8000h:2000h. Después de inicializar los registros ds/esi y
es/edi convenientemente, se ha de utilizar la instrucción repmovsd.
18. Identificar qué modos de direccionamiento están en uso en las siguientes
instrucciones así como el tamaño de los datos intervinientes en las operaciones:
i. mov ecx, [esi*4]
ii. sub bl, [ecx+35+esi]
iii. add al, [input+40+ebp]
19. En cada una de las siguientes instrucciones, calcular la dirección efectiva de cada
uno de los operandos. Asumir que ds=1000h, ss=4300h, di=4500h, bx=2000.
i. mov al, [di]
ii. add [bx+350h], ax
iii. movsx eax, byte ptr [bp]
©2003, José María Foces Morán.
57/59
Usad esta numeración:
132 de 357
Introducción.
Hoy en día, la Ingeniería y la Ciencia de Computadores son disciplinas científicas
perfectamente organizadas. A partir de los años cincuenta, la aplicación sistemática del
método científico al desarrollo de esta disciplina, ha producido unos resultados casi
milagrosos. Virtualmente, cualquier campo concreto de la ciencia de computadores en
que podamos fijarnos, nos muestra cómo el deseo de sistematización y de
estructuración del conocimiento ha promovido grandes cambios y un progreso
exponencial. Pero, a diferencia de otros campos del conocimiento humano donde el
progreso consiste en el descubrimiento de la naturaleza, en ciencia de computadores el
progreso es, de algún modo, el diseño de esa naturaleza, descubrir y realizar
transcurren paralelamente.
Ingenieros eléctricos y electrónicos, físicos, devotos, matemáticos, bohemios,
casualidad, motivación, método, buen gusto, auto confianza, esperanza y un sin fin de
otros perfiles humanos y sus virtudes han dado lugar a una sugestiva y fructífera
ciencia aplicada. Cuántos de nosotros nos hemos sentido transportados al observar
cómo nuestro pequeño hijo mental empezaba a comportarse y presentar resultados en
una pantalla de fondo negro y letras verdes.
En este capítulo me propongo explicar los principios de la programación estructurada
y cómo los pioneros de los computadores emplearon sus mejores dotes de
introspección e intuición para posibilitar el desarrollo sistemático de la programación
de ordenadores tal como la conocemos hoy en día. ¿No podrías ser tú una de las
personas que haga de la programación una disciplina respetable? Era la pregunta que
le hacía Adriaan van Wijngaarden, su director de tesis, a Edgser Dijkstra, físico teórico,
pionero de la programación estructurada y algoritmista de nacionalidad Holandesa. Y
desarrollar métodos formales para verificar la corrección funcional de los programas.
En este curso, nosotros estamos interesados fundamentalmente en desarrollar
habilidades de programación, por tanto estudiaremos técnicas programación
estructurada. Desarrollar programas en lenguaje ensamblador supone que además de
la complejidad inherente al problema que se pretende resolver, está el nivel de detalle
y el relativamente corto alcance de los paradigmas ofrecidos por la arquitectura del
microprocesador. La programación estructurada nos ayudará establecer bases firmes
sobre las que sustentar el desarrollo de los programas.
Pero, un programa representa los procesos de la información necesarios para resolver
un problema concreto: otra formulación de un algoritmo que emplea las nociones
abstractas ofrecidas por un determinado lenguaje de programación. Es decir, inmerso
en las construcciones programáticas reside la información y sus procesos, sus
transformaciones útiles. ¿Existe o puede existir un algoritmo que sistemáticamente
aplicado conduzca a la solución de este problema? Al final del tema presente,
expondré de forma resumida una noción y los usos de los algoritmos que nos permita
entender que no todos los problemas son resolubles algorítmicamente. También
explicaré que existen algunos problemas que sí aceptan una solución algorítmica, pero
©2003, José María Foces Morán.
58/59
Usad esta numeración:
133 de 357
que sin embargo, esta solución se hace inviable debido al largo tiempo que necesario
para producir la solución.
©2003, José María Foces Morán.
59/59
Usad esta numeración:
134 de 357
Apuntes de Máquinas
Electrónicas
© 1994, 2004 José María Foces Morán.
Capítulo 4
Memorias para sistemas
empotrados: DRAM.
Usad esta numeración:
135 de 357
Tipos de memorias empleadas en el diseño de sistemas empotrados.
Antes de estudiar con detalle la unidad BIU (Bus Interfaz Unit) del
microprocesador i386EX resulta conveniente conocer cuales son los tipos de
memorias más usadas en el diseño de sistemas empotrados. La unidad BIU es el
medio por el cual el núcleo computacional SX y el resto de periféricos incluidos
en el i386EX se comunican de una forma ordenada, previsible y segura con la
memoria y el resto de elementos de entradas/salidas añadidos externamente.
El subsistema de memoria de un computador contiene los datos y las
instrucciones que forman parte de los programas que se ejecutan en él.
Conceptualmente está claro el propósito de este subsistema y no sería difícil
realizar un diseño a nivel de bloques que fuese cómodo, flexible, potente y fácil
de integrar en el bus del microprocesador. ¿Por qué conceptualmente fácil?
Porque de hecho, los dispositivos de memoria disponibles pueden tener un alto
coste cuando se exige cierta facilidad de uso y para mantener los costes bajos
quizás haya que recurrir a tecnologías más baratas a cambio de una mayor
complejidad.
En esta sección estudiaremos ciertos aspectos tecnológicos relativos a las
diferentes clases de memorias. Estos aspectos nos permitirán comprender mejor
sus modelos funcionales y, así, podremos diseñar pequeños sistemas de
memoria adecuados al bus y otras facilidades del i386EX.
Comencemos por un tipo de memoria de acceso aleatorio, volátil que constituye
el bloque constructivo básico de los sistemas de memoria principal de
prácticamente cualquier computador: las memorias dinámicas, DRAM.
Las memorias dinámicas DRAM- Dynamic Random Access Memory.
Por supuesto, estas memorias son memorias realizadas con semiconductores.
La célula básica de memoria dinámica, capaz de almacenar un bit de
información, está basada en un condensador: cuando el condensador está
cargado la información que contiene la celda es un 1, cuando el condensador
posee mucha menos carga que cuando está cargado del todo, la información
contenida es un 0.
El acceso a la celda de memoria DRAM se lleva a cabo a través del canal de un
transistor MOS: cuando el canal está cerrado, la diferencia de tensión entre las
bornas del condensador se propaga hacia el exterior y, por tanto, puede usarse
para excitar a un determinado elemento de entrada. El problema es que ese
Memorias DRAM. ©1999, José M Foces Morán -- 1
Usad esta numeración:
136 de 357
elemento de entrada a pesar de su alta impedancia sensará la tensión en bornas
del condensador con la resistencia del canal en serie además de su propia
impedancia, esto, forzará una pequeña corriente que, al final, descargará el
condensador y hará que éste pierda la información almacenada. Esta celda de
memoria puede leerse como hemos visto, pero las lecturas son destructivas,
hacen que el condensador pierda casi toda la carga necesaria para una lectura
posterior del mismo dato, por tanto, será necesario regenerar el 1 –o el 0- antes
de que se pierda. A este proceso de regeneración de la carga presente en el
condensador de la celda DRAM se le conoce con el nombre de refresco. El
gráfico que sigue, la celda básica DRAM, puede ayudarnos a comprender mejor
el proceso.
C
S
Fil
a
Número de
palabra
Esta línea permite
el acceso a la puerta
(gate) de S y así
poder controlar el
canal de S.
En esta línea aparece el bit
almacenado en C cuando el
transistor S tiene su canal
cerrado
Este amplificador es sensible
a la carga de C. Es el
encargado de apmplificar la
potencia del 0 o del 1 hacia el
bus externo.
0
0
dn
#cs
0
0
w/#r
Figura. Celda básica de una memoria DRAM
Memorias DRAM. ©1999, José M Foces Morán -- 2
Interfaz
con el bus
Columna
Este amplificador excita la
linea roja siguiendo un
patrón de niveles/tiempo
adecuado a las características
del condensador.
Usad esta numeración:
137 de 357
La interfaz de la celda de memoria con el bus externo se compone de las
siguientes señales:
1. Una línea de datos. En los ciclos de lectura, el dato leído procedente de
la celda de memoria aparece en esta línea llamada d n.
2. Una línea llamada cs o chip select. Esta línea sirve para activa el chip
en el que se encuentra la celda que se desea usar en el ciclo actual. Más
tarde, cuando veamos los chips y módulos de memoria, quedará más
claro el propósito de esta línea. Por ahora basta decir que esta línea ha
de estar activa si deseamos producir lecturas y escrituras sobre la celda
recién explicada. Puesto que el nombre de la línea, #cs, viene
acompañado del símbolo de nivel de aserción bajo, el chip se
selecciona forzando un nivel bajo (0) en esta línea.
3. Una línea llamada w/#r: Un 1 en esta línea indica que el ciclo presente
es de escritura (write), es decir, que el dato presente en la línea d n va a
ser transferido a la celda de memoria. Un 0 en este línea indica que el
ciclo es de lectura y que, por tanto, el dato contenido en la celda va a
ser transferido al exterior a través de la línea dn.
La lógica digital que activa los buffers tri-estado de lectura y escritura responde
a la lógica explicada en los tres puntos anteriores y su diseño es directo.
Veamos el proceso de escritura con más detalle. Para escribir un valor, 0 ó 1, en
la celda es necesario que este bit aparezca en la línea de datos d n en primer
lugar, posteriormente se aserta la línea de selección de fila y, un cierto tiempo
más tarde, el condensador se cargará (o se descargará) al valor representado por
dn. El tiempo de esta escritura incluye el tiempo de activación de las puertas
lógicas de entrada, el tiempo de reacción de los amplificadores de escritura, el
tiempo de conmutación del transistor de acceso (también llamado transistor de
paso) y por último el tiempo de carga del propio condensador que dependerá
de la tensión de excitación procedente del amplificador de escritura y de su
capacitancia.
Ahora veamos el proceso de lectura. Para leer un valor almacenado en la celda,
en primer lugar el amplificador sensible precarga la línea de bit (columna) a un
valor de tensión aproximadamente igual a Vcc/2. Ahora se aserta la línea de
selección de palabra (fila), esto provoca el cierre del canal del transistor de
acceso lo que, a su vez, fuerza a que el nivel presente en el condensador
aparezca en la línea de datos (columna). El amplificador sensible sensa ese valor
y lo amplifica, entonces el dato ya se encuentra presente en un formato
eléctricamente compatible con las puertas lógicas de acceso al exterior. Ahora el
dato puede “salir” al bus y su información puede ser empleada para excitar a
los elementos conectados -el microprocesador.
En el proceso de lectura ha aparecido un problema –grave. La lectura ha
implicado la descarga del condensador y, por tanto, la destrucción de la
información allí almacenada de forma que es necesario recargar esa
Memorias DRAM. ©1999, José M Foces Morán -- 3
Usad esta numeración:
138 de 357
información inmediatamente. Los amplificadores sensibles y de escritura
contienen la lógica necesaria para regenerar el contenido del condensador
recién leído de forma automática: el valor sensado y amplificado es puesto de
nuevo en la columna de dato y empleado para volver a escribirlo dentro del
condensador. A este proceso se le conoce como refresco y, como hemos visto, es
inherente a toda lectura, por tanto, la lectura de una celda de memoria dinámica
implica su refresco automático. Este refresco automático es esencial para lograr
una celda útil y fiable por supuesto ¿cuál es la desventaja además de la
complejidad del circuito integrado? ¡Tiempo! Además del tiempo de lectura del
dato solicitado incurrimos un tiempo adicional en el que el chip no está
disponible para lecturas ni escrituras, esto, como veremos, afecta al ancho de
banda efectivo usable de una memoria.
El refresco de las celdas tiene todavía una componente más que debemos tener
en cuenta. El condensador C tiene unas pérdidas como todo condensador,
típicamente podemos decir que en 4 ms pierde por completo su carga. Para
lograr que no pierda la información almacenada es necesario que con una
periodicidad inferior a 4ms se refresque el contenido de cada condensador. Este
requerimiento complica el diseño de sistemas de memoria basados en DRAM
porque será necesario incluir un elemento que lleve la cuenta de qué fila tiene
que ser refrescada y el tiempo máximo que puede transcurrir antes del refresco.
A este elemento dentro del sistema microprocesador se le conoce con el término
controlador de memoria. El microprocesador i386EX contiene un contador de filas
y un temporizador especialmente dedicados al refresco de las memorias
dinámicas. La unidad del EX dedicada al refresco se llama RCU (Refresh
Control Unit).
¿Son tan importantes las memorias dinámicas como para que el EX integre su
lógica de control? Ciertamente, las memorias dinámicas, en una gran variedad
de formatos, funcionalidades, potencias y rendimientos, constituyen el medio
más barato que existe de diseñar los sistemas de memoria principal de los
computadores modernos. Los chips de memoria dinámica poseen un nivel de
integración y un precio que, aunque sea necesario incluir un controlador
específico que complica el diseño notablemente, aún resultan muy convenientes
en comparación con otras tecnologías como las memorias SRAM en sus
diversas formas. La figura siguiente ilustra la cuota de mercado que
corresponde al uso de memorias DRAM en sistemas computadores desde el
año 1992 al 2000: claramente, 2/3 de las memorias usadas son memorias
DRAM.
En el diseño de sistemas empotrados las memorias DRAM juegan un papel de
importancia parecida al del mercado general. Los sistemas empotrados
modernos requieren memorias principales amplias y con gran ancho de banda a
causa de los procesamientos de datos numéricos de alto grado de complejidad
empleados. Las tarjetas de bus VME de alto rendimiento incorporan
microprocesadores Pentium y procesadores RISC de alto rendimiento que son
en todo similares a los empleados en la estaciones de trabajo de ingeniería.
Memorias DRAM. ©1999, José M Foces Morán -- 4
Usad esta numeración:
139 de 357
Porcentaje de uso de DRAMs
2000
Año
1997
1994
1991
1988
1985
1982
0
20
40
60
80
Porcentaje
Figura. Cuota de mercado de las memorias DRAM (Fuente: Dataquest).
Los computadores empotrados basados en tarjetas de bus VME, históricamente
han sido usados en aplicaciones militares como el cálculo de órbitas de satélites
y trayectorias de misiles. Hoy en día se emplean como nodos de computadores
de procesamiento paralelo masivo que actúan como supercomputadores de
bajo coste. Las cantidades de memoria y las características exigidas por este tipo
de computadores son de una magnitud comparable a las de las workstations y
PCs de alto rendimiento.
Pero ¿Cómo se organiza la conexión de las celdas de memoria DRAM al bus de
un microprocesador como el EX? Obviamente, la organización de la celda vista
no es suficiente para conectarse al bus mencionado y construir así un sistema de
memoria principal. En la sección que sigue vamos a estudiar cómo se organiza
una memoria real que posibilite una conexión reproducible y flexible a
cualquier tipo de bus de microprocesador.
v LA ORGANIZACIÓN DE UN CHIP DE MEMORIA DINAMICA.
El diseño de los chips de memoria dinámica, al igual que el resto de chips
comerciales, está condicionado por una serie de límites que tienen que ver con
aspectos económicos, productivos, de tasa de defectos, costes de investigación y
desarrollo, etc. Desde el punto de vista técnico, tradicionalmente, una de los
retos de la integración de estos chips ha sido el número de patillas que ofrecen
Memorias DRAM. ©1999, José M Foces Morán -- 5
100
Usad esta numeración:
140 de 357
al exterior el cual siempre se ha de mantener lo más bajo posible y, también, la
inclusión de la lógica de precarga y refresco.
La forma más común en la que se presentan estos chips consiste en disponer las
celdas formando una matriz de igual número de filas y columnas. Internamente,
la dirección de memoria de un bit de una celda vendrá dada por su dirección de
fila y su dirección de columna. Por tanto, cuando a una memoria le pidamos un
bit que se encuentra en una determinada posición en la matriz, deberemos
proveerle la fila y la columna en la que se encuentra. Teóricamente tendríamos
un número de líneas de entrada a la memoria representando el número de fila y
otro número (en nuestro caso un número igual) representando la columna. Sin
embargo, el esquema desarrollado por los fabricantes consiste en utilizar las
mismas líneas de entrada para representar tanto la fila como la columna donde
se encuentra el dato, pero, si utilizamos las mismas líneas ¿Cómo distingue la
memoria la fila de la columna? La solución consiste en multiplexar en el tiempo
la fila y la columna: utilizar las mismas líneas para representar la fila, en primer
lugar y, en segundo lugar, la columna. Imaginemos una memoria de 1 Mbit,
esta memoria tendría 1024 filas y 1024 columnas y, por tanto, el número de
líneas de entrada para representar fila y columna sería de log2 1024 = 10. Para
efectuar un acceso a una determinada posición de memoria seguiríamos el
siguiente esquema:
1. Formaríamos la dirección su dirección de fila e indicaríamos que esta
dirección de fila está lista a la entrada del chip mediante la aserción de
una línea adicional llamada “fila preparada” (en Inglés Row Address
Strobe). Esta línea suele presentarse con un nivel de aserción bajo, esto
es, su nombre suele ser #ras. El chip ahora almacenaría esta dirección
de fila en un latch interno.
2. Una vez almacenada la dirección de fila, ya se puede formar la
dirección de columna a la entrada del chip y, por tanto, deberemos
indicárselo a través de una línea especialmente dedicada a este efecto:
#cas (Column Address Strobe). La memoria tiene la fila y la columna
listas, así que a partir de ahora mismo, comienza el acceso al bit
correspondiente. Como hemos explicado anteriormente, el acceso
puede ser de escritura o de lectura.
¿Qué hemos ganado a costa de la complejidad añadida por las líneas #ras, #cas
y el latch de fila? Hemos ahorrado las 10 patillas de columna y, esto es muy
importante ya que esas 10 patillas incrementan notablemente el coste del
empaquetado físico de la memoria y, este empaquetado, cuando se han
amortizado los costes de investigación, diseño e ingeniería del chip en si, es la
parte más costosa del chip final. Esta es la organización de un chip de memoria
dinámica típico.
Memorias DRAM. ©1999, José M Foces Morán -- 6
Usad esta numeración:
141 de 357
Como las direcciones que aparecen en el bus del microprocesador no
representan “filas y columnas”, será necesario convertir la dirección de
memoria del formato índice-secuencial al formato fila-columna. Esta conversión
se efectúa mediante un circuito combinacional. Este circuito no ofrece ninguna
dificultad conceptual, se trata de implementar una tabla de verdad. El problema
con esta tabla es el número de variables de entrada y de salida, ambos muy
grandes. Una de las soluciones consiste en implementar la tabla usando lógicas
programables, por ejemplo, empleando una PLA que tenga la arquitectura y
complejidad adecuadas, capturar la tabla de verdad o función lógica en un
lenguaje de programación como Abel y generar los vectores de programación
del dispositivo.
Dato solicitado en un ciclo #r
Bus de direcciones del sistema microprocesador
CAS#
Latch de índice de
columna
10 bits
Decodificador de
columna
RAS#
10 bits
Decodificador de
fila
1024 Filas
Latch de
índice de
fila
Dataout
1024 Columnas
Celda seleccionada
Figura. Ejemplo de organización de un chip de memoria DRAM de 1 Mbit.
Memorias DRAM. ©1999, José M Foces Morán -- 7
Usad esta numeración:
142 de 357
v TEMPORIZACIÓN DE LOS ACCESOS A LAS MEMORIAS DINAMICAS.
En esta sección estudiaremos con detalle cómo tiene lugar un acceso a una
memoria dinámica a lo largo del tiempo empleando la figura siguiente:
tRAS
t precarga
t A = Tiempo de acceso
#ras
#cas
Bus de
direcciones
del EX
Bus de
memoria
Row Address
Column Address
w/#r
Bus de
datos
Dato
t c = Tiempo de ciclo
Puesto que la
memoria no está
en precarga, es
correcto iniciar
un nuevo acceso.
En este instante
la memoria ha
respondido con el
dato solicitado, al
tiempo
transcurrido se le
llama tA
Fin del refresco y
fin del ciclo
completo de
acceso
Puesto que la lectura es
destructiva resulta necesario
refrescar la memoria
Figura. Cronograma de un ciclo de lectura sobre una memoria dinámica
Las direcciones de memoria que aparecen en el bus del i386EX son direcciones
de memoria real, física y están representadas en binario natural. Como hemos
estudiado en el punto anterior, esta representación de la dirección de memoria
Memorias DRAM. ©1999, José M Foces Morán -- 8
Usad esta numeración:
143 de 357
no coincide con el formato de las direcciones necesario para acceder a una celda
de memoria dinámica. El acceso a una celda de memoria dinámica sigue el
curso representado en la figura:
1. El procesador define e inicia un nuevo ciclo de bus. En el caso del i386EX
podemos pensar que es un ciclo de ancho 16 bits.
2. La dirección de memoria física es traducida a su forma fila/columna y el
controlador de memoria inicia el acceso a las 16 celdas de memoria
dinámica, cada una de ellas está incluida en un chip distinto, pero,
internamente, dentro de cada chip, todas ellas tendrán la misma
dirección de fila y de columna, por tanto, basta con efectuar la traducción
una sola vez.
3. El controlador de memoria aserta la línea #ras indicando que en los 10
bits del bus de direcciones de la DRAM se halla presente la dirección de
fila.
4. Un cierto tiempo más tarde, cuando a la memoria le ha dado tiempo a
escribir el latch de fila, el controlador de memoria aserta #cas indicando
que ya puede comenzar el acceso puesto que tanto la fila como la
columna son conocidas.
5. La memoria responde transfiriendo el bit presente en la fila direccionada a
la línea de salida de datos correspondiente a este chip concreto. El resto
de 15 chips habrán seguido un curso igual y, por tanto, en las salidas de
datos de esos chips también estarán presentes los datos solicitados.
6. Puesto que, como hemos visto anteriormente, las lecturas son
destructivas, es necesario refrescar la celda recién leída y, este proceso
lleva un tiempo conocido como tiempo de precarga.
Las ideas que acabamos de expresar constituyen los conceptos más básicos en
relación al uso de memorias DRAM, además, existe un gran número de técnicas
que permiten mejorar el ancho de banda de utilización de estos chips y que no
vamos a explicar en esta sección. Aquellos que tengan un interés por este
campo, pueden consultar alguna de las referencias bibliográficas.
Dos comentarios cortos sí son necesarios. El primero tiene que ver con el
refresco de las memorias: es esencial refrescar el contenido de todas las celdas al
menos cada 4 ms. Recordemos que al leer una celda, ésta es refrescada
automáticamente, por tanto cuando resulte necesario refrescar una celda,
bastará con leerla y descartar el dato, pero, conocida la organización interna de
los chips ¿No hay una forma más eficaz de llevar a cabo el refresco, es decir,
una forma de refresco que no nos obligue a generar todas y cada una de las
direcciones de memoria de las celdas involucradas? Sí, muchos fabricantes
incluyen la posibilidad de efectuar un tipo de refresco llamado sólo #ras en el
cual primero se especifica la dirección de la fila a refrescar y #ras es asertada
Memorias DRAM. ©1999, José M Foces Morán -- 9
Usad esta numeración:
144 de 357
durante un tiempo suficiente como para producir la lectura de la fila entera y su
refresco. La ausencia de #cas es la pista que le damos al chip de que este ciclo se
trata del refresco de ¡una fila de celdas completa!
El segundo comentario, y final, es que el número de bits que se pueden
conseguir leer de una memoria dinámica en cada segundo de tiempo no sólo es
función del tiempo de acceso si no también del tiempo de precarga y del retraso
que está en medio (llamado retardo de precarga), es decir, el ancho de banda de
utilización de una memoria dinámica está afectado por el tiempo de acceso pero
no es función únicamente de éste. Veremos más adelante que este ancho de
banda se puede mejorar notablemente con unas organizaciones de los chips de
memoria conocidas como entrelazadas. La única forma de mejorar el tiempo de
acceso es mejorar la tecnología subyacente, hacer transistores, amplificadores y
lógica todos ellos más rápidos.
Antes de introducir los módulos multi-bit de memorias dinámicas vamos a
estudiar un chip de memoria dinámica sencillo y un diseño de memoria
principal para un microprocesador genérico. El chip contiene 64K bits de
memoria dinámica y su patillaje aparece en la figura siguiente.
Nombres de
las patillas
Patillaje
#refresh
d
1
2
#w
3
15
14
#ras
a0
4
13
a1
5
6
a2
7
vcc
8
16
vss
#cas
q
a6
12 a7
11 a8
10 a9
9 a10
#refresh
Refresh
a0 - a7
0 7
d
q
Address input
#w
Read/write input
#ras
Row address strobe
#cas
vcc
Column address strobe
Power (+ 5V)
vss
Ground
Data in
Data out
Figura. Patillaje de un chip de DRAM típico de 64 Kbits de capacidad.
Finalmente, vamos a construir un sistema de memoria basado en chips como
este para un microprocesador genérico con bus de 16 bits. Este microprocesador
podría ser el EX, pero, puesto que no se ha empleado la unidad RCU, específica
del i386EX, el diseño pude considerarse válido para cualquier microprocesador
que posea un bus interfaz síncrono con indicación de fin de ciclo. El diseño del
sistema emplea 16 chips de memoria como el explicado y, para obtener las
direcciones de fila y columna, incorpora chips de lógica integrada de amplia
difusión de la serie 74 de Texas Instruments. El sistema diseñado posee una
Memorias DRAM. ©1999, José M Foces Morán -- 10
Usad esta numeración:
145 de 357
Buses de control, datos y direcciones
capacidad de 64 Kbytes de memoria. Los buffers empleados son también de
amplia difusión y sirven para efectuar el conexionado sin cargar excesivamente
las patillas del bus del microprocesador, su uso es muy común.
A01
A02
A03
A04
A05
A06
A07
A08
A09
A10
A01
A02
A03
A04
A05
A06
A07
A08
A09
A10
A11
A12
A13
A14
A15
A16
A11
A12
A13
A14
A15
A16
#ads
w/#r
#ready
Buffer del
Bus de
Datos
Control
del array
de
memoria
dinámica
R0
R1
R2
R3
A0
R4
A1
R5
A2
R6
A3
R7
A4
C0
A5
C1
A6
C2
A7
C3
C4
C5
CMultiplexor
6
de filas/
C7
columnas
A0
#w
A1
A2
#cas
A3
#ras
A4
A5
d
64Kx1
A6
RAM q
A7
D00
A0
#w
A1
#cas
A2
A3
#ras
A4
d
A5
64Kx1
A6
RAM q
A7
D01
mplx
.
.
.
.
#ras
#cas
#w
A0
w/#r
D00 D15
DIR
ENABLE
#w
A1
A2
#cas
A3
#ras
A4
A5
d
64Kx1
A6
RAM q
A7
D15
Buffers del
bus de datos
v MODULARIZACIÓN DE LOS SISTEMAS DE MEMORIAS DINÁMICAS.
Memorias DRAM. ©1999, José M Foces Morán -- 11
Usad esta numeración:
146 de 357
Desde hace varios años, los chips de memoria dinámica no suelen usarse por
separado, más bien, suelen soldarse varios de ellos formando una matriz de 8,
16 o más de chips de un cierto tamaño en bits. Cada uno de ellos es capaz de
responder con un dato de un bit a cada solicitud. Estos módulos han recorrido
una serie etapas que van desde el SIMM (single in-line memory module) de 30
patillas, fácil de instalar y adecuado para procesadores con un ancho de palabra
de 8 o 16 bits. Pasando por los SIMM de 72 patillas que llegaron a su máximo de
uso alrededor de 1995 hemos llegado, hoy en día, a dos generaciones de
módulos DIMM (Dual in-line memory module) de 168 patillas.
El DIMM de 168 patillas fue el primer módulo DIMM de 8 bytes de ancho pero
compatible con el SIMM de 4 bytes con tan solo cortocircuitar dos patillas
concretas que venían dispuestas una al lado de la otra, el módulo se comporta
ahora como dos módulos de ancho 4 bytes cada uno. Cada uno de estos
módulos es capaz de responder a una solicitud de acceso con 4 bytes en un solo
ciclo de acceso lo que hace que estos módulos sean ideales para su uso en
sistemas con 4 words de ancho (386DX por ejemplo) u 8 words de ancho
(Versiones de 64 bits del microprocesador MIPS).
En la figura siguiente aparece un módulo SIMM de 72 patillas. Este módulo es
un SIMM conforme con la norma JEDEC relativa a SIMMS. El número de
patillas es de 72 y el ancho de palabra accedida en cada transacción es de 4
bytes. IBM Microelectronics incluye lógica de corrección de errores ECC
completamente transparente al sistema. El módulo ECC incluido en el SIMM
corrige errores de un solo bit en cualquier byte de los 4 que forman el ancho
completo de palabra del SIMM. Este módulo está especialmente indicado en
sistemas que son responsables de aplicaciones críticas pero que no tienen un
soporte nativo ECC.
Figura. Simm de 72 patillas modelo IBM11D4480BG de IBM Microelectronics:
4M x 36 ECC-on-SIMM.
Otros formatos de módulos de alta densidad y de alta capacidad son los
módulos apilados SOJ, TSOJ y TSOP. La construcción de estos módulos consiste
Memorias DRAM. ©1999, José M Foces Morán -- 12
Usad esta numeración:
147 de 357
en apilar varios chips, generalmente 2 ó 4, de esta forma el ahorro de área es
muy importante.
Figura. Comparaciones de altura de módulos apilados y las capacidades
obtenidas (Fuente: IBM Microelectronics, IBM MicroNews 2Q 1997).
v UNA TÉCNICA PARA MEJORAR EL ANCHO DE BANDA DEL SISTEMA DE MEMORIA
PRINCIPAL: ENTRELAZADO DE BANCOS DE MEMORIA.
Es difícil curar los males derivados del tiempo de acceso de las memorias
(latencia), sin embargo, no lo es mejorar el ancho de banda del sistema de
memorias. Si nos fijamos en el cronograma de acceso a una memoria DRAM
podemos observar que hay un tiempo entre el instante en que los datos ya están
listos y el momento en el que termina el ciclo actual de acceso. Durante ese
tiempo el módulo de memoria no está disponible, esta siendo refrescado y por
tanto el microprocesador no puede solicitarle otro dato todavía: cierto, pero si el
microprocesador solicitase otro dato que no residiese en este banco de memoria
si no en otro distinto no incurriría en ese tiempo de espera. Esta es la idea básica
a cerca del entrelazado de bancos de memoria: organizar el sistema de memoria
de forma que grupos de 1, 2, 4 ó más direcciones de memoria residan en bancos
distintos.
Trabajemos sobre un ejemplo de un sistema basado en el microprocesador
i386EX con un sistema de memoria principal realizado a base de SIMMs de 2
bytes de ancho. El bus del microprocesador posee un ancho de 16 bits (2 bytes)
y, normalmente, los ciclos de bus usarán el ancho completo. Si disponemos los
SIMMs de forma que el primer SIMM contenga las palabras (words de 16 bits
en modo real del EX) que tienen por direcciones de memoria 0, 4, 8, etc. Y el
segundo SIMM que tenga las words con direcciones 1, 5, 9, etc. y así
sucesivamente obtendríamos un sistema con cuatro SIMMs como el siguiente:
Memorias DRAM. ©1999, José M Foces Morán -- 13
Usad esta numeración:
148 de 357
tcyc
ta
precarga
0
simm 0
direcciones 0, 4, 8, 12 etc
precarga
1
simm 1
direcciones 1, 5, 9, 13 etc
precarga
2
simm 2
direcciones 2, 6, 10, 14 etc
precarga
3
simm 3
direcciones 3, 7, 11, 15 etc
inicio
acceso 0
Bus de
direcciones
Bus de datos
fin
acceso 0
16
i386EX
Figura. Sistema de memoria principal basado en SIMMs de 2 bytes. El sistema
contiene cuatro bancos.
En este esquema el tiempo de acceso no ha mejorado, pero si la cadena de
direcciones de memoria generadas desde el microprocesador tiene un cierto
grado de secuencialidad, observaremos una mejora notable del ancho de banda
de utilización del sistema de memoria. Si el sistema no estuviera entrelazado el
dato correspondiente al acceso número 1 en vez de estar listo nada mas recibir
el dato del acceso 0, estaría listo un tiempo de precarga más tarde. Esta
situación se reproduciría a lo largo de los siguientes accesos también.
Si la cadena de direcciones de memoria no presenta un alto grado de
secuencialidad o de dispersión, rompiendo por tanto la posibilidad de utilizar
bancos distintos, la ganancia de ancho banda debido al entrelazamiento sería
ostensiblemente menor. La siguiente curva resume esta ganancia en un sistema
con entrelazamiento de orden inferior (como el ejemplo explicado) donde se
han utilizado un número variable de vías. El simulador acepta un conjunto de
direcciones de memoria generadas por un generador de cadenas de referencia.
En el eje vertical se presentan los tiempos de simulación correspondientes a la
misma cadena de referencia, puede observarse que el sistema con 16 vías es
notablemente más rápido y esto es debido a la mejora en el ancho de banda de
uso de la memoria principal.
Memorias DRAM. ©1999, José M Foces Morán -- 14
Usad esta numeración:
149 de 357
190000
185000
180000
175000
170000
165000
160000
155000
150000
145000
140000
1-way
2-way
4-way
8-way
16-way
Figura. Mejora de velocidad obtenida al usar diversos grados de entrelazado de
memorias dinámicas (Resultados obtenidos con un simulador diseñado por el
autor bajo la dirección del profesor David Bantz, Columbia University, 1995).
Memorias DRAM. ©1999, José M Foces Morán -- 15
Usad esta numeración:
150 de 357
Apuntes de Máquinas
Electrónicas
© 1994, 2004 José María Foces Morán.
Capítulo 5
Memorias para sistemas
empotrados: SRAM.
Usad esta numeración:
151 de 357
Las memorias de acceso aleatorio estáticas: SRAM, Static Random Access
Memory.
Además de la memoria DRAM estudiada, existe otro tipo de memoria que
juega un papel muy importante en el diseño de computadores, sobre todo en el
diseño de memorias cache. Este tipo de memoria se la conoce como SRAM,
haciendo énfasis en el hecho de que esta memoria a pesar de ser también una
memoria volátil, no necesita ser refrescada periódicamente para mantener sus
contenidos intactos.
Las memorias SRAM, como veremos con más detalle en los párrafos siguientes,
son memorias, en general, mucho más costosas que las DRAMs y su nivel de
integración es mucho menor dado que el tamaño de la celda básica varias veces
superior al de la celda de memoria DRAM.
Para comenzar el estudio de la celda básica SRAM, recordemos cómo puede
realizarse un elemento estático de almacenamiento de un bit, empleando
solamente nuestros conocimientos previos de electrónica digital. El ejemplo que
sigue está tomado del texto “Contemporary Logic Design” escrito por el
profesor Randy Katz de la Universidad de California en Berkeley. Es un
ejemplo casi grotesco –por su simplicidad y elegancia. La idea es, si conectamos
dos inversores en cascada en una de las conexiones tendremos un 1 y, en la otra,
obligatoriamente, tendremos un 0 -indefinidamente. Si una de estas conexiones
es la salida de nuestro circuito “almacenador de un bit”, ¡tenemos una celda de
memoria! La cuestión es ¿Cómo se escribe la celda? ¿Cómo se puede cargar
(forzar) un valor en la celda?
Figura. Celda básica de memoria.
Si consiguiéramos abrir uno de los bucles que une la salida de una puerta con la
entrada de la siguiente, podríamos entonces forzar un nivel a la entrada de esa
puerta, posteriormente cerrar el bucle y, entonces, la celda contendría el valor
deseado ¿Cómo se puede abrir el bucle en el momento en el que deseamos
escribir en la celda? Usando una puerta lógica de transmisión. Esta puerta posee
dos estados, cerrado o abierto, controlables mediante una patilla de entrada
llamada ld (del Inglés load). Es decir, la puerta de transmisión, se comporta
como un puente que “transmite” su entrada eléctricamente a su salida o bien no
lo hace. Coloquemos pués una puerta de transmisión que cierre el bucle cuando
no deseamos escribir y que abra el bucle cuando sí deseamos escribir. Al mismo
Memorias SRAM. ©1999, José M Foces Morán -- 1
Usad esta numeración:
152 de 357
tiempo, si conectamos otra puerta de transmisión a la entrada del inversor y
que funcione al revés que la anterior cuando deseemos escribir esta puerta nos
conectará con al fuente de señal y la otra puerta de transmisión abrirá el bucle:
¡así de sencillo! La figura siguiente contiene la solución:
Puerta de
transmisión
Puerta de
transmisión
Celda básica
Figura. Una celda básica de memoria a base de inversores y puertas de
transmisión.
Esta celda básica, aunque resulta ilustrativa a nivel conceptual e introductorio,
no es muy eficiente pues el número de transistores necesarios para su
implementación resulta prohibitivo. A pesar de ello, la idea de fondo es
interesante para comprender cómo funciona la celda real de memoria estática
que nos disponemos a estudiar.
v ASPECTOS BÁSICOS SOBRE LA CELDA DE MEMORIA SRAM REAL.
La celda de memoria estática empleada en el diseño de circuitos integrados es
muy parecida a la anterior, contiene 6 transistores MOS en total: 2 de ellos
constituyen la celda de almacenamiento, otros dos proveen el acceso a la celda
(gating) y, por último, otros dos transistores constituyen cargas activas que
hacen las veces de resistencias de pull-up a Vdd.
Memorias SRAM. ©1999, José M Foces Morán -- 2
Usad esta numeración:
153 de 357
Vdd
Par complementario CMOS
que forma uno de los flipflops
Entrada/salida de
datos
complementaria
Entrada/salida de
datos positiva
Puertas de
transmisión de acceso
a las celdas
Figura. Celda básica de memoria SRAM a base de transistores CMOS. No se
muestra el circuito de escritura/lectura (amplificadores sensibles).
Los transistores enmarcados en el óvalo constituyen uno de los flip-flops donde
se escribe la forma positiva del dato. Los transistores opuestos forman el otro
flip-flop de la célula, el que almacena la forma negada del dato. La escritura de
un dato en esta célula supone el cierre de los transistores de las puertas de
transmisión y por tanto la activación de la puerta de ambos transistores,
previamente el nivel de tensión adecuado al dato que se desea escribir, tanto en
su forma original como negada, tienen que asertarse en las entradas de datos.
Los fabricantes de versiones sencillas de circuitos integrados de memoria
estática MOS suelen fabricar productos con capacidades de entre 1 Kbyte y 16
Kbytes. Estos últimos dispositivos pueden llegar a contener más de 100000
transistores, siendo el nivel de integración no demasiado alto, el número de
dispositivos sí lo es, la solución más común es diseñar una celda que contenga
menos transistores. La solución comercial ha sido tradicionalmente la celda de
memoria DRAM que estudiamos en apartados anteriores.
¿Cuáles son entonces los aspectos más convenientes de este tipo de memorias?
Uno de los más notables es que estas memorias al no necesitar refresco los
diseños son tremendamente más sencillos y, además, tanto el ancho de banda
de utilización como la latencia son notablemente mejores en este tipo de
memorias. La figura siguiente muestra una comparación de velocidades entre
diversos tipos de tecnologías de memorias en los años 90.
Memorias SRAM. ©1999, José M Foces Morán -- 3
Usad esta numeración:
154 de 357
450
Velocidad en MHz
400
350
DRAM
EDO
SRAM común
DRAM Sínc
Cache SRAM
Procesadores
300
250
200
150
100
50
19
90
19
91
19
92
19
93
19
94
19
95
19
96
19
97
19
98
19
99
20
00
0
Año
Figura. Tendencia en las mejoras de velocidad de diversas tecnologías de
memorias.
En ciertos sistemas empotrados las memorias SRAM juegan un papel esencial
debido al reducido tamaño y complejidad, en estos sistemas el uso de memorias
SRAM es un deber. Ahora vamos a explorar las memorias SRAM desde el
punto de vista del diseñador de computadores, en concreto vamos a estudiar un
chip de memoria SRAM que nos dará la oportunidad de conocer su patillaje, su
modelo funcional y cómo transcurren los accesos desde el punto de vista de la
temporización.
v EL CHIP DE MEMORIA SRAM DESDE EL PUNTO DE V ISTA DEL DISEÑADOR DE LOGICA
DIGITAL.
El chip de memoria SRAM que vamos a estudiar es el modelo 51C59. En la
figura siguiente aparece el modelo estructural de este chip. En la organización
interna de los chips de SRAM no se ha optado por multiplexar las direcciones
de fila y de columna. Tradicionalmente se ha preferido la facilidad de los
diseños frente a nivel de integración. Por lo demás esta organización es similar
a la de los chips de memoria dinámica excepto, claro está, en lo referente a la
estructura de la propia celda básica. En la figura siguiente aparece el modelo
estructural de un famoso chip de memoria estática, el 51C59.
Memorias SRAM. ©1999, José M Foces Morán -- 4
Usad esta numeración:
155 de 357
18
A2
VCC
17
GND
A1
16
A0
2
A5
MEMORY ARRAY
128 ROWS
128 COLUMNS
ROW
SELECT
1
A4
4
A7
3
A6
l/O 1
COLUMN I/O CIRCUITS
COLUMN SELECT
l/O 2
l/O 3
INPUT
DATA
CONTROL
8
l/O 4
7
A11
6
A10
5
A9
19
A8
A3
CS
WE
Figura. El modelo estructural del chip 51C59 de memoria SRAM.
El array de memoria estática está formado por 128 columnas x 128 filas. En cada
posición de la matriz hay un elemento de 4 celdas SRAM. Todas las celdas de
un punto de la matriz comparten la misma dirección de fila y de columna. El
chip posee cuatro señales de i/o que le permiten escribir o leer un dato de 4 bits
una vez que ha sido formada su dirección dentro de la matriz. La capacidad
total del chip es de 27 x 27 x 4 bits = 213 bytes = 8 Kbytes. Estudiemos con mas
detalle su modelo funcional.
v EL MODELO FUNCIONAL DEL CHIP 51C59.
En el diseño de un computador real de cierto nivel de prestaciones no suele
bastar con un solo chip de memoria y, por tanto, hay que emplear varios en el
mismo diseño. Cada uno de los varios chips del mismo tipo empleados deberán
responder a rangos de direcciones de memoria distintos. Generalmente se
suelen emplear las líneas altas del bus de direcciones para diferenciar los
diferentes espacios de direccionamiento y, una vez establecido el rango de
direcciones, se usan las líneas de menor peso de este bus para direccionar la
palabra concreta deseada dentro de un chip. Concluyendo:
Memorias SRAM. ©1999, José M Foces Morán -- 5
Usad esta numeración:
156 de 357
•
Las líneas altas del bus se emplean para dividir el espacio completo de
direccionamiento del procesador en sub-espacios. Cada uno de los subespacios debe activar un chip o una fila de chips de memoria en caso
de que un solo chip no ofrezca un ancho de palabra coherente con el
ancho del bus de datos del procesador.
•
Una vez activado un determinado chip (o una fila correspondiente a
una word completa), se usan las líneas bajas del bus de direcciones
para seleccionar la palabra deseada dentro del chip, por tanto, este
grupo de líneas bajas del bus serán las que se conecten a las entradas
de direcciones de los chips de memoria.
•
El chip (o fila de chips) de memoria contiene una señal llamada #cs,
chip select. Esta señal debe ser verdadera (fijarse que la señal suele
presentar un nivel de aserción bajo) cuando la dirección de memoria
procedente del procesador coincide con el espacio correspondiente a
este chip. La evaluación de esta condición es puramente combinacional
y se puede implementar de diversas formas dependiendo de cada
situación:
o Con puertas lógicas
o Con multiplexores y decodificadores
o Con lógicas fusionables que dependiendo de la complejidad
pueden usarse todas aquellas que son combinacionales como
PLA, FPGA, etc.
o Con un circuito de aplicación específica si el diseño es
particularmente complejo ASIC).
51C69 SRAM
Modelo funcional
A0
A1
A2
A3
A4
A5
A6
A7
A8
A9
A10
A11
l/O1
l/O2
l/O3
l/O4
W E CS
Figura. El modelo funcional del chip de SRAM 51C59.
Memorias SRAM. ©1999, José M Foces Morán -- 6
Usad esta numeración:
157 de 357
Los nombres de las patillas
A
- A
0
I/O
11
Address Inputs
#we
Write Enable
#cs
Chip Select
1
V
- I/O
CC
GND
4
Data Input/Output
Power(+5V)
Ground
Figura. El modelo funcional del chip de SRAM 51C59(nombres).
•
El bus de direcciones se compone de 13 líneas de selección de
palabra.
•
El bus de datos i/on se compone de 4 bits, por tanto el ancho de
cada nodo de almacenamiento está formado por una palabra de
ancho 4 bits. Entendemos por nodo cada uno de los grupos de 4
celdas que poseen una misma dirección de palabra.
•
La patilla #we. Un cero (0) en esta línea (nivel de aserción bajo)
significa que la operación que va a ser llevada a cabo es un (w)
escritura (write). Un uno (1) en esta patilla signifca que la
operación que va a efectuarse es una lectura (r, read).
•
Por último la patilla #cs (chip select) es la línea de selección de
chip. Un cero en esta patilla significa que las líneas altas del bus
representan un espacio del mapa de memoria que coincide con el
asignado a este dispositivo y, por tanto, este dispositivo tiene que
llevar a cabo la operación designada en la patilla #w, sobre el
nodo que tiene por dirección interna aquella representada en
binario natural por el bus a0 -a11. Si la operación es una lectura el
dispositivo transferirá los cuatro bits desde el nodo hacia el
exterior a través de las líneas i/on y si la operación es una
escritura entonces el dispositivo transferirá al nodo el contenido
de las cuatro líneas i/on.
Memorias SRAM. ©1999, José M Foces Morán -- 7
Usad esta numeración:
158 de 357
A4
1
20
A5
2
19
vcc
A3
A6
3
18
A2
A7
4
17
A1
A8
5
16
A0
A9
6
15
l/O
1
A10
7
14
l/O
2
A11
8
13
l/O
3
#cs
9
12
l/O
4
GND
10
11
#we
Figura. Empaquetado físico DIL-20 del 51C59.
#cs
#we
Modo
i/on
Power
H
L
L
X
L
H
No seleccionado
High-Z
DIN
DOUT
Active
Active
Active
escritura
lectura
Figura. Tabla de verdad del 51C59
t WC
Dirección de
memoria(13
bits)
t AS
t CW
#cs
t AW
t WR
t WP
#we
t DW
t DH
Datos válidos hacia el interior
de la memoria
i/o n
t WZ
Figura. Cronograma del ciclo de escritura sobre el chip 51C59.
Memorias SRAM. ©1999, José M Foces Morán -- 8
Usad esta numeración:
159 de 357
El ciclo de escritura comienza en el momento en que la línea #cs del chip 51C59
pasa a estado asertado. En ese instante se decodifica internamente la dirección
de memoria (A12 -A0 ) para formar la dirección de fila y columna que
corresponde al nodo direccionado dentro del array. Si en ese instante o en una
ventana de tiempo no superior a twp antes del final del ciclo indicado por la
des-aserción de #cs, #w está en 0, el ciclo será de escritura, si no, será de lectura.
t RC
t AA
Dirección de
memoria
Read Cycle No. 2.
CS
t ACS
t HZ
t LZ
#we
DOUT
Figura. El ciclo de lectura del chip de SRAM 51C59.
Memorias SRAM. ©1999, José M Foces Morán -- 9
Usad esta numeración:
160 de 357
Las memoria no volátiles de mas amplio uso en el diseño de sistemas
empotrados: Eprom y Flash.
Las tecnologías de memorias que hemos revisado hasta este momento, DRAM y
SRAM, son memorias volátiles: al suprimir la potencia eléctrica de los
dispositivos el contenido se pierde. Estos dispositivos al ponerse en
funcionamiento de nuevo aparecen con contenidos aleatorios en cada una de
sus celdas y, por tanto, como veremos en el capítulo siguiente, deben ser
examinados e inicializados en el arranque del sistema computador. En los
párrafos siguientes, en cambio, revisaremos las tecnologías de memorias no
volátiles: memorias cuyo contenido no se pierde al suprimir la potencia
eléctrica.
Resulta esencial la presencia de memorias no volátiles en un sistema
computador. Estas memorias albergan el código y los parámetros de
inicialización más fundamentales del computador por lo debe existir una
garantía de que sus contenidos no se perderán al suprimir la potencia del
sistema.
Al igual que en el caso de las memorias SRAM y DRAM, las memorias volátiles
se presentan en diferentes formatos físicos, operacionales y eléctricos y, de ellos,
se derivan una serie de ventajas de diseño, económicas y de otros tipos que
veremos. Desconsiderando aspectos de interés casi exclusivamente histórico,
podemos decir que la primera memoria no-volátil de amplio uso fue la
memoria ROM inventada por ingenieros de la compañía Intel. Este tipo de
memoria más concretamente conocida como ROM de máscara, en sus diversas
formas, es un dispositivo de uso muy extendido. Se trata de un circuito
integrado en el que las celdas que almacenan los bits están permanentemente
forzadas a valor 0 o 1 y, su fabricación puede llevar un tiempo considerable
además de la exigencia de fabricar partidas de varios miles como mínimo
porque de otro modo el coste por dispositivo llegaría a ser prohibitivo. La
versión más interesante de este disposotivo, la EPROM (Electrically
Programmable Read Only Memory) es un dispositivo que puede programarse
eléctricamente y borrase mediante la exposición a luz ultravioleta.
Curiosamente, un gran invento, puede decirse que la memoria EPROM surgió
de forma casual: Intel consideraba que en ocasiones era más aleccionador saber
porqué fallaba un dispositivo que porqué funcionaba correctamente. Cuando la
compañía Intel decidió apostar por los dispositivos con puerta de silicio, a un
físico llamado Dov Forman se le encomendó la tarea de averiguar las causas de
fallo en el proceso de fabricación. Observando los dispositivos que presentaban
fallos, pensó que quizás éstos se debieran que algunas de las puertas del
dispositivo habrían llegado a desconectarse, a quedarse flotantes. Poco tiempo
después se le ocurrió que podría obtenerse un uso útil de este hecho este
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 1
Usad esta numeración:
161 de 357
fenómeno y de hecho demostró que el dispositivo podía almacenar la carga, un
semiconductor que se comportaba como una memoria ROM y podía ser
programado eléctricamente con una gran facilidad. Poco tiempo más tarde Intel
dedicó una importante masa de recursos humanos y económicos en
investigación y desarrollo de la nueva tecnología.
Otra gran ventaja de la EPROM, además de su facilidad de programación, era
su facilidad de borrado: en unos pocos minutos un ingeniero podría borrar el
dispositivo exponiéndolo a luz ultravioleta, reprogramarlo eléctricamente y
volver a probar el sistema microprocesador con un nuevo programa. El primer
dispositivo EPROM producido por Intel tenía una capacidad de 2 Kbytes cuyas
máscaras eran tan grandes que tuvieron que ser particionadas en cuatro trozos
lo que creó muchas dificultades de alineamiento.
La primera EPROM comercial tenía número de pieza de Intel 1702. Hoy en día
Intel prácticamente ha abandonado el mercado de EPROMs y ha sustituido
estos dispositivos por los más amplios, rápidos y flexibles FLASH, también
inventados por ingenieros de Intel Corp.
Al igual que con el resto de dispositivos, a modo de introducción, vamos a
estudiar la estructura de la celda básica de memoria EPROM.
v LA CELDA BÁSICA DE MEMORIA EPROM: FAMOST( FLOATING GATE AVALANCHE
INJECTION MOS TRANSISTOR).
Las memorias EPROM se pueden programar eléctricamente, esto significa que
mediante un patrón de niveles eléctricos debidamente temporizado,
conseguiremos establecer un enlace fusible entre cada celda de un bit y la línea
por donde llega el bit que deseamos grabar, un 1 o un 0. La energía de la señal
de grabación es muy superior a la energía transportada por las señales digitales
que representan los datos leídos desde esta memoria en su funcionamiento
normal: los pulsos de corriente procedentes de esta señal queman la conexión
entre la celda y la línea de bit de forma que ahora se encuentren desconectados
y, por tanto, el valor almacenado sea un 0. Todos los enlaces fusibles no
quemados representan unos (1).
La generación de los pulsos de corriente en los instantes adecuados se lleva a
cabo mediante el uso de un circuito llamado programador. Hoy en día una gran
variedad de fabricantes producen EPROMS, algunas de ellas con unas
diferencias en los algoritmos de grabación casi anecdóticas, pero, los
programadores llamados universales, contienen un computador que incorpora
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 2
Usad esta numeración:
162 de 357
los algoritmos de programación de una gran variedad de EPROMS. En el
capítulo dedicado a la integración de hardware y software, realizaremos un
pequeño computador y, allí tendremos ocasión de comprender cuales son las
funciones de un programador moderno y otras de sus características.
En la figura que sigue, aparece un transistor de almacenamiento con una puerta
flotante (¡desconectada según el inventor Dov Forman!). En este dispositivo la
puerta está ubicada entre la puerta de control y el substrato y, a diferencia de la
puerta de control del dispositivo anterior, no está conectada a ninguna línea ni
de palabra, ni de bit, sencillamente flota, es decir no tiene un valor de tensión
definido. Esta puerta flotante suele realizarse con silicio policristalino. El estado
de la puerta flotante permite mantener la información permanentemente en el
dispositivo. El transistor funciona como un transistor MOS normal cuando la
puerta flotante no tiene cargas eléctricas.¿Qué ocurre si la puerta es cargada con
electrones? Si el potencial de la puerta llega a ser muy alto, entonces el
transistor de almacenamiento puede activarse.
Para programar un 0 se necesita un pulso de 20v durante 50ms, este tiempo es
muy largo en comparación con los tiempos de escritura de las SRAM y DRAMs
con lo que se hace necesario el llevar a cabo la programación del dispositivo con
tensiones más altas (más energía por electrón) pero durante tiempos limitados
que no dañen la capa de aislamiento: el programador universal.
Para efectuar el borrado de la memoria EPROM debe ser expuesto a radiación
ultravioleta: los electrones recogidos en la puerta flotante absorben energía de
estas longitudes de onda, aumentan su temperatura y pueden entonces
abandonar la puerta flotante, de este modo el dispositivo, después de unos 20
minutos de exposición, todos los portadores de carga son forzados fuera de la
puerta flotante quedando el dispositivo listo para volver a ser programado.
Puerta
Drenador
Surtidor
Puerta de control
Puerta flotante
Oxido
Oxido
-
surtidor-n
-
-
drenador-n
-
sustrato-p
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 3
Usad esta numeración:
163 de 357
Figura. Estructura de una celda de memoria EPROM.
Los empaquetados físicos de memorias EPROM incluyen una pequeña ventana
transparente justo encima del chip. Esta ventana está realizada con cristal de
cuarzo la cual resulta muy costosa a medida que los costes incurridos en el
proceso de fabricación son menores y, la lámpara de UV representa también un
coste para el usuario final por lo que los fabricantes han desarrollado otro
dispositivo llamado EEPROM el cual sí es borrable eléctricamente (Electrically
Eraseable Programmable Read Only Memory). En estos dispositivos las cargas
encerradas en la puerta flotante pueden ser forzados hacia fuera por medio de
un pulso eléctrico. En ciertos sistemas donde se requiere una gran facilidad de
borrado, el uso de estas memorias puede ser obligado, sin embargo, dado su
alto coste, es posible que, como veremos, resulte más beneficioso el uso de
memorias FLASH.
v UN CHIP DE MEMORIA EPROM, 27256 DE INTEL.
Vamos a estudiar a hora un chip de memoria EPROM real, un dispositivo de
32Kb de capacidad. Esta memoria se presenta en dos formatos de interfaz
eléctrica externa, con niveles TTL y con niveles CMOS. El identificador de la
versión CMOS es 27C256.
El ancho de palabra de este dispositivo es de 8 bits, con lo cual, cada dirección
de memoria presente en el bus representa un byte entero dentro del dispositivo.
El bus de direcciones, por tanto, tiene un ancho de log2 32*1024 = 15 bits. Las
líneas de este bus se identifican como A0 -A15 . El bus de datos del dispositivo se
representa por O0 -O8 ya que el dispositivo sólo puede producir “outputs”,
salidas de datos procedentes de las celdas internas del chip. Obviamente, no se
pueden escribir datos en una memoria EPROM, excepto en el proceso de
programación en el cual, puesto que es completamente transparente al usuario,
nos ocuparemos de él en la construcción del proyecto final.
Las patillas Vcc y GND representan la tensión de alimentación y la masa digital,
respectivamente. Nos fijaremos ahora en las patillas #oe y #ce. La patilla #ce es
el chip enable del integrado y #oe (output enable) indica al dispositivo que active
los drivers del bus de datos, esto es, que el contenido de la celda direccionada
aparezca de hecho en la salida del bus de datos. ¿Clara la distinción entre
ambos? La aserción de #ce hace que el dispositivo decodifique la dirección que
aparece en su bus de direcciones y que seleccione el nodo (de 8 celdas de un bit)
elegido; la aserción de #oe significa habilitar la conexión eléctrica entre las
patillas exteriores del bus de datos y las líneas internas de ese mismo bus. En
realidad, la señal #oe se emplea para evitar un problema que se presenta en los
sistemas microprocesadores cuando el microprocesador lleva a cabo un ciclo de
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 4
Usad esta numeración:
164 de 357
escritura inmediatamente después de un ciclo de lectura. En esas circunstancias
si el dispositivo direccionado en el ciclo de lectura no se desconecta a tiempo
del bus de datos y el micro inicia el ciclo de escritura, puede haber una ventana
de tiempo en la que ambos, el dispositivo y el microprocesador estén excitando
eléctricamente el bus de datos. Esta situación es conocida con el nombre de
contención de bus y debe evitarse a toda costa pues puede conducir a daños en
alguno de los transistores involucrados en ambos dispositivos. Otra vez, en el
capítulo dedicado al proyecto real de integración, dedicaremos unas líneas más
a este problema y cómo efectuar diseños sin contención de bus.
Dirección de
memoria
....
VIH
Dirección válida
....
VIL
VIH
CE
....
VIL
t CE
VIH
OE
....
VIL
O 0-O 8
VIH
tACC
t OE
Alta imp
VIL
....
t OF
t OH
Datos validos
....
Alta imp
Figura. Cronograma del ciclo de lectura de una memoria EPROM Intel 27256.
A0 -A14 Bus de direcciones
#ce
Línea de selección del chip
#oe
Habilitación de salida de datos
#we
Habilitación de escritura
(Sólo en el proceso de programación)
Gnd
Nivel de referencia digital
Vcc
Tensión de alimentación
Vpp
Tensión de programación
Tabla. Nombres de las patillas de la EPROM Intel 27256
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 5
Usad esta numeración:
165 de 357
Intel
27256
1
28
2
27
A7
3
26
A6
4
25
A5
5
24
A4
6
23
A3
A2
7
22
A9
A11
#OE
8
9
21
20
A10
#CE
10
11
19
18
O7
O6
O1
O2
12
17
13
16
GND
14
15
O5
O4
O3
VPP
A12
A1
A0
O0
VCC
A14
A13
A8
Figura. Pinout de la EPROM Intel 27256
Memorias Flash.
La memoria Flash es un tipo nuevo de memoria no volátil, de sólo lectura,
borrable electrónicamente, y programable electrónicamente, por tanto muy
parecida a la memoria EEPROM –a primera vista. Veremos, sin embargo, que
las memorias Flash poseen unas propiedades interesantísimas para el diseño de
sistemas empotrados.
Las memorias Flash han revolucionado, literalmente revolucionado, el campo
de las memorias de sólo lectura porque permiten la regrabación eléctrica del
dispositivo y, al estar la memoria estructurada en bloques de diferentes
tamaños, resulta fácil estructurar datos y programas para que su actualización
dentro del propio sistema sea rápida y segura. Además de esto, el precio de este
tipo de memorias ha sufrido unas notables reducciones desde su introducción
en 1988: la tendencia ha sido claramente facilitar su integración mejorando sus
formas de uso al mismo tiempo que el precio se ha ido equiparando y
reduciendo en comparación con otras tecnologías. El gráfico siguiente ilustra la
tendencia de las diferentes tecnologías de memorias en cuanto a su precio por
Megabyte.
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 6
Usad esta numeración:
166 de 357
120
Precio en $/Mbyte
100
80
Flash
SRAM
DRAM
EPROM
60
40
20
0
1991 1992 1993 1994 1995 1996 1997 1998
Año
Figura. Comparación de costes de las diferentes tecnologías de memorias
(Fuente Dataquest 30/5/94).
v LA TECNOLOGÍA SUBYACENTE.
La estructura de la celda básica de memoria Flash es prácticamente la misma
que se usa en las EEPROM. Estas memoria usan un túnel de óxido más estrecho
que el de las EEPROM, por tanto, es posible utilizar tensiones de programación
de alrededor de 12v lo cual, a su vez, significa que la memoria aceptará unos
10000 ciclos de borrado y reprogramación. El array de celdas de memoria tiene
una estructura de filas/columnas igual a las que hemos estudiado en los
párrafos anteriores. Una diferencia notable es que estas memorias incluyen un
circuito de control muy inteligente que posibilita que las operaciones de
programación y borrado sean fáciles y directas, tan solo se requiere que el
procesador envíe el comando adecuado y, el circuito de control decodificará
este comando y procederá a ejecutar la operación solicitada inmediatamente.
Estas memorias pueden programarse con la misma facilidad que un SRAM,
pudiendo además conservar el contenido indefinidamente.
En la figura siguiente aparece un diagrama estructural de una memoria Flash.
El array de memoria está formado por células FAMOST que son direccionadas
por dos decodificadores de fila y columna. Estos decodificadores aceptan la
dirección procedente del buffer de direcciones de entrada: la parte baja del bus
de direcciones del procesador completa porque, estas memorias, al igual que las
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 7
Usad esta numeración:
167 de 357
SRAM no necesitan ser provistas de direcciones multiplexadas de fila y
columna.
V PP
Control de la Flash
Temporizador
Direcciones
Conmutador de
tensión de
Programación
Decodificador de filas
#oe
Buffer de direcciones
#ce
Registro de
Comandos
Conmutador de
tensión de borrado
Array de células de Memoria
Decodificador de
columnas
#we
Puerta de i/o
Entrada de Datos
Buffer de entrada/salida de Datos
Salida de
Datos
Figura. Diagrama estructural de un chip genérico de memoria flash.
Figura. La cuarta generación del proceso ETOX TM de Intel, el de 0.6
micras.(Cortesía de Intel Corp.)
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 8
Usad esta numeración:
168 de 357
v EL PROCESO DE PROGRAMACIÓN.
En funcionamiento normal, la tensión de alimentación del chip de memoria
Flash, exceptuando casos especiales, es de 5v, sin embargo, para programar la
memoria es necesario una tensión Vpp de 12v. El circuito de control de la Flash,
genera un pulso de 12v durante 10 us, esto produce la carga de la puerta
flotante. Una memoria Flash de 1Mbit puede ser programada en unos 2
segundos. En estas memorias el proceso de borrado en vez de ir de celda en
celda como en el caso de las EPROM, se aplica a bloques completos del array de
memoria. La gama de productos disponibles es muy amplia así que podemos
encontrarnos con memorias Flash que no necesitan +12v y, por tanto su proceso
de borrado será más lento; otras aceptarán el borrado de ciertas páginas
individuales, etc.
v EL CONTROL DE LAS MEMORIAS FLASH ILUSTRADO CON EL CHIP INTEL 28F400B.
El propósito de esta sección consiste en presentar de forma ilustrada los
conceptos, ideas y estructuras fundamentales acerca de las memorias Flash, por
tanto, no debe utilizarse como referencia de diseño ni de programación. El
manual original de Intel es la fuente de información exhaustiva que hay que
emplear en los diseños.
El microprocesador lee, programa y borra la memoria Flash enviando
comandos al controlador de la memoria. El controlador, una máquina de
estados, es altamente inteligente. En esta sección vamos a estudiar un caso
particular de memoria Flash de la compañía Intel, el chip 28F400.
Figura. Chip de memoria Flash Intel 28F400BV (Cortesía de Intel Corp.)
El chip de memoria Flash 28F400BX es una memoria flash de alto rendimiento
de 4 Mbits de capacidad organizada como 256K words (16 bits) o 512 K bytes (8
bits). A su vez, esta memoria está organizada en bloques borrables, uno de ellos
llamado bloque de arranque (boot block) es protegible contra escritura por
hardware, el tamaño de este bloque es de 16 K bytes, además la memoria
contiene 2 bloques de parámetros de 8 K bytes cada uno de ellos. Los nombres
asignados a estos bloques de la memoria nos dan una pista del posible uso para
el que podrían emplearse en proyectos reales, no significa pues que
obligatoriamente deba hacerse el uso especificado. Los bloques principales de la
memoria ocupan 96 Kbytes y tres bloques de 128 Kbytes. En la figura que sigue
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 9
Usad esta numeración:
169 de 357
aparece el mapa de memoria de esta memoria. Cada bloque de esta memoria
puede ser borrado y reprogramado unas 100000 veces si el rango de
temperatura de funcionamiento es el correspondiente a aplicaciones
comerciales, normales.
7FFFFh
60000h
Bloque principal de 128 Kbytes
5FFFFh
40000h
Bloque principal de 128 Kbytes
3FFFFh
20000h
Bloque principal de 128 Kbytes
1FFFFh
08000h
Bloque principal de 96 Kb
07FFFh
06000h
Bloque de parámetros de 8 Kb
05FFFh
04000h
Bloque de parámetros de 8 Kb
03FFFh
00000h Bloque de arranque (boot block) de 16 Kb
Tabla. El mapa de bloques de una memoria 28F404-B.
En la versión B de esta memoria –la que estamos estudiando- el bloque de
arranque (boot) se encuentra en la parte baja del mapa de memoria, esto es, en
las direcciones 0 y siguientes del dispositivo. Este bloque suele usarse para
albergar el núcleo de código de arranque esencial para la inicialización del
sistema que, por tener un carácter tan fundamental, puede ser protegido por
hardware. Las operaciones de bloqueo y desbloqueo se llevan a cabo mediante
combinaciones de estado de las patillas #wp y #rp.
Recordemos que estas memorias contienen un controlador interno que, en
forma de máquina de estados, representa el estado del dispositivo y contiene la
inteligencia necesaria para interpretar los comandos enviados por el
microprocesador y ejercer las acciones que representan a esos comandos dentro
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 10
Usad esta numeración:
170 de 357
de los bloques de memoria incluidos en el array. La interfaz de comandos entre
el controlador interno del chip de memoria Flash y el microprocesador (el host)
recibe el nombre de Command User Interface (Interfaz de Comandos de Usuario).
La máquina de estados interna (WSM, Write State Machine) ejecuta de forma
automática los algoritmos (secuencias de acciones adecuadamente
temporizadas) los cuales incluyen las verificaciones que correspondan, de este
modo, el microprocesador se ve descargado de estas tareas y los algoritmos de
programación pueden ejecutarse, por tanto, en circuito sin necesidad de separar
el componente Flash del circuito para su programación mediante un
programador universal. La máquina de estados de control, WSM contiene un
registro de status llamado SR (status register) el cual puede ser explorado por el
microprocesador e informar del éxito de las operaciones llevadas a cabo.
Las operaciones de programación y de borrado se llevan a cabo realizando dos
ciclos de escritura seguidos sobre la memoria en un formato que está
normalizado y por tanto sirve para cualquier memoria Flash de cualquier
fabricante. Las operaciones de escritura se pueden llevar a cabo byte a byte o
word a word. La programación de cada byte o word de este memoria puede ser
programada independientemente del resto, a diferencia de las operaciones de
borrado las cuales sólo pueden llevarse a cabo en bloque y borran todas las
posiciones de memoria pertenecientes a un bloque simultáneamente.
La patilla #byte sirve para especificar si un determinado acceso a la memoria va
a emplear el bus completo de 16 bits (valor 1 en #byte) o, por el contrario, va a
emplear el bus de byte (valor 0 en #byte). La interfaz de bus de esta memoria
en sus aspectos más básicos recuerda a la interfaz presentada por una EPROM.
En la tabla y figura siguientes aparecen el patillaje lógico y físico de la memoria
28F400BXT.
Patilla
Tipo
Nombre y Función
A 0-A 14
Entrada
Entrada de direcciones para Direcciones de Memoria. Las direcciones son
internamente registradas en un latch durante un ciclo de escritura.
A9
Entrada
Entrada de direcciones: Cuando A 9 está a VHH se accede al modo firma.
DQ0 -DQ 7
Entrada/Salida
Entradas y Salidas de Datos: Recoge el Array de datos de entrada en el
segundo ciclo de #ce y #we durante un comando de programa. Recibe
comandos del Interfaz de comandos de usuario cuando están activas #ce
y #we. Los datos se registran en un latch durante el ciclo de escritura.
DQ8 -DQ 15
Entrada/Salida
Entradas y Salidas de Datos: Recoge el Array de datos de entrada en el
segundo ciclo de #ce y #we durante un comando de programa. Los datos
se registran en un latch durante el ciclo de escritura. Da salida al Array de
datos de salida.
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 11
Usad esta numeración:
171 de 357
#ce
Entrada
Señal de habilitación de Chip: Activa la lógica de control de
dispositivos, los buffers de entrada, los decodificadores y los
amplificadores sensibles. Es activa baja.
#oe
Entrada
Señal de habilitación de Salida: Habilita la salida de dispositivos hacia
buffers de datos en un ciclo de lectura. Es activa baja.
#we
Entrada
Señal de habilitación de escritura: Controla la escritura en el Registro de
Comandos y en los bloques de Arrays. Es activa baja. Las direcciones y
datos son registradas en un latch en el flanco de subida del pulso #we.
#rp
Entrada
Reset/Modo de bajo consumo intenso: Utiliza tres niveles de tensión
(VIL, VIH Y VHH) para controlar dos funciones distintas Reset / Modo de
bajo consumo intenso y desbloqueo del bloque de arranque.
#wp
Entrada
Protección de escritura: Proporciona un método para desbloquear el
bloque de arranque en un sistema no alimentado a 12 V.
Nota: Este método está sobrescrito y el bloque de arranque desbloqueado
cuando #rp está a V HH .
#BYTE
Entrada
Señal de habilitación de BYTE: Controla si un dispositivo opera en modo
ancho de bus un byte o dos bytes. Debe ser controlado por niveles CMOs.
Vcc
Fuente de alimentación de dispositivos: 5.0V+/- 10%, 3.3V +/- 0.3V,
2.7V-3.6V (Sólo BE/CE).
Vpp
Fuente de alimentación de programa/borrado: Aplicando una tensión
de 5V +/-10% o 12V +/-5% en este pin, se pueden borrar bloques de
Arrays de memoria o datos de programa.
GND
Tierra digital para toda la circuitería interna.
NC
Sin conexión: Este pin puede ser alimentado o dejado al aire.
Tabla. Nombres de las patillas de la EPROM Intel Flash 28F400
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 12
Usad esta numeración:
172 de 357
Intel
28F400
VPP
1
44
#rp
#wp
2
43
#we
A 17
3
42
A8
A7
4
41
A9
A6
5
40
A 10
A5
6
39
A 11
A4
7
38
A 12
A3
8
37
A 13
A2
9
A 14
A1
10
36
35
A0
11
34
A 16
#ce
12
33
#byte
GND
13
32
GND
#oe
14
31
DQ 15 /A-1
DQ0
15
30
DQ7
DQ8
16
29
DQ 14
DQ1
17
28
DQ6
DQ9
18
27
DQ13
DQ2
19
26
DQ 5
DQ10
20
DQ3
21
24
DQ11
22
23
25
A 15
DQ 12
DQ 4
Vcc
Figura. Patillaje de la memoria Flash 28F400BXT de Intel.
v APLICACIONES.
Estas memorias son ideales para sistemas donde es previsible la actualización
relativamente frecuente de partes importantes del sistema de arranque o del
propio sistema operativo. El flujo de actividades para actualizar uno de los
bloques de parámetros, por ejemplo, incluiría las siguientes actividades:
1. Obtener el fichero con el nuevo bloque de parámetros.
2. Arrancar el sistema empotrado en el modo de actualización.
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 13
Usad esta numeración:
173 de 357
3. Conectar el sistema a un modem o a un PC portátil.
4. Indicar al sistema que baje (download) el conjunto de parámetros nuevo a
su memoria RAM.
5. Efectuar un chequeo de la integridad del conjunto de parámetros.
6. Indicar al sistema que borre el bloque de 8 Kb adecuado, esta operación
durará menos de 1 segundo de tiempo.
7. Con el nuevo bloque de parámetros en RAM, garantizando que no pueda
perderse la potencia eléctrica, instruimos al software del sistema que
programe cada una de las posiciones del bloque con los contenidos
albergados en RAM
Este tipo de memorias se ha utilizado con éxito en sistemas empotrados
basados en PC donde el BIOS del sistema puede ser albergado por entero en
una memoria Flash y, por tanto fácilmente actualizado cuando sea necesario.
Otras aplicaciones incluyen teléfonos móviles, discos electrónicos, firmware
para equipos de red y de telecomunicaciones, firmware para impresoras y
plotters.
v LAS OPERACIONES BÁSICAS DE LA MEMORIA FLASH: MÁS DETALLES.
Para que la memoria Flash se comporte como si fuese una EPROM en la que
sólo podemos efectuar ciclos de lectura, es necesario poner al controlador de la
memoria en el modo llamada Lectura de Matriz (Read Array). El comando del
CUI que hay que enviar al WSM (Write State Machine) es el comando Read
Mode, representado por FFh, es decir, antes de poder efectuar lecturas de la
memoria Flash es necesario efectuar un ciclo de escritura en el que le enviemos
el byte FFh. El ciclo de escritura es un ciclo de escritura normal del
microprocesador en uso, en nuestro caso el i386EX. A partir del instante en el
que el WSM haya llegado al estado Read Array, el microprocesador puede
efectuar ciclos de lectura sobre cualquier dirección de la memoria Flash del
mismo modo que con un EPROM. La diferencia con una EPROM en relación
con los ciclos de lectura incluyen el hecho de que la memoria flash puede
presentar un bus de 8 o de 16 bits (#byte), por lo demás, si el dispositivo está en
el modo Read Array, los ciclos de bus de lectura son iguales a los del EX sobre
una EPROM.
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 14
Usad esta numeración:
174 de 357
Las dos tablas siguientes resumen los comandos del CUI que pueden ser
enviados por el microprocesador a la WSM junto con sus códigos y los estados
del bus del microprocesador y la memoria que representan a esos comandos.
Código
del
comando
Modo de la memoria Flash
Descripción
00
Inválido/reservado
No deben usarse comandos
inválidos
FF
Read Array
La memoria se pone en el
modo Read Array, en este
modo los ciclos de bus de
lectura serán atendidos por la
memoria poniendo los datos
en el bus de datos de salida.
Este comando también puede
emplearse para abortar un
proceso de programación o de
borrado (Consultar el manual
para obtener todos los detalles
necesarios).
Lectura o abortar borrado o
programación.
40
Puesta en marcha del
proceso de programación.
Pone al CUI en un modo en el
que el siguiente ciclo de
escritura cargará los registros
de Direcciones y de Datos de
la memoria. El segundo ciclo
de lectura después del
comando 40 comenzará de
hecho a programar posiciones
de memoria.
10
Puesta en marcha del
proceso de
programación(Alternativo)
20
Puesta en marcha del
proceso de borrado
Prepara el CUI para la
recepción del comando de
confirmación de borrado.
B0
Suspensión de escritura
Sólo es válido si una operación
de borrado está en curso.
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 15
Usad esta numeración:
175 de 357
70
Lectura del registro de status Un ciclo de lectura sobre la
Flash resultará en la
transferencia de su registro de
status a través de las líneas del
bus de datos.
50
Borrado del registro de
status
90
Identificador inteligente
Un ciclo de lectura posterior a
este modo resultará en la
transferencia del código del
fabricante a través del bus de
datos.
Primer ciclo de bus
Segundo ciclo de bus
Comando
Operación Dirección Datos Operación Dirección Datos
Read Array
Escritura
(W)
X
FFh
-
-
-
Identificador
Inteligente
W
X
90h
Lectura ®
IA
IID
Lectura del registro
de status
W
X
70h
R
X
SRD
Borrado del registro
de status
W
X
50h
-
-
-
Escritura de un
byte/word
W
WA
40h
W
WA
WD
Escritura de un
byte/word (alterno)
W
WA
10h
W
WA
WD
Confirmación de
borrado en bloque
W
BA
20h
W
BA
D0h
Suspensión/reinicio
de escritura
W
X
B0h
W
X
D0h
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 16
Usad esta numeración:
176 de 357
v EL CICLO DE BUS DE LECTURA DE UNA MEMORIA FLASH.
El ciclo de bus de lectura de una memoria Flash es muy similar al de una
EPROM. El dispositivo debe hallarse en el modo Read Array. Para indicar la
transacción de lectura sobre el chip de memoria Flash, el microprocesador debe
llevar a cabo las siguientes operaciones en orden:
1. Definir un ciclo de bus que incluya la dirección de memoria Flash que
se desea leer y, por supuesto forzar su línea w/#r a valor 0. Esto hará
que se fijen las líneas #we y #rp de la flash a nivel 1. El
microprocesador en este instante tiene preparadas las líneas de salida
que representan el ancho del ciclo de bus actual, 8 o 16 bits. En el caso
del i386EX estas líneas se llaman #ble y #bhe que, estando las dos
asertadas representan un ciclo de 16 bits y que estando sólo una
asertada, significa que el ciclo de bus es de 8 bits. Un decodificador
externo fuerza la entrada #byte de la memoria Flash a 0 cuando sólo
una línea byte enable del i386EX contiene un 0 y, fuerza un 1, cuando
son ambos byte enables los que presentan un 0.
2. El decodificador de direcciones o, en el caso del i386EX el propio
microprocesador a través de sus líneas de selección de chip, debe
activar la línea #cs del chip de memoria flash. En ese momento, la
memoria inicia el ciclo de bus de lectura. Manteniendo la memoria
alimentada a 5v en unos 60ns aproximadamente, aparecerá en el bus
de datos el dato solicitado.
La figura siguiente incluye el cronograma de operaciones de lectura sobre la
memoria Flash 28F400.
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 17
Usad esta numeración:
177 de 357
Dirección
de
memoria
dirección
estable
#ce
#oe
#we
data bus
datos
válidos
alta Z
#rp
telqv
Figura. Cronograma del ciclo de lectura de una memoria flash Intel 28F400.
El resto de ciclos de bus que permiten que la memoria flash pueda ser borrada,
programada, leído su status, etc. Pueden ser consultados en el manual de Intel
“SmartVoltage Boot Block Family”.
v UN EJEMPLO MÁS PRÁCTICO.
Usando los comandos y ciclos de bus correspondientes a memorias Flash
revisados en la sección anterior, vamos a realizar un programa en lenguaje C
que nos permita programar un bloque de una memoria Flash. El bloque que va
a ser programado se encuentra en la dirección de memoria destino +
TAM_FLASH. El algoritmo supone que el dispositivo 28F400BXT se halla en el
modo de 16 bits (word).
//Comandos de la memoria Flash
#define BORRARSTATUS 0x50
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 18
Usad esta numeración:
178 de 357
#define ESCRIBIRDATOS 0x40
int programar_Flash(volatile short *dir, short *buf, int longit)
{
int val = SIN_ERRORES;
// Escribir datos, word a word
while( longit ){
if(flash_no_borrada( dir )) borrar_flash( dir );
//Borrar el status register
*(dir + TAM_FLASH) = BORRARSTATUS;
//Enviar los datos a escribir
*(dir + TAM_FLASH) = ESCRIBIRDATOS;
*(dir + TAM_FLASH) = *buf;
//¿La WSM está lista?
while( !( *(addr + TAM_FLASH) & WSMLISTA ) )
quieto();
long --;
dir++;
buf++;
}
}
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 19
Usad esta numeración:
179 de 357
El diseño de un sistema basado en el EX con un sistema de firmware residente
en una memoria flash 28F400 responde a un diagrama de bloques-circuito tan
sencillo como el siguiente. La lógica de adaptación es mínima y responde a los
dispares requerimientos de temporización de ambos chips. En concreto la PLD
incluida (Programmable Logic Device) adapta los tiempos de liberación del bus
de la memoria flash y el EX. En el capítulo de diseño de hardware
estudiaremos con más detalle estos y otros aspectos relativos a la integración de
los diferentes tipos de memoria en sistemas basados en el microprocesador
i386EX.
GPIO
#RESET
5V
5V
Vpp
A (0-7)
A (1-18)
#ce
#cs
Intel386TM EX #rd
Microprocesador
PLD
GPIO
#oe
#we
#wr
D (0 -15)
#BYTE #wp
Transceptor
GPIO
#RESET
PWRGOOD
Intel
28F400-T
DQ (0-15)
#rp
Figura. Un sistema basado en el microprocesador i386EX cuyo firmware se
halla incluido en una memoria flash Intel 28F400-T.
Por último, vamos a detallar el procedimiento para de borrado instantáneo de
un bloque completo usando un sencillo flujograma. El programador
implementaría estas operaciones en un lenguaje de programación para ser
ejecutadas por el microprocesador.
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 20
Usad esta numeración:
180 de 357
Memorias
Acceso aleatorio
RAM
DRAM
SRAM
Híbridas
NVRAM
FLASH
EEPROM
ROM
EPROM
PROM
ROM de máscara
FIFO
CAM
SERIAL
Figura. Una clasificación de las memorias más importantes.
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 21
Usad esta numeración:
181 de 357
Las memoria no volátiles de mas amplio uso en el diseño de sistemas
empotrados: Eprom y Flash.
Las tecnologías de memorias que hemos revisado hasta este momento, DRAM y
SRAM, son memorias volátiles: al suprimir la potencia eléctrica de los
dispositivos el contenido se pierde. Estos dispositivos al ponerse en
funcionamiento de nuevo aparecen con contenidos aleatorios en cada una de
sus celdas y, por tanto, como veremos en el capítulo siguiente, deben ser
examinados e inicializados en el arranque del sistema computador. En los
párrafos siguientes, en cambio, revisaremos las tecnologías de memorias no
volátiles: memorias cuyo contenido no se pierde al suprimir la potencia
eléctrica.
Resulta esencial la presencia de memorias no volátiles en un sistema
computador. Estas memorias albergan el código y los parámetros de
inicialización más fundamentales del computador por lo debe existir una
garantía de que sus contenidos no se perderán al suprimir la potencia del
sistema.
Al igual que en el caso de las memorias SRAM y DRAM, las memorias volátiles
se presentan en diferentes formatos físicos, operacionales y eléctricos y, de ellos,
se derivan una serie de ventajas de diseño, económicas y de otros tipos que
veremos. Desconsiderando aspectos de interés casi exclusivamente histórico,
podemos decir que la primera memoria no-volátil de amplio uso fue la
memoria ROM inventada por ingenieros de la compañía Intel. Este tipo de
memoria más concretamente conocida como ROM de máscara, en sus diversas
formas, es un dispositivo de uso muy extendido. Se trata de un circuito
integrado en el que las celdas que almacenan los bits están permanentemente
forzadas a valor 0 o 1 y, su fabricación puede llevar un tiempo considerable
además de la exigencia de fabricar partidas de varios miles como mínimo
porque de otro modo el coste por dispositivo llegaría a ser prohibitivo. La
versión más interesante de este disposotivo, la EPROM (Electrically
Programmable Read Only Memory) es un dispositivo que puede programarse
eléctricamente y borrase mediante la exposición a luz ultravioleta.
Curiosamente, un gran invento, puede decirse que la memoria EPROM surgió
de forma casual: Intel consideraba que en ocasiones era más aleccionador saber
porqué fallaba un dispositivo que porqué funcionaba correctamente. Cuando la
compañía Intel decidió apostar por los dispositivos con puerta de silicio, a un
físico llamado Dov Forman se le encomendó la tarea de averiguar las causas de
fallo en el proceso de fabricación. Observando los dispositivos que presentaban
fallos, pensó que quizás éstos se debieran que algunas de las puertas del
dispositivo habrían llegado a desconectarse, a quedarse flotantes. Poco tiempo
después se le ocurrió que podría obtenerse un uso útil de este hecho este
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 1
Usad esta numeración:
182 de 357
fenómeno y de hecho demostró que el dispositivo podía almacenar la carga, un
semiconductor que se comportaba como una memoria ROM y podía ser
programado eléctricamente con una gran facilidad. Poco tiempo más tarde Intel
dedicó una importante masa de recursos humanos y económicos en
investigación y desarrollo de la nueva tecnología.
Otra gran ventaja de la EPROM, además de su facilidad de programación, era
su facilidad de borrado: en unos pocos minutos un ingeniero podría borrar el
dispositivo exponiéndolo a luz ultravioleta, reprogramarlo eléctricamente y
volver a probar el sistema microprocesador con un nuevo programa. El primer
dispositivo EPROM producido por Intel tenía una capacidad de 2 Kbytes cuyas
máscaras eran tan grandes que tuvieron que ser particionadas en cuatro trozos
lo que creó muchas dificultades de alineamiento.
La primera EPROM comercial tenía número de pieza de Intel 1702. Hoy en día
Intel prácticamente ha abandonado el mercado de EPROMs y ha sustituido
estos dispositivos por los más amplios, rápidos y flexibles FLASH, también
inventados por ingenieros de Intel Corp.
Al igual que con el resto de dispositivos, a modo de introducción, vamos a
estudiar la estructura de la celda básica de memoria EPROM.
v LA CELDA BÁSICA DE MEMORIA EPROM: FAMOST( FLOATING GATE AVALANCHE
INJECTION MOS TRANSISTOR).
Las memorias EPROM se pueden programar eléctricamente, esto significa que
mediante un patrón de niveles eléctricos debidamente temporizado,
conseguiremos establecer un enlace fusible entre cada celda de un bit y la línea
por donde llega el bit que deseamos grabar, un 1 o un 0. La energía de la señal
de grabación es muy superior a la energía transportada por las señales digitales
que representan los datos leídos desde esta memoria en su funcionamiento
normal: los pulsos de corriente procedentes de esta señal queman la conexión
entre la celda y la línea de bit de forma que ahora se encuentren desconectados
y, por tanto, el valor almacenado sea un 0. Todos los enlaces fusibles no
quemados representan unos (1).
La generación de los pulsos de corriente en los instantes adecuados se lleva a
cabo mediante el uso de un circuito llamado programador. Hoy en día una gran
variedad de fabricantes producen EPROMS, algunas de ellas con unas
diferencias en los algoritmos de grabación casi anecdóticas, pero, los
programadores llamados universales, contienen un computador que incorpora
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 2
Usad esta numeración:
183 de 357
los algoritmos de programación de una gran variedad de EPROMS. En el
capítulo dedicado a la integración de hardware y software, realizaremos un
pequeño computador y, allí tendremos ocasión de comprender cuales son las
funciones de un programador moderno y otras de sus características.
En la figura que sigue, aparece un transistor de almacenamiento con una puerta
flotante (¡desconectada según el inventor Dov Forman!). En este dispositivo la
puerta está ubicada entre la puerta de control y el substrato y, a diferencia de la
puerta de control del dispositivo anterior, no está conectada a ninguna línea ni
de palabra, ni de bit, sencillamente flota, es decir no tiene un valor de tensión
definido. Esta puerta flotante suele realizarse con silicio policristalino. El estado
de la puerta flotante permite mantener la información permanentemente en el
dispositivo. El transistor funciona como un transistor MOS normal cuando la
puerta flotante no tiene cargas eléctricas.¿Qué ocurre si la puerta es cargada con
electrones? Si el potencial de la puerta llega a ser muy alto, entonces el
transistor de almacenamiento puede activarse.
Para programar un 0 se necesita un pulso de 20v durante 50ms, este tiempo es
muy largo en comparación con los tiempos de escritura de las SRAM y DRAMs
con lo que se hace necesario el llevar a cabo la programación del dispositivo con
tensiones más altas (más energía por electrón) pero durante tiempos limitados
que no dañen la capa de aislamiento: el programador universal.
Para efectuar el borrado de la memoria EPROM debe ser expuesto a radiación
ultravioleta: los electrones recogidos en la puerta flotante absorben energía de
estas longitudes de onda, aumentan su temperatura y pueden entonces
abandonar la puerta flotante, de este modo el dispositivo, después de unos 20
minutos de exposición, todos los portadores de carga son forzados fuera de la
puerta flotante quedando el dispositivo listo para volver a ser programado.
Puerta
Drenador
Surtidor
Puerta de control
Puerta flotante
Oxido
Oxido
-
surtidor-n
-
-
drenador-n
-
sustrato-p
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 3
Usad esta numeración:
184 de 357
Figura. Estructura de una celda de memoria EPROM.
Los empaquetados físicos de memorias EPROM incluyen una pequeña ventana
transparente justo encima del chip. Esta ventana está realizada con cristal de
cuarzo la cual resulta muy costosa a medida que los costes incurridos en el
proceso de fabricación son menores y, la lámpara de UV representa también un
coste para el usuario final por lo que los fabricantes han desarrollado otro
dispositivo llamado EEPROM el cual sí es borrable eléctricamente (Electrically
Eraseable Programmable Read Only Memory). En estos dispositivos las cargas
encerradas en la puerta flotante pueden ser forzados hacia fuera por medio de
un pulso eléctrico. En ciertos sistemas donde se requiere una gran facilidad de
borrado, el uso de estas memorias puede ser obligado, sin embargo, dado su
alto coste, es posible que, como veremos, resulte más beneficioso el uso de
memorias FLASH.
v UN CHIP DE MEMORIA EPROM, 27256 DE INTEL.
Vamos a estudiar a hora un chip de memoria EPROM real, un dispositivo de
32Kb de capacidad. Esta memoria se presenta en dos formatos de interfaz
eléctrica externa, con niveles TTL y con niveles CMOS. El identificador de la
versión CMOS es 27C256.
El ancho de palabra de este dispositivo es de 8 bits, con lo cual, cada dirección
de memoria presente en el bus representa un byte entero dentro del dispositivo.
El bus de direcciones, por tanto, tiene un ancho de log2 32*1024 = 15 bits. Las
líneas de este bus se identifican como A0 -A15 . El bus de datos del dispositivo se
representa por O0 -O8 ya que el dispositivo sólo puede producir “outputs”,
salidas de datos procedentes de las celdas internas del chip. Obviamente, no se
pueden escribir datos en una memoria EPROM, excepto en el proceso de
programación en el cual, puesto que es completamente transparente al usuario,
nos ocuparemos de él en la construcción del proyecto final.
Las patillas Vcc y GND representan la tensión de alimentación y la masa digital,
respectivamente. Nos fijaremos ahora en las patillas #oe y #ce. La patilla #ce es
el chip enable del integrado y #oe (output enable) indica al dispositivo que active
los drivers del bus de datos, esto es, que el contenido de la celda direccionada
aparezca de hecho en la salida del bus de datos. ¿Clara la distinción entre
ambos? La aserción de #ce hace que el dispositivo decodifique la dirección que
aparece en su bus de direcciones y que seleccione el nodo (de 8 celdas de un bit)
elegido; la aserción de #oe significa habilitar la conexión eléctrica entre las
patillas exteriores del bus de datos y las líneas internas de ese mismo bus. En
realidad, la señal #oe se emplea para evitar un problema que se presenta en los
sistemas microprocesadores cuando el microprocesador lleva a cabo un ciclo de
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 4
Usad esta numeración:
185 de 357
escritura inmediatamente después de un ciclo de lectura. En esas circunstancias
si el dispositivo direccionado en el ciclo de lectura no se desconecta a tiempo
del bus de datos y el micro inicia el ciclo de escritura, puede haber una ventana
de tiempo en la que ambos, el dispositivo y el microprocesador estén excitando
eléctricamente el bus de datos. Esta situación es conocida con el nombre de
contención de bus y debe evitarse a toda costa pues puede conducir a daños en
alguno de los transistores involucrados en ambos dispositivos. Otra vez, en el
capítulo dedicado al proyecto real de integración, dedicaremos unas líneas más
a este problema y cómo efectuar diseños sin contención de bus.
Dirección de
memoria
....
VIH
Dirección válida
....
VIL
VIH
CE
....
VIL
t CE
VIH
OE
....
VIL
O 0-O 8
VIH
tACC
t OE
Alta imp
VIL
....
t OF
t OH
Datos validos
....
Alta imp
Figura. Cronograma del ciclo de lectura de una memoria EPROM Intel 27256.
A0 -A14 Bus de direcciones
#ce
Línea de selección del chip
#oe
Habilitación de salida de datos
#we
Habilitación de escritura
(Sólo en el proceso de programación)
Gnd
Nivel de referencia digital
Vcc
Tensión de alimentación
Vpp
Tensión de programación
Tabla. Nombres de las patillas de la EPROM Intel 27256
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 5
Usad esta numeración:
186 de 357
Intel
27256
1
28
2
27
A7
3
26
A6
4
25
A5
5
24
A4
6
23
A3
A2
7
22
A9
A11
#OE
8
9
21
20
A10
#CE
10
11
19
18
O7
O6
O1
O2
12
17
13
16
GND
14
15
O5
O4
O3
VPP
A12
A1
A0
O0
VCC
A14
A13
A8
Figura. Pinout de la EPROM Intel 27256
Memorias Flash.
La memoria Flash es un tipo nuevo de memoria no volátil, de sólo lectura,
borrable electrónicamente, y programable electrónicamente, por tanto muy
parecida a la memoria EEPROM –a primera vista. Veremos, sin embargo, que
las memorias Flash poseen unas propiedades interesantísimas para el diseño de
sistemas empotrados.
Las memorias Flash han revolucionado, literalmente revolucionado, el campo
de las memorias de sólo lectura porque permiten la regrabación eléctrica del
dispositivo y, al estar la memoria estructurada en bloques de diferentes
tamaños, resulta fácil estructurar datos y programas para que su actualización
dentro del propio sistema sea rápida y segura. Además de esto, el precio de este
tipo de memorias ha sufrido unas notables reducciones desde su introducción
en 1988: la tendencia ha sido claramente facilitar su integración mejorando sus
formas de uso al mismo tiempo que el precio se ha ido equiparando y
reduciendo en comparación con otras tecnologías. El gráfico siguiente ilustra la
tendencia de las diferentes tecnologías de memorias en cuanto a su precio por
Megabyte.
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 6
Usad esta numeración:
187 de 357
120
Precio en $/Mbyte
100
80
Flash
SRAM
DRAM
EPROM
60
40
20
0
1991 1992 1993 1994 1995 1996 1997 1998
Año
Figura. Comparación de costes de las diferentes tecnologías de memorias
(Fuente Dataquest 30/5/94).
v LA TECNOLOGÍA SUBYACENTE.
La estructura de la celda básica de memoria Flash es prácticamente la misma
que se usa en las EEPROM. Estas memoria usan un túnel de óxido más estrecho
que el de las EEPROM, por tanto, es posible utilizar tensiones de programación
de alrededor de 12v lo cual, a su vez, significa que la memoria aceptará unos
10000 ciclos de borrado y reprogramación. El array de celdas de memoria tiene
una estructura de filas/columnas igual a las que hemos estudiado en los
párrafos anteriores. Una diferencia notable es que estas memorias incluyen un
circuito de control muy inteligente que posibilita que las operaciones de
programación y borrado sean fáciles y directas, tan solo se requiere que el
procesador envíe el comando adecuado y, el circuito de control decodificará
este comando y procederá a ejecutar la operación solicitada inmediatamente.
Estas memorias pueden programarse con la misma facilidad que un SRAM,
pudiendo además conservar el contenido indefinidamente.
En la figura siguiente aparece un diagrama estructural de una memoria Flash.
El array de memoria está formado por células FAMOST que son direccionadas
por dos decodificadores de fila y columna. Estos decodificadores aceptan la
dirección procedente del buffer de direcciones de entrada: la parte baja del bus
de direcciones del procesador completa porque, estas memorias, al igual que las
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 7
Usad esta numeración:
188 de 357
SRAM no necesitan ser provistas de direcciones multiplexadas de fila y
columna.
V PP
Control de la Flash
Temporizador
Direcciones
Conmutador de
tensión de
Programación
Decodificador de filas
#oe
Buffer de direcciones
#ce
Registro de
Comandos
Conmutador de
tensión de borrado
Array de células de Memoria
Decodificador de
columnas
#we
Puerta de i/o
Entrada de Datos
Buffer de entrada/salida de Datos
Salida de
Datos
Figura. Diagrama estructural de un chip genérico de memoria flash.
Figura. La cuarta generación del proceso ETOX TM de Intel, el de 0.6
micras.(Cortesía de Intel Corp.)
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 8
Usad esta numeración:
189 de 357
v EL PROCESO DE PROGRAMACIÓN.
En funcionamiento normal, la tensión de alimentación del chip de memoria
Flash, exceptuando casos especiales, es de 5v, sin embargo, para programar la
memoria es necesario una tensión Vpp de 12v. El circuito de control de la Flash,
genera un pulso de 12v durante 10 us, esto produce la carga de la puerta
flotante. Una memoria Flash de 1Mbit puede ser programada en unos 2
segundos. En estas memorias el proceso de borrado en vez de ir de celda en
celda como en el caso de las EPROM, se aplica a bloques completos del array de
memoria. La gama de productos disponibles es muy amplia así que podemos
encontrarnos con memorias Flash que no necesitan +12v y, por tanto su proceso
de borrado será más lento; otras aceptarán el borrado de ciertas páginas
individuales, etc.
v EL CONTROL DE LAS MEMORIAS FLASH ILUSTRADO CON EL CHIP INTEL 28F400B.
El propósito de esta sección consiste en presentar de forma ilustrada los
conceptos, ideas y estructuras fundamentales acerca de las memorias Flash, por
tanto, no debe utilizarse como referencia de diseño ni de programación. El
manual original de Intel es la fuente de información exhaustiva que hay que
emplear en los diseños.
El microprocesador lee, programa y borra la memoria Flash enviando
comandos al controlador de la memoria. El controlador, una máquina de
estados, es altamente inteligente. En esta sección vamos a estudiar un caso
particular de memoria Flash de la compañía Intel, el chip 28F400.
Figura. Chip de memoria Flash Intel 28F400BV (Cortesía de Intel Corp.)
El chip de memoria Flash 28F400BX es una memoria flash de alto rendimiento
de 4 Mbits de capacidad organizada como 256K words (16 bits) o 512 K bytes (8
bits). A su vez, esta memoria está organizada en bloques borrables, uno de ellos
llamado bloque de arranque (boot block) es protegible contra escritura por
hardware, el tamaño de este bloque es de 16 K bytes, además la memoria
contiene 2 bloques de parámetros de 8 K bytes cada uno de ellos. Los nombres
asignados a estos bloques de la memoria nos dan una pista del posible uso para
el que podrían emplearse en proyectos reales, no significa pues que
obligatoriamente deba hacerse el uso especificado. Los bloques principales de la
memoria ocupan 96 Kbytes y tres bloques de 128 Kbytes. En la figura que sigue
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 9
Usad esta numeración:
190 de 357
aparece el mapa de memoria de esta memoria. Cada bloque de esta memoria
puede ser borrado y reprogramado unas 100000 veces si el rango de
temperatura de funcionamiento es el correspondiente a aplicaciones
comerciales, normales.
7FFFFh
60000h
Bloque principal de 128 Kbytes
5FFFFh
40000h
Bloque principal de 128 Kbytes
3FFFFh
20000h
Bloque principal de 128 Kbytes
1FFFFh
08000h
Bloque principal de 96 Kb
07FFFh
06000h
Bloque de parámetros de 8 Kb
05FFFh
04000h
Bloque de parámetros de 8 Kb
03FFFh
00000h Bloque de arranque (boot block) de 16 Kb
Tabla. El mapa de bloques de una memoria 28F404-B.
En la versión B de esta memoria –la que estamos estudiando- el bloque de
arranque (boot) se encuentra en la parte baja del mapa de memoria, esto es, en
las direcciones 0 y siguientes del dispositivo. Este bloque suele usarse para
albergar el núcleo de código de arranque esencial para la inicialización del
sistema que, por tener un carácter tan fundamental, puede ser protegido por
hardware. Las operaciones de bloqueo y desbloqueo se llevan a cabo mediante
combinaciones de estado de las patillas #wp y #rp.
Recordemos que estas memorias contienen un controlador interno que, en
forma de máquina de estados, representa el estado del dispositivo y contiene la
inteligencia necesaria para interpretar los comandos enviados por el
microprocesador y ejercer las acciones que representan a esos comandos dentro
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 10
Usad esta numeración:
191 de 357
de los bloques de memoria incluidos en el array. La interfaz de comandos entre
el controlador interno del chip de memoria Flash y el microprocesador (el host)
recibe el nombre de Command User Interface (Interfaz de Comandos de Usuario).
La máquina de estados interna (WSM, Write State Machine) ejecuta de forma
automática los algoritmos (secuencias de acciones adecuadamente
temporizadas) los cuales incluyen las verificaciones que correspondan, de este
modo, el microprocesador se ve descargado de estas tareas y los algoritmos de
programación pueden ejecutarse, por tanto, en circuito sin necesidad de separar
el componente Flash del circuito para su programación mediante un
programador universal. La máquina de estados de control, WSM contiene un
registro de status llamado SR (status register) el cual puede ser explorado por el
microprocesador e informar del éxito de las operaciones llevadas a cabo.
Las operaciones de programación y de borrado se llevan a cabo realizando dos
ciclos de escritura seguidos sobre la memoria en un formato que está
normalizado y por tanto sirve para cualquier memoria Flash de cualquier
fabricante. Las operaciones de escritura se pueden llevar a cabo byte a byte o
word a word. La programación de cada byte o word de este memoria puede ser
programada independientemente del resto, a diferencia de las operaciones de
borrado las cuales sólo pueden llevarse a cabo en bloque y borran todas las
posiciones de memoria pertenecientes a un bloque simultáneamente.
La patilla #byte sirve para especificar si un determinado acceso a la memoria va
a emplear el bus completo de 16 bits (valor 1 en #byte) o, por el contrario, va a
emplear el bus de byte (valor 0 en #byte). La interfaz de bus de esta memoria
en sus aspectos más básicos recuerda a la interfaz presentada por una EPROM.
En la tabla y figura siguientes aparecen el patillaje lógico y físico de la memoria
28F400BXT.
Patilla
Tipo
Nombre y Función
A 0-A 14
Entrada
Entrada de direcciones para Direcciones de Memoria. Las direcciones son
internamente registradas en un latch durante un ciclo de escritura.
A9
Entrada
Entrada de direcciones: Cuando A 9 está a VHH se accede al modo firma.
DQ0 -DQ 7
Entrada/Salida
Entradas y Salidas de Datos: Recoge el Array de datos de entrada en el
segundo ciclo de #ce y #we durante un comando de programa. Recibe
comandos del Interfaz de comandos de usuario cuando están activas #ce
y #we. Los datos se registran en un latch durante el ciclo de escritura.
DQ8 -DQ 15
Entrada/Salida
Entradas y Salidas de Datos: Recoge el Array de datos de entrada en el
segundo ciclo de #ce y #we durante un comando de programa. Los datos
se registran en un latch durante el ciclo de escritura. Da salida al Array de
datos de salida.
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 11
Usad esta numeración:
192 de 357
#ce
Entrada
Señal de habilitación de Chip: Activa la lógica de control de
dispositivos, los buffers de entrada, los decodificadores y los
amplificadores sensibles. Es activa baja.
#oe
Entrada
Señal de habilitación de Salida: Habilita la salida de dispositivos hacia
buffers de datos en un ciclo de lectura. Es activa baja.
#we
Entrada
Señal de habilitación de escritura: Controla la escritura en el Registro de
Comandos y en los bloques de Arrays. Es activa baja. Las direcciones y
datos son registradas en un latch en el flanco de subida del pulso #we.
#rp
Entrada
Reset/Modo de bajo consumo intenso: Utiliza tres niveles de tensión
(VIL, VIH Y VHH) para controlar dos funciones distintas Reset / Modo de
bajo consumo intenso y desbloqueo del bloque de arranque.
#wp
Entrada
Protección de escritura: Proporciona un método para desbloquear el
bloque de arranque en un sistema no alimentado a 12 V.
Nota: Este método está sobrescrito y el bloque de arranque desbloqueado
cuando #rp está a V HH .
#BYTE
Entrada
Señal de habilitación de BYTE: Controla si un dispositivo opera en modo
ancho de bus un byte o dos bytes. Debe ser controlado por niveles CMOs.
Vcc
Fuente de alimentación de dispositivos: 5.0V+/- 10%, 3.3V +/- 0.3V,
2.7V-3.6V (Sólo BE/CE).
Vpp
Fuente de alimentación de programa/borrado: Aplicando una tensión
de 5V +/-10% o 12V +/-5% en este pin, se pueden borrar bloques de
Arrays de memoria o datos de programa.
GND
Tierra digital para toda la circuitería interna.
NC
Sin conexión: Este pin puede ser alimentado o dejado al aire.
Tabla. Nombres de las patillas de la EPROM Intel Flash 28F400
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 12
Usad esta numeración:
193 de 357
Intel
28F400
VPP
1
44
#rp
#wp
2
43
#we
A 17
3
42
A8
A7
4
41
A9
A6
5
40
A 10
A5
6
39
A 11
A4
7
38
A 12
A3
8
37
A 13
A2
9
A 14
A1
10
36
35
A0
11
34
A 16
#ce
12
33
#byte
GND
13
32
GND
#oe
14
31
DQ 15 /A-1
DQ0
15
30
DQ7
DQ8
16
29
DQ 14
DQ1
17
28
DQ6
DQ9
18
27
DQ13
DQ2
19
26
DQ 5
DQ10
20
DQ3
21
24
DQ11
22
23
25
A 15
DQ 12
DQ 4
Vcc
Figura. Patillaje de la memoria Flash 28F400BXT de Intel.
v APLICACIONES.
Estas memorias son ideales para sistemas donde es previsible la actualización
relativamente frecuente de partes importantes del sistema de arranque o del
propio sistema operativo. El flujo de actividades para actualizar uno de los
bloques de parámetros, por ejemplo, incluiría las siguientes actividades:
1. Obtener el fichero con el nuevo bloque de parámetros.
2. Arrancar el sistema empotrado en el modo de actualización.
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 13
Usad esta numeración:
194 de 357
3. Conectar el sistema a un modem o a un PC portátil.
4. Indicar al sistema que baje (download) el conjunto de parámetros nuevo a
su memoria RAM.
5. Efectuar un chequeo de la integridad del conjunto de parámetros.
6. Indicar al sistema que borre el bloque de 8 Kb adecuado, esta operación
durará menos de 1 segundo de tiempo.
7. Con el nuevo bloque de parámetros en RAM, garantizando que no pueda
perderse la potencia eléctrica, instruimos al software del sistema que
programe cada una de las posiciones del bloque con los contenidos
albergados en RAM
Este tipo de memorias se ha utilizado con éxito en sistemas empotrados
basados en PC donde el BIOS del sistema puede ser albergado por entero en
una memoria Flash y, por tanto fácilmente actualizado cuando sea necesario.
Otras aplicaciones incluyen teléfonos móviles, discos electrónicos, firmware
para equipos de red y de telecomunicaciones, firmware para impresoras y
plotters.
v LAS OPERACIONES BÁSICAS DE LA MEMORIA FLASH: MÁS DETALLES.
Para que la memoria Flash se comporte como si fuese una EPROM en la que
sólo podemos efectuar ciclos de lectura, es necesario poner al controlador de la
memoria en el modo llamada Lectura de Matriz (Read Array). El comando del
CUI que hay que enviar al WSM (Write State Machine) es el comando Read
Mode, representado por FFh, es decir, antes de poder efectuar lecturas de la
memoria Flash es necesario efectuar un ciclo de escritura en el que le enviemos
el byte FFh. El ciclo de escritura es un ciclo de escritura normal del
microprocesador en uso, en nuestro caso el i386EX. A partir del instante en el
que el WSM haya llegado al estado Read Array, el microprocesador puede
efectuar ciclos de lectura sobre cualquier dirección de la memoria Flash del
mismo modo que con un EPROM. La diferencia con una EPROM en relación
con los ciclos de lectura incluyen el hecho de que la memoria flash puede
presentar un bus de 8 o de 16 bits (#byte), por lo demás, si el dispositivo está en
el modo Read Array, los ciclos de bus de lectura son iguales a los del EX sobre
una EPROM.
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 14
Usad esta numeración:
195 de 357
Las dos tablas siguientes resumen los comandos del CUI que pueden ser
enviados por el microprocesador a la WSM junto con sus códigos y los estados
del bus del microprocesador y la memoria que representan a esos comandos.
Código
del
comando
Modo de la memoria Flash
Descripción
00
Inválido/reservado
No deben usarse comandos
inválidos
FF
Read Array
La memoria se pone en el
modo Read Array, en este
modo los ciclos de bus de
lectura serán atendidos por la
memoria poniendo los datos
en el bus de datos de salida.
Este comando también puede
emplearse para abortar un
proceso de programación o de
borrado (Consultar el manual
para obtener todos los detalles
necesarios).
Lectura o abortar borrado o
programación.
40
Puesta en marcha del
proceso de programación.
Pone al CUI en un modo en el
que el siguiente ciclo de
escritura cargará los registros
de Direcciones y de Datos de
la memoria. El segundo ciclo
de lectura después del
comando 40 comenzará de
hecho a programar posiciones
de memoria.
10
Puesta en marcha del
proceso de
programación(Alternativo)
20
Puesta en marcha del
proceso de borrado
Prepara el CUI para la
recepción del comando de
confirmación de borrado.
B0
Suspensión de escritura
Sólo es válido si una operación
de borrado está en curso.
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 15
Usad esta numeración:
196 de 357
70
Lectura del registro de status Un ciclo de lectura sobre la
Flash resultará en la
transferencia de su registro de
status a través de las líneas del
bus de datos.
50
Borrado del registro de
status
90
Identificador inteligente
Un ciclo de lectura posterior a
este modo resultará en la
transferencia del código del
fabricante a través del bus de
datos.
Primer ciclo de bus
Segundo ciclo de bus
Comando
Operación Dirección Datos Operación Dirección Datos
Read Array
Escritura
(W)
X
FFh
-
-
-
Identificador
Inteligente
W
X
90h
Lectura ®
IA
IID
Lectura del registro
de status
W
X
70h
R
X
SRD
Borrado del registro
de status
W
X
50h
-
-
-
Escritura de un
byte/word
W
WA
40h
W
WA
WD
Escritura de un
byte/word (alterno)
W
WA
10h
W
WA
WD
Confirmación de
borrado en bloque
W
BA
20h
W
BA
D0h
Suspensión/reinicio
de escritura
W
X
B0h
W
X
D0h
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 16
Usad esta numeración:
197 de 357
v EL CICLO DE BUS DE LECTURA DE UNA MEMORIA FLASH.
El ciclo de bus de lectura de una memoria Flash es muy similar al de una
EPROM. El dispositivo debe hallarse en el modo Read Array. Para indicar la
transacción de lectura sobre el chip de memoria Flash, el microprocesador debe
llevar a cabo las siguientes operaciones en orden:
1. Definir un ciclo de bus que incluya la dirección de memoria Flash que
se desea leer y, por supuesto forzar su línea w/#r a valor 0. Esto hará
que se fijen las líneas #we y #rp de la flash a nivel 1. El
microprocesador en este instante tiene preparadas las líneas de salida
que representan el ancho del ciclo de bus actual, 8 o 16 bits. En el caso
del i386EX estas líneas se llaman #ble y #bhe que, estando las dos
asertadas representan un ciclo de 16 bits y que estando sólo una
asertada, significa que el ciclo de bus es de 8 bits. Un decodificador
externo fuerza la entrada #byte de la memoria Flash a 0 cuando sólo
una línea byte enable del i386EX contiene un 0 y, fuerza un 1, cuando
son ambos byte enables los que presentan un 0.
2. El decodificador de direcciones o, en el caso del i386EX el propio
microprocesador a través de sus líneas de selección de chip, debe
activar la línea #cs del chip de memoria flash. En ese momento, la
memoria inicia el ciclo de bus de lectura. Manteniendo la memoria
alimentada a 5v en unos 60ns aproximadamente, aparecerá en el bus
de datos el dato solicitado.
La figura siguiente incluye el cronograma de operaciones de lectura sobre la
memoria Flash 28F400.
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 17
Usad esta numeración:
198 de 357
Dirección
de
memoria
dirección
estable
#ce
#oe
#we
data bus
datos
válidos
alta Z
#rp
telqv
Figura. Cronograma del ciclo de lectura de una memoria flash Intel 28F400.
El resto de ciclos de bus que permiten que la memoria flash pueda ser borrada,
programada, leído su status, etc. Pueden ser consultados en el manual de Intel
“SmartVoltage Boot Block Family”.
v UN EJEMPLO MÁS PRÁCTICO.
Usando los comandos y ciclos de bus correspondientes a memorias Flash
revisados en la sección anterior, vamos a realizar un programa en lenguaje C
que nos permita programar un bloque de una memoria Flash. El bloque que va
a ser programado se encuentra en la dirección de memoria destino +
TAM_FLASH. El algoritmo supone que el dispositivo 28F400BXT se halla en el
modo de 16 bits (word).
//Comandos de la memoria Flash
#define BORRARSTATUS 0x50
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 18
Usad esta numeración:
199 de 357
#define ESCRIBIRDATOS 0x40
int programar_Flash(volatile short *dir, short *buf, int longit)
{
int val = SIN_ERRORES;
// Escribir datos, word a word
while( longit ){
if(flash_no_borrada( dir )) borrar_flash( dir );
//Borrar el status register
*(dir + TAM_FLASH) = BORRARSTATUS;
//Enviar los datos a escribir
*(dir + TAM_FLASH) = ESCRIBIRDATOS;
*(dir + TAM_FLASH) = *buf;
//¿La WSM está lista?
while( !( *(addr + TAM_FLASH) & WSMLISTA ) )
quieto();
long --;
dir++;
buf++;
}
}
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 19
Usad esta numeración:
200 de 357
El diseño de un sistema basado en el EX con un sistema de firmware residente
en una memoria flash 28F400 responde a un diagrama de bloques-circuito tan
sencillo como el siguiente. La lógica de adaptación es mínima y responde a los
dispares requerimientos de temporización de ambos chips. En concreto la PLD
incluida (Programmable Logic Device) adapta los tiempos de liberación del bus
de la memoria flash y el EX. En el capítulo de diseño de hardware
estudiaremos con más detalle estos y otros aspectos relativos a la integración de
los diferentes tipos de memoria en sistemas basados en el microprocesador
i386EX.
GPIO
#RESET
5V
5V
Vpp
A (0-7)
A (1-18)
#ce
#cs
Intel386TM EX #rd
Microprocesador
PLD
GPIO
#oe
#we
#wr
D (0 -15)
#BYTE #wp
Transceptor
GPIO
#RESET
PWRGOOD
Intel
28F400-T
DQ (0-15)
#rp
Figura. Un sistema basado en el microprocesador i386EX cuyo firmware se
halla incluido en una memoria flash Intel 28F400-T.
Por último, vamos a detallar el procedimiento para de borrado instantáneo de
un bloque completo usando un sencillo flujograma. El programador
implementaría estas operaciones en un lenguaje de programación para ser
ejecutadas por el microprocesador.
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 20
Usad esta numeración:
201 de 357
Memorias
Acceso aleatorio
RAM
DRAM
SRAM
Híbridas
NVRAM
FLASH
EEPROM
ROM
EPROM
PROM
ROM de máscara
FIFO
CAM
SERIAL
Figura. Una clasificación de las memorias más importantes.
Memorias EPROM y FLASH. ©1999, José M Foces Morán -- 21
Usad esta numeración:
202 de 357
Apuntes de Máquinas
Electrónicas
© 1994, 2004 José María Foces Morán.
Capítulo 7
Organización del
microprocesador i386ex
Usad esta numeración:
203 de 357
Organización del
microprocesador i386EX.
Introducción.
i386EX es el nombre comercial de un microprocesador de la compañía Intel al
que dedicaremos el presente capítulo entero. La letra i significa “intel”, el
número 386 indica que el microprocesador tiene un ancho de palabra de 32 bits
y que su arquitectura es del tipo intel 86, por fin, EX significa “embedded”, es
decir, microprocesador pensado para su uso en proyectos empotrados. Los
sistemas computadores empotrados o, de una forma más corta, sistemas
empotrados, son sistemas basados en microprocesadores que generalmente se
emplean para un propósito concreto, para una aplicación concreta. Un ejemplo
de sistema empotrado puede ser el sistema de control de consumo de los
automóviles modernos: se trata de un computador que ejecuta un sistema
operativo de tiempo real en miniatura sobre el que se ejecutan un cierto número
de programas de aplicación, cada uno de los cuales lleva a cabo unas funciones
concretas. Estos programas, se sincronizan entre sí utilizando primitivas
ofrecidas por el sistema operativo, para construir un verdadero de sistema de
aplicaciones cooperativas que, eventualmente, logran monitorizar, registrar,
procesar y controlar diversas partes del motor del automóvil para que el
consumo y las emisiones resulten óptimas.
A diferencia de los computadores normales, o de propósito general, los
microprocesadores empotrados suelen incluir elementos adicionales además de
su núcleo computacional. El núcleo está constituido por una ruta de datos y una
unidad de control internas con el mismo estilo que el núcleo del
microprocesador MIPS estudiado en el texto de los profesores Patterson y
Hennessy. Los elementos adicionales incluidos sirven para que el diseño del
computador empotrado resulte más fácil y menos costoso. Algunos de los
elementos sólo se usan en sistemas empotrados, por ejemplo, el WDT (Watch
Dog Timer, Temporizador de vigilancia) del i386EX.
Organización General i386EX ©1999, José M Foces Morán -- 1
Usad esta numeración:
204 de 357
El i386EX a vista de pájaro.
El microprocesador empotrado i386EX fue introducido por la compañía Intel a
mediados de los 90. Esta implementación de la arquitectura x86 es una
implementación estática que logra reducir notablemente el consumo de
potencia, este requerimiento que es esencial en ciertas aplicaciones empotradas
de pequeño tamaño.
Una de las características más interesantes de este microprocesador es que
implementa la arquitectura x86 completa y, por tanto, resulta fácil y directa la
ejecución de software compatible con el PC. Además, el i386EX, puede
funcionar con tensiones de 3.3V, incluye una unidad de gestión de consumo
eléctrico y una gran variedad de periféricos, casi todos ellos compatibles con los
del PC.
El microprocesador i386EX posee un bus de datos de 16 bits, aunque su
arquitectura interna es de 32 bits. El bus de direcciones es más amplio que el de
uno de sus predecesores, el i386SX y, llega a los 26 bits, con lo que la capacidad
de direccionamiento en bytes es de unos 64Mb.
La arquitectura IA, como hemos visto en los capítulos dedicados a ella, posee
un modo protegido especialmente pensado para el diseño de sistemas
operativos multitarea, gestión de memoria virtual paginada y segmentada.
Todos estos aspectos están incluidos en el i386EX.
Poder diseñar software para este microprocesador empleando plataformas
basadas en PC es una gran ventaja dado el abanico tan amplio de posibilidades
y precios. No solo puede diseñarse el software, además puede verificarse en
una plataforma PC antes incluso de que el hardware esté listo, lo cual es otra
ventaja muy notable.
Como hemos mencionado anteriormente, la mayoría de los periféricos incluidos
en el microprocesador i386EX, se hallan presentes en el PC, con lo cual se
facilitan los diseños, dada la popularidad de la plataforma PC hoy en día. Los
periféricos incluidos en el chip del i386EX constituyen bloques funcionales
esenciales para el diseño de sistemas empotrados. Así nos encontramos con
decodificadores de direcciones, controladores de interrupciones y de DMA,
temporizadores, registros de control de refresco de memorias DRAM, puertos
de comunicaciones y otros.
El funcionamiento con tensiones tan bajas como los 3.3V hace que sea más fácil
el diseño de sistemas de bajo consumo, basados en batería o pilas y, la ventaja
adicional de un menor ruido electromagnético inducido. El sistema de control
de consumo del chip incluye un modo exclusivo llamado SMM. El modo
System Management Mode sirve para la ejecución controlada de ciertas rutinas
que pueden apagar algunos subsistemas del microprocesador cuando no es
necesario su funcionamiento y, de este modo, reducir el consumo de potencia.
El microprocesador i386EX posee un modo Idle en el que suprime el reloj de la
CPU y mantiene el de los periféricos. De este modo, gracias a su
implementación estática, la CPU consigue mantener su estado hasta que ocurra
un determinado evento externo, momento en el que la unidad de control de
Organización General i386EX ©1999, José M Foces Morán -- 2
Usad esta numeración:
205 de 357
reloj vuelve a proveer el reloj a la CPU y ésta puede retomar el flujo de
ejecución de instrucciones anterior.
En este capítulo, estudiaremos cada uno de los bloques funcionales del
microprocesador i386EX y su bus interfaz: el medio que nos permitirá conectar
el microprocesador con las memorias y quizás con otros elementos de
comunicación externa, como por ejemplo, un controlador Ethernet. Además,
describiremos los diferentes tipos de memorias usadas en el diseño de sistemas
empotrados.
El i386EX no es el único microprocesador empotrado de la compañía Intel, otra
familia llamada i960, está formada por un gran número de procesadores
empotrados basados en tecnología RISC de muy altas prestaciones. Estos
últimos procesadores se emplean en subsistemas de gestión de entradas/salidas
que se emplean en computadores de muy alto rendimiento, como por ejemplo,
en servidores de red. Existen en el mercado otros microprocesadores
empotrados con unas excelentes prestaciones y características como por
ejemplo la familia 68x32 de Motorola. Algunos de los microprocesadores
empotrados de Motorola contienen procesadores de protocolos, ideales para su
uso en sistemas de comunicaciones.
La tabla siguiente contiene una comparación de características entre los
diferentes miembros de la familia 386 de microprocesadores y
microprocesadores empotrados de la compañía Intel:
Frecuencia de reloj EXTB EXTC CXSA CXSB
DX
SX
SX Estático
16 MHz
-
-
-
3.0V 5.0V 5.0V -
20 MHz
3.0V -
-
-
25 MHz
3.3V 5.0V 5.0V 3.3V 5.0V 5.0V 5.0V
33 MHz
-
5.0V 5.0V 3.3V 5.0V 5.0V 5.0V
40 MHz
-
-
5.0V -
5.0V 5.0V -
-
-
5.0V
Tabla. Frecuencias y tensiones de alimentación de la familia 386 (Cortesía de
Intel Corp).
Los bloques funcionales periféricos del i386EX.
El microprocesador empotrado i386EX contiene un gran número de bloques
funcionales interesantes en el diseño de sistemas empotrados y, de la familia
386, es el que más elementos integrados contiene. La tabla siguiente compara
las características de los otros miembros de la familia 386. Esta tabla nos sirve
ahora para presentar la terminología que vamos a emplear en el resto del
Organización General i386EX ©1999, José M Foces Morán -- 3
Usad esta numeración:
206 de 357
capítulo, eso sí, con el aviso de que la información presentada no es
exhaustiva y, por tanto, si esa información fuese necesaria, la única fuente fiable
es el propio manual y hoja técnica de la casa Intel. El sitio web de Intel dedicado
a la provisión de información técnica y de soporte tiene el siguiente URL:
http://developer.intel.com
Versión /
Característica
EXTB
CXSA
CXSB
DX
SX
25, 33
25,
33,
40
16,
25,
33
16,
20,
25,
33
16,
20,
25,
33
25,
33,
40
3.0,
3.3
5V
5.0
3.0,
3.3
5.0
5.0
5.0
Implementación
estática
Sí
Sí
Sí
Sí
-
-
Sí
Modo de gestión
del sistema
Sí
Sí
Sí
Sí
-
-
-
Gestión de
potencia
Sí
Sí
Sí
(1)
Sí
(1)
-
-
Sí
Puerta A20
compatible PC
Sí
Sí
Sí
Sí
-
-
-
64Mb
64Mb
64Mb
64Mb
4Gb
16Mb
16Mb
Ancho de dirección 26
26
26
26
32
24
24
Número de canales 2
de DMA
2
-
-
-
-
-
Temporizadores y
contadores
3
3
-
-
-
-
-
Temporizador de
vigilancia
Sí
Sí
-
-
-
-
-
Señales de
selección de chip
8
8
-
-
-
-
-
Patillas de e/s de
propósito general
24
24
-
-
-
-
-
2
2
-
-
-
-
-
Frecuencia de reloj 25
Vcc
Espacio de
Direccionamiento
Controlador de
EXTC
Organización General i386EX ©1999, José M Foces Morán -- 4
Static
SX
Usad esta numeración:
207 de 357
interrupciones
(8259A) (8259A)
Puertos serie
3
3
-
-
-
-
-
Sí
Sí
-
-
-
-
-
PQFP
132,
PQFP
132,
PQFP
100,
PQFP
100,
PGA
132,
PQFP
100
PQFP
100
TQFP
144
TQFP
144
SQFP
SQFP
100
100
PQFP
132
Unidad de gestión
de refresco de
DRAMs
Empaquetado
físico
Tabla. Características de microprocesadores de la familia 386 de Intel.
En la tabla aparecen dos entradas correspondientes al microprocesador i386EX,
específicamente, EXTB y EXTC. La explicación es que existen diversas
versiones distintas del chip que son funcionalmente equivalentes pero que
presentan entre sí ciertas diferencias a nivel eléctrico, de temporización y
operativo. A estas diferencias la compañía Intel las llama “steppings”. El
microprocesador i386EX ha pasado por tres steppings: A, B y C. Actualmente,
sólo está disponible el stepping C.
En las secciones que siguen, vamos a detallar más las características y usos de
cada uno de los subsistemas anteriores del microprocesador exclusivamente.
Organización General i386EX ©1999, José M Foces Morán -- 5
Usad esta numeración:
208 de 357
i386EX
Address Bus
CSU
JTAG
Reloj y Potencia
Controlador de
DMA, acceso
directo a memoria
WDT/ Monitor
de Bus
Árbitro
del Bus
SIO 1 y 2
SSIO
I/O Ports
ICU
Unidad
de
control
del bus
Núcleo computacional
microprocesador
i386 SX
Cola de
prerrecojida de
instrucciones
Datapath y Unidades de
Ejecución
Data Bus
Figura. Diagrama de bloques del microprocesador i386EX.
Organización General i386EX ©1999, José M Foces Morán -- 6
Usad esta numeración:
209 de 357
v UNIDAD DE GESTIÓN DE POTENCIA ELECTRICA Y DE RELOJ –CLOCK
GENERATION AND POWER MANAGEMENT UNIT.
El circuito de generación de reloj incluye un contador de división por 2, un
divisor programable para la generación de un reloj pre-escalado (PSCLK), un
contador de división por 2 para las entradas de velocidad en baudios y, por
último, la circuitería de reset sincronizado. La señal CLK2, provista
externamente, constituye el elemento fundamental de temporización de todo el
chip. Internamente es dividido por 2 y, de ahí, se derivan dos fases de reloj
llamadas PH1 y PH2. Estas dos fases presentan un ciclo de trabajo del 50%. La
unidad de gestión de potencia se aprovecha de otras dos fases de reloj
específicas para ella, las PH1C y PH2C y los periféricos internos también
reciben señales de reloj específicas, PH1P y PH2P.
En su modo Idle, este microprocesador suprime la señal de reloj del núcleo
computacional. A este núcleo, de ahora en adelante, lo llamaremos CPU
simplemente. Las señales PH1C y PH2C están en bajo y alto respecti vamente,
sin embargo, los relojes de los periféricos sí siguen funcionando.
El modo llamado Powerdown suprime los relojes de la CPU y de sus periféricos
internos con estados de PH1C y PH2C conocidos e iguales que en el caso Idle.
En este caso la unidad de bus interfaz, BIU, no responderá a peticiones de
DMA, ni de refresco de memorias DRAM ni tampoco peticiones de cesión de
bus, HOLD, porque todos los relojes del dispositivo están suprimidos.
v UNIDAD DE GENERACIÓN DE SEÑALES DE SELECCIÓN DE CHIP – CHIP SELECT UNIT,
CSU.
El procesador se comunica con la memoria y con los periféricos externos a
través de un conjunto de líneas de status, control, direcciones y datos a los que
colectivamente nos referimos con el término BUS. Los diferentes tipos de ciclos
de bus permiten al procesador gestionar los procesos de comunicación de
información de forma que ésta llegue a su destino con fiabilidad.
La CSU decodifica todos los ciclos de bus y activa las señales de selección de
chip que correspondan a cada ciclo. Las señales de activación de chip o Chip
Seletcs son el medio a través del cual se le indica a un chip periférico o a
memoria que tiene que responder a la solicitud efectuada en el ciclo de bus
presente, que tiene que ser un determinado grupo de chips de memoria o de
periféricos los que respondan a esta transacción y no otros.
Organización General i386EX ©1999, José M Foces Morán -- 7
Usad esta numeración:
210 de 357
En los sistemas basados en microprocesadores convencionales, esto es, no
empotrados, la generación de chip selects la lleva a cabo un sistema externo
llamado decodificador de direcciones. Este sistema, generalmente se diseña con
lógicas programables, FPGA, PAL, etc. Algunos chip sets modernos incorporan
esta capacidad controlable vía software en el arranque del sistema
microprocesador. A pesar de que conceptualmente la decodificación de
direcciones se reduce a implementar funciones lógicas combinacionales, el
número y la complejidad de éstas hace que sea todo un reto lograr una solución
que sea viable económica y técnicamente. Por tanto, es una gran ventaja que
esta lógica se halle integrada en el i386EX.
La CSU es capaz de generar 8 señales de selección de chip separadas. Cada una
de las patillas de selección de chip puede estar asociada a una región del mapa
de memoria o del mapa de entradas y salidas. El esquema de asociación es muy
flexible en cuanto a las direcciones base y los tamaños de las regiones asociadas
a cada patilla.
v LA UNIDAD DE CONTROL DE INTERRUPCIONES – ICU, INTERRUPT CONTROL UNIT.
La unidad ICU del i386EX procesa interrupciones que proceden del exterior del
microprocesador y de algunos de sus periféricos internos. Una interrupción es
una forma de indicación o aviso de que ha ocurrido un evento, una situación de
algún modo especial. Las interrupciones toman forma de señales eléctricas,
flancos o niveles digitales. Estas señales eléctricas son entregadas a la unidad
ICU donde se les asignará una prioridad y esperarán a ser convenientemente
procesadas.
La unidad ICU se compone de dos controladores de interrupciones
funcionalmente equivalentes a los de un PC. El nombre con el que se conoce a
estos controladores es 8259A. Internamente, al igual también que en el caso de
un PC, los dos 8259A’s están conectados en cascada.
Cada uno de los 8259s puede aceptar un máximo de 8 interrupciones. El
diseñador del sistema puede decidir si las interrupciones correspondientes a
cada una de las 8 líneas de entrada está representada por un flanco positivo o
por un nivel alto. Esta opción se programa usando un registro del 8259.
Internamente al ICU, las interrupciones pendientes, aquellas que aún no han
recibido servicio, son registradas por éste en uno de sus registros internos
llamado Interrupt Request Register (IRR) el cual contiene un bit por cada línea de
interrupción. Cuando llega una de éstas, se activa el bit correspondiente del
registro IRR, entonces, un circuito de resolución de prioridad establece cuál de
los bits de interrupción activos posee la prioridad más alta y, por tanto, debe ser
Organización General i386EX ©1999, José M Foces Morán -- 8
Usad esta numeración:
211 de 357
propagado hasta la línea de interrupción “central” del núcleo
computacional del EX: la CPU. La resolución de prioridades puede ser
programada de diversos modos: circular, fija, etc.
v UNIDAD DE CONTROL DE TEMPORIZADORES – TCU, TIMER CONTROL UNIT.
Al igual que en el caso anterior, también esta unidad presenta una
compatibilidad completa con el elemento correspondiente de un PC. En este
caso el contador/temporizador 82C54. La unidad TCU contiene tres contadores
independientes de 16 bits, cada uno de los cuales es capaz de manejar relojes de
hasta 8 MHz. Los contadores poseen seis modos programables que permiten
que se comporten como: contadores de eventos, indicadores de tiempo
transcurrido, monoestables programables y otros. Todos estos modos son
programables vía software.
v TEMPORIZADOR DE VIGILANCIA – WDT, WATCHDOG TIMER UNIT.
El WDT consiste en un contador de cuenta atrás de 32 bits que se decrementa
con cada ciclo de la señal PH1P, permitiendo así un máximo de 4300 millones
de cuentas. Cuando el contador de cuenta atrás llega a cero, el contador lleva a
estado activo al pin WDTOUT durante 16 ciclos de CLK2. Esta señal puede
usarse para provocar una interrupción, o para indicar al usuario que se ha
producido una situación en la que la señal #ready está “colgada”. El WDT
también puede usarse en un modo monitor de bus o como temporizador de
propósito general.
v UNIDAD ASÍNCRONA DE TRANSMISIÓN/RECEPCIÓN SERIE – SIO, ASYNCHRONOUS
SERIAL I/O.
El i386EX contiene dos puertos serie compatibles con la UART (Universal
Asynchronous Receiver Transceiver, Receptor Transceptor Universal
Asíncrono) de la compañía National, NS16450. Los dos canales son full-duplex.
Un canal serie recibe datos en forma de un flujo de bits en formato serie
asíncrono. Esto significa que cada uno de los bits de un carácter recibido va
acompañado de un marco de bits formado por un bit de comienzo fijo y por
uno o más bits de final, también fijos. En medio de los bits de comienzo (start) y
de final (stop) se “enmarcan” los 8 bits de la palabra. Este número de bits por
palabra puede ser distinto a 8 también.
Organización General i386EX ©1999, José M Foces Morán -- 9
Usad esta numeración:
212 de 357
Cuando el SIO recibe un marco completo de bits, extrae los bits de
enmarcado y almacena en un registro la secuencia de bits de la palabra o dato.
Más tarde, estos 8 bits podrán ser leídos desde la CPU del EX y allí podrán ser
analizados, quizás almacenados en un buffer para un análisis posterior. Todas
estas opciones dependerán, lógicamente, del uso que se le vaya a dar a la
información recibida y, por tanto, estarán enteramente bajo el control del
programador del sistema. Por supuesto, también es posible el proceso inverso
en el que el programador escribe un carácter en un registro de salida del SIO
para que éste lo formatee adecuadamente y lo transmita bit a bit al elemento
conectado al otro lado de la línea serie.
Los SIO ofrecen otras funciones de interés en la construcción de software de
comunicaciones como pueden ser la lectura de status del SIO desde la CPU del
EX: errores en la recepción debidos a desacuerdos en la paridad de los datos,
errores de enmarcado de datos, errores de velocidad, etc.
Cada canal serie del SIO incluye un conjunto completo de señales de modem
que son completamente programables: #cts, #rts, #dsr, #dtr, #ri y #dcd. Otras
opciones programables del SIO permiten al programador especificar la longitud
de cada carácter transmitido/recibido: 5, 6, 7 u 8 bits. También se pueden
programar el número de bits de parada (stop) que pueden ser 1, 1,5 o 2 y, por
último, la paridad empleada que puede ser par, impar, forzada o ninguna. El
generador de velocidad de reloj puede funcionar entre 0 y 512Kbaudios.
v UNIDAD DE SÍNCRONA DE TRANSMISIÓN SERIE - SSIO, SYNCHRONOUS SERIAL I/O
UNIT.
Esta unidad de transmisión serie síncrona, bidireccional y full-duples, puede
funcionar con relojes distintos en transmisión y recepción. Cada una de las dos
unidades puede generar el reloj o usar el del otro extremo. El reloj de entrada es
de un máximo de 12,5 MHz, el SIO puede funcionar a un máximo de 5 Mbits
por segundo. Cada canal posee un buffer doble. Los canales pueden funcionar
con un máximo de 16 bits por palabra transmitida/recibida y comparten tanto
el generador de reloj como el multiplicador por 2. El SSIO puede ser
programado para producir interrupciones al ICU bajo ciertas circunstancias.
v UNIDAD DE TRANSMISIÓN/RECEPCIÓN DE DATOS EN PARALELO- PIO, PARALLEL I/O
UN IT.
El i386EX posee tres puertos de propósito general de 8 bits. Todos las patillas
son bidireccionales con niveles de entrada y de salida CMOS. Todas las patillas
Organización General i386EX ©1999, José M Foces Morán -- 10
Usad esta numeración:
213 de 357
tienen dos modos de funcionamiento: normal y periférico. En modo
normal, cada patilla está conectada al bit del puerto paralelo que corresponda,
en modo periférico, el pin estará asociado con un periférico del i386EX. El
puerto 3 es el que más potencia posee.
v UNIDAD DE DMA Y DE ARBITRAJE DEL BUS- DMA AND BUS ARBITER UNIT.
El controlador de DMA del i386EX es un controlador con dos canales
independientes. Es un controlador muy versátil y, en muchos aspectos, superior
al 8237A presente en el PC. Puede ser configurado para funcionar exactamente
igual que un 8237A, eso sí, con sólo dos canales. Cada uno de los canales puede
ser programado para que el controlador transfiera datos entre el espacio de i/o
y memoria, memoria e i/o en cualquier ancho de 8 o 16 bits. Cada canal posee
un registro temporal que puede ensamblar y desensamblar datos desde o hacia
fuentes o destinos desalineados o alineados, mejorando así notablemente el
ancho de banda del bus.
El árbitro del bus, que es una parte del controlador de DMA, es el elemento que,
dentro del i386EX, arbitra el uso del bus frente a solicitudes de periféricos
externos o internos. El árbitro recibe solicitudes procedentes de los dos canales
de DMA, del bus master del i386EX y del controlador de refresco de memorias
dinámicas (RCU).
Cada uno de los canales de DMA se compone de tres componentes principales:
el peticionador, el destino y la cuenta de bytes. Estos componentes están
identificados por el contenido de ciertos registros programables que definen al
dispositivo de i/o o de memoria que está siendo provisto de servicio por parte
del controlador de DMA. El peticionador es el dispositivo que requiere y
solicita servicio del controlador de DMA. Sólo el peticionador es considerado
capaz de iniciar o terminar un proceso de DMA. El destino es el dispositivo con
el que el peticionador desea establecer la comunicación de datos a través del
bus del i386EX. El DMA considera que el destino es incapaz de controlar el
proceso. La cuenta de bytes establece la cantidad de datos que deben ser
transferidos.
v LA UNIDAD DE CONTROL DE REFRESCO DE MEMORIAS DINÁMICAS- RCU, REFRESH
CONTROL UNIT.
La unidad de control de refresco simplifica el diseño de controladores de
memoria gracias a sus registros internos de dirección y de reloj. Al integrar la
unidad RCU dentro del i386EX permite que el controlador externo de memorias
Organización General i386EX ©1999, José M Foces Morán -- 11
Usad esta numeración:
214 de 357
dinámicas pueda usar los chip-selects, la lógica de generación de estados
de espera (wait states) y las líneas de status.
La unidad RCU del i386EX contiene cuatro funciones básicas.
1) Temporizador de intervalos de tiempo que lleva la cuenta de tiempo
transcurrido.
2) Lógica de solicitud de bus que le permite ganar el bus del i386EX y así
poder refrescar las memorias dinámicas externas. La unidad RCU es la
unidad que posee mayor prioridad a la hora de ganar el bus.
3) Lógica para generar las direcciones de fila de las memorias dinámicas
de forma que puedan refrescarse filas específicas completas.
4) Lógica para indicar el inicio de un ciclo de refresco.
Además, la RCU contiene un contador de direcciones de 13 bits que conforma la
dirección de memoria que se va a refrescar pudiendo así funcionar con DRAMS
de hasta 13 filas de celdas de memoria lo cual permite usar prácticamente todos
los tamaños adecuados al tamaño del espacio de memoria máxima
direccionable por el i386EX, 64Mb.
v UNIDAD DE AUTOTEST JTAG- JTAG TEST-LOGIC UNIT.
La unidad de autotest JTAG provee el acceso a las patillas de los dispositivos y
a otras patillas testeables del i386EX. La funcionalidad cumple la norma IEEE
1149.1 y, de este modo, el i386EX posee cinco patillas llamadas #trst, tck, tms,
tdi y tdo dedicadas a la interfaz JTAG. Además, el i386EX contiene una
máquina de estados que implementa el Puerto de Acceso de Autotest (Test
Access Port, TAP), un registro de instrucciones de 4 bits, un registro de
identificación de 32 bits y un registro de bypass de 4 bits. La unidad JTAG
contiene también la lógica necesaria para generar el reloj y las señales de control
para la cadena de Boundary Scan.
Puesto que la lógica de la unidad JTAG contiene su propio reloj y su señal de
reset, puede funcionar autónomamente. De este modo, mientras el resto del
microprocesador está en modo reset o powerdown, la unidad JTAG puede leer y
escribir varias cadenas de registros.
En la figura de la página 6 se presenta un diagrama de bloques del
microprocesador i386Ex. En la sección que sigue revisaremos los conceptos e
ideas fundamentales a cerca del core del i386EX- el núcleo computacional.
Organización General i386EX ©1999, José M Foces Morán -- 12
Usad esta numeración:
215 de 357
El núcleo computacional del i386EX: conceptos e ideas esenciales.
El microprocesador empotrado i386EX contiene un núcleo computacional
basado en una implementación estática de un microprocesador de la casa Intel
llamado 80386SX. Este microprocesador posee una arquitectura de 386 de Intel
completa y como producto comercial presentaba dos frecuencias de reloj, 16 y
25 MHz. El 386SX posee un bus de datos de 16 bits y un bus de direcciones de
24 bits, por tanto, su potencia de direccionamiento es de 16Mb como máximo.
Este microprocesador fue usado con amplio éxito en las primeras generaciones
de computadores personales PC.
v ¿QUÉ AÑADE EL I386EX CON RESPECTO AL I386SX?
El núcleo computacional del i386EX, además de la funcionalidad del i386SX,
añade un modo de ejecución llamado System Management Mode (SMI), 2 líneas
de direcciones más y el hecho de ser una implementación estática.
El modo SMM permite que el procesador i386EX pueda ejecutar algunos
programas de gestión completa del sistema de forma transparente a los
programas de aplicación y al propio sistema operativo. Podría decirse que este
modo trasciende al propio sistema operativo y también al posible BIOS.
Fundamentalmente este modo sirve para gestionar los cambios de estado del
sistema de gestión de potencia del microprocesador. La arquitectura de este
modo se compone de los siguientes elementos:
1. La línea de interrupción #smi que sirve para invocar el modo
SMM.
2. Una salida nueva, #smiact, que sirve para identificar este modo
de ejecución hacia los periféricos externos.
3. Una instrucción nueva, rsm (resume), que sirve para abandonar
el modo SMM y retornar a la ejecución normal.
4. Debido a que el microcódigo que ejecuta las instrucciones en
modo SMM es diferente del microcódigo “normal”, hay ciertas
instrucciones que ven modificado su número de ciclos de reloj.
La otra mejora mencionada, el número de líneas del bus de direcciones, ha sido
incrementado en este procesador de 24 a 26, por tanto, el espacio de
direccionamiento alcanzable por este procesador llega a los 64Mb. Una mejora
notable teniendo en cuenta el crecimiento de los sistemas operativos que
incorporan API’s complejas e interfaces humanas basadas en GUI’s –ventanas,
ratón etc.
v UN VISTAZO A LA MICROARQUITECTURA DEL CORE I386SX.
Organización General i386EX ©1999, José M Foces Morán -- 13
Usad esta numeración:
216 de 357
Antes de describir las características de la pipeline (cauce) del i386SX vamos
a establecer un contexto sencillo que nos permita comprender mejor esta
excelente y elegante idea.
Alrededor del año 1955 la compañía IBM contrató a Frederick P. Brooks para
que trabajase en el equipo de ingenieros que colaboraron en el diseño del
computador Stretch, el primer supercomputador del mundo estrenado allá por
el año 1961. Se trataba de un computador científico, pionero en un gran número
de conceptos heredados en los modernos computadores con arquitecturas RISC,
entre otros una técnica conocida como pipelining, ejecución encauzada.
Fueron John Cocke y Harwood Kolsky los que inventaron la ejecución encauzada
de instrucciones. La ejecución de una instrucción en un computador suele
conllevar una serie de pasos cuya secuencia y función suelen ser bastante
constantes. Estas fases incluyen la recogida de la instrucción desde la memoria,
su decodificación, la generación y validación de acceso a la posición de
memoria donde se encuentran los operandos de la instrucción, la realización de
la operación correspondiente con los operandos y, por fin, la última fase suele
consistir en salvar el resultado recién producido, dentro de un registro o en una
posición de memoria, esta última posibilidad depende mucho del tipo de
arquitectura del set de instrucciones involucrado. En la arquitectura de Intel sí
es posible salvar el resultado de una operación lógica o aritmética directamente
en memoria.
La ejecución encauzada de instrucciones consiste en asociar una unidad
funcional autónoma que lleve a cabo cada una de las fases anteriores
perteneciente a una secuencia de instrucciones presentes en memoria. Así la
última unidad funcional estaría efectuando la última fase de ejecución de una
determinada instrucción, la fase anterior estaría trabajando en ese mismo
espacio de tiempo en la instrucción anterior la cual se encontraría en su fase de
penúltima fase de ejecución. Procediendo de este modo nos encontraríamos a la
primera fase del cauce de ejecución (pipeline) recogiendo una instrucción
procedente de la memoria, esta instrucción estaría separada por n instrucciones
de la instrucción que está en último lugar en el cauce.
Mediante esta técnica la ejecución de una instrucción lleva exactamente el
mismo tiempo en ejecutarse que en el caso en el que no se emplea la ejecución
encauzada de instrucciones. ¿Cuál es pues la mejora? Ancho de banda de ejecución
de instrucciones: el cauce está trabajando simultáneamente sobre n instrucciones,
en cada ciclo de reloj el trabajo realizado es n fases de una instrucción, antes el
trabajo realizado era 1 fase solamente: hemos multiplicado por n la tasa de
ejecución de instrucciones.
Organización General i386EX ©1999, José M Foces Morán -- 14
Usad esta numeración:
217 de 357
John Cocke de IBM inventó esta técnica y, años más tarde, sentó las bases
de lo que más tarde llegó a ser una tecnología, una arquitectura, una nueva
forma de computador: los computadores RISC utilizando el término acuñado
por el profesor David Patterson de la Universidad de California en Berkeley,
otro pionero de la computación moderna. El profesor Patterson fue el diseñador
del primer microprocesador RISC, el RISC I, el cual fue la base de un excelente
microprocesador RISC, el SPARC de Sun Microsystems. Las ideas de John
Cocke no solo incluían la ejecución encauzada de instrucciones, también
produjo importantes pistas en la traducción eficiente de código de alto nivel
que se ajustase eficazmente a los requerimientos de la arquitectura del set de
instrucciones subyacente. De su mismo grupo en IBM, Joel Birnbaum, llegó a
ser el Director de los Laboratorios de Hewlett-Packard donde se produjo otra
arquitectura RISC de gran éxito, la Arquitectura de Precisión, que últimamente
se la conoce con el nombre comercial PA-RISC.
Resulta muy conveniente para nosotros comprender que el núcleo del i386EX
contiene un cauce de ejecución porque esto afecta de forma muy notable al
diseño de sistemas basados en este microprocesador así como las estrategias de
comprobación de dichos sistemas: ocurre con mucha frecuencia que al observar
el bus de un microprocesador encauzado se producen efectos en el bus que
pueden conducirnos a confusiones. Conociendo estos aspectos de la
microarquitectura sin duda ayudará a ahorrar tiempo y esfuerzo. Podríamos
incluso decir que, de algún modo, es peor conocer otros microprocesadores noencauzados antes de abordar un diseño basado en el EX. Obviamente esto no es
cierto, pero sí que ocurre que los efectos del cauce producen una confusión
instantánea mayor en el experto que en el no experto: la ingenuidad del no
experto le hará tomarse las paradojas del bus como una componente más de su
falta de experiencia. De cualquier forma, esto no es mortalmente importante y,
para eso le estamos dedicando tiempo a este tema, continuemos pues.
¿Cuáles son las características del cauce de ejecución del i386SX (EX)? El núcleo
386Sx contiene ocho sub-bloques funcionales internos principales. El cauce
interno del 386Sx puede estar trabajando en paralelo sobre un máximo de seis
instrucciones en un instante dado. Cuando el procesador recoge instrucciones
procedentes de la memoria, éstas son albergadas primero en una cola de
instrucciones llamada la cola de instrucciones no decodificadas, prefecth buffer.
Posteriormente , la primera instrucción del buffer es sometida a un proceso de
decodificación primario y traducida a una forma binaria más próxima a la que
necesitan las unidades de ejecución para, de hecho, efectuar la ejecución de la
instrucción.
La cola de instrucciones no decodificadas posee una profundidad de 16 bytes.
En esos 16 bytes, normalmente, cabe la forma binaria de varias instrucciones.
Recordemos que este procesador utiliza longitud variable de instrucciones, así,
podemos encontrar instrucciones que tienen un ancho desde un byte, dos, tres
Organización General i386EX ©1999, José M Foces Morán -- 15
Usad esta numeración:
218 de 357
hasta varios otros tamaños. La unidad funcional encargada de acceder a la
memoria en busca de los “paquetes de bytes de instrucciones” es la unidad de
bus del procesador (BIU) anteriormente descrita brevemente. Esta unidad, si no
está siendo usada por el controlador de Refresco de Memorias dinámicas
(RCU), efectúa continuamente ciclos de bus definidos del tipo recogida de
instrucción con un ancho definido de 16 bits, de forma que la cola siempre
tienda a estar llena.
Cuando la unidad de control microprogramada del núcleo computacional
i386SX recoge una instrucción que ha sido ya decodificada lo que ocurre es una
búsqueda dentro de la ROM de microcódigo (CROM): esta CROM contiene el
microcódigo correspondiente a cada macroinstrucción. De este modo el
microcódigo dirige, o controla, el funcionamiento de la ALU, del desplazador
en bloque (barrel shifter) y de la lógica de multiplicación y división.
Si la instrucción requiere algún operando procedente de la memoria, la
dirección de éste es construida utilizando las unidades de segmentación y de
paginación del microprocesador: al final de este proceso relativo a direcciones
de operandos, la unidad de bus (BIU) recibe una dirección física, real de
memoria y la definición de un ciclo de bus que va a ser llevado a cabo sobre esa
dirección de memoria.
Como podemos ver, tenemos varias unidades funcionales trabajando en
paralelo:
1. Una unidad funcional, la BIU recogiendo 2 bytes más de instrucciones
desde la memoria y emplazándolos en el último puesto en la cola de
instrucciones no decodificadas.
2. La cola de instrucciones no decodificadas proveyendo una instrucción
completa al decodificador de instrucciones de primer nivel.
3. El decodificador de instrucciones de primer nivel efectuando una
primera decodificación de la instrucción y del modo de
direccionamiento empleado en la misma.
4. Una unidad de ejecución efectuando una operación aritmética, lógica o
un desplazamiento.
5. Un acceso a la memoria principal en busca de un operando.
6. La retroescritura de un resultado en un registro del fichero de
registros del procesador o quizás en una posición de memoria.
Este grado de paralelismo en el cauce del núcleo i386SX multiplica la
productividad de ejecución de instrucciones (instruction execution bandwidth)
por 6 o ligeramente menos, dependiendo lógicamente de los conflictos que se
produzcan entre las 6 etapas en sus exigencias de uso exclusivo de recursos
arquitecturados del procesador. Este aumento del rendimiento supuso un reto
para los ingenieros de Intel porque está comprobado que resulta notablemente
más difícil encauzar un procesador que usa un formato de instrucción de
longitud variable que un procesador que utiliza un ancho fijo –el caso de los
Organización General i386EX ©1999, José M Foces Morán -- 16
Usad esta numeración:
219 de 357
procesadores RISC, por ejemplo el procesador MIPS. Pero, lo lograron: a
base de una gran complejidad del hardware la arquitectura CISC del i386
contiene un cauce de ejecución de instrucciones eficaz y funcionalmente
correcto. Usando las palabras de uno de los ingenieros del equipo de diseño del
386 en su versión con bus externo completo de 32 bits conocida como 80386DX,
Pat Gelsinger:
“La arquitectura interna y los atributos físicos del 80386 hicieron que su diseño
fuese especialmente difícil: nuestra estrategia consistió en descomponer el problema,
establecer una metodología y por último seleccionar las herramientas de CAD que nos
permitiese implementar los conceptos.
Empleamos tanto el método top-down (de arriba abajo) como el bottom-up (abajoarriba). El flujo top-down consistió en la definición de la arquitectura externa, de la
arquitectura interna, de la lógica de transferencia de registros interna (RTL) y de la
lógica detallada final.
El flujo de abajo arriba consistió en el diseño detallado de los transistores y de las
células y su disposición horizontal, después el diseño de los circuitos de los bloques
(ALU, PLA, etc) y finalmente el diseño global de los circuitos y su disposición física...
Conforme la definición de la arquitectura externa llegó a asentarse, el equipo de
diseño comenzó a trabajar en la arquitectura interna de la máquina de la cual resultó su
microarquitectura. El resultado es el esquema de 8 bloques funcionales principales
siguiente:”. ©IEEE Design & Test 1987, Patrick P. Gelsinger, Intel Corp.
El diseño del microprocesador i386EX con todos sus periféricos integrados, un
verdadero computador dentro de un chip, debe haber sido más complicado aún
que el de la versión DX. A pesar de ello, los procesos de fabricación de circuitos
integrados han mejorado notablemente desde el momento en que la versión DX
del 386 apareció en el mercado y lo mismo ha ocurrido con los entornos
integrados de ingeniería de diseño, test, fabricación gestión de proyectos de
CAD electrónico. Sin duda alguna el i386EX es un microprocesador muy
complejo, pero, el esfuerzo de estudio y comprensión de este microprocesador
redundará en grandes ventajas a la hora de comprender el funcionamiento de
un computador moderno, sofisticado y por supuesto será una gran ventaja a la
hora de diseñar con otros microprocesadores de complejidad parecida.
El siguiente gráfico resume la mejora de rendimiento del i386EX al incorporar
ejecución encauzada con respecto a otros microprocesadores que no la
incorporan.
Organización General i386EX ©1999, José M Foces Morán -- 17
Usad esta numeración:
220 de 357
Procesador no encauzado
Tiempo transcurrido
Recogida
instr 1
Decodificación instr 1
Ejecución
instr 1
Recogida
instr 2
Decodificación instr 2
Ejecución
instr 2
Recogida
instr 2
Recogida
instr 3
Recogida
instr 4
W
resultado 1
Recogida
instr 5
Decod 1
Decod 2
Decod 3
Decod 4
Cauce de ejecución del i386EX
BIU
Recogida
instr 1
Unidad de
decodificación
Una unidad de
ejecución
MMU
Exec 1
Exec 2
Addr &
MMU
Exec 3
Decod 5
Exec 4
Addr &
MMU
Figura. El cauce de ejecución del i386EX.
v EL RESTO DE UNIDADES FUNCIONALES DEL NÚCLEO COMPUTACIONAL 386SX.
El resto de elementos del cauce de ejecución del microprocesador 386SX son los
siguientes:
1. La unidad de bus del núcleo computacional. Este elemento es el medio
por el cual el procesador se comunica con su entorno: el resto de
periféricos del microprocesador i386EX. Esta unidad acepta peticiones
procedentes de las unidades de generación de direcciones de memoria
y de la unidad de recogida anticipada de instrucciones. Al mismo
tiempo, la unidad asigna una prioridad a cada una de las unidades
internas susceptibles de efectuar solicitudes. Esta unidad, por último,
genera las señales que definen el ciclo de bus actual, las señales de
concesión del bus, etc.
2. La unidad de recogida anticipada de instrucciones lleva a cabo la
función de búsqueda anticipada de instrucciones procedentes de la
memoria. Cuando la BIU no está ocupada realizando ciclos de bus a
favor de las unidades de recogida de operandos (para poder ejecutar
una determinada instrucción), esta unidad continuamente está
solicitando ciclos de bus de lectura de instrucciones. Estos ciclos de bus
tienen un ancho de 16 bits en modo real. Estos bytes de instrucciones
son almacenados en la cola de instrucciones no decodificadas. Esta cola
tiene una profundidad de 16 bytes.
Estos ciclos de bus de recogida de instrucciones poseen una prioridad
Organización General i386EX ©1999, José M Foces Morán -- 18
Usad esta numeración:
221 de 357
más baja que los ciclos de bus correspondientes a transferencias
de datos. Usando esta técnica se consigue una utilización mayor del
ancho de banda disponible del bus.
3. Unidad de decodificación de instrucciones. Esta unidad acepta bytes
procedentes de la cola de recogida anticipada y los traduce a
direcciones de memoria dentro de la memoria de microcódigo. Las
instrucciones decodificadas son entonces almacenadas en una cola de
instrucciones de profundidad 3 donde esperan a ser procesadas por
parte de una de las unidades de ejecución. Los datos inmediatos
incluidos como constantes dentro de los bits que representan a la
instrucción y los offsets constantes son también tomados desde esta
cola. La unidad de decodificación lleva a cabo su tarea cuando hay al
menos un espacio en la cola de 3 instrucciones y además hay bytes
suficientes para formar una instrucción completa en la cola de
instrucciones no decodificadas.
4. Unidades de ejecución. La cola de instrucciones provee instrucciones a
las unidades de ejecución para ser ejecutadas.
a. La unidad de control microprogramada del i386SX contiene un
hardware paralelo que es capaz de efectuar multiplicaciones con
mucha rapidez así como divisiones y los cálculos de las
direcciones efectivas.
b. El datapath contiene una ALU, un fichero de 8 registros
arquitecturados, un desplazador rápido de 64 bits capaz de
efectuar varios desplazamientos en un solo ciclo de reloj. La ruta
de datos efectúa las operaciones solicitadas por la unidad de
control.
c. La unidad de chequeos de protección comprueba que las
direcciones de memoria de operandos no violan el esquema de
protección entre espacios virtuales.
5. Unidad de segmentación. La unidad de segmentación traduce
direcciones lógicas en direcciones lineales a petición de la unidad de
ejecución. La caché de descriptores de segmento incorporada dentro
del propio chip almacena el conjunto de descriptores de memoria que
están en uso actualmente de modo que la formación de direcciones sea
lo más rápida posible, es decir, que no se necesite acceder a memoria
principal en busca de un descriptor para formar la dirección de
memoria de un operando. Además esta unidad chequea que no se
producen violaciones de segmentación en los ciclos de bus formados.
Las direcciones físicas formadas son truncadas a direcciones físicas de
26 bits.
6. La unidad de paginación. Esta unidad forma las direcciones de
memoria física, reales. Esta unidad contiene un buffer de
almacenamiento de traducciones recientes, Translation Lookaside
Buffer. Este buffer almacena direcciones físicas correspondientes a
Organización General i386EX ©1999, José M Foces Morán -- 19
Usad esta numeración:
222 de 357
direcciones virtuales de instrucciones y operandos. Cuando una
traducción no se encuentra en el buffer, automáticamente se examina la
tabla de páginas presente en memoria principal, se guarda esa
traducción en el TLB y se efectúa el ciclo de bus solicitado.
Núcleo computacional 386SX
Bus de direcciones efectivas
32
Cache de páginas
PLA de control y de
atriibutos
lineales
Recogida de código y de páginas
32
es
Bus de direccion
Bus de direcciones efectivas
32
Sumador
32
Registro de
descriptores
PLA de límites y
atributos
Datapath
Multiplicador, divisor y
desplazador rápido
Bus de control interno
32
Unidad de control
del bus
Drivers de
direcciones
Control de
tamaño del
bus y
encauzamiento del
bus
#be0,
#be1,
A25:1
m/#io
d/#c
#lock
#ads #na
#ready
Anticipador de recogida
de instrucciones
Cola de instrucciones
de 16 bytes
ALU
Fichero de registros
Control de prioridad de
acceso al bus
Sumador de 3 entradas
32
Unidad de paginación
Bus de direcciones reales
Unidad de segmentación
Árbitro
del Bus
hold
intr
nmi
#error
#busy
reset
#smiact
pereq
Status
flags
Control
de la
ALU
Unidad
de
Control
32
Flujo de
código
Transceptores y
Multiplexores
Decodificador de
instrucciones
Cola de 3
instrucciones
decodificadas
Microprogramada y
ROM de
microcódigo
Bus de la ALU dedicado
32
Figura . Núcleo computacional 386SX.
Organización General i386EX ©1999, José M Foces Morán -- 20
d15:0
Usad esta numeración:
223 de 357
Organización General i386EX ©1999, José M Foces Morán -- 21
Usad esta numeración:
224 de 357
Apuntes de Máquinas
Electrónicas
© 1994, 2004 José María Foces Morán.
Capítulo 8
El bus del microprocesador
i386ex
Usad esta numeración:
225 de 357
El bus del microprocesador i386EX.
La versión del microprocesador i386EX con encapsulado PQFP contiene 132
patillas. De las 132 patillas algunas de ellas están especialmente dedicadas a la
conexión de periféricos internos del microprocesador, otras conectan los tres
puertos de i/o de propósito general y, un último grupo de patillas engloban la
funcionalidad del bus del i386EX. Debemos comprender ahora que esta es una
funcionalidad compleja y extensa que no se reduce a un simple número de
líneas de conexión que transportan señales digitales y que se suelen representar
gráficamente agrupadas. Quizás sea un buen momento para repasar los
conceptos e ideas más importantes acerca de los procesos de i/o en sistemas
computadores y su conexión con periféricos, recuerda consultar el capítulo 8
del texto de los profesores P&H.
El bus del i386EX está formado por una serie de aspectos que vamos a
desarrollar progresivamente en este tema. Todos estos aspectos posibilitan la
comunicación del microprocesador con todos los elementos externos
conectados a éste de una forma que garantice que las comunicaciones se van a
producir de forma fiable y que el esquema permita ampliaciones y
modificaciones. Estos aspectos incluyen los siguientes:
1. Eléctricos. Los dispositivos conectados a cualquiera de las versiones del
i386EX deben cumplir con los niveles máximos y mínimos de tensión, de
carga y de disipación de potencia aceptables por el i386EX.
2. Operativos. Los dispositivos conectados al bus del i386EX deben poder
entender los comandos, órdenes y cualesquiera semánticas derivadas de
las acciones del microprocesador, en otro caso, el diseñador de sistemas
empotrados deberá incorporar la lógica de adaptación que posibilite la
comunicación.
3. De temporización. Los dispositivos conectados al bus del i386EX deben
cumplir las especificaciones de tiempos máximos y mínimos en los que
será necesario presentar datos estables en los buses.
4. Otras especificaciones. De tipo mecánico, de seguridad, etc.
v LAS PATILLAS DEL I386EX QUE FORMAN PARTE DEL BUS BÁSICO.
Para comenzar vamos a explorar los nombres y las funciones de cada una de las
señales que forman parte del bus del i386EX. En la tabla sólo aparecen las
señales incluidas en el bus y que son compatibles con otros microprocesadores
no empotrados de intel basados en la arquitectura IA, es decir, no aparecen
listadas aquellas señales relacionadas con la funcionalidad concreta de algún
periférico interno del microprocesador y algunas señales que la documentación
de original del microprocesador sí incluye como formando parte del bus.
Nosotros estudiaremos esas señales en el capítulo de diseño de hardware para
BUS i386EX I. ©1999, José M Foces Morán -- 1
Usad esta numeración:
226 de 357
sistemas empotrados; entonces tendremos ocasión de estudiar el resto de
componentes internos de este microprocesador.
Patilla Tipo Nombre y función cuando la señal es asertada
a25:1
o
El todo importante Bus de Direcciones
#ads
O
Address Status
El procesador informa de que las líneas de definición del
ciclo de bus y el bus de direcciones están estables. Esto
significa que ha dado comienzo un nuevo ciclo de bus
justo en el flanco activo de reloj que tuvo lugar unos
pocos ns atrás y que el tiempo de éste está
transcurriendo ya.
#bhe
Byte High Enable
O
El procesador informa de que en el ciclo de bus
presente va a ser usada la ruta alta del bus de datos
(d8:15).
#ble
Byte Low Enable
O
El procesador informa de que en el ciclo de bus
presente va a ser usada la ruta baja del bus de datos
(d0:7).
#bs8
Bus Size
I
El ciclo de bus actual corresponde a una zona de
memoria o de i/o capaz de transferencias de datos
limitadas a 8 bits.
#busy
Busy
I
El coprocesador de punto flotante está ocupado.
Si esta señal se encuentra asertada en el flanco de
bajada de #reset, el procesador llevará a cabo un autotest interno.
Clk2
Clock
ST
(Schmit
Trigger).
La señal de reloj que constituye el parámetro
fundamental de temporización de todas las operaciones
del microprocesador.
BUS i386EX I. ©1999, José M Foces Morán -- 2
Usad esta numeración:
227 de 357
D15:0
I/O
Data Bus
Estas líneas transportan los datos procedentes del
microprocesador en los ciclos de escritura y los datos
destinados al microprocesador en los ciclos de lectura.
D/#C
O
Data/control
Si esta línea se encuentra asertada, el ciclo de bus es del
tipo “control”(interrupción, halt o lectura de una
instrucción).
Su estado no-asertado significa que el ciclo de bus
actual es un ciclo de lectura o de escritura de datos.
#error
I
Error.
El coprocesador de punto flotante está en un estado de
error.
#flt
I
Float.
Esta línea fuerza al i386EX a dejar todas las líneas del
bus en estado flotante (excepto la línea TDO).
Hlda
O
Bus Hold Acknowledge
El procesador ha cedido el control del bus a otro bus
master que previamente lo ha solicitado.
Hold
I
Bus Hold Request
Cuando el i386EX explora esta señal asertada procederá
a ceder el bus a otro bus master. La indicación de cesión
del bus se efectúa a través de hlda.
#lock
O
Lock
El procesador indica a los bus masters potenciales que
en el ciclo de bus presente no pueden solicitar el bus.
M/#io
O
Memory i/o
A través de esta línea de definición de ciclo bus, el
i386EX indica si el ciclo de bus actual se aplica al mapa
de memoria o al de entradas y salidas (i/o).
#na
I
Next address (Use address pipelining).
Mediante la aserción de esta línea, el sistema externo
indica que puede aceptar ciclos de bus encauzados, esto
es, en medio del ciclo actual, el procesador fuerza la
BUS i386EX I. ©1999, José M Foces Morán -- 3
Usad esta numeración:
228 de 357
salida del ciclo de bus siguiente.
Nmi
ST
Nonmaskable interrupt request
Entrada de interrupción no enmascarable. Cuando el
i386EX recibe este tipo de interrupción detiene el flujo
de ejecución actual y ejecuta un ciclo de bus de
reconocimiento de interrupción y salta a un vector fijo
donde se encuéntrala rutina de servicio de esta
interrupción.
Pereq
I
Processor Extensión request
El coprocesador de punto flotante tiene datos listos
para enviar al procesador.
Reset
St
Reset
Suspende cualquier operación en curso y efectúa un
proceso de reset de la CPU.
Vcc
P
System Power
La entrada de tensión de alimentación nominal
conectada externamente al plano de alimentación de la
placa de circuito impreso.
Vss
G
System ground
La referencia general desde la que se consideran todas
las tensiones del microprocesador.
W/#r
O
Write/#Read
Indica si el ciclo actual es de escritura o de lectura.
Forma parte de las señales de definición de ciclo de bus,
las cuales son válidas coincidiendo con el flanco activo
de la señal #ads.
Tabla. Las señales que constituyen el bus básico del i386EX.
La unidad BIU del i386EX- Bus Interface Unit.
El bloque funcional del i386EX que provee todas las funciones mencionadas es
la unidad BIU. Es importante comprender que esta unidad de comunicaciones
masivas del microprocesador constituye un bloque funcional independiente,
autónomo que es usado por el resto de bloques funcionales incluidos en el
BUS i386EX I. ©1999, José M Foces Morán -- 4
Usad esta numeración:
229 de 357
microprocesador. Entre estos bloques funcionales internos del i386EX se
encuentra el propio núcleo computacional SX (Ver figura ...).
La unidad BIU gestiona un complejo canal de comunicaciones, compartido por
un gran número de otros bloques funcionales internos y también externos. El
canal de comunicaciones debe poder conectar cualquier bloque interno con
cualquier bloque externo. Los bloques internos ya los conocemos. Los bloques
externos están formados por todas las posibilidades de elementos de memoria y
de i/o existentes.
El procesador usa la memoria para salvar los programas que ejecuta y los datos
que manejan estos programas. También, el procesador, se comunica con otros
elementos que le sirven como puentes hacia otros computadores o hacia el
usuario. Cuando un sistema computador se encuentra en funcionamiento, el
flujo de información que pasa por el bus gestionado por la unidad BIU es
prácticamente continuo: no hay pausas, la ejecución de los programas provoca
el que continuamente se estén transfiriendo informaciones de todo tipo entre el
interior y el exterior del microprocesador.
v UN AGRUPAMIENTO DE LAS SEÑALES DEL I386EX.
Las señales del bus constituyen el sustrato físico a través del cual circulan las
corrientes eléctricas. Los niveles de tensión presentes en estas líneas representan
los estados lógicos 0 o 1 y, a su vez, dependiendo del nivel de aserción de cada
una de ellas su semántica.
Datos e instrucciones se representan del mismo modo: cadenas de bits de
acuerdo con los esquemas de codificación concretos de cada caso, pero, siempre
son cadenas de bits, de símbolos 0 o 1. Esto significa que las instrucciones y los
datos con los que funciona el microprocesador poseen una misma
representación y, por tanto, pueden ser transmitidos utilizando el mismo medio
físico de transmisión. Ciertamente, las instrucciones llegan al i386EX a través
del bus de datos y, los datos que serán procesados por esas instrucciones,
también llegan al i386EX a través del bus de datos.
Recordaremos ahora que tanto instrucciones como datos residen en memoria,
en zonas separadas de la memoria y que para poder ser transferidos al
microprocesador es necesario informar a la memoria acerca de su ubicación. Las
instrucciones y datos se hallan almacenados en direcciones de memoria
conocidas. La forma más sencilla de representar una dirección de memoria es
en el código conocido como binario natural, es decir, una dirección de memoria
es un entero positivo que, a bajo nivel, está representado en base binaria.
¿Puede usarse el mismo bus, llamado bus de datos, para transmitir direcciones
de memoria? La respuesta es, claramente sí, de hecho, algunos
BUS i386EX I. ©1999, José M Foces Morán -- 5
Usad esta numeración:
230 de 357
microprocesadores de la compañía Intel, compartían en el bus de datos y a
través de ese mismo bus, se transmitían datos y direcciones de memoria. A esta
forma de multiplexar datos y direcciones se la conocía con el término buses
multiplexados, pero, es una práctica abandonada por entero. Hecha esta
digresión, hagamos énfasis en que el i386EX utiliza dos conjuntos de líneas
separada, uno para enviar direcciones y el otro para e enviar y recibir datos. El
bus de datos, según podemos observar en la tabla adjunta se denota por d0:15
y, el bus de direcciones se denota por A1:26.
A través del bus de datos el EX recibe las instrucciones que constituyen los
programas y los datos que éstas procesan. También, a través del bus de datos, el
procesador envía los datos que se almacenarán en la memoria.
A través del bus de direcciones el microprocesador envía las direcciones de
memoria donde residen instrucciones y datos: este bus siempre contiene la
representación binaria de la dirección de memoria involucrada en cualquier
transferencia.
Puesto que el bus de datos tiene un ancho de 16 bits, un máximo de una word
del modo real (una word de 16 bits) podrá ser transferida en una transferencia
simple. El entero representado por una dirección de memoria presente en el
bus de direcciones es el número de word que va a ser leída o escrita en el ciclo
de bus presente, es decir, el número binario presente en el bus es el número de
la word que va a ser transferida a través del bus de datos. Es muy importante
comprender que si el contenido del bus es, por ejemplo 0000003h, el “objeto”
direccionado en memoria será la word número 3. El gráfico de la figura ilustra
esta idea.
Continuando con la relación entre el bus de direcciones y el de datos, ¿El único
ancho de dato transferible por el EX es 16 bits? No, lo que hemos explicado es
que en una sola transacción el EX es capaz de transaccionar un máximo de 16
bits (una word), pero, ciertamente, son aceptables otros anchos de datos: el
i386EX puede transferir una doble word (dword) pero, necesitará más de un
ciclo para poder efectuar esa transferencia. También, el EX puede solicitar la
transferencia de un solo byte. Por ahora, este último caso es el que va a merecer
nuestra atención en el punto siguiente y, antes de estudiarlo, vamos a hacer un
esquema de los dos grupos de señales vistas hasta ahora:
BUS i386EX I. ©1999, José M Foces Morán -- 6
Usad esta numeración:
231 de 357
El bus del i386EX
Bus de datos
16 bits
Ruta baja 0:7
Resto de grupos
Bus de direcciones
25 bits
Ruta alta 8:15
Figura. Los grupos de señales del Bus del i386EX vistos hasta ahora.
El byte bajo de la word 3, su
dirección de byte es la 6
Byte alto
Byte bajo
7
El byte alto de la word 3, su
dirección de byte es la 6+1
6
5
La word
direccionada
es la número
3
4
3
Bus de
direcciones.
2
1
Contenido = 3
0-7
0
8-15
i386EX
Nº
word
Bus de datos 0-15
Figura. El bus de direcciones contiene el número de word direccionada.
v LAS SEÑALES DE HABILITACIÓN DE SUB-RUTAS DEL BUS DE DATOS.
El microprocesador i386EX, por supuesto, sí es capaz de efectuar transferencias
de bytes individuales a través del bus de datos. La organización vista en el
apartado anterior deja claro que esto es perfectamente posible, sin embargo, hay
un cierto número de aspectos que tenemos que dejar claros:
1. Cada una de las words presentes en la memoria se componen de dos
bytes y, la dirección de word no es la misma que la dirección de byte,
BUS i386EX I. ©1999, José M Foces Morán -- 7
Usad esta numeración:
232 de 357
pero dada una de las direcciones la obtención de la otra es directo y
sencillo. Sin embargo, sí es importante tener en cuenta que
programáticamente las direcciones que manejamos son direcciones de
byte que, cuando aparecen en el bus, aparecen traducidas a direcciones
de word en el bus de direcciones y una componente más: las señales de
habilitación de bus.
2. Las señales de habilitación de bytes individuales reciben los nombre
#ble y #bhe (consultar tabla). Si en una transacción de bus aparece la
señal #ble activada y #bhe no aparece activada, eso significa que se va
a utilizar sólo la ruta baja del bus de datos (low) para transferir un byte
a través de ella. Pero ¿Qué byte es el que se va a transferir (leer o
escribir)? Es sencillo: la dirección de la word en la que está incluido
este byte ya la conocemos, en binario natural es el propio bus de
direcciones, y dentro de la word, sólo hay un byte bajo (low):
¡encontrado!
Este nivel 0 en la señal #ble significa que
el procesador necesita usar la ruta baja del
bus de datos
#ble
El byte residente en la
dirección (de byte) número 6
es el único byte direccionado
en esta transacción
#ble
Byte alto
Byte bajo
7
6
La word direccionada es
la número 3
i386EX
Nº
word
5
Bus de
direcciones.
4
Contenido = 3
2
3
1
0-7
8-15
0
Bus de datos 0-15
Figura. El microprocesador indica que necesita usar la ruta baja del bus de
datos mediante la aserción de la línea #ble.
BUS i386EX I. ©1999, José M Foces Morán -- 8
Usad esta numeración:
233 de 357
3. Supongamos ahora que el procesador necesita efectuar una transacción
sobre el byte que está en la dirección de memoria 7. Vamos a derivar el
estado del bus de direcciones y de las señales de habilitación de bytes:
a. Este byte reside en la dirección de word número 7/2 = 3, luego
ya conocemos el contenido del bus de direcciones, un 3
representado en binario haciendo uso de las líneas a1:25. Como
esta dirección de memoria sólo nos indica la word direccionada
nos falta determinar qué posición ocupa el byte 7 dentro de la
word 3: la alta o la baja.
b. Como 7/2 tiene resto (un 1, lógicamente), concluimos que como
el primer byte de una word es el bajo, en este caso la referencia
es al byte alto de la word número 7/2=3.
c. Pero ¿Cómo indica el EX que desea efectuar una transacción de
un solo byte y que en este caso es el byte alto de la word cuyo
número aparece en el bus de direcciones? El EX aserta la línea
de habilitación de byte alto cuyo nombre es #bhe. En la figura
aparece ilustrado este ejemplo.
BUS i386EX I. ©1999, José M Foces Morán -- 9
Usad esta numeración:
234 de 357
Este nivel 1 en la señal #ble significa que
el procesador no necesita usar la ruta baja
del bus de datos
#ble
Este nivel 0 en la señal #bhe significa que
el procesador necesita usar la ruta alta del
bus de datos
#bhe
El byte residente en la
dirección (de byte) número 7
es el único byte direccionado
en esta transacción
#ble
#bhe
Byte alto
Byte bajo
7
6
La word direccionada es
la número 3
i386EX
Nº
word
5
Bus de
direcciones.
4
3
Contenido = 3
2
1
0-7
8-15
0
Bus de datos 0-15
Figura. Transacción de 8 bits sobre el byte presente en la dirección de byte 7.
Este byte se encuentra en la word número 7/2=3 en su parte alta.
Antes de continuar resumiremos un principio ya conocido en este curso: el
objeto más pequeño que posee una dirección de memoria única es el byte, un
agregado de 8 bits.
Realizaremos un último ejemplo que ilustre el significado de las líneas de
habilitación de bytes: una transacción de una word completa ¿Qué estado
presentan las señales de habilitación de bytes en estas transacciones? El ejemplo
que vamos a realizar es sencillamente simplista: imaginemos una situación en la
que el microprocesador necesita leer una word completa, concretamente la –
BUS i386EX I. ©1999, José M Foces Morán -- 10
Usad esta numeración:
235 de 357
famosa- word número 3, pues bien, en esta situación, ambas señales de
habilitación de bytes estarán activas. Esto podíamos preverlo ya, puesto que, si
se ha de leer una word se han de leer los bytes que forman parte de ella, esto es,
su byte bajo y su byte alto, por tanto deberán habilitarse ambas rutas. Debemos
insistir en que el planteamiento de este problema es simplista y que, pronto,
añadiremos una cierta complejidad a este problema y, también sabremos cómo
resolver los problemas derivados de esa complejidad (pista: Endianismo y
alineamiento de datos en la arquitectura IA). Por ahora, resolvamos este caso
sencillo:
#ble
#ble
Este nivel 0 en la señal #ble significa que
el procesador necesita usar la ruta baja del
bus de datos
#bhe
Este nivel 0 en la señal #bhe significa que
el procesador necesita usar la ruta alta del
bus de datos
#bhe
La word direccionada es la
número 3 y como se han
activado ambos byte enables,
ambos bytes, bajo y alto,
serán transferidos a través de
las rutas alta y baja del bus
de datos
Byte alto
Byte bajo
Nº
word
7
6
5
i386EX
Bus de
direcciones.
4
Contenido = 3
2
3
1
0-7
8-15
0
Bus de datos 0-15
Figura. Ambos byte enables activados: un ciclo en el que se transfiere una word
entera (sus dos bytes).
BUS i386EX I. ©1999, José M Foces Morán -- 11
Usad esta numeración:
236 de 357
Incluyamos los byte enables en el agrupamiento de señales del i386EX.
Continuaremos estudiando el grupo de señales que definen el carácter de un
ciclo de bus.
El bus del i386EX
Bus de datos
16 bits
bidireccional
Ruta baja 0:7
Resto de grupos
Bus de direcciones
25 bits
Unidireccional
Byte enables
#ble
#bhe
Ruta alta 8:15
Figura. Los grupos de señales del Bus del i386EX vistos hasta ahora.
v NOCIONES SOBRE TIPOS DE TRANSFERENCIAS REALIZADAS USANDO EL BUS.
Informalmente, hemos comentado anteriormente acerca de la existencia de dos
mapas de memoria desde el punto de vista del microprocesador i386EX: el
mapa de memoria principal y el mapa de i/o (entradas y salidas). El mapa de
memoria corresponde al conjunto de chips que colectivamente constituyen la
memoria principal del computador. El mapa de i/o corresponde al conjunto de
dispositivos que posibilitan la entrada de datos desde el exterior del
computador y, también, la salida de esos datos. Los dispositivos incluidos en el
mapa de i/o cumplen otras funciones de comunicación que ahora no
detallaremos. Las formas de acceso a la memoria y a los dispositivos suelen ser
bien diferentes y el hecho de que la memoria principal quizá forme parte de una
jerarquía de memorias completa, hace conveniente la existencia de un mapa de
i/o completamente separado del mapa de memoria general. Esta es la razón de
la existencia de instrucciones especiales de transferencia a/desde el mapa de
i/o en el i386EX: las instrucciones in y out de la arquitectura IA transfieren
datos desde registros del microprocesador a una dirección de i/o y al revés.
La verificación del hardware de los sistemas emp otrados es una tarea que
requiere mucha experiencia previa y una gran atención a los detalles. La
integración del software con el hardware requiere esas mismas habilidades pero
en contextos de conocimiento que se consideran separados: los programas de
computador y las señales eléctricas. Resulta esencial contar con herramientas
que permitan conocer el estado del microprocesador y el estado algorítmico del
software que está ejecutando. El microprocesador i386EX contiene algunas
señales de definición de ciclo de bus y algunas otras no pertenecientes a este
grupo que se emplean para informar a las herramientas de verificación acerca
del estado del bus, etc.
BUS i386EX I. ©1999, José M Foces Morán -- 12
Usad esta numeración:
237 de 357
El microprocesador contiene un conjunto de líneas conocidas como líneas de
definición del ciclo bus que son usadas por el diseñador de sistemas basados en
microprocesadores y por las herramientas de verificación (depuradores,
emuladores, etc.). Este conjunto de líneas, en cada ciclo de bus iniciado por el
procesador, indican si el ciclo de bus actual es de lectura o de escritura, si se lee
un dato o una instrucción, si la dirección de word presente en el bus de
direcciones y los byte enables han de proyectarse sobre el mapa de memoria
principal o sobre el mapa de i/o además de algunos otros atributos de los ciclos
de bus. ¿Cuál es la indicación de que un ciclo de bus ha comenzado?
v EL I386EX ES UN SISTEMA DIGITAL SÍNCRONO (CON ALGUNAS PARTES NOTABLEMENTE
ASÍNCRONAS).
Los sistemas síncronos cambian de estado en ciertos instantes precisos
marcados por una señal llamada señal de reloj. La señal de reloj del i386EX es
una señal de pulsos periódicos que suele presentarse con un ciclo de trabajo del
50%. Los flancos de subida de la señal de reloj llamada clk2 marcan los cambios
de estado del microprocesador. Internamente, el microprocesador divide esta
señal por 2 y forma una nueva señal de reloj llamada clkout o processor clock. La
unidad interna del i386EX encargada de generar clkout y otras referencias es la
CPMU (Clock and Power Management Unit).
clk2
clkout
(ph1)
clk2
clkout
Estado T1
Estado T2
i386EX
Ciclo de bus mínimo
ph2
Figura. La señal externa de reloj clk2.
BUS i386EX I. ©1999, José M Foces Morán -- 13
Usad esta numeración:
238 de 357
?
?
?
PH2
PH1
PH2
clk2
ph1
ph2
reset
boo
t
clk2
reset
clkout
i386EX
Figura. Sincronización del microprocesador i386EX con el flanco de bajada de la
señal de reset.
La señal que emplean los periféricos internos del microprocesador i386EX es
derivada de clk2 y su nombre es ph1p. Los periféricos conectados al
microprocesador pueden sincronizarse con éste mediante esta señal ya que
ph1p es una réplica de clkout. Todas las entradas al i386EX son muestreadas en
los flancos de subida de clkout y todas las salidas están estables en este mismo
flanco, de esta forma, el diseño con dispositivos activados por flancos es directo.
Con estas ideas acerca de la naturaleza síncrona del i386EX y de su bus,
podemos continuar con la descripción del conjunto de señales de definición de
ciclos de bus y su temporización.
v LA SEÑAL DE INDICACIÓN DE COMIENZO DE UN NUEVO CICLO DE BUS –ADDRESS
STROBE, #ADS.
Los flujos de datos y de instrucciones del microprocesador son posibles gracias
a las transferencias de información entre éste y la memoria y éstas
transferencias de información están formadas por uno o varios ciclos de bus. Un
ciclo de bus es una secuencia de cambios de estado en las señales del bus cuyo
objetivo es la transferencia de un dato simple, así el microprocesador:
BUS i386EX I. ©1999, José M Foces Morán -- 14
Usad esta numeración:
239 de 357
•
Lee una instrucción procedente de la memoria efectuando un ciclo
de bus de lectura de instrucción cuyo ancho de dato es siempre una
word.
•
Escribe un dato del tipo dword (alineada) efectuando dos ciclos de
bus de escritura con un ancho de 16 bits (word) cada uno de ellos
respetando el convenio de ordenamiento little-endian.
La señal #ads representa el inicio de un nuevo ciclo de bus. El instante de
tiempo en el que se inicia el ciclo de bus es el flanco activo de clk2 anterior al
flanco activo de #ads. En el momento preciso en el que el microprocesador
aserta #ads las señales que definen el carácter del ciclo de bus deben estar
estables, es decir, la lógica externa puede muestrear estas señales y actuar en
consecuencia puesto que en este instante (#ads↓) tenemos garantía de que están
estables y que representan el ciclo de bus que necesita el i386EX. #ads activa
significa también que la dirección de word contenida en el bus de direcciones
está, también, estable y que, por tanto, puede ser decodificada por un
decodificador de direcciones externo –si éste es necesario, recordemos que el
i386EX puede decodificar hasta 8 espacios de direccionamiento y generar las
señales de selección de chip internamente, sin necesidad de lógica adicional.
¿Cuáles son las señales de definición de ciclo de bus? En este momento vamos a
estudiar sólo algunas de ellas:
•
w/#r: Identifica a este ciclo como de lectura o de escritura.
•
m/#io: La dirección contenida en el bus de direcciones
corresponde al espacio de memoria principal o al espacio de i/o.
•
d/#c: Este ciclo de bus es de control (p.ej. la lectura de una
instrucción) o de datos (cualquier lectura o escritura de datos).
El cronograma siguiente representa el inicio de un ciclo de lectura de una word
entera procedente de una cierta dirección de word :
•
A partir de #ads↓ las señales w/#r, d/#c y m/#io representan el
ciclo de bus especificado.
•
La dirección de word está estable (puede ser decodificada)
•
Los byte enables también están estables y por tanto el sistema
externo puede activar los chips que constituyen las columnas de
byte alto y bajo que correspondan.
BUS i386EX I. ©1999, José M Foces Morán -- 15
Usad esta numeración:
240 de 357
Estado T1
Fase 1 (ph1)
Estado T2
Fase 2 (ph2)
Ciclo de bus mínimo
clk2
clkout
clk2
#ads
i386EX
w/#r
A1:25
d/#c
m/#io
A1:25
Memoria
Dirección de memoria
(word)
válida
#ble
#bhe
¿Qué señales del i386EX conocemos hasta ahora?
BUS i386EX I. ©1999, José M Foces Morán -- 16
clkout
#ads
w/#r
d/#c
m/#io
Usad esta numeración:
241 de 357
El bus del i386EX
Reloj externo
clk2
Reloj del procesador
clkout
Bus de datos
16 bits
bidireccional
A0:7
Bus de direcciones
25 bits
Unidireccional
A8:15
Inicio de ciclo de bus
#ads
Byte enables
#ble
#bhe
Definición de ciclo de bus
w/#r m/#io d/#c
v CÓMO FINALIZA UN CICLO DE BUS: LA SEÑAL DE INDICACIÓN DE FIN DE CICLO DE BUS,
#READY.
En el punto anterior explicamos cómo transcurre el inicio de un ciclo de bus,
pero no hemos explicado cómo termina, es decir, cuál es el momento preciso en
el que el dato transferido es almacenado en su destino. El almacenamiento de
este dato no puede producirse mientras el dispositivo que lo produce está aun
escribiéndolo en el bus de datos y por tanto estas señales estarán aún
adquiriendo valores estables.
Recordemos que todas las memorias empiezan a decodificar la dirección
provista en su bus de direcciones en el momento en que se activa la señal de
habilitación de chip correspondiente. El tiempo que transcurre entre la
habilitación del chip y el instante en el que la memoria es capaz de proveer el
dato con fiabilidad en su bus de datos, se conoce como tiempo de acceso. El
tiempo de acceso de cada memoria es un parámetro medido por el fabricante en
unas determinadas condiciones ambientales y de alimentación eléctrica. En el
diseño de un computador se incluye un elemento llamado generador de estados
de espera, este circuito es el encargado de determinar cuando puede terminar
un acceso a una memoria, básicamente lo que hace es comenzar una cuenta
atrás en el momento en el que se habilita el chip y cuando llega a cero, activar
una señal de fin de ciclo. La activación de esta señal es la indicación de que
puede leerse el dato presente en el bus de datos con fiabilidad y, por tanto, dar
BUS i386EX I. ©1999, José M Foces Morán -- 17
Usad esta numeración:
242 de 357
por terminado el ciclo presente. La unidad CSU (Chip Select Unit) del i386EX
puede generar automáticamente #ready sin necesidad de ninguna lógica
adicional externa. Esta unidad contiene 8 canales de selección de chip los cuales
son configurados individualmente por el diseñador del sistema para que se
activen cuando se producen accesos a determinados rangos de direcciones de
memoria. La unidad CSU activará el canal de chip select que corresponda.
Además, esta unidad también es capaz de temporizar el acceso según la
explicación anterior y generar la señal #ready internamente y, por tanto, poder
terminar los ciclos de bus que se lleven a cabo sobre las memorias o elementos
de i/o que “caigan” en los rangos de direcciones de memoria configurados en
cada uno de los ocho canales.
En los sistemas basados en microprocesadores que no son empotrados, como es
el caso del Pentium III, el decodificador de direcciones ha de ser provisto
externamente, así como el generador de estados de espera (wait states). Estos
elementos en un computador como el mencionado, forman parte de un
conjunto de chips llamado chip set que típicamente cumple, entre otras, las
siguientes funciones:
•
Decodificación de direcciones
•
Generación de estados de espera
•
Refresco de las memorias dinámicas
•
Multiplexación de fila/columna en el acceso a memorias
dinámicas
•
Gestión de interrupciones
•
Acceso directo a memoria
La señal de entrada al i386EX y que le permite ser avisado del final de los ciclos
de bus se llama #ready. En esta sección pretendemos comprender su significado
y sus usos, de momento no estamos interesados en las diversas formas de
generar #ready, ya sea usando la CSU o circuitería externa.
Un ciclo de bus se extiende durante al menos dos estados de bus: el estado T1
inicial y un estado T2 siguiente. Empleando una terminología más antigua, T1
es el estado de direcciones y T2 es un estado de datos. Un ciclo de bus se
compone de un estado T1 seguido de, al menos, un estado T2. El tiempo de
acceso a una memoria suele ser muy superior al tiempo de ciclo del procesador,
por esta razón el procesador siempre que accede a la memoria debe esperar por
el dato solicitado. La unidad de tiempo mínima, indivisible de la que se
componen las esperas es un estado de bus completo, el procesador no puede
esperar medio estado de bus. Supongamos que el procesador inicia un ciclo de
bus sobre un dispositivo lento que no puede garantizar que tendrá el dato listo
BUS i386EX I. ©1999, José M Foces Morán -- 18
Usad esta numeración:
243 de 357
al final de T2, no nos queda más remedio que añadir una, dos o más unidades
de tiempo de longitud igual a un estado de bus. A estas unidades de tiempo se
les llama estados de espera.
La señal #ready se muestrea al final del estado T2. Para comprender el
momento preciso en el que el microprocesador muestrea #ready dentro de T2,
será necesario consultar la figura . #ready es muestreada en el
flanco de bajada de ph2 dentro de T2: si en ese instante el estado de #ready es 0,
el ciclo de bus se da por concluido, si en ese instante el estado de #ready es 1, el
procesador espera otro estado T2 completo y en ese estado otra vez volverá a
muestrear #ready en el flanco de bajada de ph2. El cronograma siguiente
completa el cronograma que ilustraba el inicio de un ciclo de bus, en este caso,
supondremos que un generador externo de estados de espera, devuelve #ready
en el tiempo adecuado al tiempo de acceso de la memoria.
Estado T1
Fase 1 (ph1)
Estado T2
Fase 2 (ph2)
Ciclo de bus mínimo
clk2
ph2
clk2
#ads
#ready
#ready
D0:15
Datos
listos
w/#r
i386EX
A1:25
Generador
externo de
wait states
d/#c
m/#io
A1:25
Dirección de memoria
(word)
válida
Memoria
#ble
#bhe
Figura. Ciclo de lectura completo.
BUS i386EX I. ©1999, José M Foces Morán -- 19
clkout
#ads
w/#r
d/#c
m/#io
d0:15
Usad esta numeración:
244 de 357
v ¿ES POSIBLE QUE EL MICROPROCESADOR NO NECESITE REALIZAR CICLOS DE BUS?
La unidad de bus (BIU) del microprocesador i386EX está compartida por todos
los bloques funcionales incluidos en el microprocesador. La BIU realiza ciclos
de bus a petición del núcleo computacional, del controlador de DMA (Direct
Memory Access, acceso directo memoria) y del controlador de refresco de
DRAMs. Con este cuadro en mente, no es difícil imaginarse una situación en la
que el controlador de DMA no tiene ninguna transferencia pendiente, al
controlador de refresco le falta tiempo aún para iniciar el refresco de la fila que
corresponda y, por último, la cola de carga anticipada de instrucciones del
cauce de ejecución está llena y, las instrucciones presentes en el cauce no
efectúan operaciones de lectura ni de escritura en memoria. En este caso, no
sería necesario llevar a cabo ningún ciclo de bus ¿Qué estado presentarían las
señales del bus en ese estado?
El bus del i386EX puede permanecer en un estado, llamémoslo quieto, mientras
no haya solicitudes de ciclos de bus. El tiempo mínimo en el que el bus puede
estar en estado quieto (idle) es, como en el caso de los estados de espera, un
estado de bus. Estos ciclos de bus quietos se componen de un solo estado, cuyo
nombre es Ti. Lógicamente, estos ciclos idle no se componen de estado de
direcciones (T1) y estado de datos (T2) ya que no hay direcciones de memoria
estables en el bus de direcciones y, tampoco hay transferencias de datos. La
característica distintiva de estos ciclos es que, no habiéndose iniciado ningún
ciclo de bus y habiendo finalizado el último, la señal #ads no pasa a estado
activo. En la figura siguiente, el ciclo de bus que sigue al ciclo inicial de lectura
es un ciclo idle.
Estado T1
Fase 1 (ph1)
Estado T2
Fase 2 (ph2)
Idle bus cycle
Ciclo de bus mínimo
clk2
ph2
#ads
#ready
Figura. El ciclo de bus idle.
BUS i386EX I. ©1999, José M Foces Morán -- 20
Usad esta numeración:
245 de 357
v UN DIAGRAMA DE ESTADOS DEL BUS DEL I386EX.
Vamos a plasmar en forma de diagrama de estados todos los estados de bus
que conocemos hasta este momento. Esta formalización nos ayudará a entender
mejor estas ideas y conceptos, además, el bus del i386EX contiene otras facetas
que iremos descubriendo progresivamente y será necesario resumir las nuevas
funcionalidades con diagramas cada vez más complejos.
Por el momento, construyamos el diagrama de estados. Los círculos
representan los estados, las flechas representan las transiciones entre estados y,
las etiquetas que acompañan a las transiciones representan las condiciones
lógicas que tienen que darse para que esa transición concreta se produzca.
#ready asertada
& no hay
solicitud de
ciclo de bus
Ti
Sí hay una
solicitud de
ciclo de bus
no hay
ninguna
solicitud
de ciclo de bus
T1
siempre
T2
#ready asertada
& SI hay
solicitud de
ciclo de bus
#ready no
asertada
Figura. Diagrama de estados básico del bus del i386EX.
m/#io d/#c w/#r Tipo de ciclo de bus
0
0
0
Ciclo de reconocimiento de interrupción
(Interrupt Acknowledge Cycle).
0
0
1
-
0
1
0
Lectura de datos procedente del espacio de i/o
BUS i386EX I. ©1999, José M Foces Morán -- 21
Usad esta numeración:
246 de 357
0
1
1
Escritura de datos hacia el espacio de i/o
1
0
0
Lectura de instrucción desde memoria
1
0
1
Ciclo de halt o shutdown
1
1
0
Lectura de datos desde memoria
1
1
1
Escritura de datos en memoria
Tabla. Los tipos de ciclo de bus posibles en un i386EX.
BUS i386EX I. ©1999, José M Foces Morán -- 22
Usad esta numeración:
247 de 357
sistemas empotrados; entonces tendremos ocasión de estudiar el resto de
componentes internos de este microprocesador.
Patilla Tipo Nombre y función cuando la señal es asertada
a25:1
o
El todo importante Bus de Direcciones
#ads
O
Address Status
El procesador informa de que las líneas de definición del
ciclo de bus y el bus de direcciones están estables. Esto
significa que ha dado comienzo un nuevo ciclo de bus
justo en el flanco activo de reloj que tuvo lugar unos
pocos ns atrás y que el tiempo de éste está
transcurriendo ya.
#bhe
Byte High Enable
O
El procesador informa de que en el ciclo de bus
presente va a ser usada la ruta alta del bus de datos
(d8:15).
#ble
Byte Low Enable
O
El procesador informa de que en el ciclo de bus
presente va a ser usada la ruta baja del bus de datos
(d0:7).
#bs8
Bus Size
I
El ciclo de bus actual corresponde a una zona de
memoria o de i/o capaz de transferencias de datos
limitadas a 8 bits.
#busy
Busy
I
El coprocesador de punto flotante está ocupado.
Si esta señal se encuentra asertada en el flanco de
bajada de #reset, el procesador llevará a cabo un autotest interno.
Clk2
Clock
ST
(Schmit
Trigger).
La señal de reloj que constituye el parámetro
fundamental de temporización de todas las operaciones
del microprocesador.
BUS i386EX I. ©1999, José M Foces Morán -- 2
Usad esta numeración:
248 de 357
D15:0
I/O
Data Bus
Estas líneas transportan los datos procedentes del
microprocesador en los ciclos de escritura y los datos
destinados al microprocesador en los ciclos de lectura.
D/#C
O
Data/control
Si esta línea se encuentra asertada, el ciclo de bus es del
tipo “control”(interrupción, halt o lectura de una
instrucción).
Su estado no-asertado significa que el ciclo de bus
actual es un ciclo de lectura o de escritura de datos.
#error
I
Error.
El coprocesador de punto flotante está en un estado de
error.
#flt
I
Float.
Esta línea fuerza al i386EX a dejar todas las líneas del
bus en estado flotante (excepto la línea TDO).
Hlda
O
Bus Hold Acknowledge
El procesador ha cedido el control del bus a otro bus
master que previamente lo ha solicitado.
Hold
I
Bus Hold Request
Cuando el i386EX explora esta señal asertada procederá
a ceder el bus a otro bus master. La indicación de cesión
del bus se efectúa a través de hlda.
#lock
O
Lock
El procesador indica a los bus masters potenciales que
en el ciclo de bus presente no pueden solicitar el bus.
M/#io
O
Memory i/o
A través de esta línea de definición de ciclo bus, el
i386EX indica si el ciclo de bus actual se aplica al mapa
de memoria o al de entradas y salidas (i/o).
#na
I
Next address (Use address pipelining).
Mediante la aserción de esta línea, el sistema externo
indica que puede aceptar ciclos de bus encauzados, esto
es, en medio del ciclo actual, el procesador fuerza la
BUS i386EX I. ©1999, José M Foces Morán -- 3
Usad esta numeración:
249 de 357
salida del ciclo de bus siguiente.
Nmi
ST
Nonmaskable interrupt request
Entrada de interrupción no enmascarable. Cuando el
i386EX recibe este tipo de interrupción detiene el flujo
de ejecución actual y ejecuta un ciclo de bus de
reconocimiento de interrupción y salta a un vector fijo
donde se encuéntrala rutina de servicio de esta
interrupción.
Pereq
I
Processor Extensión request
El coprocesador de punto flotante tiene datos listos
para enviar al procesador.
Reset
St
Reset
Suspende cualquier operación en curso y efectúa un
proceso de reset de la CPU.
Vcc
P
System Power
La entrada de tensión de alimentación nominal
conectada externamente al plano de alimentación de la
placa de circuito impreso.
Vss
G
System ground
La referencia general desde la que se consideran todas
las tensiones del microprocesador.
W/#r
O
Write/#Read
Indica si el ciclo actual es de escritura o de lectura.
Forma parte de las señales de definición de ciclo de bus,
las cuales son válidas coincidiendo con el flanco activo
de la señal #ads.
Tabla. Las señales que constituyen el bus básico del i386EX.
La unidad BIU del i386EX- Bus Interface Unit.
El bloque funcional del i386EX que provee todas las funciones mencionadas es
la unidad BIU. Es importante comprender que esta unidad de comunicaciones
masivas del microprocesador constituye un bloque funcional independiente,
autónomo que es usado por el resto de bloques funcionales incluidos en el
BUS i386EX I. ©1999, José M Foces Morán -- 4
Usad esta numeración:
250 de 357
microprocesador. Entre estos bloques funcionales internos del i386EX se
encuentra el propio núcleo computacional SX (Ver figura ...).
La unidad BIU gestiona un complejo canal de comunicaciones, compartido por
un gran número de otros bloques funcionales internos y también externos. El
canal de comunicaciones debe poder conectar cualquier bloque interno con
cualquier bloque externo. Los bloques internos ya los conocemos. Los bloques
externos están formados por todas las posibilidades de elementos de memoria y
de i/o existentes.
El procesador usa la memoria para salvar los programas que ejecuta y los datos
que manejan estos programas. También, el procesador, se comunica con otros
elementos que le sirven como puentes hacia otros computadores o hacia el
usuario. Cuando un sistema computador se encuentra en funcionamiento, el
flujo de información que pasa por el bus gestionado por la unidad BIU es
prácticamente continuo: no hay pausas, la ejecución de los programas provoca
el que continuamente se estén transfiriendo informaciones de todo tipo entre el
interior y el exterior del microprocesador.
v UN AGRUPAMIENTO DE LAS SEÑALES DEL I386EX.
Las señales del bus constituyen el sustrato físico a través del cual circulan las
corrientes eléctricas. Los niveles de tensión presentes en estas líneas representan
los estados lógicos 0 o 1 y, a su vez, dependiendo del nivel de aserción de cada
una de ellas su semántica.
Datos e instrucciones se representan del mismo modo: cadenas de bits de
acuerdo con los esquemas de codificación concretos de cada caso, pero, siempre
son cadenas de bits, de símbolos 0 o 1. Esto significa que las instrucciones y los
datos con los que funciona el microprocesador poseen una misma
representación y, por tanto, pueden ser transmitidos utilizando el mismo medio
físico de transmisión. Ciertamente, las instrucciones llegan al i386EX a través
del bus de datos y, los datos que serán procesados por esas instrucciones,
también llegan al i386EX a través del bus de datos.
Recordaremos ahora que tanto instrucciones como datos residen en memoria,
en zonas separadas de la memoria y que para poder ser transferidos al
microprocesador es necesario informar a la memoria acerca de su ubicación. Las
instrucciones y datos se hallan almacenados en direcciones de memoria
conocidas. La forma más sencilla de representar una dirección de memoria es
en el código conocido como binario natural, es decir, una dirección de memoria
es un entero positivo que, a bajo nivel, está representado en base binaria.
¿Puede usarse el mismo bus, llamado bus de datos, para transmitir direcciones
de memoria? La respuesta es, claramente sí, de hecho, algunos
BUS i386EX I. ©1999, José M Foces Morán -- 5
Usad esta numeración:
251 de 357
microprocesadores de la compañía Intel, compartían en el bus de datos y a
través de ese mismo bus, se transmitían datos y direcciones de memoria. A esta
forma de multiplexar datos y direcciones se la conocía con el término buses
multiplexados, pero, es una práctica abandonada por entero. Hecha esta
digresión, hagamos énfasis en que el i386EX utiliza dos conjuntos de líneas
separada, uno para enviar direcciones y el otro para e enviar y recibir datos. El
bus de datos, según podemos observar en la tabla adjunta se denota por d0:15
y, el bus de direcciones se denota por A1:26.
A través del bus de datos el EX recibe las instrucciones que constituyen los
programas y los datos que éstas procesan. También, a través del bus de datos, el
procesador envía los datos que se almacenarán en la memoria.
A través del bus de direcciones el microprocesador envía las direcciones de
memoria donde residen instrucciones y datos: este bus siempre contiene la
representación binaria de la dirección de memoria involucrada en cualquier
transferencia.
Puesto que el bus de datos tiene un ancho de 16 bits, un máximo de una word
del modo real (una word de 16 bits) podrá ser transferida en una transferencia
simple. El entero representado por una dirección de memoria presente en el
bus de direcciones es el número de word que va a ser leída o escrita en el ciclo
de bus presente, es decir, el número binario presente en el bus es el número de
la word que va a ser transferida a través del bus de datos. Es muy importante
comprender que si el contenido del bus es, por ejemplo 0000003h, el “objeto”
direccionado en memoria será la word número 3. El gráfico de la figura ilustra
esta idea.
Continuando con la relación entre el bus de direcciones y el de datos, ¿El único
ancho de dato transferible por el EX es 16 bits? No, lo que hemos explicado es
que en una sola transacción el EX es capaz de transaccionar un máximo de 16
bits (una word), pero, ciertamente, son aceptables otros anchos de datos: el
i386EX puede transferir una doble word (dword) pero, necesitará más de un
ciclo para poder efectuar esa transferencia. También, el EX puede solicitar la
transferencia de un solo byte. Por ahora, este último caso es el que va a merecer
nuestra atención en el punto siguiente y, antes de estudiarlo, vamos a hacer un
esquema de los dos grupos de señales vistas hasta ahora:
BUS i386EX I. ©1999, José M Foces Morán -- 6
Usad esta numeración:
252 de 357
El bus del i386EX
Bus de datos
16 bits
Ruta baja 0:7
Resto de grupos
Bus de direcciones
25 bits
Ruta alta 8:15
Figura. Los grupos de señales del Bus del i386EX vistos hasta ahora.
El byte bajo de la word 3, su
dirección de byte es la 6
Byte alto
Byte bajo
7
El byte alto de la word 3, su
dirección de byte es la 6+1
6
5
La word
direccionada
es la número
3
4
3
Bus de
direcciones.
2
1
Contenido = 3
0-7
0
8-15
i386EX
Nº
word
Bus de datos 0-15
Figura. El bus de direcciones contiene el número de word direccionada.
v LAS SEÑALES DE HABILITACIÓN DE SUB-RUTAS DEL BUS DE DATOS.
El microprocesador i386EX, por supuesto, sí es capaz de efectuar transferencias
de bytes individuales a través del bus de datos. La organización vista en el
apartado anterior deja claro que esto es perfectamente posible, sin embargo, hay
un cierto número de aspectos que tenemos que dejar claros:
1. Cada una de las words presentes en la memoria se componen de dos
bytes y, la dirección de word no es la misma que la dirección de byte,
BUS i386EX I. ©1999, José M Foces Morán -- 7
Usad esta numeración:
253 de 357
pero dada una de las direcciones la obtención de la otra es directo y
sencillo. Sin embargo, sí es importante tener en cuenta que
programáticamente las direcciones que manejamos son direcciones de
byte que, cuando aparecen en el bus, aparecen traducidas a direcciones
de word en el bus de direcciones y una componente más: las señales de
habilitación de bus.
2. Las señales de habilitación de bytes individuales reciben los nombre
#ble y #bhe (consultar tabla). Si en una transacción de bus aparece la
señal #ble activada y #bhe no aparece activada, eso significa que se va
a utilizar sólo la ruta baja del bus de datos (low) para transferir un byte
a través de ella. Pero ¿Qué byte es el que se va a transferir (leer o
escribir)? Es sencillo: la dirección de la word en la que está incluido
este byte ya la conocemos, en binario natural es el propio bus de
direcciones, y dentro de la word, sólo hay un byte bajo (low):
¡encontrado!
Este nivel 0 en la señal #ble significa que
el procesador necesita usar la ruta baja del
bus de datos
#ble
El byte residente en la
dirección (de byte) número 6
es el único byte direccionado
en esta transacción
#ble
Byte alto
Byte bajo
7
6
La word direccionada es
la número 3
i386EX
Nº
word
5
Bus de
direcciones.
4
Contenido = 3
2
3
1
0-7
8-15
0
Bus de datos 0-15
Figura. El microprocesador indica que necesita usar la ruta baja del bus de
datos mediante la aserción de la línea #ble.
BUS i386EX I. ©1999, José M Foces Morán -- 8
Usad esta numeración:
254 de 357
3. Supongamos ahora que el procesador necesita efectuar una transacción
sobre el byte que está en la dirección de memoria 7. Vamos a derivar el
estado del bus de direcciones y de las señales de habilitación de bytes:
a. Este byte reside en la dirección de word número 7/2 = 3, luego
ya conocemos el contenido del bus de direcciones, un 3
representado en binario haciendo uso de las líneas a1:25. Como
esta dirección de memoria sólo nos indica la word direccionada
nos falta determinar qué posición ocupa el byte 7 dentro de la
word 3: la alta o la baja.
b. Como 7/2 tiene resto (un 1, lógicamente), concluimos que como
el primer byte de una word es el bajo, en este caso la referencia
es al byte alto de la word número 7/2=3.
c. Pero ¿Cómo indica el EX que desea efectuar una transacción de
un solo byte y que en este caso es el byte alto de la word cuyo
número aparece en el bus de direcciones? El EX aserta la línea
de habilitación de byte alto cuyo nombre es #bhe. En la figura
aparece ilustrado este ejemplo.
BUS i386EX I. ©1999, José M Foces Morán -- 9
Usad esta numeración:
255 de 357
Este nivel 1 en la señal #ble significa que
el procesador no necesita usar la ruta baja
del bus de datos
#ble
Este nivel 0 en la señal #bhe significa que
el procesador necesita usar la ruta alta del
bus de datos
#bhe
El byte residente en la
dirección (de byte) número 7
es el único byte direccionado
en esta transacción
#ble
#bhe
Byte alto
Byte bajo
7
6
La word direccionada es
la número 3
i386EX
Nº
word
5
Bus de
direcciones.
4
3
Contenido = 3
2
1
0-7
8-15
0
Bus de datos 0-15
Figura. Transacción de 8 bits sobre el byte presente en la dirección de byte 7.
Este byte se encuentra en la word número 7/2=3 en su parte alta.
Antes de continuar resumiremos un principio ya conocido en este curso: el
objeto más pequeño que posee una dirección de memoria única es el byte, un
agregado de 8 bits.
Realizaremos un último ejemplo que ilustre el significado de las líneas de
habilitación de bytes: una transacción de una word completa ¿Qué estado
presentan las señales de habilitación de bytes en estas transacciones? El ejemplo
que vamos a realizar es sencillamente simplista: imaginemos una situación en la
que el microprocesador necesita leer una word completa, concretamente la –
BUS i386EX I. ©1999, José M Foces Morán -- 10
Usad esta numeración:
256 de 357
famosa- word número 3, pues bien, en esta situación, ambas señales de
habilitación de bytes estarán activas. Esto podíamos preverlo ya, puesto que, si
se ha de leer una word se han de leer los bytes que forman parte de ella, esto es,
su byte bajo y su byte alto, por tanto deberán habilitarse ambas rutas. Debemos
insistir en que el planteamiento de este problema es simplista y que, pronto,
añadiremos una cierta complejidad a este problema y, también sabremos cómo
resolver los problemas derivados de esa complejidad (pista: Endianismo y
alineamiento de datos en la arquitectura IA). Por ahora, resolvamos este caso
sencillo:
#ble
#ble
Este nivel 0 en la señal #ble significa que
el procesador necesita usar la ruta baja del
bus de datos
#bhe
Este nivel 0 en la señal #bhe significa que
el procesador necesita usar la ruta alta del
bus de datos
#bhe
La word direccionada es la
número 3 y como se han
activado ambos byte enables,
ambos bytes, bajo y alto,
serán transferidos a través de
las rutas alta y baja del bus
de datos
Byte alto
Byte bajo
Nº
word
7
6
5
i386EX
Bus de
direcciones.
4
Contenido = 3
2
3
1
0-7
8-15
0
Bus de datos 0-15
Figura. Ambos byte enables activados: un ciclo en el que se transfiere una word
entera (sus dos bytes).
BUS i386EX I. ©1999, José M Foces Morán -- 11
Usad esta numeración:
257 de 357
Incluyamos los byte enables en el agrupamiento de señales del i386EX.
Continuaremos estudiando el grupo de señales que definen el carácter de un
ciclo de bus.
El bus del i386EX
Bus de datos
16 bits
bidireccional
Ruta baja 0:7
Resto de grupos
Bus de direcciones
25 bits
Unidireccional
Byte enables
#ble
#bhe
Ruta alta 8:15
Figura. Los grupos de señales del Bus del i386EX vistos hasta ahora.
v NOCIONES SOBRE TIPOS DE TRANSFERENCIAS REALIZADAS USANDO EL BUS.
Informalmente, hemos comentado anteriormente acerca de la existencia de dos
mapas de memoria desde el punto de vista del microprocesador i386EX: el
mapa de memoria principal y el mapa de i/o (entradas y salidas). El mapa de
memoria corresponde al conjunto de chips que colectivamente constituyen la
memoria principal del computador. El mapa de i/o corresponde al conjunto de
dispositivos que posibilitan la entrada de datos desde el exterior del
computador y, también, la salida de esos datos. Los dispositivos incluidos en el
mapa de i/o cumplen otras funciones de comunicación que ahora no
detallaremos. Las formas de acceso a la memoria y a los dispositivos suelen ser
bien diferentes y el hecho de que la memoria principal quizá forme parte de una
jerarquía de memorias completa, hace conveniente la existencia de un mapa de
i/o completamente separado del mapa de memoria general. Esta es la razón de
la existencia de instrucciones especiales de transferencia a/desde el mapa de
i/o en el i386EX: las instrucciones in y out de la arquitectura IA transfieren
datos desde registros del microprocesador a una dirección de i/o y al revés.
La verificación del hardware de los sistemas emp otrados es una tarea que
requiere mucha experiencia previa y una gran atención a los detalles. La
integración del software con el hardware requiere esas mismas habilidades pero
en contextos de conocimiento que se consideran separados: los programas de
computador y las señales eléctricas. Resulta esencial contar con herramientas
que permitan conocer el estado del microprocesador y el estado algorítmico del
software que está ejecutando. El microprocesador i386EX contiene algunas
señales de definición de ciclo de bus y algunas otras no pertenecientes a este
grupo que se emplean para informar a las herramientas de verificación acerca
del estado del bus, etc.
BUS i386EX I. ©1999, José M Foces Morán -- 12
Usad esta numeración:
258 de 357
El microprocesador contiene un conjunto de líneas conocidas como líneas de
definición del ciclo bus que son usadas por el diseñador de sistemas basados en
microprocesadores y por las herramientas de verificación (depuradores,
emuladores, etc.). Este conjunto de líneas, en cada ciclo de bus iniciado por el
procesador, indican si el ciclo de bus actual es de lectura o de escritura, si se lee
un dato o una instrucción, si la dirección de word presente en el bus de
direcciones y los byte enables han de proyectarse sobre el mapa de memoria
principal o sobre el mapa de i/o además de algunos otros atributos de los ciclos
de bus. ¿Cuál es la indicación de que un ciclo de bus ha comenzado?
v EL I386EX ES UN SISTEMA DIGITAL SÍNCRONO (CON ALGUNAS PARTES NOTABLEMENTE
ASÍNCRONAS).
Los sistemas síncronos cambian de estado en ciertos instantes precisos
marcados por una señal llamada señal de reloj. La señal de reloj del i386EX es
una señal de pulsos periódicos que suele presentarse con un ciclo de trabajo del
50%. Los flancos de subida de la señal de reloj llamada clk2 marcan los cambios
de estado del microprocesador. Internamente, el microprocesador divide esta
señal por 2 y forma una nueva señal de reloj llamada clkout o processor clock. La
unidad interna del i386EX encargada de generar clkout y otras referencias es la
CPMU (Clock and Power Management Unit).
clk2
clkout
(ph1)
clk2
clkout
Estado T1
Estado T2
i386EX
Ciclo de bus mínimo
ph2
Figura. La señal externa de reloj clk2.
BUS i386EX I. ©1999, José M Foces Morán -- 13
Usad esta numeración:
259 de 357
?
?
?
PH2
PH1
PH2
clk2
ph1
ph2
reset
boo
t
clk2
reset
clkout
i386EX
Figura. Sincronización del microprocesador i386EX con el flanco de bajada de la
señal de reset.
La señal que emplean los periféricos internos del microprocesador i386EX es
derivada de clk2 y su nombre es ph1p. Los periféricos conectados al
microprocesador pueden sincronizarse con éste mediante esta señal ya que
ph1p es una réplica de clkout. Todas las entradas al i386EX son muestreadas en
los flancos de subida de clkout y todas las salidas están estables en este mismo
flanco, de esta forma, el diseño con dispositivos activados por flancos es directo.
Con estas ideas acerca de la naturaleza síncrona del i386EX y de su bus,
podemos continuar con la descripción del conjunto de señales de definición de
ciclos de bus y su temporización.
v LA SEÑAL DE INDICACIÓN DE COMIENZO DE UN NUEVO CICLO DE BUS –ADDRESS
STROBE, #ADS.
Los flujos de datos y de instrucciones del microprocesador son posibles gracias
a las transferencias de información entre éste y la memoria y éstas
transferencias de información están formadas por uno o varios ciclos de bus. Un
ciclo de bus es una secuencia de cambios de estado en las señales del bus cuyo
objetivo es la transferencia de un dato simple, así el microprocesador:
BUS i386EX I. ©1999, José M Foces Morán -- 14
Usad esta numeración:
260 de 357
•
Lee una instrucción procedente de la memoria efectuando un ciclo
de bus de lectura de instrucción cuyo ancho de dato es siempre una
word.
•
Escribe un dato del tipo dword (alineada) efectuando dos ciclos de
bus de escritura con un ancho de 16 bits (word) cada uno de ellos
respetando el convenio de ordenamiento little-endian.
La señal #ads representa el inicio de un nuevo ciclo de bus. El instante de
tiempo en el que se inicia el ciclo de bus es el flanco activo de clk2 anterior al
flanco activo de #ads. En el momento preciso en el que el microprocesador
aserta #ads las señales que definen el carácter del ciclo de bus deben estar
estables, es decir, la lógica externa puede muestrear estas señales y actuar en
consecuencia puesto que en este instante (#ads↓) tenemos garantía de que están
estables y que representan el ciclo de bus que necesita el i386EX. #ads activa
significa también que la dirección de word contenida en el bus de direcciones
está, también, estable y que, por tanto, puede ser decodificada por un
decodificador de direcciones externo –si éste es necesario, recordemos que el
i386EX puede decodificar hasta 8 espacios de direccionamiento y generar las
señales de selección de chip internamente, sin necesidad de lógica adicional.
¿Cuáles son las señales de definición de ciclo de bus? En este momento vamos a
estudiar sólo algunas de ellas:
•
w/#r: Identifica a este ciclo como de lectura o de escritura.
•
m/#io: La dirección contenida en el bus de direcciones
corresponde al espacio de memoria principal o al espacio de i/o.
•
d/#c: Este ciclo de bus es de control (p.ej. la lectura de una
instrucción) o de datos (cualquier lectura o escritura de datos).
El cronograma siguiente representa el inicio de un ciclo de lectura de una word
entera procedente de una cierta dirección de word :
•
A partir de #ads↓ las señales w/#r, d/#c y m/#io representan el
ciclo de bus especificado.
•
La dirección de word está estable (puede ser decodificada)
•
Los byte enables también están estables y por tanto el sistema
externo puede activar los chips que constituyen las columnas de
byte alto y bajo que correspondan.
BUS i386EX I. ©1999, José M Foces Morán -- 15
Usad esta numeración:
261 de 357
Estado T1
Fase 1 (ph1)
Estado T2
Fase 2 (ph2)
Ciclo de bus mínimo
clk2
clkout
clk2
#ads
i386EX
w/#r
A1:25
d/#c
m/#io
A1:25
Memoria
Dirección de memoria
(word)
válida
#ble
#bhe
¿Qué señales del i386EX conocemos hasta ahora?
BUS i386EX I. ©1999, José M Foces Morán -- 16
clkout
#ads
w/#r
d/#c
m/#io
Usad esta numeración:
262 de 357
El bus del i386EX
Reloj externo
clk2
Reloj del procesador
clkout
Bus de datos
16 bits
bidireccional
A0:7
Bus de direcciones
25 bits
Unidireccional
A8:15
Inicio de ciclo de bus
#ads
Byte enables
#ble
#bhe
Definición de ciclo de bus
w/#r m/#io d/#c
v CÓMO FINALIZA UN CICLO DE BUS: LA SEÑAL DE INDICACIÓN DE FIN DE CICLO DE BUS,
#READY.
En el punto anterior explicamos cómo transcurre el inicio de un ciclo de bus,
pero no hemos explicado cómo termina, es decir, cuál es el momento preciso en
el que el dato transferido es almacenado en su destino. El almacenamiento de
este dato no puede producirse mientras el dispositivo que lo produce está aun
escribiéndolo en el bus de datos y por tanto estas señales estarán aún
adquiriendo valores estables.
Recordemos que todas las memorias empiezan a decodificar la dirección
provista en su bus de direcciones en el momento en que se activa la señal de
habilitación de chip correspondiente. El tiempo que transcurre entre la
habilitación del chip y el instante en el que la memoria es capaz de proveer el
dato con fiabilidad en su bus de datos, se conoce como tiempo de acceso. El
tiempo de acceso de cada memoria es un parámetro medido por el fabricante en
unas determinadas condiciones ambientales y de alimentación eléctrica. En el
diseño de un computador se incluye un elemento llamado generador de estados
de espera, este circuito es el encargado de determinar cuando puede terminar
un acceso a una memoria, básicamente lo que hace es comenzar una cuenta
atrás en el momento en el que se habilita el chip y cuando llega a cero, activar
una señal de fin de ciclo. La activación de esta señal es la indicación de que
puede leerse el dato presente en el bus de datos con fiabilidad y, por tanto, dar
BUS i386EX I. ©1999, José M Foces Morán -- 17
Usad esta numeración:
263 de 357
por terminado el ciclo presente. La unidad CSU (Chip Select Unit) del i386EX
puede generar automáticamente #ready sin necesidad de ninguna lógica
adicional externa. Esta unidad contiene 8 canales de selección de chip los cuales
son configurados individualmente por el diseñador del sistema para que se
activen cuando se producen accesos a determinados rangos de direcciones de
memoria. La unidad CSU activará el canal de chip select que corresponda.
Además, esta unidad también es capaz de temporizar el acceso según la
explicación anterior y generar la señal #ready internamente y, por tanto, poder
terminar los ciclos de bus que se lleven a cabo sobre las memorias o elementos
de i/o que “caigan” en los rangos de direcciones de memoria configurados en
cada uno de los ocho canales.
En los sistemas basados en microprocesadores que no son empotrados, como es
el caso del Pentium III, el decodificador de direcciones ha de ser provisto
externamente, así como el generador de estados de espera (wait states). Estos
elementos en un computador como el mencionado, forman parte de un
conjunto de chips llamado chip set que típicamente cumple, entre otras, las
siguientes funciones:
•
Decodificación de direcciones
•
Generación de estados de espera
•
Refresco de las memorias dinámicas
•
Multiplexación de fila/columna en el acceso a memorias
dinámicas
•
Gestión de interrupciones
•
Acceso directo a memoria
La señal de entrada al i386EX y que le permite ser avisado del final de los ciclos
de bus se llama #ready. En esta sección pretendemos comprender su significado
y sus usos, de momento no estamos interesados en las diversas formas de
generar #ready, ya sea usando la CSU o circuitería externa.
Un ciclo de bus se extiende durante al menos dos estados de bus: el estado T1
inicial y un estado T2 siguiente. Empleando una terminología más antigua, T1
es el estado de direcciones y T2 es un estado de datos. Un ciclo de bus se
compone de un estado T1 seguido de, al menos, un estado T2. El tiempo de
acceso a una memoria suele ser muy superior al tiempo de ciclo del procesador,
por esta razón el procesador siempre que accede a la memoria debe esperar por
el dato solicitado. La unidad de tiempo mínima, indivisible de la que se
componen las esperas es un estado de bus completo, el procesador no puede
esperar medio estado de bus. Supongamos que el procesador inicia un ciclo de
bus sobre un dispositivo lento que no puede garantizar que tendrá el dato listo
BUS i386EX I. ©1999, José M Foces Morán -- 18
Usad esta numeración:
264 de 357
al final de T2, no nos queda más remedio que añadir una, dos o más unidades
de tiempo de longitud igual a un estado de bus. A estas unidades de tiempo se
les llama estados de espera.
La señal #ready se muestrea al final del estado T2. Para comprender el
momento preciso en el que el microprocesador muestrea #ready dentro de T2,
será necesario consultar la figura . #ready es muestreada en el
flanco de bajada de ph2 dentro de T2: si en ese instante el estado de #ready es 0,
el ciclo de bus se da por concluido, si en ese instante el estado de #ready es 1, el
procesador espera otro estado T2 completo y en ese estado otra vez volverá a
muestrear #ready en el flanco de bajada de ph2. El cronograma siguiente
completa el cronograma que ilustraba el inicio de un ciclo de bus, en este caso,
supondremos que un generador externo de estados de espera, devuelve #ready
en el tiempo adecuado al tiempo de acceso de la memoria.
Estado T1
Fase 1 (ph1)
Estado T2
Fase 2 (ph2)
Ciclo de bus mínimo
clk2
ph2
clk2
#ads
#ready
#ready
D0:15
Datos
listos
w/#r
i386EX
A1:25
Generador
externo de
wait states
d/#c
m/#io
A1:25
Dirección de memoria
(word)
válida
Memoria
#ble
#bhe
Figura. Ciclo de lectura completo.
BUS i386EX I. ©1999, José M Foces Morán -- 19
clkout
#ads
w/#r
d/#c
m/#io
d0:15
Usad esta numeración:
265 de 357
v ¿ES POSIBLE QUE EL MICROPROCESADOR NO NECESITE REALIZAR CICLOS DE BUS?
La unidad de bus (BIU) del microprocesador i386EX está compartida por todos
los bloques funcionales incluidos en el microprocesador. La BIU realiza ciclos
de bus a petición del núcleo computacional, del controlador de DMA (Direct
Memory Access, acceso directo memoria) y del controlador de refresco de
DRAMs. Con este cuadro en mente, no es difícil imaginarse una situación en la
que el controlador de DMA no tiene ninguna transferencia pendiente, al
controlador de refresco le falta tiempo aún para iniciar el refresco de la fila que
corresponda y, por último, la cola de carga anticipada de instrucciones del
cauce de ejecución está llena y, las instrucciones presentes en el cauce no
efectúan operaciones de lectura ni de escritura en memoria. En este caso, no
sería necesario llevar a cabo ningún ciclo de bus ¿Qué estado presentarían las
señales del bus en ese estado?
El bus del i386EX puede permanecer en un estado, llamémoslo quieto, mientras
no haya solicitudes de ciclos de bus. El tiempo mínimo en el que el bus puede
estar en estado quieto (idle) es, como en el caso de los estados de espera, un
estado de bus. Estos ciclos de bus quietos se componen de un solo estado, cuyo
nombre es Ti. Lógicamente, estos ciclos idle no se componen de estado de
direcciones (T1) y estado de datos (T2) ya que no hay direcciones de memoria
estables en el bus de direcciones y, tampoco hay transferencias de datos. La
característica distintiva de estos ciclos es que, no habiéndose iniciado ningún
ciclo de bus y habiendo finalizado el último, la señal #ads no pasa a estado
activo. En la figura siguiente, el ciclo de bus que sigue al ciclo inicial de lectura
es un ciclo idle.
Estado T1
Fase 1 (ph1)
Estado T2
Fase 2 (ph2)
Idle bus cycle
Ciclo de bus mínimo
clk2
ph2
#ads
#ready
Figura. El ciclo de bus idle.
BUS i386EX I. ©1999, José M Foces Morán -- 20
Usad esta numeración:
266 de 357
v UN DIAGRAMA DE ESTADOS DEL BUS DEL I386EX.
Vamos a plasmar en forma de diagrama de estados todos los estados de bus
que conocemos hasta este momento. Esta formalización nos ayudará a entender
mejor estas ideas y conceptos, además, el bus del i386EX contiene otras facetas
que iremos descubriendo progresivamente y será necesario resumir las nuevas
funcionalidades con diagramas cada vez más complejos.
Por el momento, construyamos el diagrama de estados. Los círculos
representan los estados, las flechas representan las transiciones entre estados y,
las etiquetas que acompañan a las transiciones representan las condiciones
lógicas que tienen que darse para que esa transición concreta se produzca.
#ready asertada
& no hay
solicitud de
ciclo de bus
Ti
Sí hay una
solicitud de
ciclo de bus
no hay
ninguna
solicitud
de ciclo de bus
T1
siempre
T2
#ready asertada
& SI hay
solicitud de
ciclo de bus
#ready no
asertada
Figura. Diagrama de estados básico del bus del i386EX.
m/#io d/#c w/#r Tipo de ciclo de bus
0
0
0
Ciclo de reconocimiento de interrupción
(Interrupt Acknowledge Cycle).
0
0
1
-
0
1
0
Lectura de datos procedente del espacio de i/o
BUS i386EX I. ©1999, José M Foces Morán -- 21
Usad esta numeración:
267 de 357
0
1
1
Escritura de datos hacia el espacio de i/o
1
0
0
Lectura de instrucción desde memoria
1
0
1
Ciclo de halt o shutdown
1
1
0
Lectura de datos desde memoria
1
1
1
Escritura de datos en memoria
Tabla. Los tipos de ciclo de bus posibles en un i386EX.
BUS i386EX I. ©1999, José M Foces Morán -- 22
Usad esta numeración:
268 de 357
Apuntes de Máquinas
Electrónicas
© 1994, 2004 José María Foces Morán.
Capítulo 9
Ciclos de bus especiales del
microprocesador i386ex
Usad esta numeración:
269 de 357
Ciclos de bus especiales del i386EX.
Los ciclos de bus que hemos estudiado hasta este momento constituyen el
conjunto básico de ciclos de bus del microprocesador i386EX. Además, otros
tipos de ciclos permiten que este procesador pueda integrarse en sistemas de
tiempo real gracias a los procesos de interrupción, ejecutar instrucciones
especiales de detención del procesador y compartir el bus con otros dispositivos
capaces de controlarlo (bus masters). En el siguiente cuadro resumiremos los
tipos de ciclos de bus del i386EX.
v LOS TIPOS DE CICLOS DE BUS CONOCIDOS HASTA AHORA.
Ciclos de bus del i386EX
Básicos
Especiales
byte
word
dword
alineados
desalineados
Lectura
Datos
Memoria
Instrucciones
i/o
Escritura
Datos
Memoria
i/o
Figura. Los tipos básicos de ciclos de bus del i386EX.
v EL CICLO DE BUS DE RECONOCIMIENTO DE INTERRUPCIÓN.
Una interrupción es un proceso por el cual la ocurrencia de un evento en el
exterior del procesador provoca que éste abandone temporalmente el flujo de
ejecución de instrucciones actual y salte a otro programa llamado rutina de
BUS i386EX III. ©1999, José M Foces Morán -- 1
Usad esta numeración:
270 de 357
servicio de interrupción. Al ejecutar la isr (Interrupt Service Routine), el
microprocesador pone en marcha los mecanismos que tratan la situación, de
alguna manera excepcional, creada por la interrupción y. Cuando estos
procesos han sido comenzados con fiabilidad, el microprocesador puede
retornar al contexto de programa abandonado y continuar su ejecución.
tiempo
programa en
ejecución
rutina de
servicio (isr) #1
iret
rutina de
servicio #2
iret
rutina de
servicio #3
iret
Interrupción #1
Interrupción #2
Interrupción #3
Figura. El concepto de interrupción y de rutina de servicio de interrupción (isr).
El núcleo computacional SX posee una capacidad muy limitada de interrupción,
tan sólo una línea de interrupción llamada intr. Por esta razón se añade a este
microprocesador un circuito capaz de capturar un número mayor de
interrupciones y multiplexarlas todas ellas a través del único canal de
interrupción presente en el microprocesador. Este elemento es conocido como
ICU (Interrupt Control Unit, Unidad de Control de Interrupciones) y el i386EX
contiene dos ICU’s. Claramente, cuando uno de los icu’s recibe una
interrupción activa la línea intr del núcleo y entonces, el microprocesador
responde al icu de una forma establecida: solicitándole cual es el vector
correspondiente a la interrupción que ha ocurrido. Este vector contiene la
información precisa que necesita el núcleo para salta a la isr correspondiente.
Esta secuencia de operaciones en la que el núcleo y el icu intercambian
información a través del bus se conoce como ciclo de bus de reconocimiento de
interrupción. El estudio detallado del funcionamiento del icu lo trataremos en el
capítulo dedicado al diseño de hardware para sistemas empotrados. En esta
sección sólo estamos interesados en comprender este ciclo de bus en el que el
BUS i386EX III. ©1999, José M Foces Morán -- 2
Usad esta numeración:
271 de 357
objetivo consiste en entregar al núcleo el número de vector de interrupción
correspondiente a la interrupción que ha tenido lugar.
Para que el procesador atienda las interrupciones que le son entregadas a través
de su patilla intr., es necesario que el bit IF del registro FLAGS tenga un valor 1,
esto es, que la recepción de interrupciones mascarables esté habilitada.
Suponiendo que esto es cierto, cuando el procesador, inmediatamente después
de terminar la instrucción que esté ejecutando ahora mismo, muestrea intr
activada, comienza el proceso de aceptación (reconocimiento) de interrupción el
cual consiste en los siguientes pasos:
1. Primer ciclo de bus de iack (Interrupt ACKnowledge).:
a. Se inicia con #ads activa y el siguiente conjunto de señales de
definición de ciclo de bus: m/#io, d/#c y w/#r en 0
b. #lock en 0: el núcleo se apropia del bus impidiendo que otros bus
master puedan solicitarlo.
c. La dirección de byte es 4.
d. El bus de datos está flotante.
e. Puesto que la unidad ICU es un periférico interno del i386EX, ella
misma será capaz de generar #ready. En sistemas basados en
microprocesadores no-empotrados donde el controlador de
interrupciones es un chip externo, posiblemente formando parte
de un chip set, el decodificador de direcciones será el encargado
de generar #ready.
2. El procesador introduce ahora cuatro ciclos idle seguidos.
3. Nuevo ciclo de bus de iack.
a. Se inicia con #ads activa y el siguiente conjunto de señales de
definición de ciclo de bus: m/#io, d/#c y w/#r en 0
b. #lock en 0: el núcleo se apropia del bus impidiendo que otros bus
master puedan solicitarlo.
c. La dirección de byte es 0.
d. El ICU que capturó la interrupción calcula el número de vector
que le corresponde y lo escribe en el bus de bus de datos de forma
que el núcleo pueda leerlo al final del ciclo.
e. Puesto que la unidad ICU es un periférico interno del i386EX, ella
misma será capaz de generar #ready. En sistemas basados en
microprocesadores no-empotrados donde el controlador de
interrupciones es un chip externo, posiblemente formando parte
de un chip set, el decodificador de direcciones será el encargado
de generar #ready.
BUS i386EX III. ©1999, José M Foces Morán -- 3
Usad esta numeración:
272 de 357
Estado T1
Fase 1 (ph1)
Estado T2
Fase 2 (ph2)
Ciclo de bus mínimo
clk2
Ciclo de bus mínimo
ph2
#ads
#ready
número
de
vector
D0:15
w/#r
d/#c
m/#io
A1:25
Dirección de memoria
(word)
2
2
#ble
#bhe
#lock
ciclo de bus
iack 1
4 ciclos
idle
ciclo de bus
iack 2
Figura. Temporización de los ciclos de reconocimiento de interrupción (iack).
v CICLOS DE BUS HALT Y SHUTDOWN.
La instrucción hlt hace que el microprocesador i386EX deje de ejecutar
instrucciones, a este estado se le conoce como estado halt. El procesador puede
volver a ejecutar instrucciones si se le provoca una interrupción NMI (una
interrupción no mascarable que usa una patilla específica llamada nmi), una
interrupción intr o un reset. La instrucción hlt es, por supuesto, una instrucción
privilegiada, así que, si el procesador está ejecutando en modo protegido, la
rutina que ejecute hlt debe poseer un nivel de privilegio 0.
BUS i386EX III. ©1999, José M Foces Morán -- 4
Usad esta numeración:
273 de 357
El estado de shutdown es muy parecido al de halt, la diferencia es que shutdown
tiene lugar a causa de que el procesador ha recibido una excepción mientras
estaba procesando otra y además en el procesamiento de la segunda, se ha
encontrado con un error de protección. El procesador no puede recuperarse de
esta circunstancia, así que procede a abandonar la ejecución de instrucciones,
algo equivalente a la ejecución de la instrucción hlt.
Externamente, los estados halt y shutdown presentan un aspecto bastante
similar. La secuencia de señales correspondiente a la ejecución de la instrucción
hlt es la siguiente:
1. #ads está activa.
m/#io y w/#r están en estado 1.
d/#c está en estado 0.
El bus de direcciones y los byte enables forman una dirección de byte 2
(halt) o de 0 (shutdown).
2. #ready puede ser generada internamente gracias a una programación
especial de la unidad de gestión de reloj y potencia. Si #ready se
genera externamente, deben añadirse wait states.
v EL CICLO DE BUS DE REFRESCO DE DRAM’S.
En la introducción de este capítulo donde dimos la lista de bloques funcionales
incluidos en el microprocesador i386EX, vimos que este microprocesador
contiene casi toda la lógica necesaria para producir el refresco de las memorias
dinámicas. Ciertamente, la unidad RCU (Refresh Control Unit) simplifica el
diseño del controlador de memoria. La unidad RCU contiene sus registros
específicos y, también genera un ciclo de bus específico de refresco. En este
apartado estudiaremos el cronograma del ciclo de bus de refresco. En el
capítulo dedicado al desarrollo de software para sistemas empotrados
estudiaremos con más detalle la estructura de registros de la RCU (Estudio del
caso de la placa de evaluación de Intel EV386EX).
El ciclo de bus de refresco transcurre mediante la secuencia de señales
siguiente:
1. Activación de #ads.
•
m/#io y d/#c toman valor 1.
•
d/#c y #refresh toman valor 0.
•
#bhe y #ble ambos 1.
BUS i386EX III. ©1999, José M Foces Morán -- 5
Usad esta numeración:
274 de 357
•
A1:25 representan la dirección de word contenida en el
registro RAC (Contador de dirección de refresco,
Refresh Address Counter).
2. El ciclo termina como cualquier ciclo de bus, mediante la activación de
#ready. Esta activación puede obtenerse externamente o internamente
empleando la CSU (Chip Select Unit).
Estado T1
Fase 1 (ph1)
Estado T2
Fase 2 (ph2)
Ti
idle
Ciclo de bus mínimo
clk2
Ti
idle
Ciclo de bus mínimo
ph2
#ads
#ready
datos
estables
D0:15
indefinido
alta Z
w/#r
d/#c
m/#io
A1:25
2
#ble
#bhe
ciclo de bus
cualquiera
ciclo de bus
haltshutdown
La CPU
permanece en
este estado
hasta recibir
intr, nmi o
reset
Figura. Temporización del ciclo de bus de halt-shutdown.
Es crucial que el refresco de las memorias se produzca lo antes posible,
recordaremos que el periodo de refresco es de unos 4ms, si no se respeta este
tiempo máximo, las memorias dinámicas pueden sufrir corrupción de datos,
por tanto, en caso de que el bus esté siendo usado por un determinado bus
master, es esencial que el árbitro del bus (Bus arbiter) conceda el bus ante una
BUS i386EX III. ©1999, José M Foces Morán -- 6
Usad esta numeración:
275 de 357
petición de la RCU lo antes posible. La RCU es el bus master con mayor
prioridad. El nivel de detalle adecuado acerca del arbitraje del bus durante los
ciclos de refresco puede encontrarse en el manual de usuario del
microprocesador i386EX.
Estado T1
Fase 1 (ph1)
Estado T2
Fase 2 (ph2)
Ciclo de bus mínimo
clk2
Ciclo de bus mínimo
T1
T2
T2
ph2
#ads
#ready
datos
estables
D0:15
alta Z
w/#r
d/#c
m/#io
Dirección de la word que se va a
refrescar
A1:25
#ble
#bhe
#refresh
ciclo de bus
cualquiera
ciclo de bus
de refresco
ciclo de bus
cualquiera
Figura. Ciclo de bus de refresco
v EL BUS DEL I386EX ES UN RECURSO COMPARTIDO.
El bus del i386Ex puede verse como un medio de comunicación compartido
entre varias unidades funcionales internas del microprocesador y, posiblemente
también, varias unidades externas. El procesador contiene un circuito que
arbitra la concesión del bus a las unidades funcionales que lo soliciten,
BUS i386EX III. ©1999, José M Foces Morán -- 7
Usad esta numeración:
276 de 357
empleando un protocolo. Este protocolo emplea las líneas hold y hlda. Los
masters internos del procesador son los posibles peticionadores del bus y son
estos:
•
Unidad de refresco de memorias dinámicas (RCU)
•
Núcleo (core) computacional SX.
•
Controlador de DMA interno 1 (DMA, Direct Memory
Access, Controlador de acceso directo a memoria).
•
Controlador de DMA interno 2.
Cuando el árbitro del bus interno recibe una petición de bus a través de una de
las cuatro posibles líneas de petición, aserta la línea HOLD del núcleo. El
núcleo, entonces, termina el posible ciclo de bus no-bloqueado que está
ejecutándose ahora y, cuando esto ha ocurrido, aserta su línea HLDA
informando al árbitro del bus de que ya puede asertar su línea hlda y así
informar al solicitante de que tiene el bus.
i386EX
Unidad de
refresco de
memorias
dinámicas RCU
HOLD
Árbitro
del Bus
HLDA
Núcleo
microprocesador
na#
i386 SX
Cola de
prerrecojida de
instrucciones
Unidades de
Ejecución
Controlador de
DMA, acceso
directo a memoria
bs8#
ready#
Unidad de
control del
bus
(Bus
Interface
Unit,
BIU)
ads#
w/r#
m/io#
d/c#
lock#
Word Address Bus
1-25
ble#
bhe#
Data Bus
0-15
Figura. El bus arbiter arbitra el acceso al bus por parte de elementos externos y
unidades funcionales internas del procesador.
BUS i386EX III. ©1999, José M Foces Morán -- 8
Usad esta numeración:
277 de 357
Esquema de aspectos acerca de la temporización de hld y hlda. La señal hold
del i386EX es asíncrona con respecto a clk2. El microprocesador responde así
ante hold activa:
•
Termina el ciclo de bus actual
•
Des-aserta #wr, #rd, #smiact, #ucs, #cs6:0 y #refresh y
pone en tri-estado el resto de salidas excepto hlda.
•
Aserta hlda, esto significa que el solicitante ya tiene el
bus y por tanto puede comenzar a introducir ciclos de
bus inmediatamente.
El elemento que ha solicitado el bus debe mantener hold activa durante tantos
ciclos como le resulte necesario poseer el bus. Cuando el solicitante termina de
usar el bus, des-aserta hold y, en respuesta a esto, el microprocesador des-aserta
hlda. Ahora el microprocesador vuelve a tener el bus, la unidad funcional
interna que lo va a usar será la elegida por el árbitro: probablemente un ciclo de
refresco o un ciclo de bus solicitado por el núcleo (la lectura de una instrucción
o algún ciclo de bus de lectura/escritura de memoria o i/o) o, por último, una
transferencia vía DMA que aún pudiera estar pendiente).
Dada la naturaleza urgente que tiene el refresco de las memorias dinámicas,
cuando el microprocesador ha concedido el bus a un master externo y recibe
una petición de refresco, puede hacer que la señal hlda pase a estado inactivo,
en cuyo caso, el master puede o puede no desactivar hold. Si ocurre lo último,
podrían corromperse datos en las memorias dinámicas, por tanto, para
garantizar un funcionamiento fiable del computador, será necesario diseñar los
bus masters externos de forma que si sensan hlda desacertada, cedan el bus
inmediatamente al i386EX.
Si el núcleo está en posesión del bus y se produce una solicitud, el núcleo no le
indica al árbitro que puede conceder el bus, hasta que ha terminado el ciclo de
bus actual.
Cuando el núcleo no está en posesión del bus, aún es posible la ejecución de
instrucciones directamente desde la cola de anticipación, particularmente si las
instrucciones allí presentes no solicitan ningún ciclo de bus de lectura –posible.
Si las instrucciones presentes en la cola solicitan un solo ciclo de escritura,
entonces, gracias al write buffer (Cola de escrituras anticipadas que es de
profundidad 1, pero que, además del dato a escribir, guarda también la
dirección del mismo), no será necesario detener el flujo de ejecución de
instrucciones durante el tiempo en que el núcleo no esté en posesión de bus.
v CICLOS DE BUS INDIVISIBLES –SEÑAL #LOCK.
BUS i386EX III. ©1999, José M Foces Morán -- 9
Usad esta numeración:
278 de 357
Los sistemas basados en el microprocesador i386EX pueden contener otros
dispositivos bus master. Estos dispositivos pueden solicitar el bus y,
consiguientemente, usarlo para introducir sus propios ciclos de bus.
La actualización de variables del tipo semáforo ha de hacerse de forma que la
lectura-modificación-y-escritura transcurran como operaciones secuenciales
indivisibles; si no es así, estas variables pueden tomar valores incorrectos,
valores que no se corresponderían con el estado verdadero del recurso para el
que fueron pensadas. Una de las maneras en que puede garantizarse la
actualización correcta de las variables semáforo es haciendo que el hardware
subyacente pueda garantizar el acceso exclusivo a las celdas de memoria que
contienen el valor de la variable semáforo. En un sistema basado en el i386EX,
la serie de operaciones r(read)-m(modify)-w(write), puede no producirse de
forma indivisible si, por ejemplo, en medio de r-m, otro bus master solicita el
bus y le es concedido. Para garantizar la atomicidad de las operaciones r-m-w, el
bus del i386EX contiene una señal llamada #lock, que, cuando está asertada,
indica al resto de bus masters que el bus no puede ser solicitado. De esta forma,
ningún bus master asertará hold. Además, mientras #lock esté asertada, el
procesador no reconoce la activación de hold.
Hay ciertas instrucciones que, cuando son ejecutadas por el i386EX, activan
#lock automáticamente, como por ejemplo:
•
la instrucción xchg en el formato reg-mem o mem-reg.
•
La actualización de descriptores de memoria (en modo
protegido).
•
Ciclos de reconocimiento de interrupciones (Vistos
anteriormente en este mismo capítulo).
Si el programador desea forzar a que una instrucción genere ciclos indivisibles
puede usar el prefijo de instrucción llamado lock. No todas las instrucciones
garantizan la activación de lock en respuesta al prefijo, pero sí algunas de ellas.
En el ejemplo siguiente, el programador desea forzar r-m-w atómico en el
incremento de una variable que reside en memoria:
lock inc dword ptr fs:[bx+si]
Temporización de la señal #lock. #lock permanece activada desde el primer
flanco de clk2 perteneciente a la secuencia de ciclos indivisibles hasta el
momento en que la señal #ready es muestreada asertada. El tiempo máximo
que #lock puede estar asertada será de unos 9 ciclos de bus más 15 pulsos de
clk2. Esta situación sólo es posible en el tratamiento de interrupciones en modo
protegido.
BUS i386EX III. ©1999, José M Foces Morán -- 10
Usad esta numeración:
279 de 357
v LOS CICLOS DE BUS SOBRE DISPOSITIV OS O MEMORIA QUE POR DISEÑO POSEEN UN
AN CH O DE 8 BITS.
Al estudiar el bus del i386EX y, mas concretamente, su bus de direcciones,
mencionamos que éste siempre contiene el número de word direccionada en la
memoria. Esto está ocasionado por el hecho de que el bus es de 16 bits: la
memoria será un dispositivo de 16 bits. Sin embargo, hay dispositivos que en su
diseño original presentan un ancho de 8 bits solamente y, no tiene sentido
cablear dos en paralelo para formar un bus de 16 bits.
Podemos imaginar un dispositivo de comunicaciones serie que, por simplicidad
de su diseño, ha sido provisto de un bus de sólo 8 bits: el dispositivo, cuando se
comunica con los elementos externos a los que está conectado, traduce cada
dato recibido en formato serie a un byte que será posteriormente enviado al
host (el microprocesador probablemente). En este caso, si conectásemos dos
dispositivos en paralelo para formar un bus de 16 bits, uno de ellos, estaría sin
conexión serie, no tendríamos fuente ni destino de datos para él: no tiene
sentido formar el bus de 16 bits a base de dos dispositivos de 8. La pregunta
ahora es ¿Cómo conectar un dispositivo de 8 bits de ancho al microprocesador
i386EX?
La señal #bs8 de entrada al i386EX. Para permitir la conexión de dispositivos
de 8 bits al bus de 16 del i386EX, el microprocesador incorpora una señal
llamada #bs8 que, cuando se activa, indica al procesador que el dispositivo es
de 8 bits de ancho y que por tanto, no puede utilizar la ruta de datos superior
en ningún acceso al dispositivo, ya sea en transacciones mono-byte o multibyte.
El decodificador de direcciones será el encargado de activar #bs8 al principio de
cada ciclo de bus cuya dirección de memoria se corresponda con una zona que
está implementada con dispositivos de ancho 8 bits. Los dispositivos de ancho 8
bits pueden ser tanto memoria como dispositivos de i/o. Sí tiene sentido
utilizar zonas de memoria conectadas al microprocesador vía una interfaz de 8
bits: el cableado, sobre todo en prototipos, será notablemente más sencillo.
Cuando el i386EX inicia un ciclo de bus y, en el estado T2 muestrea #bs8
activada, generará la secuencia y número de ciclos de bus adecuada, coherente
con la semántica de la instrucción que se esté ejecutando. Del mismo modo que,
con un ancho de bus de 16 bits, fuimos capaces de efectuar transacciones que
involucraban datos de tipo dword y datos desalineados, también con un
esquema de bus de 8 bits, el i386EX puede realizar las mismas transacciones.
BUS i386EX III. ©1999, José M Foces Morán -- 11
Usad esta numeración:
280 de 357
El bus de 8 bits del i386EX está formado por los siguientes elementos:
•
La ruta inferior del bus de datos ( d0:7).
•
El byte enable de esta ruta, #ble.
•
El bus de direcciones de byte:
§
El bus de direcciones de words a25:1
§
La línea a0 que coincide con #ble.
Ciclos de escritura. En los ciclos que siguen se supone que en T2 la señal #bs8
ha sido muestreada activa.
•
Si el ciclo de bus actual es de ancho un byte con #bhe
activa y #ble inactiva, el procesador copia el byte
contenido en la ruta alta, d8:15, a la ruta baja d0:7. Es
necesario que el procesador se comporte de este modo
pues su única vía de conexión con el dispositivo es la
ruta baja y, al principio del ciclo de bus de escritura, el
microprocesador aún no sabe que la dirección de
memoria formada corresponde con un dispositivo de 8
bits.
•
Si el ciclo de bus actual es de un byte de ancho con
#bhe inactiva y #ble inactiva, no es necesario tener en
cuenta el estado de #bs8 porque el ciclo es ya de 8 bits
de ancho y esos 8 bits se van a entregar a través de la
ruta de conexión activa, d0:7.
•
Si el ciclo de bus actual es de ancho una word con #bhe
y #ble activas y el procesador muestrea #bs8 activa en
T2 (cuando #ready es muestreada activa), el procesador
espera a que este ciclo termine y, seguidamente ejecuta
otro ciclo de bus con los 8 bits superiores, d8:15
copiados a la ruta inferior del bus de datos, d0:7,
desactivando en este segundo ciclo #ble, esto es , el
primer ciclo transcurre con la [inexistente] línea A0 a
valor 0 y el segundo con A0 a valor 1.
Ciclos de lectura. En los ciclos que siguen se supone que en T2 la señal #bs8 ha
sido muestreada activa.
•
Si el ciclo actual es de ancho un byte con #bhe activa y
#ble inactiva, el procesador registra en un latch de
entrada no arquitecturado el dato entregado a través de
d0:7 e internamente, a través de la red de intercambio
de bytes, lo enruta hacia la posición alta en su registro
de destino.
BUS i386EX III. ©1999, José M Foces Morán -- 12
Usad esta numeración:
281 de 357
Apuntes de
Máquinas
Electrónicas
© 1994-2005, José María Foces Morán.
Prácticas 10-15.
Técnicas de programación para
sistemas empotrados.
Usad esta numeración:
282 de 357
MÁQUINAS ELECTRÓNICAS
Práctica 10: Ejemplos de iniciación a la programación en lenguaje de
ensamblaje de la arquitectura IA-32.
Introducción.
Debug es un depurador de programas que contienen instrucciones de la
arquitectura IA escritos para el modo real. El sistema operativo MS-DOS® incluye
debug y, actualmente, también viene incluido en todas las versiones del sistema
operativo Windows. Se trata de un simulador que, de forma parecida al simulador
Spim en la arquitectura del microprocesador MIPS, ofrece al programador un
entorno desde el que pueda controlar la ejecución y los recursos de los programas.
Debug nos permite ejecutar paso a paso -instrucción a instrucción- nuestro
programa, presentar en pantalla el contenido de los registros y de las zonas de
memoria que nos interesen, etc. Debug ofrece también la posibilidad de
desensamblar zonas de memoria.
¿Qué es desensamblar una zona de memoria? En primer lugar, recordaremos lo
que significa ensamblar un programa. Básicamente, ensamblar un programa
consiste en generar un programa (código) ejecutable a partir de un programa
(código) fuente escrito en lenguaje de ensamblaje. El proceso de desensamblaje es
el contrario: Si sabemos que un determinado programa en su forma ya ejecutable
se encuentra ubicado en memoria a partir de una determinada dirección, Debug
puede obtener el programa fuente de este ejecutable y dejárnoslo ver en pantalla.
Estas y otras posibilidades del programa son las que vamos a ensayar en esta
práctica.
Sesión interactiva con debug.
Como paso previo a la introducción del depurador DEBUG, realizaremos un
programa simple y lo ensamblaremos con el ensamblador MASM. El listado del
programa aparece a continuación y, para ensamblarlo, simplemente lo abriremos
desde el programa PWB (Incluye MASM) y usaremos el comando build.
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 1/45
Usad esta numeración:
283 de 357
TITLE HELLO: Programa que imprime el mensaje típico "HELLO WORLD!"
; Programa de demostración del depurador DEBUG.
; Ensamblar con MASM(PWB) y producir un ejecutable .com
; Modelo de memoria small, un sólo segmento físico que albergará los
; segmentos de código, datos y pila de nuestro programa.
.MODEL SMALL
;Reservar un espacio de 256 posiciones de memoria para el segmento de
;pila de este programa
.STACK 100H
;Crear un segmento de datos (DATA) para el programa.
Saludo SEGMENT PARA PUBLIC 'DATA'
;En este segmento de datos sólo crearemos un array de caracteres cuyo
;nombre es Mensaje
Mensaje DB 'Hola mundo!', 7, 13, 10,'$'
; |
| |
|------------------->|
; |
| primer carácter del array es H, el último es ‘$’
; |
|
; |
Directiva DB (Define Byte): Definir bytes
; Nombre del array de bytes es Mensaje
Saludo ENDS ;Fin del segmento de datos Saludo (END SEGMENT)
;Comienzo del segmento de código
.CODE
;Comienzo del procedimiento MAIN:
MAIN PROC
;Inicializar el segmento de datos DS.
MOV AX, Saludo
MOV DS, AX
;Presentar el mensaje en pantalla usando la función 9 de la interrupción 21h del DOS.
LEA DX, Mensaje
MOV AH, 9
INT 21h
;Retornar al sistema operativo de forma ordenada, usando la función 4CH del DOS
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 2/45
Usad esta numeración:
284 de 357
MOV AH, 4CH
INT 21h
MAIN ENDP
END MAIN
Una vez ensamblado con éxito, cambiad al directorio donde se encuentra el fichero
con extensión “.com”. El depurador DEBUG se arranca ejecutando el comando
DEBUG.COM. Basta con teclear DEBUG en la línea de comandos de una shell.
Una vez que hemos copiado hello.com al directorio \temp, podemos comenzar la
sesión de trabajo con debug, para ello, ejecutas los siguientes comandos:
C:\> cd <directorio donde está el fichero hello.com>
C:\temp\> debug hello.com
El guión anterior es el prompt de DEBUG, el cual indica que el programa DEBUG
ha leído con éxito nuestro programa llamado hello.com y que está listo para recibir
comandos. Escribamos el comando R, este comando de DEBUG nos permite ver los
registros de la CPU y la primera instrucción de nuestro programa. La primera
línea del listado siguiente presenta los GPR (Registros de Propósito General)y la
siguiente presenta los registros de segmento, el registro instruction pointer (IP) y
el registro de banderas o flags. La última línea contiene la primera instrucción de
nuestro programa, la cual se encuentra ubicada en la dirección 15FC:0000 y la
instrucción es MOV AX, 15FD. Las direcciones de memoria que aparecen en los
ejemplos, en general, no coincidirán con las que te van a aparecer en tu sesión.
Esto, como sabemos, es debido a que cada sistema tendrá su estado particular de
uso de memoria debido a los programas y datos que se encuentran cargados
actualmente.
-R
AX=0000BX=0000CX=00EDDX=0000SP=0100BP=0000SI=0000DI=0000
DS=15ECES=15ECSS=15FECS=15FCIP=0000 NV UP EI PL NZ NA PO NC
15FC:0000 B8FD15
MOV
AX,15FD
Escribamos ahora el comando que nos va a permitir alterar, por ejemplo, el
contenido del registro CX. Ese comando es el mismo comando R anterior, pero,
seguido inmediatamente por el nombre del registro que queremos alterar, en este
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 3/45
Usad esta numeración:
285 de 357
caso CX. En la siguiente línea, DEBUG responde con el contenido actual (En este
caso es 00ED) y nos pone el símbolo : en la linea siguiente. Después de los :
debemos escribir el valor hexadecimal que queremos poner dentro del registro CX.
Después ejecutamos el comando R otra vez para ver el contenido de todos los
registros y así comprobar que CX tiene el valor deseado. Importante: La única base
numérica en la que DEBUG acepta datos es la base hexadecimal, por tanto, no es
necesario denotar tal base, puesto que, como hemos comentado, es la única que
acepta DEBUG. Esta es una diferencia notable con MASM, el ensamblador, el
cual acepta constantes en las cuatro bases usuales.
-RCX
CX 00ED
:1234
-R
AX=0000BX=0000CX=1234DX=0000SP=0100BP=0000SI=0000DI=0000
DS=15ECES=15ECSS=15FECS=15FCIP=0000 NV UP EI PL NZ NA PO NC
15FC:0000 B8FD15
MOV
AX,15FD
Recordemos que DEBUG tiene a nuestro programa 'bajo su control'. Vamos ahora a
desensamblar este programa con el comando U. Simplemente escribimos U y
pulsamos Retorno. DEBUG responde con el listado siguiente al comando U. Cada
línea tiene una estructura como esta:
SEGMENTO:OFFSET Instrucción Completa en Formato HEX Instrucción Completa En Formato fuente.
-U
15FC:0000
15FC:0003
15FC:0005
15FC:0008
15FC:000A
15FC:000C
15FC:000E
B8FD15
8ED8
BA0000
B409
CD21
B44C
CD21
MOV
MOV
MOV
MOV
INT
MOV
INT
AX,15FD
DS,AX
DX,0000
AH,09
21
AH,4C
21
Ya vemos pues el aspecto que tiene el programa fuente. Es el momento de
ejecutarlo, pero, eso sí, instrucción a instrucción. Esto se hace escribiendo el
comando T (Trace, trazar, 'perseguir'). Este comando en su forma más sencilla
ejecuta exactamente una instrucción. Veamos qué instrucción se ejecutará al
ejecutar el comando T. Ejecutemos R de nuevo para ver los registros y la
instrucción que se va a ejecutar al pulsar T:
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 4/45
Usad esta numeración:
286 de 357
-R
AX=0000BX=0000CX=1234DX=0000SP=0100BP=0000SI=0000DI=0000
DS=15ECES=15ECSS=15FECS=15FCIP=0000 NV UP EI PL NZ NA PO NC
15FC:0000 B8FD15
MOV
AX,15FD
La instrucción MOV
AX,15FD es la que se va a ejecutar. Hagámoslo:
-T
AX=15FDBX=0000CX=1234DX=0000SP=0100BP=0000SI=0000DI=0000
DS=15ECES=15ECSS=15FECS=15FCIP=0003 NV UP EI PL NZ NA PO NC
15FC:0003 8ED8
MOV
DS,AX
¿Cómo podemos saber que la anterior instrucción se ejecutó? Es sencillo: la
instrucción
MOV
AX,15FD lleva al registro AX el valor 15FD. Miremos pués en
el listado anterior a ver si el registro AX tiene el valor 15FD. ¿Es así? Sí lo és. Sigamos
trazando nuestro programa, ejecutemos T varias veces y observemos en cada caso
si el resultado obtenido es el esperado.
-T
AX=15FDBX=0000CX=1234DX=0000SP=0100BP=0000SI=0000DI=0000
DS=15FDES=15ECSS=15FECS=15FCIP=0005 NV UP EI PL NZ NA PO NC
15FC:0005 BA0000
MOV
DX,0000
-T
AX=15FDBX=0000CX=1234DX=0000SP=0100BP=0000SI=0000DI=0000
DS=15FDES=15ECSS=15FECS=15FCIP=0008 NV UP EI PL NZ NA PO NC
15FC:0008 B409
MOV
AH,09
-T
AX=09FDBX=0000CX=1234DX=0000SP=0100BP=0000SI=0000DI=0000
DS=15FDES=15ECSS=15FECS=15FCIP=000A NV UP EI PL NZ NA PO NC
15FC:000A CD21
INT
21
La instrucción actual, INT 21, básicamente consiste en una llamada a una
subrutina -programa- especial conocido como interrupción software. Lo que nos
importa en este caso es que INT 21, efectúa una llamada al sistema operativo MSDOS, esa llamada lo que hace es imprimir en pantalla un mensaje que está dentro
de nuestro programa. Esto conlleva la ejecución de varias instrucciones, no sólo
una, como parece a primera vista. Nosotros no queremos ver cómo se ejecutan
todas esas instrucciones que 'pertenecen' a INT 21.Lo que queremos es ver el efecto
de ejecutarla. En este caso pués no usaremos el comando T sino el comando P
(Proceed, seguir). Hagámoslo:
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 5/45
Usad esta numeración:
287 de 357
-P
Hello World
AX=0924BX=0000CX=1234DX=0000SP=0100BP=0000SI=0000DI=0000
DS=15FDES=15ECSS=15FECS=15FCIP=000C NV UP EI PL NZ NA PO NC
15FC:000C B44C
MOV
AH,4C
Hay algo nuevo an la ejecución anterior.¿Qué es esa frase que ha aparecido debajo
del comando y antes del listado típico posterior al comando T o P? Es el resultado
de ejecutar int 21 y evidentemente también de las instrucciones anteriores.
Hemos imprimido un mensaje en la pantalla! Hello World
Sigamos ejecutando paso a paso. Usemos ahora el comando T puesto que ahora no
tenemos ninguna instrucción int.
-T
AX=4C24BX=0000CX=1234DX=0000SP=0100BP=0000SI=0000DI=0000
DS=15FDES=15ECSS=15FECS=15FCIP=000E NV UP EI PL NZ NA PO NC
15FC:000E CD21
INT
21
La instrucción que se va a ejecutar ahora es int 21. Para ejecutarla usamos el
comando P.
-P
Programa ha terminado normalmente
¡Al ejecutar int 21 en esta ocasión se ha producido la terminación del programa!
El sistema operativo nos dice que se han liberado con éxito los recursos asignados
a nuestro programa: "Programa ha terminado normalmente". Ciertamente, la
forma correcta de terminar un programa consiste en usar una llamada del MS-DOS
conocida como "Terminate program" la cual se activa mediante int 21 cargando
previamente el registro AH con valor 4C... ¿En nuestro programa hemos hecho esto
o no?
Si el programa ha terminado quizá sea oportuno aprender el comando que nos
permita salirnos de DEBUG. El comando es Q (Quit, abandonar).
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 6/45
Usad esta numeración:
288 de 357
-Q
Al salir de DEBUG obtenemos de nuevo el prompt típico del DOS. Este prompt
junto con el directorio actual no tienen por que ser iguales al del sistema concreto
en uso ahora mismo.
Iniciemos una nueva sesión de DEBUG que nos permita ejecutar nuestro programa
de principio a fin, de una vez:
C:\temp
Veamos los registros de nuevo:
-R
AX=0000BX=0000CX=00EDDX=0000SP=0100BP=0000SI=0000DI=0000
DS=15ECES=15ECSS=15FECS=15FCIP=0000 NV UP EI PL NZ NA PO NC
15FC:0000 B8FD15
MOV
AX,15FD
Ejecutemos el programa entero, sin detenciones al final de cada instrucción.
Usamos el comando G ('Go', ponerse en marcha)
-G
Hello World
Programa ha terminado normalmente
Como es habitual DEBUG ejecuta nuestro programa, cuyo resultado es imprimir
en pantalla el mensaje Hello World. Al final el MS-DOS nos dice que nuestro
programa terminó con éxito.
Salgamos de DEBUG y volvamos a entrar de nuevo:
-Q
c:\temp> debug
Veamos el listado del programa 'fuente' en lenguaje de ensamblaje, para ello es
necesario ejecutar el comando U:
-U
15FC:0000 B8FD15
15FC:0003 8ED8
15FC:0005 BA0000
MOV
MOV
MOV
AX,15FD
DS,AX
DX,0000
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 7/45
Usad esta numeración:
289 de 357
15FC:0008
15FC:000A
15FC:000C
15FC:000E
-
B409
CD21
B44C
CD21
MOV
INT
MOV
INT
AH,09
21
AH,4C
21
Fijémonos en las dos primeras líneas del programa:
Movemos a AX el valor 15FD (Hexadecimal) y :
El valor en AX subsiguientemente lo llevamos al registro de segmento de datos DS.
Eso quiere decir que de ahora en adelante todas las referencias a datos en memoria
van a tener lugar dentro del segmento albergado en DS, i.e.: 15FD. ¿Sería interesante
ver el contenido de las direcciones de memoria 15FD:0000 y siguientes?...
Hagámoslo:
Usamos el comando D (DUMP, volcar memoria):
-D 15FD:0000
15FD:0000 48
15FD:0010 42
15FD:0020 00
15FD:0030 00
15FD:0040 00
15FD:0050 84
15FD:0060 13
15FD:0070 4D
65
30
01
00
74
00
01
41
6C
32
00
00
00
07
00
49
6C
92
0A
02
83
4D
00
4E
6F
00
50
04
01
65
00
01
20
00
52
4D
02
73
02
02
57
00
41
41
01
73
10
0A
6F-72
00-00
43-54
49-4E
01-00
61-67
00-00
50-52
6C
00
31
01
7F
65
00
41
64
00
2E
0A
0F
05
10
43
07
10
6F
00
05
11
00
54
0D
00
62
75
00
00
00
31
0A
00
6A
80
00
00
00
2E
24
00
00
83
01
00
00
61
4E
00
00
00
00
00
04
73
Hello World...$N
B02.............
....hello.obj..
.....MAIN...u...
.t..............
...Message......
................
MAIN...hello.as
El formato del listado es sencillo. Tenemos tres bloques de información:
1. Direcciones de memoria en formato SEGMENTO:OFFSET.
2. Contenidos de las 16 celdas que correspondan en cada fila en formato HEX.
3. Esos mismos contenidos pero en formato ASCII.
Fijémonos en el contenido de las direcciones 15FD:0000 hasta 15FD:000E. Vemos
que aparece la cadena de caracteres que imprime nuestro programa: 'Hello
World...' Los datos que maneja nuestro programa se encuentran en una zona de
memoria cuyo valor de segmento se encuentra en un registro selector de segmento
de datos, en este caso DX.
Terminemos la sesión saliendo de DEBUG.
-Q
c:\temp>
Por último ejecutemos nuestro programa directamente desde el DOS:
c:\temp\hello
Hello World
c:\temp\hello
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 8/45
Usad esta numeración:
290 de 357
Todo parece correcto. Nuestro programa imprime el mensaje esperado. ¿No sería
interesante hechar un vistazo al programa fuente en lenguaje de ensamblaje? Este
programa fué ensamblado con MASM versión 6.0. Podéis ver el listado en el
fichero hello.asm; también aparece en la última página de esta práctica.
Comparar detalladamente este listado fuente original con el que nos da DEBUG
cuando ejecutamos el comando U.
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 9/45
Usad esta numeración:
291 de 357
Máquinas Electrónicas/04.
Práctica 11: Significado y uso de los bits aritméticos de E-FLAGS: CF,
ZF, OF y SF.
Introducción.
En esta práctica estudiaremos el significado y cómo se usan los bits aritméticos del
registro FLAGS, para ello, haremos un buen número de pequeños ejemplos en
lenguaje de ensamblaje empleando el programa debug.
Nos valemos de debug para realizar estos ejercicios porque una nos permite
observar los cambios producidos por la ejecución de una instrucción en los bits
flags a medida que vamos ejecutando cada una de ellas. Los cambios en el
programa se pueden realizar justo en el momento en que se necesitan y, además,
sus efectos son inmediatos debido a que debug ensambla las instrucciones justo en
el momento en que se escriben.
El registro E-Flags del uP i386.
La estructura del registro flags es la que aparece en la figura adjunta. Este registro,
no sólo contiene bits de uso con instrucciones aritméticas y lógicas, contiene otros
bits cuyo significado completaremos en las clases de teoría. A pesar de que la
figura indica el número de bit ocupado por cada uno de ellos, no es necesario
recordar de memoria esas posiciones puesto que las instrucciones que afectan a
estos bits son coherentes con las instrucciones que los leen.
1
1
1
8
5
1
7 6
0
+-------------------------------------+
¦A¦V¦R¦0¦N¦IO-¦O¦D¦I¦T¦S¦Z¦0¦A¦0¦P¦1¦C¦
¦C¦M¦F¦ ¦F¦PL ¦F¦F¦F¦F¦F¦F¦ ¦F¦ ¦F¦ ¦F¦
+-------------------------------------+
Flag
Nombre
Si está puesto a 1, significa:
CF
Carry/Borrow
Acarreo/Toma.
PF
AF
ZF
SF
Parity
Aux. Carry
Zero
Sign
Carry or borrow to destination operand
Ha habido carry después de una suma o bien
ha habido toma después de una resta.
Even parity in low-order byte of result
Carry or borrow to low four-order bits
Result equal to 0
Result has negative sign
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 10/45
Usad esta numeración:
292 de 357
TF
IF
DF
OF
IOPL
NF
RF
Trap
Interrupt
Direction
Overflow
I/O
Priv
Nested Task
Resume
VM
Virtual 8086
AC
Alignment
Check
Switches to single-step mode
Enables interrupts (disables if cleared)
String operations process down rather than up
Result too large or small for destination
I/O Privilege Level for IN, INS, OUT, OUTS,
CLI, STI (286+)
Instruction caused nested task switch (286+)
Debug faults disabled during instruction
execution (386+)
Currently executing 8086 code on virtual
processor (386+)
Data aligned to four-byte boundary (486+)
Los programas que vamos a realizar son muy sencillos; a pesar de ello deben
aclararnos el conjunto de circunstancias que hacen que se activen o que se
desactiven ciertos bits de EFLAGS. Esas 'circunstancias' son ni mas ni menos que
instrucciones del procesador que producen determinados resultados o que
simplemente afectan a algún o algunos bits del registro. Así que, nuestra primera
pregunta podría ser: ¿En líneas generales cuáles son las instrucciones que afectan al
registro EFLAGS? Respondamos a esta pregunta con la siguiente tabla:
Ejemplo de instrucción.
MOV
IN
ADD
SUB
AND
NOT
TEST
J condición
CLC
OF
*
*
0,00
0,00
-
ZF
*
*
*
-
SF
*
*
*
-
CF
*
*
0,00
0,00
0,00
Instrucciones que afectan al registro FLAGS.
El registro EFLAGS con cada uno de sus bits constituye la base sobre la cual el
procesador implementa las instrucciones de salto condicional. Estas instrucciones,
para tomar el salto condicional o no, leen alguno o algunos bits del registro flags y,
en función de los valores que aparezcan en ellos, se toma el salto. Es el nemónico
de la instrucción el que nos da la pista acerca de qué bits se van a tomar como base
para decidir si el salto se toma o no. Vamos a comenzar por estudiar el bit de cero
(Zero flag).
BIT DE CERO(zf).
El bit de cero se activa cuando el resultado producido por la última instrucción que
afectó al registro fue cero. El programa que vamos a ensamblar mediante DEBUG
carga un 5 en el registro CX, decrementa este registro y determina si el resultado
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 11/45
Usad esta numeración:
293 de 357
fue cero o no. En caso de que el resultado no fuera cero, salta a la dirección 0003 y
continúa cíclicamente hasta que se produce un cero en un decremento. En ese
momento deja de ejecutarse el bucle y, por tanto, no se rompe el flujo secuencial.
Llamamos entonces a la int 21h, función 01 con parámetro 07 en el registro DL, i.e.
mandamos el carácter ASCII 07 (BEL) al display con lo cual hacemos sonar la
campana 'beep'.
El propósito del programa es observar que el bit CF se activa después de la
ejecución de DEC CX cuando CX contiene un 1: Al llevar a cabo la operación 1-1, se
produce el cero que activará el bit de cero del registro FLAGS. El programa
DEBUG indica la activación del bit CF haciendo aparecer la palabra ZR en el
conjunto de flags.
Procedamos pues a arrancar el programa DEBUG y a ensamblar nuestro programa
en una zona de memoria de programas como la 4000:0000 por ejemplo. En los
ejemplos se ha utilizado otra zona por una razón que veremos mas adelante. Si se
necesita determinar qué zonas de RAM están disponibles para programas de
usuario basta ejecutar el comando MEM /D como en el siguiente ejemplo.
Fijémonos en las direcciones físicas que aparecen con tipo -- LIBRE --. Esas zonas
de memoria son las que podemos utilizar para albergar nuestros programas
cuando utilizamos DEBUG.
C:\> MEM /D | MORE
Direcc.
Nombre
-------------000000
000400
000500
000700
IO
Tamaño
-----000400
000100
000200
Tipo
-----Vector de Interrupción
Area de Comunicaciones ROM
Area de Comunicaciones DOS
000A60
Datos de sistema
Controlador de
Controlador de
Controlador de
Controlador de
Controlador de
Controlador de
Controlador de
Controlador de
Controlador de
Controlador de
Controlador de
Controlador de
CON
AUX
PRN
CLOCK$
A: - C:
COM1
LPT1
LPT2
LPT3
COM2
COM3
COM4
001160
MSDOS
002530
IO
0054A0
0013D0
002F60
SETVER
000190
SETVERXX
HIMEM
000410
XMSXXXX0
EMM386
001090
$MMXXXX0
000820
000100
000200
0001C0
000BD0
MSDOS
000040
Dispositivo
Dispositivo
Dispositivo
Dispositivo
Dispositivo
Dispositivo
Dispositivo
Dispositivo
Dispositivo
Dispositivo
Dispositivo
Dispositivo
del
del
del
del
del
del
del
del
del
del
del
del
Sistema
Sistema
Sistema
Sistema
Sistema
Sistema
Sistema
Sistema
Sistema
Sistema
Sistema
Sistema
Datos de sistema
Datos de sistema
DEVICE=
Controlador de Dispositivo Instalado
DEVICE=
Controlador de Dispositivo Instalado
DEVICE=
Controlador de Dispositivo Instalado
FILES=
FCBS=
BUFFERS=
LASTDRIVE=
STACKS=
Programa de sistema
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 12/45
Usad esta numeración:
294 de 357
0054F0
005F90
005FE0
0061F0
006250
0063A0
006930
006AA0
007000
007170
007C10
007E20
007F90
01F690
09FFF0
COMMAND
win386
COMMAND
COMMAND
WIN
WIN
win386
win386
COMMAND
COMMAND
COMMAND
MEM
MEM
MSDOS
SISTEMA
000A90
000040
000200
000050
000140
000580
000160
000550
000160
000A90
000200
000160
0176F0
080950
028EB0
Programa
Datos
Ambiente
Datos
Ambiente
Programa
Ambiente
Programa
Datos
Programa
Ambiente
Ambiente
Programa
-- Libre -Programa de sistema
0C8EB0
IO
001070
001060
0C9F30
0C9F80
0CE310
0CFB50
0D6A40
0D6BA0
0D83F0
0D85D0
0D9610
ANSI
CON
win386
MOUSE
SHARE
SMARTDRV
win386
KEYB
MODE
DOSKEY
win386
Datos de sistema
DEVICE=
Controlador de Dispositivo Instalado
Datos
Programa
Programa
Programa
Datos
Programa
Programa
Programa
Datos
655360
655360
622672
7471104
0
1048576
C:\>
C:\>
C:\>
C:\>
000040
004380
001830
006EE0
000150
001840
0001D0
001030
0069E0
bytes es el total de memoria convencional
bytes quedan disponibles para MS-DOS
programa ejecutable más largo en tamaño
bytes total contiguos a memoria extendida
bytes disponibles contiguos a memoria extendida
bytes disponibles en memoria XMS
MS-DOS residente en Area de Memoria Alta
CD MEL2003
MKDIR PRACT9
CD PRACT9
DEBUG
-a
0DFE:0000 MOV CX,5
0DFE:0003 DEC CX
0DFE:0004 JNZ 0003
0DFE:0006 MOV AH,7
0DFE:0008 INT 21
0DFE:000A MOV AH,4C
0DFE:000C INT 21
0DFE:000E
-R
AX=0000BX=0000CX=0000DX=0000SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0000 NV UP EI PL NZ NA PO NC
0DFE:0000 B90500
MOV
CX,0005
-R
AX=070DBX=0000CX=0000DX=0000SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0000 NV UP EI PL ZR NA PE NC
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 13/45
Usad esta numeración:
295 de 357
0DFE:0000 B90300
-T
MOV
CX,0003
AX=070DBX=0000CX=0003DX=0000SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0003 NV UP EI PL ZR NA PE NC
0DFE:0003 49
DEC
CX
-T
AX=070DBX=0000CX=0002DX=0000SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0004 NV UP EI PL NZ NA PO NC
0DFE:0004 75FD
JNZ
0003
-T
AX=070DBX=0000CX=0002DX=0000SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0003 NV UP EI PL NZ NA PO NC
0DFE:0003 49
DEC
CX
-T
AX=070DBX=0000CX=0001DX=0000SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0004 NV UP EI PL NZ NA PO NC
0DFE:0004 75FD
JNZ
0003
-R
AX=070DBX=0000CX=0001DX=0000SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0004 NV UP EI PL NZ NA PO NC
0DFE:0004 75FD
JNZ
0003
-T
AX=070DBX=0000CX=0001DX=0000SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0003 NV UP EI PL NZ NA PO NC
0DFE:0003 49
DEC
CX
Fijémonos cómo el bit de cero (ZF) está en cero: NZ y también en el valor de CX el
cual es 0001. Al ejecutar la instrucción DEC CX se producirá el decremento de CX a
valor 0 y esto se reflejará en los bits del registro EFLAGS que correspondan.
Hagámoslo:
-T
AX=070DBX=0000CX=0000DX=0000SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0004 NV UP EI PL ZR NA PE NC
0DFE:0004 75FD
JNZ
0003
Ciertamente vemos la palabra ZR en los identificadores de bits, por tanto se ha
producido un cero. El interés del resto del programa es anecdótico por lo que no lo
hacemos aparecer en este punto de la explicación.
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 14/45
Usad esta numeración:
296 de 357
Realizar un programa que al igual que el anterior active el bit ZF pero en vez de
utilizar una instrucción DEC que utilice una instrucción INC.
BIT DE SIGNO (SF).
Recordemos que el bit de signo representa al signo del resultado producido por la
última instrucción que afectó al registro EFLAGS. Realicemos un nuevo programa
que afecte a este flag. El programa carga un 9 en AX y un 8 en DX y, a
continuación, resta DX de AX, i.e.: DX – AX. Esta operación afecta, entre otros, al
bit de signo (SF): SI el resultado es positivo, SF tomará valor 0 y, si el resultado es
negativo, tomará valor 1.
La instrucción NOP no afecta a ninguna de las partes del estado del procesador, su
tarea consiste solamente en producir un retardo, pero, ninguna operación (No
operation). En ocasiones, esta instrucción se emplea para completar una zona de
memoria que contiene un programa y que no puede quedar vacía de forma que,
rellenando con NOPs nos aseguramos de que si el procesador en algún momento
llega a ejecutar esas instrucciones, no afectará para nada al estado algorítmico del
programa en ejecución.
C:\MEL2003\PRACT9> DEBUG
-A 0DFE:0000
0DFE:0000 MOV AX,9
0DFE:0003 MOV DX,8
0DFE:0006 SUB AX,DX
0DFE:0008 NOP
0DFE:0009
-RIP
IP 0000
:0000
Comenzamos a trazar el programa:
-R
AX=0001BX=0000CX=0000DX=0008SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0000 NV UP EI PL NZ NA PO NC
0DFE:0000 B80900
MOV
AX,0009
-T
AX=0009BX=0000CX=0000DX=0008SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0003 NV UP EI PL NZ NA PO NC
0DFE:0003 BA0800
MOV
DX,0008
-T
AX=0009BX=0000CX=0000DX=0008SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0006 NV UP EI PL NZ NA PO NC
0DFE:0006 29D0
SUB
AX,DX
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 15/45
Usad esta numeración:
297 de 357
Tracemos esta instrucción:
-T
AX=0001BX=0000CX=0000DX=0008SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0008 NV UP EI PL NZ NA PO NC
0DFE:0008 90
NOP
El operando de destino de la instrucción SUB anterior, es el registro AX y, su
contenido es 1 como cabía esperar y sabemos que su signo es 0, i.e. signo positivo.
¿Cuál es el valor del bit SF? Es 0 puesto que aparece la palabra PL la cual significa
PLUS o positivo.
Realicemos ahora un cambio en el programa: Cargaremos el registro DX con un
valor mayor que 9, lo cual producirá en la resta un resultado negativo:
-A 0DFE:0003
0DFE:0003 MOV DX,000A
0DFE:0006
-RIP
IP 0008
:0000
-R
AX=0001BX=0000CX=0000DX=0008SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0000 NV UP EI PL NZ NA PO NC
0DFE:0000 B80900
MOV
AX,0009
-T
AX=0009BX=0000CX=0000DX=0008SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0003 NV UP EI PL NZ NA PO NC
0DFE:0003 BA0A00
MOV
DX,000A
-T
AX=0009BX=0000CX=0000DX=000ASP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0006 NV UP EI PL NZ NA PO NC
0DFE:0006 29D0
SUB
AX,DX
Al trazar la instrucción SUB, observaremos que se activa el bit SF. La indicación de
que SF=1 consiste en la aparición de la palabra NG(NEGATIVE) en la fila de
identificadores de flags. Hagámoslo:
-T
AX=FFFFBX=0000CX=0000DX=000ASP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0008 NV UP EI NG NZ AC PE CY
0DFE:0008 90
NOP
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 16/45
Usad esta numeración:
298 de 357
BIT DE CARRY (Cf).
Como hemos visto en la parte 3 del tema 3 -Set de Instrucciones del 386-, el bit
llamado CF (Carry Flag) del registro EFLAGS tiene un significado diferente
dependiendo de que se interprete después de una suma o de una resta.
En el caso de la suma le llamamos CARRY y tiene significado sólo en aritmética
sin signo: El resultado requiere más bits que los disponibles en la precisión actual.
En el caso de la resta le llamamos TOMA y al igual que en el caso anterior, sólo
tiene sentido en aritmética sin signo: El sustraendo es mayor que el minuendo por
lo que el resultado no es representable con el número de bits disponibles en la
precisión actual.
Como vemos, en ambos casos el bit CF significa desbordamiento en aritmética sin
signo. Estudiémoslo, pues, con detalle.
SUMA SIN ACARREO.
Mediante el programa DEBUG ensamblaremos un programa que sumará los
contenidos de los registros AL y DL y no producirá acarreo:
-A 0000
0DFE:0000 MOV AL,02
0DFE:0002 MOV DL,02
0DFE:0004 ADD AL,DL
0DFE:0006 NOP
0DFE:0007
-RIP
IP 0000
:0000
-R
AX=0000BX=0000CX=0000DX=0000SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0000 NV UP EI PL NZ NA PO NC
0DFE:0000 B002
MOV
AL,02
-T
AX=0002BX=0000CX=0000DX=0000SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0002 NV UP EI PL NZ NA PO NC
0DFE:0002 B202
MOV
DL,02
-T
AX=0002BX=0000CX=0000DX=0002SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0004 NV UP EI PL NZ NA PO NC
0DFE:0004 00D0
ADD
AL,DL
-T
AX=0004BX=0000CX=0000DX=0002SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0006 NV UP EI PL NZ NA PO NC
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 17/45
Usad esta numeración:
299 de 357
0DFE:0006 90
NOP
Como vemos la SUMA no ha producido carry, i.e.: tenemos la palabra NC (No
Carry).
En el programa siguiente sí se produce desbordamiento.
SUMA CON ACARREO.
Sumaremos en este caso los números 01 y FF.
-A 0000
0DFE:0000 MOV AL,01
0DFE:0002 MOV DL,FF
0DFE:0004 ADD AL,DL
0DFE:0006 NOP
0DFE:0007
-RIP
IP 0006
:0000
-R
AX=0004BX=0000CX=0000DX=0002SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0000 NV UP EI PL NZ NA PO NC
0DFE:0000 B001
MOV
AL,01
-T
AX=0001BX=0000CX=0000DX=0002SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0002 NV UP EI PL NZ NA PO NC
0DFE:0002 B2FF
MOV
DL,FF
-T
AX=0001BX=0000CX=0000DX=00FFSP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0004 NV UP EI PL NZ NA PO NC
0DFE:0004 00D0
ADD
AL,DL
-T
AX=0000BX=0000CX=0000DX=00FFSP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0006 NV UP EI PL ZR AC PE CY
0DFE:0006 90
NOP
Confirmamos que se ha producido acarreo observando que sí aparece la palabra
CY (CARRY) en la fila de flags.
En base al volcado de registros anterior y el estado de EFLAGS deducir el
resultado de la suma el cual obviamente es:
FFh + 01h = 100h.
RESTA SIN SIGNO con MIN > SUSTR.
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 18/45
Usad esta numeración:
300 de 357
En la resta sin signo al igual que en la suma sin signo el bit que indica
desbordamiento es el bit CF. En este caso al bit CF se le llama TOMA y se activa
cuando MIN <= SUSTR. Realicemos un primer ejemplo de resta sin signo en el que
no hay desbordamiento.
-A 0000
0DFE:0000 MOV AL,0A
0DFE:0002 MOV DL,05
0DFE:0004 SUB AL,DL
0DFE:0006 NOP
0DFE:0007
-RIP
IP 0001
:0000
-R
AX=000ABX=0000CX=0000DX=0005SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0000 NV UP EI PL ZR AC PE NC
0DFE:0000 B00A
MOV
AL,0A
-T
AX=000ABX=0000CX=0000DX=0005SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0002 NV UP EI PL ZR AC PE NC
0DFE:0002 B205
MOV
DL,05
-T
AX=000ABX=0000CX=0000DX=0005SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0004 NV UP EI PL ZR AC PE NC
0DFE:0004 28D0
SUB
AL,DL
Tracemos la instrucción y confirmemos si hay desbordamiento o no:
-T
AX=0005BX=0000CX=0000DX=0005SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0006 NV UP EI PL NZ NA PE NC
0DFE:0006 90
NOP
Ciertamente no ha habido desbordamiento puesto que no tenemos TOMA, i.e.
aparece la palabra NC (No Borrow en este caso por que la operación anterior ha
sido una resta!).
RESTA SIN SIGNO con MIN < SUSTR.
Forcemos en este caso la aparición de TOMA:
-A 0000
0DFE:0000 MOV AL,0A
0DFE:0002 MOV DL,0F
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 19/45
Usad esta numeración:
301 de 357
0DFE:0004 SUB AL,DL
0DFE:0006 NOP
0DFE:0007
-RIP
IP 0006
:0000
-R
AX=0005BX=0000CX=0000DX=0005SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0000 NV UP EI PL NZ NA PE NC
0DFE:0000 B00A
MOV
AL,0A
-T
AX=000ABX=0000CX=0000DX=0005SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0002 NV UP EI PL NZ NA PE NC
0DFE:0002 B20F
MOV
DL,0F
-T
AX=000ABX=0000CX=0000DX=000FSP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0004 NV UP EI PL NZ NA PE NC
0DFE:0004 28D0
SUB
AL,DL
-T
AX=00FBBX=0000CX=0000DX=000FSP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0006 NV UP EI NG NZ AC PO CY
0DFE:0006 90
NOP
Puesto que la última instrucción que afectó a EFLAGS fue una RESTA, el bit CF se
llama TOMA o borrow y en este caso como vemos está activado: Aparece la
palabra CY. ¿Qué significa la presencia de TOMA después de una resta? Que ha
habido desbordamiento, es decir que el resultado no se puede representar con el
número de bits disponibles en la precisión que está en uso.
Con respecto al párrafo anterior ¿Cuál es la precisión en uso? ¿Por qué?
Aritmética CON SIGNO.
En aritmética con signo empleamos los bits llamados SF (Sign Flag) y
OF(Overflow flag). Estos bits nos dan el signo del resultado de la última operación
que afectó a EFLAGS y, si hubo o no desbordamiento en aritmética con signo,
respectivamente. Recordemos que, dada una precisión n en aritmética con signo,
reservamos un bit de esos n para representar al signo, estando el valor
representado en complemento a 2.
En la siguiente tabla damos las reglas que sigue el bit OF para su activación según
lo explicado en clase:
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 20/45
Usad esta numeración:
302 de 357
OPERACION
SUMA
RESTA
OP.
A+B=
Result
AB=Result
OF se activa si:
Signo(A) = Signo(B) <>
SIgno(Result)
Signo(A) <> Signo(B) =
Signo(Result)
Suma sin desbordamiento.
Estudiemos el primer ejemplo. Realicemos un programa que sume los números
125D y -100D. El resultado ha de ser +25D y obviamente no habrá
desbordamiento y el bit de signo será PL. Hagámoslo:
-A 0000
0DFE:0000 MOV AL, 7D
0DFE:0002 MOV DL, 9C
0DFE:0004 ADD DL, AL
0DFE:0006 NOP
0DFE:0007
-RIP
IP 0006
:0000
-R
AX=00FBBX=0000CX=0000DX=000FSP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0000 NV UP EI NG NZ AC PO CY
0DFE:0000 B07D
MOV
AL,7D
-T
AX=007DBX=0000CX=0000DX=000FSP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0002 NV UP EI NG NZ AC PO CY
0DFE:0002 B29C
MOV
DL,9C
-T
AX=007DBX=0000CX=0000DX=009CSP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0004 NV UP EI NG NZ AC PO CY
0DFE:0004 00C2
ADD
DL,AL
-T
AX=007DBX=0000CX=0000DX=0019SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0006 NV UP EI PL NZ AC PO CY
0DFE:0006 90
NOP
El resultado de ADD DL,AL se carga en el operando de destino, en este caso DL y
el valor es 19H = 25D. El signo evidentemente es positivo (PL) y, no ha habido
overflow (NV: No overflow).
SUMA con desbordamiento, A < 0, B < 0.
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 21/45
Usad esta numeración:
303 de 357
Forcemos en este caso un desbordamiento en aritmética con signo sumando dos
números negativos (-100D) y (-110D) los cuales rebosarán la precisión de 7 bits:
-A 0000
0DFE:0000 MOV AL,9C
0DFE:0002 MOV DL,92
0DFE:0004 ADD AL,DL
0DFE:0006 NOP
0DFE:0007
-RIP
IP 0006
:0000
-R
AX=0080BX=0081CX=0000DX=0090SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0000 NV UP EI NG NZ NA PO CY
0DFE:0000 B09C
MOV
AL,9C
-T
AX=009CBX=0081CX=0000DX=0090SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0002 NV UP EI NG NZ NA PO CY
0DFE:0002 B292
MOV
DL,92
-T
AX=009CBX=0081CX=0000DX=0092SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0004 NV UP EI NG NZ NA PO CY
0DFE:0004 00D0
ADD
AL,DL
-T
AX=002EBX=0081CX=0000DX=0092SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0006 OV UP EI PL NZ NA PE CY
0DFE:0006 90
NOP
Ciertamente, el resultado con signo no 'cabe' en los 7 bits y por tanto está activado
el bit de overflow. Observemos también que el signo del resultado es positivo
(Tenemos la palabra PL), ese signo es contrario al de los dos argumentos, los cuales
eran negativos, así que ciertamente el bit de overflow ha de estar activado.
RESTA sin desbordamiento con A < 0 y B < 0.
Realicemos ahora dos ejemplos de la resta con signo, el primero de ellos sin
desbordamiento y el segundo con desbordamiento.
-a 0000
0DFE:0000
0DFE:0002
0DFE:0004
0DFE:0006
B09C
B292
28D0
90
MOV
MOV
SUB
NOP
AL,9C
DL,92
AL,DL
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 22/45
Usad esta numeración:
304 de 357
-r
AX=0000BX=0000CX=0000DX=0000SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0000 NV UP EI PL NZ NA PO NC
0DFE:0000 B09C
MOV
AL,9C
-t
AX=009CBX=0000CX=0000DX=0000SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0002 NV UP EI PL NZ NA PO NC
0DFE:0002 B292
MOV
DL,92
-t
AX=009CBX=0000CX=0000DX=0092SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0004 NV UP EI PL NZ NA PO NC
0DFE:0004 28D0
SUB
AL,DL
-t
AX=000ABX=0000CX=0000DX=0092SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0006 NV UP EI PL NZ NA PE NC
0DFE:0006 90
NOP
Explicar en el caso precedente por qué no hay overflow y por qué el signo del
resultado es positivo.
RESTA con desbordamiento con A > 0, B < 0.
Este caso vamos a estudiarlo como si se tratase de un ejercicio: Lo que hay que
hacer es ejecutar debug y ensamblar el programa siguiente:
0DFE:0000 B064
MOV
AL,64
0DFE:0002 B292
MOV
DL,92
0DFE:0004 28D0
SUB
AL,DL
0DFE:0006 90
NOP
-rip
IP 0006
:0000
-r
AX=000ABX=0000CX=0000DX=0092SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0000 NV UP EI PL NZ NA PE NC
0DFE:0000 B064
MOV
AL,64
-t
AX=0064BX=0000CX=0000DX=0092SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0002 NV UP EI PL NZ NA PE NC
0DFE:0002 B292
MOV
DL,92
-t
AX=0064BX=0000CX=0000DX=0092SP=FFEEBP=0000SI=0000DI=0000
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0004 NV UP EI PL NZ NA PE NC
0DFE:0004 28D0
SUB
AL,DL
-t
AX=00D2BX=0000CX=0000DX=0092SP=FFEEBP=0000SI=0000DI=0000
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 23/45
Usad esta numeración:
305 de 357
DS=0DC7ES=0DC7SS=0DC7CS=0DFEIP=0006 OV UP EI NG NZ NA PE CY
0DFE:0006 90
NOP
Responded a las siguientes preguntas:
•
¿Cual es el signo y la magnitud de los argumentos de la resta anterior?
•
¿Cual es el signo y la magnitud del resultado y en que registro se almacena?
•
¿Qué bits se han activado y cuál es su significado?
•
¿El bit de carry está activado? ¿Si es así por qué?¿Hemos de considerar ese hecho
para algo?
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 24/45
Usad esta numeración:
306 de 357
MÁQUINAS ELECTRÓNICAS
Práctica 12: Instrucciones de salto y el stack en la arquitectura IA.
Introducción.
En esta práctica estudiamos las instrucciones de salto y una introducción al stack
de la arquitectura IA. En comparación con las instrucciones de salto que
estudiamos en el capítulo 3 de P&H, observaremos que la arquitectura IA contiene
una gran variedad de instrucciones de salto condicional, podríamos decir incluso
que resulta sorprendente que su número sea tan grande. Los ejercicios incluidos en
esta práctica nos permitirán comprender el significado y los usos de los diferentes
tipos de instrucciones de salto.
Un objetivo adicional para esta práctica consiste en aprender a usar el entorno de
programación PWB. El PWB es un entorno gráfico (Semigráfico) que incluye las
siguientes herramientas de desarrollo:
-
Editor
Macroensamblador.
Enlazador
Gestor de librerías
Depurador simbólico (Codeview).
Otros comandos y utilidades
Volcado de la tabla de caracteres ASCII por pantalla.
En el capítulo 4 del texto estudiamos que los caracteres se representan de acuerdo
con el código ASCII. Se trata de un código de 8 bits pudiendo, por tanto,
representar un máximo de 256 caracteres. Dentro de estos 256 caracteres tenemos
caracteres de control, caracteres alfanuméricos, signos de puntuación y caracteres
extendidos. Los caracteres extendidos son aquellos cuyo bit de mayor peso es 1, i.e.
aquellos que están por encima de 127D.
Para realizar el programa, haremos uso de la función 02h del sistema MS-DOS. El
concepto de uso de estos servicios del DOS es el siguiente:
1. Cargar en el registro AH el número del servicio que se desea invocar.
2. Cargar en otros registros los parámetros que necesite ese servicio. De
cada servicio debemos conocer qué parámetros se le han de pasar y en
qué registros; esta información la podemos obtener usando la referencia
técnica del sistema. Típicamente, en las prácticas de MEL incluimos las
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 25/45
Usad esta numeración:
307 de 357
interfaces programáticas de las funciones que se van a usar en cada
programa.
3. Llamar a la interrupción software número 21h si el servicio que
deseamos invocar es un servicio del MS-DOS, esto se lleva a cabo con la
instrucción siguiente: int 21h
El siguiente código nos sirve de ejemplo de cómo usar esta función. Si, por
ejemplo, queremos imprimir en pantalla el carácter Y, escribiríamos el siguiente
programa:
mov ah, 02h
mov dl, ‘Y’
int 21h
Para imprimir todos los caracteres ASCII, necesitamos un registro en el que poder
generar cada una de las combinaciones posibles de 8 bits, para cada una de ellas,
invocaremos adecuadamente el servicio 02h. Puesto que la función 02h espera que
le pasemos el carácter a imprimir en el registros DL, lógicamente, elegimos el
registro DL como almacén de la combinación binaria a imprimir.
Ahora tenemos que elegir un registro en el que llevaremos la cuenta de cuántas
veces se ha repetido la acción de imprimir carácter, como sabemos que esta acción
tiene que repetirse 256 veces, cargaremos el registro CX, por ejemplo, con el valor
256 inicialmente y, cada vez que saquemos por pantalla el carácter actual,
decrementaremos CX y, en el momento en que detectemos que se ha producido el
paso por 0, sabremos que es el momento de no repetir el bucle mas veces.
El programa siguiente es la primera solución del problema. Fíjate cómo el
programa incluye palabras reservadas del Macroensamblador que no conocemos.
Estas palabras reservadas normalmente son de dos tipos: Macros y directivas.
Cumplen funciones parecidas a la macro #include, #define y otras que vimos
cuando hicimos los primeros ejercicios de lenguaje C. Vamos a dar una
introducción a cada una de ellas, en este momento:
Palabra reservada
del
Macroensamblador
TITLE
.model
.stack
.code
Significado y uso
Título del programa.
La palabra que sigue es el modelo de
memoria que se va a aplicar al programa.
Crear un segmento de pila con el tamaño
que aparece a continuación
Crear el segmento de código del programa.
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 26/45
Usad esta numeración:
308 de 357
end
Fin del programa. A continuación aparece
el nombre de la etiqueta que representa el
punto de inicio (entrada ) del programa.
;------------------------------------------------------TITLE PRACTICA 10. Saltos y set ASCII del PC. Versión 1.
;------------------------------------------------------.MODEL
SMALL
; Modelo de memoria small
; Crear un segmento de pila de 256 posiciones de memoria
.STACK 100
.CODE
main:
;Función 02: Imprimir un carácter en la pantalla.
mov ah, 2
MOV CX,256
MOV DL,00
;Num. de caracteres a presentar.
;DL contendrá el carácter ASCII NULL.
imprimir:
INT 21H
INC DL
DEC CX
JNZ imprimir
;Imprimir el carácter en pantalla.
;Incrementar el índice del código ASCII.
;Decrementar contador de caracteres.
;Retorno al DOS a través del servicio 4Ch del DOS.
MOV AH, 4Ch
INT 21h
end main
¿Cómo compilamos el programa? Para compilar el programa, vamos a hacer uso
del entorno integrado PWB (Programmer’s WorkBench). Desde una shell tecleas el
nombre del programa:
c:\> cd masm611
c:\masm611> pwb
A continuación, aparecerá una ventana similar a la siguiente:
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 27/45
Usad esta numeración:
309 de 357
La forma de usar los menús es la habitual con este tipo de programas: Para activar
la barra de menús se ha de pulsar la tecla Alt, una vez hecho esto, se puede
cambiar de un menú a otro usando las teclas del cursor. También, una vez activada
la barra de menús, se pueden usar la letra sobresaltada como acelerador del
comando correspondiente. Ahora, ejecutaremos File | New para crear un nuevo
fichero y poder entrar el programa que acabamos de escribir.
El editor incluido en el PWB es un editor basado en menús, parecido al edit del
MS-DOS, pero más potente. Recuerda que:
-
El menú se activa con alt
Para introducir caracteres que se forman con la tecla Alt Gr, es necesario
haber configurado el switch llamado Enablealtgr a valor yes. Esto se
hace en Options | Setting .. Switch list. Si este switch no está puesto
a yes, entonces, al teclear cualquier combinación Alt Gr, recibirás un
mensaje de error. Sin embargo, siempre es posible entrar cualquier
carácter con la combinación Alt y el código ASCII del carácter. Los mas
comunes son los siguientes:
o [ Alt-91
o \ Alt-92
o ] Alt-93
o { Alt-123
o | Alt-124
o } Alt-125
Antes de compilar el programa, es necesario salvarlo en disco con un nombre que
termine en extensión .asm, por tanto, ejecutamos alt-f-a que significa salvar el
fichero actual con un nombre que tecleará el usuario, aparecerá la siguiente
ventana, basta, pues con que escribas el nombre ascii.asm:
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 28/45
Usad esta numeración:
310 de 357
Ahora, compilaremos el programa, siguiendo los siguientes pasos:
Paso 1. Fijar el conjunto de opciones de trabajo para la sesión completa actual:
alt-o-d. Usaremos pues las opciones debug.
Paso 2. Fijar el tipo de fichero de programa que queremos formar: Un fichero
exe o un fichero com. Los ficheros exe contienen código reubicable, los ficheros
com contienen código pseudo-absoluto y presentan algunas limitaciones , así que,
en esta práctica formaremos un fichero con extensión exe: Código reubicable
cargable dinámicamente. alt-o-t-s:
F1 Tecla de ayuda
Esc Cancelar esta operación
Tab Saltar al siguiente campo
Paso 3. alt-p-r. Reconstruir todos los módulos de nuestro proyecto.
Responderemos yes en la ventana de diálogo que aparecerá a continuación:
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 29/45
Usad esta numeración:
311 de 357
Paso 4. Ahora, si el programa no contiene errores, PWB nos dará la posibilidad de
ejecutar nuestro programa, o de depurarlo, usando el diálogo que aparece en la
siguiente ventana. Por el momento, vamos a ejecutarlo pulsando el botón <Run
Program> :
Inmediatamente, se ejecutará el programa que imprimirá el conjunto completo de
caracteres ASCII con extensiones PC-8, el que corresponde a MS-DOS o a una
ventan de shell bajo Windows. El resultado tendrá un aspecto parecido al de la
siguiente ventana:
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 30/45
Usad esta numeración:
312 de 357
Paso 5. El PWB, contiene un depurador simbólico muy potente llamado Codeview.
Este depurador es compatible con el depurador debug que hemos usado en las
prácticas anteriores a esta, pero, es simbólico, esto es, nos va a permitir efectuar la
depuración del ejecutable tomando como base el texto fuente de nuestro programa,
eso significa que podremos ver y modificar los mismo identificadores de variables
que empleamos en el fuente del programa. Además, Codeview, está basado en el
mismo sistema de ventanas semigráficas que el PWB y, por tanto, a medida que
vamos trazando la ejecución de nuestro programa, podemos visualizar los
registros, zonas de memoria, etc.
Vamos a llevar a cabo, ahora, una sesión de depuración de nuestro programa.
Como ya hemos formado el fichero ejecutable, no será necesario volver a compilar,
así que, directamente haremos alt-r-d y, se creará el entorno de ventanas típico
de Codeview:
Ventana
con el
código
fuente
original
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 31/45
Usad esta numeración:
313 de 357
Ventana de
registros
Ventana de
memoria en
formato
BYTE.
Fila de comandos asociados a
teclas de función
Explicación del texto fuente del programa.
El programa usa la función número 2 del conjunto de llamadas del MS-DOS. Todos
los sistemas Windows contienen las mismas llamadas del MS-DOS además de
otras muchas específicas de la interface programática WIN32. Para nosotros, en
este momento, lo importante es que cualquier Windows, en una ventana de shell,
tiene un comportamiento similar al MS-DOS. Evidentemente, hay inmensas
diferencias entre ambos, pero, para el tipo de programas que vamos a realizar en
este momento, podemos considerarlos equivalentes.
Hemos incluido la documentación de la función 2 y la función 4ch, original de
Microsoft en la página siguiente. Intencionalmente, no las hemos traducido, con el
objeto de adquirir un poco de práctica en comprender este tipo de documentación.
Como ya sabemos, las funciones del MS-DOS, todas ellas se invocan mediante la
interrupción software número 21h, por tanto, tendremos que ejecutar int 21h.
Antes de invocar int 21h, tendremos que indicarle qué función queremos llamar,
en este caso, la función es la 02h (Imprimir un carácter en pantalla). En este caso
específico, lógicamente, esta fundón necesita conocer el carácter que deseamos
imprimir, así que, ese carácter se lo pasaremos en el registro que se nos indique en
la documentación. El registro que contiene el carácter a imprimir es el registro DL.
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 32/45
Usad esta numeración:
314 de 357
Comprueba, ahora, que la explicación dada coincide con los requerimientos de la
función 2 y la función 4ch observando la siguiente información:
Interrupt:
21h
Title:
Character Output
Function:
02h
Description:
Outputs a character to the standard output device. Output can be
redirected. However if output is redirected, there is no way to
detect that the disk is full.
Input
Output
AH = 02h
DL = 8-bit data for output
None
Interrupt:
21h
Title:
Terminate Process with Return Code
Function:
4Ch
Description:
Terminates the current process, passing a return code to the
parent process. This is one of several methods that a program can
use to perform a final exit.
If you use the .STARTUP directive, you should also use the .EXIT
directive, which automatically generates code to call this
function.
Input
AH = 4Ch
AL = Return code
Output
None
El programa carga en el registro AH un 2, en preparación para la llamada la
interrupción 21h (int 21h). Como vemos, el primer carácter ASCII que se
imprime es el 00h el cual es un carácter de control llamado NULL y... no saldrá por
pantalla dado que no tiene una representación imprimible. Los siguientes a éste,
sin embargo, sí saldrán impresos en la pantalla. La siguiente tabla ASCII, puede ser
de ayuda:
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 33/45
Usad esta numeración:
315 de 357
La tabla ASCII
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
nul
soh ^A
stx ^B
etx ^C
eot ^D
enq ^E
ack ^F
bel ^G
bs
^H
tab
^I
lf
^J
vt
^K
np
^L
cr
^M
so
^N
si ¤ ^O
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
de 7 bits- American Standard Code for
Information Interchange:
dle ^P
dc1 ^Q
dc2 ^R
dc3 ^S
dc4 ¶ ^T
nak § ^U
syn ^V
etb ^W
can ^X
em
^Y
eof ^Z
esc ^[
fs
^\
gs
^]
rs
^^
us
^_
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
!
"
#
$
%
&
'
(
)
*
+
,
.
/
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
0
1
2
3
4
5
6
7
8
9
:
;
<
=
>
?
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
@
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
P
Q
R
S
T
U
V
W
X
Y
Z
[
\
]
^
_
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
`
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
p
q
r
s
t
u
v
w
x
y
z
{
|
}
~
En el registro CX mantenemos la cuenta de caracteres no impresos todavía.
Cargamos, pues, el registro CX con un 256. Fijémonos que este 256 no viene
acompañado de ningún carácter indicador de base. ¿Por qué? La razón es que en el
texto fuente de un programa la base que se asume por defecto para las cantidades
numéricas que no lleven indicador es 10 o decimal. Recordemos que en DEBUG la
base que se supone por defecto es HEX. Con el Macroassmbler (MASM), los
sufijos indicadores de base son los siguientes:
-
Si no se indica nada y la constante está bien formada, se entiende base
10, por ejemplo, la constante 1024 representa 102410.
Si la constante termina en el sufijo B, se entiende base binaria, por
ejemplo: 11110001B.
Si la constante termina en H, es entiende base hexadecimal, por ejemplo:
ff00112bH.
Si la constante termina en O, es entiende base octal, por ejemplo: 0207O.
La etiqueta BUCLE_PANT encabeza un bucle en el cual se llama a la interrupción
21h, función 02h en primer lugar, i.e. imprimimos el carácter que esté en DL en la
pantalla, incrementamos DL y después decrementamos CX puesto que ya nos
queda un carácter menos a imprimir y ¡MUY IMPORTANTE!: en la línea siguiente
usamos una instrucción de salto condicional para decidir si saltamos a
BUCLE_PANT, o bien, terminamos el programa. Si al decrementar CX éste llega a
cero entonces el bit ZF se pone a 1 ¿Qué es ZF?
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 34/45
Usad esta numeración:
316 de 357
JNZ BUCLE_PANT transfiere el control a BUCLE_PANT si el flag ZF está a 0; si está a
1 el control continúa en la siguiente instrucción.
Fijémonos en los FLAGS que se ven afectados por la instrucción DEC y, también ,
cómo la instrucción JNZ examina estos flags para decidir si se produce el salto a
la etiqueta o no.
Al final, cuando CX llega a cero, el programa continúa y se produce entonces la
llamada a la función 4Ch de la int 21h la cual como sabemos produce la salida
ordenada al MS-DOS. Esta función se llama 'Terminate'.
Otra versión del programa de volcado de la tabla de caracteres ASCII.
Para ilustrar el uso de algunas de las instrucciones 'J' de la arquitectura IA
vamos a realizar una segunda versión del programa anterior. Las modificaciones
consisten en utilizar un solo contador: el registro DX va a actuar como índice del
carácter a imprimir y como contador de caracteres que ya han sido impresos. La
comparación, sin embargo, será distinta, en este caso tendremos que comprobar a
ver si DX es igual o menor que 255 para decidir si saltamos o continuamos. ¿Qué
instrucción de salto emplearemos? Como se trata de comparar números enteros no
negativos, usaremos una de las instrucciones de salto condicional sin signo, en
concreto, en este caso en el que queremos comprobar si DX es menor o igual que
255, usaremos jbe, esto es, jump below or equal.
TITLE ascii2.asm:Instrucciones de salto y set ASCII del PC.
.MODEL SMALL
.STACK 100
.CODE
MAIN:
MOV AH,2
MOV DX,0
ASCII:
BUCLE_PANT:
INT
INC
CMP
JBE
;Función 02: Imprimir un carácter
;por pantalla.
;Cargamos DL con un 0, el primer
;carácter ASCII de la tabla.
;NULL (Cero, 0).
21H
DX
DX, 255
BUCLE_PANT
;Imprimir el carácter en pantalla.
;Incrementar índice código ASCII.
;¿Es el último carácter?
;Si es menor o igual a 255 salta.
; Si no, continúa.
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 35/45
Usad esta numeración:
317 de 357
MOV AH, 4Ch
INT 21h
END MAIN
;Retorno al DOS ordenadamente
Ahora, es necesario capturar el programa usando el editor del PWB, compilarlo y
ejecutarlo, para ello, llevaremos a cabo la misma secuencia de pasos que en la
versión anterior de ascii.asm. Al ejecutarlo, saldrá por pantalla la misma tabla lista
de caracteres que en el caso anterior.
C:\temp\masm611\ascii.exe
¤¶§ !"#$%&'()*+,./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^
_`abcdefghijklmnopqrstuvwxyz{|}~ ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢
£¥PƒáíóúñѪº¿_¬½¼¡«
»___¦¦¦¦++¦¦++++++--+-+¦¦++--¦-+---++++++++__¦___ß_¶__µ__________±____÷_°•·_n²_
_
Ciertamente, la salida de ascii2.exe producida por pantalla, es exactamente la
misma que la de su versión anterior.
Para terminar, comentaremos brevemente que la instrucción CMP efectúa una resta
del operando 'destino - fuente' y que, el resultado no se almacena en
destino. ¿Cual es pues el efecto de ejecutar esta instrucción? La utilidad de esta
instrucción consiste solamente en modificar los flags, los bits individuales del
registro de estado del i386ex. Si la diferencia de los dos operandos produce un
cero, i.e. ambos son iguales, entonces se activa el bit ZF (Llamado Zero Flag), etc.
Veamos la documentación original de intel, alternativamente, puedes consultar el
capítulo sobre el set de instrucciones IA de la asignatura MEL.
Title:
Compare Two Operands
Syntax:
CMP dest,src
Flags: O D I T S Z A P C
----------------±
± ± ± ± ±
See also: TEST, Jcond, CMPS, SUB, CMPXCHG
Description:
Compares two operands as a test for a subsequent conditional
jump or set instruction. CMP does this by subtracting the source operand
from the destination operand and setting the flags according to the result.
CMP is the same as the SUB instruction, except that the result is not
stored.
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 36/45
Usad esta numeración:
318 de 357
¿Qué bits de EFLAGS se activan como resultado de ejecutar la instrucción CMP en
el caso en que destino sea menor que fuente? Responderemos a esta pregunta
depurando nuestro programa, del mismo modo que en la práctica anterior, sin
embargo, en esta práctica, puedes utilizar Codeview, es más cómodo y, acepta los
mismos comandos que debug básicamente. Los resultados de depurar ascii2.exe,
los vamos a presentar en el formato debug, sin embargo. Es esencial que, a medida
que vas trazando la ejecución del programa, vayas comprendiendo porqué se
producen los cambios que observarás en los registros.
Comencemos por efectuar una traza de la primera versión, ascii.exe.
C:\temp\ascii.exe > DEBUG ascii.exe
-U
0DFE:0000 B402
MOV
AH,02
0DFE:0002 B90001
MOV
CX,0100
0DFE:0005 B200
MOV
DL,00
0DFE:0007 CD21
INT
21
0DFE:0009 FEC2
INC
DL
0DFE:000B 49
DEC
CX
0DFE:000C 75F9
JNZ
0007
0DFE:000E B44C
MOV
AH,4C
0DFE:0010 CD21
INT
21
Veamos cual es el estado de los registros:
-R
AX=0000BX=0000CX=00C0DX=0000SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=0000 NV UP EI PL NZ NA PO NC
0DFE:0000 B402
MOV
AH,02
Comenzamos a trazar el programa. Tengamos presente que las direcciones
correspondientes al registro CS y al registro IP serán distintas de máquina a
máquina(No coincidirán con las de tu PC, en general):
-T
AX=0200BX=0000CX=00C0DX=0000SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=0002 NV UP EI PL NZ NA PO NC
0DFE:0002 B90001
MOV
CX,0100
-T
AX=0200BX=0000CX=0100DX=0000SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=0005 NV UP EI PL NZ NA PO NC
0DFE:0005 B200
MOV
DL,00
-T
AX=0200BX=0000CX=0100DX=0000SP=0064BP=0000SI=0000DI=0000
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 37/45
Usad esta numeración:
319 de 357
DS=0DEEES=0DEESS=0E00CS=0DFEIP=0007 NV UP EI PL NZ NA PO NC
0DFE:0007 CD21
INT
21
En este caso usamos el comando P de DEBUG (Proceed), recordemos que es
necesario hacerlo así porque, vamos a ejecutar una interrupción software, la nº
21h. Si ejecutamos T (TRACE) entonces trazaremos todas las instrucciones de la
rutina de int 21h con AX=02h y, evidentemente no es ese nuestro proósito ahora.
Lo que queremos es que la int 21h se ejecute de principio a fin y que, se detenga
en la siguiente instrucción a int 21h.
-P
AX=0200BX=0000CX=0100DX=0000SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=0009 NV UP EI PL NZ NA PO NC
0DFE:0009 FEC2
INC
DL
-T
AX=0200BX=0000CX=0100DX=0001SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=000B NV UP EI PL NZ NA PO NC
0DFE:000B 49
DEC
CX
-T
AX=0200BX=0000CX=00FFDX=0001SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=000C NV UP EI PL NZ AC PE NC
0DFE:000C 75F9
JNZ
0007
Se ha ejecutado la instrucción de decrementación, DEC CX y, como resultado
vemos que en el campo correspondiente a este registro en el listado aparece 00FFh
lo cual es correcto si miramos al contenido de CX en el listado anterior, el cual era
0100h. Bien, tenemos en CX un 00FFh. Sigamos trazando:
-T
AX=0200BX=0000CX=00FFDX=0001SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=0007 NV UP EI PL NZ AC PE NC
0DFE:0007 CD21
INT
21
Se ha producido el salto a la dirección 0DFE:0007h !!! ¿Por qué? JNZ 0007
transfiere control a la dirección efectiva 0007 dentro del segmento CS si el flag FZ
está borrado. Veamos cómo estaba el flag antes de que se ejecutase la instrucción;
para ello consultamos el listado anterior, como en el listado de flags aparece el
acrónimo NZ, i.e. 'NO-ZERO', el resultado anterior no fue cero, por lo tanto se
transfiere control a la dirección efectiva 0007h.
Sigamos entonces trazando nuestro programa.
-P
AX=0201BX=0000CX=00FFDX=0001SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=0009 NV UP EI PL NZ AC PE NC
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 38/45
Usad esta numeración:
320 de 357
0DFE:0009 FEC2
INC
DL
-T
AX=0201BX=0000CX=00FFDX=0002SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=000B NV UP EI PL NZ NA PO NC
0DFE:000B 49
DEC
CX
-T
AX=0201BX=0000CX=00FEDX=0002SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=000C NV UP EI PL NZ NA PO NC
0DFE:000C 75F9
JNZ
0007
-T
AX=0201BX=0000CX=00FEDX=0002SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=0007 NV UP EI PL NZ NA PO NC
0DFE:0007 CD21
INT
21
-P
AX=0202BX=0000CX=00FEDX=0002SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=0009 NV UP EI PL NZ NA PO NC
0DFE:0009 FEC2
INC
DL
Ejecutemos el programa hasta el final:
-G
¤¶§ !"#$%&'()*+,./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^
_`abcdefghijklmnopqrstuvwxyz{|}~ ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢
£¥PƒáíóúñѪº¿_¬½¼¡«
»___¦¦¦¦++¦¦++++++--+-+¦¦++--¦-+---++++++++__¦___ß_¶__µ__________±____÷_°•·_n²_
_
Programa ha terminado normalmente
-Q
Ahora, trazaremos de nuevo el mismo programa, pero, esta vez vamos a alterar
manualmente el contenido del registro CX para forzar un cero en el subsiguiente
DEC CX. Comencemos:
C:\ C:\temp\ascii.exe> DEBUG ascii.exe
-U
0DFE:0000 B402
MOV
AH,02
0DFE:0002 B90001
MOV
CX,0100
0DFE:0005 B200
MOV
DL,00
0DFE:0007 CD21
INT
21
0DFE:0009 FEC2
INC
DL
0DFE:000B 49
DEC
CX
0DFE:000C 75F9
JNZ
0007
0DFE:000E B44C
MOV
AH,4C
0DFE:0010 CD21
INT
21
0DFE:0012 4E
DEC
SI
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 39/45
Usad esta numeración:
321 de 357
0DFE:0013 42
INC
DX
0DFE:0014 3032
XOR
[BP+SI],DH
0DFE:0016 7C00
JL
0018
0DFE:0018 0000
ADD
[BX+SI],AL
0DFE:001A 0000
ADD
[BX+SI],AL
0DFE:001C 0000
ADD
[BX+SI],AL
0DFE:001E 1200
ADC
AL,[BX+SI]
-R
AX=0000BX=0000CX=00C0DX=0000SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=0000 NV UP EI PL NZ NA PO NC
0DFE:0000 B402
MOV
AH,02
-T
AX=0200BX=0000CX=00C0DX=0000SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=0002 NV UP EI PL NZ NA PO NC
0DFE:0002 B90001
MOV
CX,0100
-T
AX=0200BX=0000CX=0100DX=0000SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=0005 NV UP EI PL NZ NA PO NC
0DFE:0005 B200
MOV
DL,00
-T
AX=0200BX=0000CX=0100DX=0000SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=0007 NV UP EI PL NZ NA PO NC
0DFE:0007 CD21
INT
21
-P
AX=0200BX=0000CX=0100DX=0000SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=0009 NV UP EI PL NZ NA PO NC
0DFE:0009 FEC2
INC
DL
-T
AX=0200BX=0000CX=0100DX=0001SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=000B NV UP EI PL NZ NA PO NC
0DFE:000B 49
DEC
CX
-T
AX=0200BX=0000CX=00FFDX=0001SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=000C NV UP EI PL NZ AC PE NC
0DFE:000C 75F9
JNZ
0007
-T
AX=0200BX=0000CX=00FFDX=0001SP=0064BP=0000SI=0000DI=0000
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 40/45
Usad esta numeración:
322 de 357
DS=0DEEES=0DEESS=0E00CS=0DFEIP=0007 NV UP EI PL NZ AC PE NC
0DFE:0007 CD21
INT
21
-P
AX=0201BX=0000CX=00FFDX=0001SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=0009 NV UP EI PL NZ AC PE NC
0DFE:0009 FEC2
INC
DL
-T
AX=0201BX=0000CX=00FFDX=0002SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=000B NV UP EI PL NZ NA PO NC
0DFE:000B 49
DEC
CX
Antes de que se ejecute DEC CX vamos a alterar el registro CX según lo explicado
anteriormente:
-RCX
CX 00FF
:01
Comprobemos que CX contiene un 0001h:
-R
AX=0201BX=0000CX=0001DX=0002SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=000B NV UP EI PL NZ NA PO NC
0DFE:000B 49
DEC
CX
Sigamos en estas condiciones trazando el programa:
-T
AX=0201BX=0000CX=0000DX=0002SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=000C NV UP EI PL ZR NA PE NC
0DFE:000C 75F9
JNZ
0007
¿Qué ha ocurrido al ejecutar DEC CX esta vez? Se ha producido un cero en CX y por
tanto el flag FZ de FLAGS está a cero como podemos comprobar en el siguiente
listado. Cuando tracemos la instrucción JNZ 0007h no se producirá el salto,
porque en esta ocasión ZF=1 -recordemos que JNZ significa 'saltar si no ha sido
cero'-, así que la ejecución del programa continúa en la línea siguiente. Hagámoslo:
-T
AX=0201BX=0000CX=0000DX=0002SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=000E NV UP EI PL ZR NA PE NC
0DFE:000E B44C
MOV
AH,4C
Ciertamente, estamos en la línea siguiente, no hemos saltado a CS:0007h:
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 41/45
Usad esta numeración:
323 de 357
-T
AX=4C01BX=0000CX=0000DX=0002SP=0064BP=0000SI=0000DI=0000
DS=0DEEES=0DEESS=0E00CS=0DFEIP=0010 NV UP EI PL ZR NA PE NC
0DFE:0010 CD21
INT
21
-P
Programa ha terminado normalmente
-Q
Ejercicio propuesto. Siguiendo una secuencia de pasos similar a la anterior,
estudia el funcionamiento de ascii2.exe y de las instrucciones j que incluye,
usando Codeview.
La pila (stack) en la arquitectura IA y un programa de aplicación.
En el capítulo titulado Fundamentos de Programación de MEL, estudiamos con
detalle el concepto, la implementación y diversos programas de aplicación de la
pila o stack. Como hemos estudiado en clase, la estructura de datos llamada pila o
stack es una estructura lifo. Las instrucciones que manejan esta estructura de datos
son básicamente las instrucciones PUSH y POP, las cuales sirven respectivamente
para introducir y extraer objetos (datos) en la pila. Es necesario recordar que, la
arquitectura del microprocesador MIPS, a diferencia de la arquitectura IA, no
incluye instrucciones especiales para el manejo de la pila.
Las estructuras de datos conocidas como pilas o stacks son esenciales a la hora de
construir los procedimientos o subrutinas. Un procedimiento es una "parte" de un
programa que está dedicada a realizar alguna tarea específica. Cuando resulte
necesario usar ese procedimiento se le invoca (se le llama) usando una instrucción
de llamada a subrutina o llamada a procedimiento (CALL) y, entonces se ejecutan las
acciones especificadas en el mismo y, cuando esas acciones terminan, se produce el
regreso automático al punto del programa principal donde se produjo la
invocación o llamada. Este regreso automático al punto de llamada tiene lugar
gracias a que la instrucción CALL guarda la dirección de retorno en la pila o stack, de
forma que, cuando el procedimiento está a punto de retornar , recupera la
dirección mencionada del stack y, carga los regsitrsos CS e IP, posibilitando así el
retorno a su procedimiento llamador.
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 42/45
Usad esta numeración:
324 de 357
Como hemos mencionado, un stack es una estructura de datos unidimensional. Un
ejemplo familiar es una pila de platos; el último plato se encuentra en lo más alto
de la pila y, es el único que puede ser eliminado "fácilmente" (Directamente).
La pila se asigna a un segmento del programa ¿Cómo se especifican los
segmentos en el texto fuente de un programa?
En el capítulo 3 de P&H, estudiamos que un programa en memoria, está
organizado en varias zonas distintas conocidas como segmentos, típicamente, un
programa se compone de los siguientes segmentos:
‰
Segmento de código
‰
Segmento de stack
‰
Segmento de datos
‰
Otros segmentos que pueden contener código ejecutable adicional al ya
mencionado o espacio adicional para la pila y datos.
Comencemos por aprender cómo crear un segmento de stack: si en un programa
queremos hacer uso de esta estructura de datos tenemos que reservar espacio para
ella en memoria prinicpal. El directivo de MASM que se usa para reservar espacio
en el segmento de STACK es .STACK. Veamos un ejemplo:
.STACK 100H;Reservar 256 bytes para el segmento de pila
El directivo anterior le indica al macroensamblador que reserve 256 bytes de
memoria principal para "ubicar" allí el stack. Cuando ensamblemos nuestro
programa y éste se cargue en memoria, el registro SS (Stack segment
register) contendrá la dirección de memoria del segmento de stack recién
creado y el registro SP (Stack Pointer) se inicializará automáticamente a
valor 100H (256D).
El registro SP, si ha sido correctamente inicializado, contendrá el offset dentro
del segmento de stack, correspondiente a la siguiente posición libre del stack.
Ejercicios propuestos. Realizad ahora, los siguientes ejercicios:
- Entrar en PWB y usar el sistema de ayuda para ver la documentación de las
instrucciones PUSH y POP.
- ¿Cuál es el propósito de las instrucciones PUSHF y POPF?
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 43/45
Usad esta numeración:
325 de 357
- ¿Cual es el propósito de las instrucciones PUSHA y POPA?
- El registro SS contiene 1000H y el registro SP contiene 0600H, ejecutamos la
instrucción PUSH AX. ¿Qué valor tiene SS después de la ejecución de la
instrucción? ¿Qué valor tiene SP?
Una aplicación del stack.
Puesto que el stack se comporta como una estructura last-in/first-out, el orden en
que los artículos salen del stack es justo inverso al orden en que entraron.
Realicemos como ejemplo un programa que imprima en pantalla los caracteres que
ha tecleado el usuario en orden inverso al que se entraron. Haremos uso del stack
para su implementación.
Para realizar el programa, será necesario llevar a cabo la secuencias de pasos
explicada con detalle en el ejercicio ascii.exe.
Ejercicio propuesto. El listado del programa invers.asm está incompleto, las
partes que faltan, están señaladas con puntos suspensivos (…). Completad las
partes que faltan y probad el programa resultante.
TITLE Programa de inversión de user-input invers.asm
.MODEL SMALL
.STACK 100H ; ¿Qué hace este directivo?
.CODE
; Aquí comienza el segmento de código.
.STARTUP
MOV AH,2 ;Presentar un prompt al usuario.
MOV DL,'?'
INT 21H
;Inicializar la cuenta de carácteres -Registro CX:
...
;Leer un carácter desde el teclado.
MOV AH, 1
INT 21H
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 44/45
Usad esta numeración:
326 de 357
;Mientras el carácter leído no séa un CR hacer:
WHILE_:
CMP AL, 0DH
JE END_WHILE
;Salvar carácter en el stack e incrementar la cuenta de
;caracteres entrados:
PUSH AX
INC
; Falta el operando, ponedlo.
; Leer un carácter
INT 21H
JMP WHILE_
END_WHILE:
;Enviar un CR(Carriage return) a la pantalla
; Completad el código:
...
...
;Enviar un LF (Line feed) a la pantalla
;Escribir código:
...
...
JCXZ EXIT ; ¿Cual es el propósito de esta instrucción?
;Presentar caracteres en pantalla en orden inverso:
TOP:
;Sacar un carácter del stack
POP DX
;Presentarlo en pantalla
INT 21H
...
;Fin del bucle.
EXIT:
;Escribid las instrucciones para regresar al MS-DOS:
...
END
Pract 10, 11 y 12. Foces Morán ©2005 --- Pag. 45/45
Usad esta numeración:
327 de 357
•
Si el ciclo de bus actual es de ancho un byte con #bhe
inactiva y #ble activa, lo mismo que en el ciclo de
escritura, el estado de #bs8 es ignorado.
•
Si el ciclo actual es la lectura de una word que presenta
ambos #bhe y #ble activos, el procesador termina el
ciclo actual registrando en el latch superior no
arquitecturado el dato leído desde la ruta d0:8,
seguidamente, ejecuta otro ciclo de bus con #ble
inactiva y registra el dato leido en el latch inferior. Al
final, ambos latches son transferidos directamente al
registro arquitecturado de destino.
Los ciclos de bus sobre dispositivos de ancho 8 bits, necesarios para satisfacer la
lectura o la escritura de una dword transcurren de forma similar, teniendo en
cuenta que los ciclos de 16 bits se descomponen en ciclos de 8 bits seguidos y,
que la única ruta de comunicación con el procesador es la ruta d0:7 del bus de
datos. Propuesta: estudiar con detalle el flujo de operaciones de bus necesarias
para completar la lectura y la escritura de una dword que reside en
2. La dirección de byte 0.
3. La dirección de byte 1.
4. La dirección de byte 2.
Funcionando con el bus de 16 bits ¿Por qué razón el orden temporal que siguen
los ciclos de bus correspondientes a una dword desalineada siempre empieza
con la dirección de word más alta? Pista: Tiene que ver con el instante preciso
en el que el procesador muestrea de #bs8.
BUS i386EX III. ©1999, José M Foces Morán -- 13
Usad esta numeración: Prof. José María Foces Morán © 1994, 1997, 2003
328 de 357
MÁQUINAS ELECTRÓNICAS
Prof. Jose M Foces.
Práctica 14: Tutorial sobre conceptos y posibilidades de las
interrupciones en la arq. IA.
Presentación del ejercicio.
En esta práctica estudiamos dos ejemplos de utilización del sistema de
interrupciones de un ordenador típico basado en un microprocesador con
arquitectura IA en modo real. Nos proponemos manejar las estructuras de
datos y los recursos que ofrecen el microprocesador y el sistema operativo MSDOS para capturar y tratar interrupciones. Los mecanismos que vamos a poner
en marcha son básicos en la construcción de sistemas de monitorización y
control industrial .
Introducción.
Las interrupciones son quizá el recurso más importante de que dispone un
computador para interactuar con su contexto. En las clases teóricas hemos
mencionado que un sistema de tiempo real es aquel que, en respuesta a un
cierto conjunto de eventos simultáneos, garantiza un tiempo de respuesta
máximo a cada uno de ellos. Este tiempo de respuesta máximo en el caso pero
estará garantizado si el número de eventos no supera el límite máximo con el
que se diseñó el sistema. Las interrupciones constituyen el mecanismo
fundamental por el que el sistema computador es notificado que un
determinado evento ha tenido lugar.
¿Qué es una interrupción?
En el capítulo 5 del libro de texto de computadores estudiamos el concepto de
excepción y vimos cómo añadir soporte de excepciones al minimicroprocesador MIPS. Cuando tiene lugar una excepción, la secuencia de
estados de secuenciación de instrucciones se ve alterada para dar servicio a esa
excepción. Cuando la rutina de servicio de la excepción ha completado su
ejecución se restaura el contexto dinámico del procesador para que pueda
continuar con la ejecución del programa que se vió interrumpido, sin
comprometer la integridad del sistema.
Prof. José María Foces Morán © 2003
1
Usad esta numeración: Prof. José María Foces Morán © 1994, 1997, 2003
329 de 357
La familia de microprocesadores Intel 80x86 es capaz de manejar 256 niveles de
interrupciones priorizadas, las cuales pueden ser originadas por tres tipos de
eventos:
•
Interrupciones hardware internas, conocidas como excepciones.
Este tipo de interrupciones se clasifican a su vez en faults, traps y
aborts. En general, diremos que son generadas por eventos que
ocurren en la misma ejecución de un programa, un ejemplo lo
tenemos cuando un programa ejecuta una instrucción de división
cuyo denominador es cero. La asignación de tales eventos a
números de interrupción está cableada dentro del procesador y no
es modificable.
•
Interrupciones hardware externas, conocidas como
interrupciones propiamente. Este tipo de interrupciones son
generadas por dispositivos controladores de periféricos o por
coprocesadores como el 80387. Estas interrupciones pueden
conectarse al pin NMI o al pin INTR del 386. NMI corresponde a
la interrupción no mascarable dle 386 e INTR corresponde a la
interrupción mascarable que, para nosotros, es la que tiene más
importancia como veremos en el desarrollo de esta práctica.
La diferencia entre NMI e INTR, consiste en que podemos impedir
que el procesador "atienda" a una interrupción INTR (interrupción
sí mascarable) simplemente poniendo un cero en el bit IN del
registro flags. Sin embargo, no hay forma de impedir que el
procesador "atienda" a una interrupción que "llega" a éste a través
de la línea NMI. La línea NMI se reserva para interrupciones
correspondientes a eventos "catastróficos", irrecuperables, tales
como errores de paridad de memoria o fallos de la línea de
alimentación.
En vez de cablear los elementos generadores de interrupciones
directamente a las líneas de interrupción del procesador, se
acostumbra a colocar en medio un adaptador conocido como
controlador de interrupciones. Este chip nos permite hacer un uso
mas racional de un recurso "escaso" como la línea INTR. El chip
controlador de interrupciones incluído en el chipset típico de
computadores basados en micros i386 e i486 es el PIC. Los
chipsets para Pentium en cualquiera de sus versiones son
compatibles con el PIC, pero, tienen más potencia. El PIC acepta 8
líneas de interrupciones externas conocidas como IRQ0 a IRQ7 y
podemos programarlo para que atienda selectivamente a ciertas
líneas de interrupción y, quizás, a otras no y, podemos
programarlo también para que priorice esas líneas de
Prof. José María Foces Morán © 2003
2
Usad esta numeración: Prof. José María Foces Morán © 1994, 1997, 2003
330 de 357
interrupción. La priorización consiste en dar servicio a unas
interrupciones por delante de otras cuando tienen lugar
simultáneamente.
Las interrupciones mascarables (INTR), pueden ser globalmente
habilitadas o deshabilitadas mediante el uso de las instrucciones
STI y CLI respectivamente. Estas instrucciones actúan sobre el bit
de FLAGS mencionado anteriormente. Recordemos que este bit no
tiene ninguna influencia sobre las interrupciones que llegan al
procesador a través del pin NMI.
•
Interrupciones software. Un programa puede generar
interrupciones síncronas ejecutando la instrucción de interrupción
software que, en la arquitectura de intel es int. Como hemos
explicado en clase, el efecto derivado de la ejecución de una
instrucción int es exactamente igual al efecto producido por la
recepción de una interrupción hardware externa. Las
interrupciones software constituyen una herramienta potente En
en un PC (Ordenador Personal basado en un chip 80x86) se
subdividen en dos tipos fundamentales:
- Interrupciones del BIOS (Sistema básico de entradas/salidas).
- Interrupciones del MS-DOS.
Los tipos mencionados de interrupciones software constituyen los
puntos de entrada de un gran número de funciones o subrutinas
que efectúan funciones esenciales en diversos aspectos del sistema
computador.
La tabla de vectores de interrupción.
Como hemos visto en clase, los 1024 primeros bytes en el mapa de memoria de
un PC contienen una estructura de datos conocida como tabla de vectores de
interrupción. Cada entrada de esta tabla está formada por cuatro bytes los
cuales constituyen un vector de interrupción, esto es, el punto de entrada de la
rutina de servicio de interrupción correspondiente a ese número de
interrupción.
La respuesta del procesador a una interrupción consiste en reconocer tal
interrupción asertando la línea #inta. Cuando el controlador de interrupciones
comprueba que esta línea esta asertada, transfiere al bus de datos el número de
vector de interrupción que le corresponde y, cuyo cálculo explicamos a
continuación.
EL número de vector de interrupción se obtiene sumando 8 al número de IRQ
que provocó la interrupción. El procesador lee este número desde el bus y, lo
Prof. José María Foces Morán © 2003
3
Usad esta numeración: Prof. José María Foces Morán © 1994, 1997, 2003
331 de 357
usa cómo índice en una tabla de saltos similar a la que estudiamos en el capítulo
5. Esta tabla de saltos es la tabla de vectores de interrupción.
Cada una de las entradas de la tabla de vectores de interrupción contiene la
dirección de memoria inicial donde debe estar instalada la rutina de servicio de
interrupción a la que corresponda. El procesador, lee el segmento y el offset y
salta a la dirección de memoria que representan. El vector se encuentra en una
dirección de memoria cuyo valor se obtiene multiplicando por 4 el número de
vector. Así, el vector 12 se encuentra en la dirección 48, el vector 13 se encuentra
en la dirección 52, y así sucesivamente.
Manejadores de interrupciones.
Un programa que se ejecuta como respuesta a la recepción de una determinada
interrupción se le llama manejador interrupción o rutina de servicio de
interrupción y, utilizaremos el acrónimo inglés ISR. Lo importante para
nosotros, por ahora, es comprender que toda interrupción ha de tener un
programa que la maneje, en caso de que ésta tenga lugar el procesador saltará al
programa manejador. La secuencia de acciones que la ISR lleve a cabo es
enteramente su responsabilidad y, dependerá del tipo de dispositivo que
produjo la interrupción.
En la presente práctica vamos a desarrollar un programa manejador de la
interrupción número 1bh. Esta interrupción se genera por parte del controlador
de teclado cada vez que el usuario pulsa la combinación de teclas CTRL-Inter,
en inglés CTRL-Break.
Calculemos es el vector de interrupción de la interrupción 1bh:
Vector 1bh = 1bh x 4 = 6ch, i.e.:
SEGMENTO = 0000h, OFFSET = 006ch
En esa dirección de memoria se encuentra la dirección física SEG:OFF del punto
de entrada de la rutina de servicio de la interrupción 1bh, es decir, cada vez que
el usuario presiona las teclas Control-Inter el controlador de teclado genera
una interrupción tipo 1bh y, la respuesta del procesador a ese evento consiste
en ejecutar la rutina cuya dirección se encuentra albergada en el vector 1bh, o
sea en las direcciones 0000h:006ch, 006dh, 006eh y 006fh.
Comprobamos que es directo sustituir un vector de interrupción por el punto
de entrada de una rutina escrita por nosotros, basta acceder a su dirección y
escribir allí la dirección de memoria donde hemos ubicado nuestra ISR. Sin
embargo, el MS-DOS nos ofrece la posibilidad de llevar a cabo esta tarea de una
forma, llamémosla limpia: La función 35h del MS-DOS. Hay otra función
complementaria que devuelve el vector de interrupción que le solicitemos: La
función 25H del MS-DOS obtiene un vector de interrupción para que podamos
guardarlo si lo deseamos.
Prof. José María Foces Morán © 2003
4
Usad esta numeración: Prof. José María Foces Morán © 1994, 1997, 2003
332 de 357
Pero ¿Cual es el punto de entrada a nuestro programa manejador (ISR) y cómo
podemos hacer que nuestro programa se quede residente en memoria
permanentemente? Recordemos que, normalmente terminamos los programas a
través de la función 4CH de retorno al MS-DOS. Esta función produce el
retorno al programa que ejecutó a nuestro programa, que normalmente es una
shell, pero, nuestro programa no permanecerá en memoria. ¿Cómo conseguir
que se quede residente en memoria para que pueda ser ejecutado cuando tenga
lugar la interrupción?
El sistema MS-DOS tiene otra función de terminación de un programa además
de la número 4ch. Esta función, cuyo nombre es keep program o Terminate & Stay
Resident corresponde al número 31h. Esta función del MS-DOS permite que
nuestro programa permanezca en memoria después de haber terminado su
ejecución. Cuando invocamos esta función, hemos de pasarle un parámetro:
Cuántos párrafos (bloques alienados de 16 bytes de memoria) ocupa la parte de
nuestro programa que deseamos que permanezca residente.
Nuestro programa manejador de la interrupción 1bh simplemente imprimirá
un mensaje en pantalla. No es necesario que, por el momento, haga nada mas.
Conceptualmente habremos cubierto nuestro objetivo de comprender a nivel
introductorio todos los procesos de configuración necesarios para poner en
marcha un software de tratamiento de interrupciones.
Un manejador de la interrupción 1bh.
El listado del programa explicado anteriormente está listado al final de esta
sección. Para ensamblar este programa, es necesario utilizar la plantilla de
proyecto etiquetada como “DOS COM”, es decir, vamos a producir un fichero
ejecutable del tipo COM.
A pesar de que el programa está suficientemente documentado, resultará
necesario ver la ayuda correspondiente a las funciones 25H, 31H y 35H, para
ello, podéis usar la ayuda del PWB.
Una vez que el programa esté ensamblado, al ejecutar el programa, pulsaremos
simultáneamente las teclas CTRL-Inter y no ocurrirá nada. Después
ejecutaremos PRACT11. A partir de ese momento veremos que al pulsar CTRLInter se activa el manejador (ISR).
Es recomendable efectuar cambios tanto en el procedimiento Install como en el
propio manejador de cara a experimentar con las diferentes posibilidades y
funciones y hacer uso del sistema de ayuda en línea de PWB.
Prof. José María Foces Morán © 2003
5
Usad esta numeración: Prof. José María Foces Morán © 1994, 1997, 2003
333 de 357
;*********************************************************
;PROGRAMA DE ESTUDIO DE INTERRUPCIONES 1:
;Handler for CTRL-BREAK.
;*********************************************************
.MODEL SMALL
.DOSSEG
.STACK 100
.DATA
KEEP_CS WORD ?
KEEP_IP WORD ?
message1 BYTE 13, 10, "Programa manejador de CTRL-Break!", 7, 13, 10
counter BYTE 4
getIntVectf = 35H
setIntVectf = 25H
keepProgf
= 31H
ctrlBreak
= 1BH
wFileDevf
= 40H
m1Length EQU $ - message1
.CODE
.STARTUP
JMP Install ; Al ejecutar el programa desde la shell del DOS
; se produce el salto al programa de instalación:
; Install.
ctrlBreakHandler PROC FAR USES AX BX CX DX DS
;Este es el programa manejador el cual va a quedar
;residente en memoria capturando la interrupción
;1BH correspondiente a CTRL-Inter.
mov
mov
mov
mov
mov
mov
int
bx,
cx,
ax,
ds,
dx,
ah,
21h
1
m1Length
seg Message1
ax
offset Message1
wFileDevf
iret
ctrlBreakHandler ENDP
Prof. José María Foces Morán © 2003
6
Usad esta numeración: Prof. José María Foces Morán © 1994, 1997, 2003
334 de 357
Install PROC USES AX BX CX DX DS
mov al, ctrlBreak ;Pedimos el vector de la interrupción 1BH a
mov ah, getIntVectf ;través de la función 35H
mov KEEP_CS, ES ; Almacenamos el vector (SEGMENTO y OFFSET)
mov KEEP_IP, BX ;en las variables KEEP_CS y KEEP_IP
;respectivamente.
;El segmento del programa manejador
;lo llevamos al registro dx y del
;registro dx al ds.
mov dx, seg ctrlBreakHandler
mov ds, dx
;El offset del programa manejador lo llevamos al
;registro dx.
mov dx, offset ctrlBreakHandler
mov al, ctrlBreak ;En el registro AL cargamos el número del
;vector de interrupción que queremos
;sustituir por nuestro programa manejador.
mov ah, setIntVectf ;En ah cargamos un 25H, Función:
; "Poner vector de interrupción."
int 21h
; El vector 1BH ya contiene el punto de
; entrada delprograma manejador.
; Salgamos del programa de instalación dejando que
; que el manejador permanezca en memoria: usamos
; la función 31H, keep program.
mov dx, OFFSET Install ;DX = bytes que ocupa la porción
;residente.
mov cl, 4
shr dx, cl ; Convertirlo a número de párrafos mas uno.
inc dx
mov ax, 3100h ; Pedimos la función 31H, código de
; error 0.
int
21h
Install ENDP
END
Prof. José María Foces Morán © 2003
7
Usad esta numeración: Prof. José María Foces Morán © 1994, 1997, 2003
335 de 357
Un experimento con la uart/tact National 16550, el controlador de
interrupciones de intel 8237a y el i486.
En el capítulos 8 hemos demostrado que los procesos de i/o controlados por
interrupciones hacen que el sistema sea considerablemente más eficaz que en
aquellos donde se emplea el método de exploración (polling). Vamos a realizar
un ejercicio en el que se pone de manifiesto la relación que existe entre un cierto
número de chips incorporados en el PC, básicamente manejaremos el siguiente
esquema:
1. La UART incluida dentro del chip TACT 16550 es capaz de serializar
datos hacia fuera del PC y de des-serializar datos procedentes de fuera.
Vamos a programar este chip para que cada vez que reciba un nuevo
dato genere una interrupción.
2. La interrupción generada por el chip anterior será capturada por el
controlador de interrupciones (intel PIC 8237A). En el programa incluido
en la práctica también programaremos el PIC, pues éste deberá:
a. Aceptar interrupciones del canal de interrupción IRQ4 y dejar el resto
tal como estuviera programado por el sistema operativo.
b. Volver a su estado normal cuando se haya acabado de dar servicio a
una interrupción suya. Esto se realiza programáticamente dentro de la
rutina de servicio de interrupción (ISR) enviando el comando EOI (End
of interrupt) al final de la anterior.
Todos los procesos de programación de chips de i/o se realiza
accediendo a sus registros, los cuales están mapeados en el espacio de
direcciones de entrada/salida del i486, tal como hemos estudiado en las
clases. Una vez mas, nos referimos al capítulo 8 de P&H: Entradas y
salidas mediante el uso de espacios separados e instrucciones especiales
de i/o. A estas direcciones de i/o las conocemos con el término puerto.
En el programa adjunto se pueden ver algunos de los puertos del PIC y
de la UART/TACT.
1 - Activar las interrupciones mascarables del i486, para lo cual ejecutamos la
instrucción STI (Set Interrupt Flag). Esta instrucción pone a 1 el flag de
interrupción del registro FLAGS. A partir de ese momento, si se produjesen
interrupciones a través de la línea INTR, éstas, serían aceptadas por parte del
procesador. La instrucción CLI (Clear Interrupt Flag) hace justo lo
contrario: pone un cero en el bit IF de FLAGS -si se produjese alguna
interrupción a través de la linea INTR no sería reconocida por el procesador
hast que se ejecutase una instrucción STI.
Prof. José María Foces Morán © 2003
8
Usad esta numeración: Prof. José María Foces Morán © 1994, 1997, 2003
336 de 357
2 – Ahora, el programa debería dejar residente en memoria la parte de sí mismo
encargada de dar servicio a las interrupciones –la ISR. En el listado del
programa, la ISR corresponde a las líneas comprendidas entre las etiquetas de
procedimiento COM1drv e Install. Esta taréa se lleva cabo en las líneas
finales de Install, donde se llama a la función 25H.
3 - Una vez que la ISR se encuentra residente en memoria y por tanto
conocemos su punto de entrada (dirección de memoria) debemos sustituir el
vector de interrupción 8H + 4H = 0CH por esa dirección de memoria, así
cuando se produzca la interrupción correspondiente a IRQ4 la rutina que le
dará servicio será la que acabamos de dejar residente en memoria.
pasos a seguir.
Lo que pretendemos es generar interrupciones INTR. De los chips incluidos en
los PC's uno que es interesante y relativamente fácil de usar es la UART. La
UART número 1 de un PC, en MS-DOS llamada COM1, está conectada al PIC a
través de la línea IRQ4, ésta UART generará una interrupción INTR cada vez
que reciba un nuevo byte en formato serie procedente del exterior, si la
programamos para ello. Conectaremos pues los ordenadores en pares, uno de
los cuales ejecutará nuestro programa y en el otro ejecutaremos un programa
emulador de terminal de MS-Windows. Seguiremos la suguiente secuencia:
1. Conectar los dos computadores mediante el cable serie preparado al
efecto.
2. Arrancar los ordenadores. En uno de los dos ordenadores
efectuaremos las siguientes operaciones:
1- Ejecutar PWB. Abrir el fichero DRIVER18.ASM.
2- Las opciones en este caso son especiales: Eliminar todas las
opciones que hagan referencia a Codeview y, en general cualquier
opción de MASM y de LINK. En este caso se ha de generar un
ejecutable DOS COM, no DOS EXE. En realidad, este ejecutable ha
sido generado ya y existe en el directorio actual, su nombre es
DRIVER18.COM.
3- Salir de PWB.
3. En el otro computador, efectuaremos las siguientes operaciones:
1- Arrancar windows
2- Ejecutar el programa de emulación de terminal que esté
disponible.
4. Al ejecutar driver18.com, aparecerá en pantalla un símbolo de
admiración !. Comprobad que en el otro computador aparece un símbolo
dolar $.
Prof. José María Foces Morán © 2003
9
Usad esta numeración: Prof. José María Foces Morán © 1994, 1997, 2003
337 de 357
En el computador 1, pulsad $, de forma que se envíe un $al ordenador
remoto. Al recibir este $el programa DRIVER18.COM parece terminar, y así es
de hecho. Pero ha terminado de una forma un tanto especial: Parte del
programa se ha quedado residente. Esa parte residente del programa
constituye una ISR, rutina de servicio de interrupciones. Cada vez que en el
emulador de terminal le mandamos un carácter cualquiera, éste nos lo
devuelve, de forma que podemos verlo en pantalla dentro de la ventana del
emulador. Si le enviamos una ? el programa nos retorna un carácter distinto.
Fijarse que todo esto está teniendo lugar incluso cuando el computador 1 puede
estar ejecutando cualquier otro programa.
Como ejemplo de lo anterior, ejecutemos un comando que lleve un tiempo largo
como por ejemplo:
DIR /S C:\
Si mientras se ejecuta el comando anterior, enviamos caracteres
desde el emulador, observaremos cómo el i486 está ejecutando ambas
rutinas, la de servicio de interrupciones y el comando anterior.
Estudiad con detalle la documentación adjunta donde se explican las funciones
del MS-DOS que nos permiten dejar residente parte de un programa (o un
programa entero.
Prof. José María Foces Morán © 2003
10
Usad esta numeración: Prof. José María Foces Morán © 1994, 1997, 2003
338 de 357
Programa driver18.com.
;*********************************************************
;PROGRAMA DE ESTUDIO DE INTERRUPCIONES 1:
;Handler for CTRL-BREAK.
;*********************************************************
.MODEL tiny
.STACK 200H
com1A EQU 03F8H ;Port Address for MS-DOS LDEV COM1.
picA EQU 020H ;Port Address for Peripheral Interrupt Controller.
picAint EQU 021H ;Port Address for PIC indiv. interrupts.
linectrlR EQU 03FBH ;Dirección del registro Line Control Register.
modemctrlR EQU 03FCH ;Dirección del registro Modem Control Register.
inputbufferR EQU 03F8H ; input buffer register.
outputbufferR EQU 03F8H ; output buffer register.
intenableR EQU 03F9H
; interrupt enable register.
intidR EQU 03FAH
;interrupt identification register.
divlatchLSB EQU 03F8H ;divisor latch LSB.
divlatchMSB EQU 03F9H ;divisor latch MSB.
linestatR EQU 03FDH
;line status register.
.CODE
.STARTUP
jmp
Install
COM1drv PROC FAR USES AX BX CX DX DS BP
sti
;Habilitar interrupciones inmediatamente.
push cs
pop ds
;Hacer que el segmento de datos apunte al segmento
;de código (Fichero ejecutable de tipo COM)
mov dx, inputbufferR
in al, dx
cmp al, '?'
je graphic
out dx, al
jmp bye
; Leer el carácter que acaba de llegar.
; Es un carácter '?' ?
;Devolver el carácter al sistema remoto.
graphic: mov al, '@'
mov dx, outputbufferR
out dx, al
Prof. José María Foces Morán © 2003
11
Usad esta numeración: Prof. José María Foces Morán © 1994, 1997, 2003
339 de 357
bye:
iret
mov al, 20h ;Enviar el comando EOI al PIC, así
out picA, al ;podrá aceptar otras interrupciones.
;Abandonamos la rutina de servicio de la
;interrupción 1CH.
COM1drv ENDP
Install PROC USES AX BX CX DX DS
push cs ; Poner DS = CS. Los ficheros ejecutables
pop ds ; tipo COM sólo tienen un segmento.
mov dl, '!' ; Enviar un ! a la pantalla.
mov ah, 2
int 21h
mov dx, linectrlR
mov al, 10000011b ; Poner bit 7 para permitir el
acceso al Divisor
out dx, al ; Latch Register y poder así programar la velocidad.
mov dx, divlatchMSB
mov al, 00 ;MSB del Div. Latch Reg. para 9600 bps.
out dx, al
mov dx, divlatchLSB
mov al, 0Ch ;LSB del Div. Latch Reg. para 9600 bps.
out dx, al
mov dx, linectrlR ; Programar el Line Control Register:
mov al, 00000011B ; 8 bits, sin paridad, 1 bit de stop y
; PERMITIR acceso a los buffers de entrada y de salida
;(inputbuffer y outputbuffer).
out dx, al
mov dx, outputbufferR
mov al, '$'
out dx, al
;Enviar un carácter $ de chequeo al sistema
;remoto.
notdollar: in al, dx ; Esperar
cmp al, '$'
jne notdollar
mov dx, intenableR ;Habilitar
mov al, 1b ;Data Received".
out dx, al
un carácter $ para comenzar.
interrupciones de la UART en COM1
Prof. José María Foces Morán © 2003
12
Usad esta numeración: Prof. José María Foces Morán © 1994, 1997, 2003
340 de 357
mov dx, modemctrlR
in al, dx
or al, 00001000B
out dx, al
; Asertar bit 3 del Modem Control Register:
in al, 21h
and al, 11101111b
out picAint, al
; Habilitar la IRQ4 del PIC.
;
;
;
;
; bit3 = 1 => OUT2* = 0.
LLamar a la función del DOS 25H:
Set Interrupt Vector
Sustituir el vector de la interrupción 1CH
por la dirección de nuestro driver COM1drv
mov ax, 250Ch
; Request function 25h
mov dx, OFFSET COM1drv ; DS:DX points to new COM1 handler
int 21h
; Set vector with address of COM1drv
; Llamar a la función 31H del DOS:
; Keep Program (TSR)
mov dx, OFFSET Install ; DX = bytes in resident section
mov
cl, 4
shr
dx, cl
; Convert to number of paragraphs
inc
dx
; plus one
mov
ax, 3100h ; Request function 31h, error code=0
int
21h
; Terminate-and-Stay-Resident
Install ENDP
END
Prof. José María Foces Morán © 2003
13
Usad esta numeración:
341 de 357
Universidad de León
Dept. IEE
Máquinas Electrónicas.
José M. Foces y José Luis Barreales, 96/97.
Práctica 15. Software para un computador
empotrado escrito en C y ASM.
El proyecto “HELLO” es un programa que muestra cómo la placa de evaluación del
microprocesador i386EX llamada EV386EX puede arrancarse con un programa realizado por el
usuario.
1. Descripción de los archivos que se utilizan en el
proceso:
Para construir el ejemplo disponemos de seis ficheros fuentes (más cuatro de inclusión, dos en
ensamblador y dos en “C”) y de cuatro ficheros más necesarios (como más adelante veremos) para el
proceso de construcción.
Codigo de Arranque en lenguaje de ensamblaje (*.asm):
rStartUp.asm
Código de arranque en modo Real.
Ficheros fuente en “C” (*.c):
Hello.c
Serial.c
Timer.c
ICU.c
rSetIntr.c
Cuerpo principal del programa (función main)
Rutinas de manejo del puerto serie.
Rutinas de manejo de la Unidad de Relojes/Contadores.
Rutinas de manejo de la Unidad de Control de Interrupciones.
Rutina para fijar vectores de interrupción en modo Real.
Usad esta numeración:
342 de 357
MÁQUINAS ELECTRÓNICAS
Práctica 15
Ficheros de Inclusión. Ficheros de inclusión de “C” (*.H), de “ensamblador” (*.INC):
80386EX.H
80386EX.INC
Macros para acceder a registros del 80386EX y mapa de memoria.
Macros para acceder a registros del 80386EX y mapa de memoria.
EV386EX.H
EV386EX.INC
Define símbolos y funciones usados por el ejemplo.
Macro para fijar los ChipSelects.
Además se utilizan los ficheros de inclusión estandar:
STDIO.H
STRING.H
CONIO.H
DOS.H
Programas de construcción del ejemplo:
Build.bat
C600.bat
HELLO.FLT
HELLO.CMD
Para construir el programa de ejemplo usando herramientas para DOS.
Para correr el compilador de Microsoft basado en el DOS.
Fichero de comandos del linker.
Fichero de comandos del localizador CSI pra cargar en la memoria Flash
2. Construcción del programa ejemplo:
Gráfico 2.1
RstartUp.asm
Serial.c
ICU.c
lo.c
Hel
c
Timer.
RsetIntr.c
Nosotros disponemos de los archivos
fuente (descritos anteriormente), pero
hay en total seis (más cuatro archivos de
inclusión) con lo que el proceso de
construcción puede ser una tarea ardua
y resultar bastante confusa si deseamos
hacerlo dentro del entorno del
Programer’s Work Bench.
Para
evitar
complicaciones
innecesarias, se ha diseñado un proceso
automático de construcción del
programa en el que no hay que
Página 2
Curso 96-97
Usad esta numeración:
343 de 357
MÁQUINAS ELECTRÓNICAS
Práctica 15
preocuparse más que de tener todos los archivos situados convenientemente en un directorio, además
de incluir el grupo de programas que en la descripción hemos llamado: “Programas de construcción del
proceso”.
El programa de ejemplo se va a construir usando las siguientes herramientas software:
• Microsoft C Compiler Versión 6.00
• Microsoft Assembler (MASM) Versión 6.11
• CSI-Locate Versión 1.31, programa de la casa CSI que convierte un fichro ejecutable en
código absoluto (no reubicable).
El proceso de construcción se lleva a cabo mediante la línea de comandos del ensamblador, el
compilador y el linker (se ha usado el linker que acompaña al ensamblador, pero igualmente se podría
haber utilizado el que viene con el compilador).
Todo este proceso se realiza mediante el archivo de procesamiento por lotes llamado
BUILD.BAT, que pasaremos a describir brevemente:
En la etiqueta :ASEMBLE del archivo BUILD.BAT comienza el proceso de
construcción del ejecutable final. En este apartado se ensambla la parte del código que viene en
lenguaje de ensamblaje usando la linea de comandos del macroensamblador (ml.exe) con las
siguientes opciones:
/Zi
→
/Cp
→
/Fl
→
/c
<fichero fuente> (*.asm)
→
→
Genera información completa para Codeview en un
fichero de salida (“Output File”).
Hace distinciones entre los id entificadores que se
deletrean igual pero con diferencias de
mayúsculas o minúsculas.
Especifica que va a haber un fichero de salida (en
este caso un listado (*.lst).
Ensambla sin linkar (sólo crea un *.obj).
Nombre del fichero fuente, con la extensión asm.
Después se pasa a compilar los ficheros en C. Para ello se emplea un bucle, ya que son
cinco los archivos que se han de compilar. Este proceso se realiza con el comando de
procesamiento por lotes: for. Para cada uno de los elementos dentro del paréntesis (los cinco
ficheros fuente en C) se ejecuta la acción de llamar al fichero C600.BAT pasándole como
parámetro el propio fichero (para ello se utiliza el %%f).
Pasaremos ahora a explicar el archivo C600.BAT, que es el que realiza el proceso de
compilación propiamente dicho. Es la última línea de este archivo la que realiza el proceso de
compilación utilizando la línea de comandos del compilador (cl.exe) con las siguientes opciones:
Página 3
Curso 96-97
Usad esta numeración:
344 de 357
MÁQUINAS ELECTRÓNICAS
Práctica 15
/Fc → Genera un fichero de salida (*.cod) que es un listado
combinado de fuente y objeto.
/Oi → Genera funciones intrínsecas (hace el programa más
rápido y generalmente más largo).
/Zi → Genera información para Codeview.
/Gs → No genera código para comprobar que las llamadas al
stack se hagan correctamente, como consecuencia
reduce el tamaño del programa y lo hace más rápido.
/batch → Indica que el proceso de compilación se realiza desde un
fichero de procesamiento por lotes.
/c → Compilar sin linkar (genera sólo el *.obj).
/G2 → Genera código para el 80286.
/AL → Modelo de memoria large.
<fichero fuente> (*.c) → Nombre del fichero fuente con extención c.
Después de compilar todos los ficheros *.c, se procede a linkar los *.obj creados con el
ensamblador y el compilador. Para ello también se utiliza la linea de comandos del linker (link.exe)
como viene siendo habitual y para poder hacer todo el proceso con un archivo de procesamiento
por lotes. Las opciones del linker son:
/NOD
→
/MAP
→
/CO
@ hello.flt
→
→
Provoca que el linker ignore las librerías por defecto
nombradas en los ficheros objeto. Si no se pone ninguna
librería como parámetro, se ignoran todas; si se pone
alguna, sólo se ignora esa (se debe hacer una vez para
cada librería si fuera necesario ignorar más de una pero
no todas).
Fuerza al linker a crear un archivo *.map y controla la
información que allí se pone.
Añade información para depuración en el fichero ejecutable
Fichero que contiene opciones para el linkaje.
A continuación explicamos el fichero HELLO.FLT, éste trae más opciones para el linker.
Cada campo de entrada debe estar en una línea aparte o separado por una coma (,), se
pueden extender el campo a más de una línea poniendo un signo más (+) al final de la línea actual.
El punto y coma (;) indica que para los campos restantes se emplea la configuración por defecto.
Deben aparecer en total cinco campos, estos son:
∗ Nombre/s de los ficheros *.obj
∗ Nombre del fichero ejecutable *.exe
∗ Nombre del fichero *.map
Página 4
Curso 96-97
Usad esta numeración:
345 de 357
MÁQUINAS ELECTRÓNICAS
Práctica 15
∗ Librerías a utilizar
∗ Fichero *.def (para crear *.dll o un archivo solapado). No se utiliza en este caso.
BUILD.BAT
Una vez ya se tiene el ejecutable (HELLO.EXE) debido a que en nuestra placa no
tenemos un sistema operativo que coja las direcciones relativas de este fichero y las situe
adecuadamente en memoria, debemos cambiar esas direcciones relativas en direcciones
absolutas. Esto lo hace el localizador (csiloc) con ayuda del fichero que le pasamos como
parámetro: el HELLO.CMD (etiqueta :Locate).
Con ayuda de este fichero, creamos un ejecutable (HELLO.HEX) con direcciones
absolutas que ya podemos bajar a la placa para ejecutarlo en ella. La última línea de el fichero
HELLO.CMD es la que decide cuál es la dirección de comienzo del código que vamos a situar
en la memoria FLASH.
C600.BAT
Hello.c +
Serial.c +
RstartUp.ams
RsetIntr.c +
Timer.c +
ICU.c +
LINKER
(Hello.flt)
Localizador
HELLO.EXE
HELLO.HEX
CSILOC
(Hello.cmd)
Página 5
Curso 96-97
Usad esta numeración:
346 de 357
MÁQUINAS ELECTRÓNICAS
Práctica 15
Gráfico 2.2
Para llevar a cabo todos los procesos de ensamblaje, compilación y linkaje simultaneamente, es
necesario fijar bien el entorno para que no haya ningún problema al ejecutar el archivo de procesamiento
por lotes (cuya ejecución es automática). Lo que haremos es poner los archivos: *.inc, *.asm, *.h,
(sólo los particulares), *.c, *.bat, *.flt y *.cmd en el mismo directorio; en este directorio hemos
copiado además el ml.exe y el dosxnt.exe (necesarios para compilar) y el csiloc.exe (localizador); sólo
nos queda fijar las variables de entorno: PATH, LIB, INCLUDE y HELPFILES del compilador para
que se pueda compilar sin problemas. Una vez lo tenemos todo bien preparado sólo tenemos que
ejecutar el BUILD.BAT y, si todo está bien, nos generará automáticamente el fichero HELLO.HEX
que luego bajaremos a la placa.
3. Desarrollo de la práctica:
Ahora vamos a explicar paso a paso cómo se va a realizar la práctica.
3.1. Equipo necesario:
Estos son los componentes que debes tener para la realización de esta práctica:
• Placa de evaluación Intel386 EX, con 1 MByte de memoria RAM y 512 KBytes de
•
•
•
•
memoria FLASH.
Cable serie para conectar la placa al ordenador.
Una fuente de alimentación para conectar a la placa.
Un diskette con las “Flash Utilities”, o bien tenerlas accesibles para el disco duro.
Un ordenador con características mínimas de:
• Hardware:
∗ Microprocesador 386 o más avanzado, compatible con la arquitectura PC/AT.
∗ Un mínimo de 640 KBytes de RAM.
∗ Un mínimo de 2.5 MBytes de espacio en el disco duro.
∗ Un puerto serie (COM1 o COM2) disponible.
• Software:
∗ MS-DOS 3.0 o versión más nueva.
3.2. Conexión del equipo:
Página 6
Curso 96-97
Usad esta numeración:
347 de 357
MÁQUINAS ELECTRÓNICAS
Práctica 15
La placa requiere una fuente de potencia que provea voltajes de +5V, +12V y -12V. Se debe
clablear adecuadamente según la siguiente figura:
Gráfico 3.1
1
2
3
4
5
6
OPEN
GND
+5V
GND
+ 12 V
- 12 V
El gráfico 3.2 describe
las conexiones que hay que
hacer entre los distintos
equipos para comenzar la
práctica:
Gráfico 3.2
Página 7
Curso 96-97
Usad esta numeración:
348 de 357
MÁQUINAS ELECTRÓNICAS
Práctica 15
FLASH CMD: init com1
SERIAL
CABLE
Power
Connector
PC Host
POWER
SUPPLY
EV386EX
Conecta la placa de evaluación del 386 EX a tu PC y fuente de potencia como sigue:
1. Echa un vistazo a la apariencia física de la placa, fíjate en la localización de los conectores, los
jumpers y los componentes principales (microprocesador, memoria FLASH y memoria
RAM). Compárala con el gráfico que tendrás en la mesa.
2. Comprueba la configuración de los jumpers. En la tabla 3.1 encontrarás la configuración
adecuada de los que nos interesan para el correcto funcionamiento de la placa. El que más nos
interesa es el JP1, ya que es el que decide de dónde arranca el microprocesador después de
un reset o cuando se conecta la alimentación. Si está puesto, el microprocesador arrancará del
bloque protegido de la memoria FLASH en el cual se encuentra el programa target
(386TARG.BIN) con el cual se comunicará el FLASHLDR.EXE (que hacemos correr en
nuestro PC); si está quitado, arrancará sobre el programa que nosotros le hayamos indicado
(una vez se lo hayamos bajado y programado adecuadamente). Los demás no tendremos que
tocarlos y deben estar bien configurados.
Tabla 3.1
Nombre del
Configuración
Función que realiza
jumper
Página 8
Curso 96-97
Usad esta numeración:
349 de 357
MÁQUINAS ELECTRÓNICAS
JP1
JP4
JP8
JP9
JP6
JP10
JP11
JP12
JP13
1y2
5y6
1y2
1y2
1y 2
5y6
7y8
9 y 10
1y2
1y2
No instalado
1y3
2y4
Práctica 15
Arranque desde la flash
Tamaño de memoria de 1 MByte
Tamaño de memoria de 1 MByte
Tamaño de memoria de 1 a 4 MBytes
IRQ14 del bus se conecta a la INT7 del procesador
IRQ7 del bus se conecta a la INT3 del procesador
IRQ6 del bus se conecta a la INT2 del procesador
IRQ5 del bus se conecta a la INT1 del procesador
Pulsador SMI habilitado
Pulsador NMI habilidato
Selecciona el resistor de 0Ω en R12 para proteger el bloque de
arranque de la memoria FLASH
Memoria DRAM seleccionada por CS4#
Memoria SRAM seleccionada por CS2#
3. Apaga la fuente de potencia y el PC (si estuvieran encendidos).
4. Conecta el cable que te proporcionamos (conector hembra) a un canal serie del PC (elige el
que quieras (COM1 o COM2), y recuérdalo)
5. Conecta el otro extremo (conector macho) al conector P2 de la placa de evaluación.
6. Cablea convenientemente la fuente de potencia al conector J3 de la placa (comprueba muy
bien los voltajes, pues de conectarlo mal puedes estropear la placa).
7. Enciende el PC y la fuente de alimentación.
8. Comprueba que el LED CR1 se enciende, esto indica que se están proporcionando + 5 V a la
placa.
Lo que sigue no hay que hacerlo, ya está hecho en el PC nº4 (mel4):
Una vez tienes conectada la placa y el ordenador, hay que instalar en el PC el software necesario
para comunicar ambos (el de la placa ya está instalado). Para ello sigue los siguientes pasos:
1. Inserta el diskette en la unidad A.
2. Escribe en el prompt: A:install.
Ahora efectúa las conexiones de red D: a \\brooks \develoment y E: a \\brooks\est4.
Para comprobar que el ordenador y la placa están bien conectados, vamos a hacer una prueba
con el programa FLASHLDR, encargado de la conexión software con la placa. Para ello sigue los
siguientes pasos:
1. Cambiate al directorio de la unidad E: llamado: E:\INTEL\EV386EX\FLASH. En él debes
encontrar el archivo Flashldr.exe. Puedes hacer un dir para comprobarlo.
2. Dentro de este directorio escribe en la línea de comandos: flashldr.
Página 9
Curso 96-97
Usad esta numeración:
350 de 357
MÁQUINAS ELECTRÓNICAS
Práctica 15
3. Debes de haber entrado en el programa, esto lo sabrás porque ha cambiado el prompt del
sistema, ahora éste debe ser: FLASH CMD:. Este programa tiene una serie de comandos
específicos para comunicarse con la placa y, una vez establecida la conexión, realizar acciones
como ver el contenido de la memoria FLASH, borrar programas en ella, programarla, etc.
4. Para conectarte (asegúrate que tienes el JP1 puesto) debes escribir: init com1 (suponiendo
que hayas conectado el cable serie al COM1 de tu ordenador, si no fuera así escribe en el
lugar de ‘com1’ el puerto del PC al que lo tengas conectado). Si la conexión se realiza sin
problemas no saldrá nigún error en pantalla. Si sucediera algún error: comprueba que has
realizado bien todos los pasos (comprueba el conector de la fuente de potencia, la conexión
de los canales serie, la configuración del JP1...), presiona el botón de reset e intenta otra vez la
conexión software.
5. Una vez estés conectado, puedes por ejemplo ver el contenido del directorio de la memoria
FLASH de la placa escribiendo en tu ordenador el comando dir (a partir del prompt
“FLASH CMD:”).
6. Para comprobar que el micro está ejecutando un programa, vamos a arrancar un emulador de
terminal (programa que recoge todo lo que recibe por el canal serie del PC y lo saca por
pantalla, asímismo lo que tu teclees lo manda por el canal serie del PC) que viene incorporado
con el FLASHLDR, para ello debes teclear term. Comprobarás que el micro está ejecutando
un programa (concretamente el mencionado 386TARG.BIN) que, aparentemente, sólo envía
‘A’s por el canal serie; pero no solo realiza eso, al contrario, la parte más importante del
programa es la que se dedica a comunicarse con el FLASHLDR.EXE que tenemos en
nuestro PC.
7. Ya has comprobado que funciona, ahora debes desconectarte de la placa antes de salir del
programa FLASHLDR. Para ello escribe shutdown. Después sal del programa tecleando exit
o quit.
3.3. Proceso de ensamblaje-compilación-linkaje-localización:
Una vez ya tenemos los equipos conectados y hemos comprobado que todo funciona bien, es
hora de comenzar a construir nuestro programa de ejemplo.
Como ya hemos dicho, tenemos código en lenguage de ensamblaje y código en “C”. Para poder
juntarlo todo vamos a ensamblar y compilar sin linkar, es decir, creando sólo ficheros objeto (*.obj).
Una vez que tengamos los *.obj de todos los ficheros fuente, los linkaremos todos juntos
obteniendo como resultado un ejecutable (HELLO.EXE).
Pero este fichero es ejecutable bajo el entorno de un sistema operativo (MS-DOS en nuestro
caso) el cual mira cual es su extensión, cual es el espacio disponible en memoria y lo localiza en un
hueco adecuado de la memoria, reasignando todas las direcciones de memoria en función de cuál sea la
dirección base.
El problema que se nos plantea es que no tenemos un sistema operativo que realice esas
operaciones, por lo que tenemos quitar las direcciones relativas de memoria del HELLO.EXE y
transformarlas en direcciones absolutas, proceso en el que crearemos el código binario final que se
almacenará en el archivo llamado HELLO.HEX. Esto lo hará un programa llamado localizador, en
Página 10
Curso 96-97
Usad esta numeración:
351 de 357
MÁQUINAS ELECTRÓNICAS
Práctica 15
concreto disponemos del CSILOC 1.31. Este programa al que le pasamos como parámetro el fichero
Hello.cmd, utiliza las opciones de este fichero para crear el fichero *.HEX.Explicaremos brevemente
este archivo:
• Las lineas que comienzan con dos barras (//) son comentarios.
• En la quinta línea se le dice al localizador cuál es el fichero que se ha de procesar.
• En la sexta y séptima se crea el fichero HELLO.ABS que es una versión del HELLO.EXE
pero con direcciones absolutas (físicas), aunque todavía no tiene la dirección base a partir de
la cual se debe colocar el programa.
• A partir de la octava ya se crea el fichero final HELLO.HEX. Se le dice que el programa va a
ser ejecutado por un microprocesador 86 en modo real y que situe los segme ntos de datos,
constantes y stack a partir de la dirección 4000H (memoria RAM) y el segmento de código a
partir de la dirección 80000H (comienzo de la memoria FLASH).
Nota: estas direcciones de memoria están calculadas para que el programa se
ejecute correctamente una vez se haya bajado a la placa, se quite el
JP1 y se haga un reset. No en vano la configuración de memoria que
nuestro programa de ejemplo hace es: 1MB de espacio de memoria
direccionable (en modo real sólo se puede direccionar 1MB); los
primero 512 KB son de memoria RAM (aún sobran 512 KB del Mega
disponible en la placa) y los 512 KB restantes son de memoria FLASH
(toda de la que disponemos).
La estructura de directorio que se ha creado para ordenar todos los archivos necesarios para la
práctica es la siguientes:
Gráfico 3.3
Página 11
Curso 96-97
Usad esta numeración:
352 de 357
MÁQUINAS ELECTRÓNICAS
Práctica 15
C:\
PRACTICA\
ml.exe
dosxnt.exe
Linker.exe
80386ex.inc
CsiLoc.exe
ev386ex.inc
FUENTES\
rstartup.asm
80386ex.h
Hello.c
ev386ex.h
RsetIntr.c
Timer.c
ICU.c
PROCESO\
Build.bat
C600.bat
Hello.flt
Hello.cmd
Para la creación del programa se han de seguir los siguientes pasos:
Los pasos 1 , 2 y 3 no hay que llevarlos a cabo, ya están hechos en el PC nº4 (mel4):
1. Copia los ficheros del directorio D:\PRACT11\FUENTES al C:\PRACTICA.
2. Copia los ficheros del directorio C:\PRACT11\PROCESO al C:\PRACTICA.
3. Ahora se deben fijar las variables de entorno adecuadamente para poder compilar bien. Si no se
ha hecho al arrancar el ordenador (en el AUTOEXEC.BAT), escribe después del prompt del DOS
lo siguiente:
SET PATH=C:\C600\BIN;C:\C600\BINB;%PATH%
SET INCLUDE=C:\C600\INCLUDE
SET LIB=C:\C600\LIB
4. Ya estamos preparados para ejecutar el archivo de procesamiento por lotes (BUILD.BAT),
que nos generará automáticamente el fichero que bajaremos a la placa (HELLO.HEX), tal
como ya hemos explicado. Teclea build.
5. Si el proceso va bien, al final dará dos warnings y ningún error. No nos debe preocupar que de
dos warnings, posiblemente sea alguna opción del compilador.
6. Comprueba que se ha generado el archivo HELLO.HEX (por ejemplo, con un dir). Si es así
podemos continuar, si no fuera así debes repetir el proceso fijándote bien en no equivocarte.
Página 12
Curso 96-97
Usad esta numeración:
353 de 357
MÁQUINAS ELECTRÓNICAS
Práctica 15
3.4. Download (transferencia) del código binario a la memoria FLASH
presente en la placa EV386EX:
Una vez que tenemos el archivo HELLO.HEX, vamos a utilizar el programa FLASHLDR para
cargarlo en la placa. Para ello sigue las instrucciones:
Cambia al directorio de la unidad C: llamado: E:\PRACT11\
Escribe en la línea de comandos: \PRACT11\INTEL\EX386EX\FLASH\flashldr.
El prompt del sistema debe ser: FLASH CMD:
Asegúrate que tienes el JP1 puesto y escribe: init com1 (recuerda que para que esto sea
correcto debes haber conectado el cable serie al COM1 de tu ordenador, si no fuera así
escribe en el lugar de ‘com1’ el puerto del PC al que lo tengas conectado). Si la conexión se
realiza sin problemas no saldrá ningún error en pantalla. Si sucediera algún error: comprueba
que has realizado bien todos los pasos (comprueba el conector de la fuente de potencia, la
conexión de los canales serie, la configuración del JP1...), presiona el botón de reset e intenta
otra vez la conexión software.
5. Haz un dir para comprobar que la memoria FLASH está vacía. Si no fuera así escrible del
<nombre> donde nombre es el del programa que aparezca en el directorio.
6. Puedes comenzar con la tarea de programar la memoria FLASH. Comenzamos poniendo
file=C:\PRACT11\HELLO.HEX (debe estar el fichero en este directorio).
7. Comprueba que la variable name=HELLO y format=HEX. Para ello escribe después del prompt
“FLASH CMD:” la palabra opt (de options), te saldrá toda la lista de las variables de entorno
del programa FLASHLDR. Si el valor de las variables mencionadas fuera distinto: escríbelo tú
a mano.
8. Ahora escribe el comando que propiamente programa la memoria FLASH una vez le hemos
dicho qué fichero queremos bajar a la placa, con qué nombre lo conoceremos y qué formato
tiene. Este comando es: program.
9. Cuando el FLASHLDR haya terminado de programar la memoria FLASH, escribe update
para actualizar las tablas de la memoria FLASH. Si haces un dir comprobaras que ahora
aparece el programa HELLO en el directorio de la memoria FLASH; verás además otros
datos, como dirección (física) de comienzo y final, tamaño, etc.
10.Teclea el comando setboot HELLO para indicar al micro que la próxima vez que arranque
estando el JP1 quitado lo haga desde el programa que hemos bajado a la placa.
11.Teclea shutdown para desconectarte de la placa.
1.
2.
3.
4.
3.5. Comprobación del correcto funcionamiento del programa:
Ahora vamos a comprobar que nuestro programa funciona. Para ello podemos utilizar cualquier
emulador de terminal, que como ya hemos explicado es un programa que recoge todo lo que recibe por
Página 13
Curso 96-97
Usad esta numeración:
354 de 357
MÁQUINAS ELECTRÓNICAS
Práctica 15
el canal serie del PC y lo saca por pantalla y debido a que estamos comunicándonos con la placa
mediante el canal serie es la vía para comprobar el funcionamiento del programa.
Debido a que el FLASHLDR ya incorpora este programa, no debemos complicarnos más y
utilizaremos este. Para arrancar este programa basta con escribir term en la línea de comandos del
FLASHLDR (deberías estar dentro de este último, pero si por cualquier causa has salido, debes entrar
antes de teclear el comando citado).
Si no has tocado la placa, después de arrancar el terminal deberían aparecer en tu pantalla una fila
de ‘A’s indefinidamente.
Ahora es cuando debes quitar del jumper JP1 y rearrancar el sistema (pulsando el boton de
reset). Una vez hayas hecho esto debe aparecer en tu pantalla el mensaje:
***MEL 96/97
Bienvenido, envíame la clave :−)
El programa se quedará esperando hasta que escribas una ‘o’ y una ‘k’, después de lo cual saldrá
lo siguiente por pantalla:
* * Ok * *
:−)
Hello from the EV386EX Rev. 2.1 Evaluation Board
El número de caracteres que has escrito es / caracteres
El programa se queda aquí esperando hasta que pulses retorno, después de lo cual vuelve a salir
el mensaje de saludo seguido del número de caracteres que has presionado en el teclado hasta ese
momento (más exactamente los que le has mandado por el puerto serie: las teclas ALT, CONTROL,
MAYS... no envían nada) sin contar el retorno. Así indefinidamente. Un ejemplo de lo que podría salir
es:
Hello from the EV386EX Rev. 2.1 Evaluation Board
El número de caracteres que has escrito es 24 caracteres
El programa sólo cuenta hasta 999 (números de tres cifras), si el número de caracteres fuera igual
o mayor de mil, el mensaje sería:
Hello from the EV386EX Rev. 2.1 Evaluation Board
El número de caracteres que has escrito es mayor de 1000
Página 14
Curso 96-97
Usad esta numeración:
355 de 357
MÁQUINAS ELECTRÓNICAS
Práctica 15
Es un programa sencillo, en apariencia, pues hay que tener en cuenta de que estamos en el nivel
más bajo de programación, pues no tenemos un sistema operativo que nos arranque el microprocesador
y todos sus periféricos adecuadamente; eso tenemos que hacerlo nosotros antes de intentar hacer
cualquier otro tipo de acción, como puede ser aprovechar las interrupciones del canal serie para contar
el número de caracteres que mandamos por dicho canal.
Para salir del emulador de terminal hay que presionar la tecla ESC.
Recuerda que para salir del FLASHLDR debes teclear exit o quit. Y ya estás en el DOS.
La práctica ha terminado. A continuación tienes dos tablas con los comandos y variables de
entorno del programa FLASHLDR.
Tabla 3.2: Comandos del FLASHLDR
COMANDO
abort
boot <name>
checkhex <file>
delete <name>
dir
dos
echo <text>
exit, quit
help, h, ?
init <port> [baud=x]
map
noboot <name>
options
program <file> [name =x]
[group =x] [version =x ]
reinittbls <board> [system=x ]
setboot <name>
shutdown
DESCRIPCIÓN
Sale del programa sin actualizar las tablas
Permite arrancar un programa inmediatamente (sin actualizar las
tablas de la memoria)
Saca en pantalla las direcciónes del fichero *.HEX
Borra el programa de la FLASH
Muestra el directorio de los ficheros programados
Shell de DOS
Muestra mensajes en ficheros de comando (*.flc). Es similar al
comando echo del DOS en archivos de procesamiento por lotes
Ejecuta un shutdown y sale del programa
Ayuda
Establece conexión entre el host y el target
Enseña el mapa de memoria de la FLASH
Establece que ya no se arranque desde ese programa
Enseña las variables de entorno actuales
Programa el fichero especificado (si no ha sido fijado anteriormente)
en la memoria FLASH usando las variables actuales (nombre,
formato, etc.)
Reinicia las tablas de la memoria flash
Fija el programa del que se arrancará (con el JP1 quitado)
Actualiza la información sobre la FLASH de las tablas de la RAM a
la memoria FLASH y cierra la comunicación entre el target y el
host. Se debe realizar para salvar los cambios.
Página 15
Curso 96-97
Usad esta numeración:
356 de 357
MÁQUINAS ELECTRÓNICAS
term <vtport> [vtbaud=x]
update
Práctica 15
Emulador de terminal (para el puerto “debug” (P2) de la EV386EX)
Actualiza información de las tablas de la memoria RAM a la
memoria FLASH
Tabla 3.2: Comandos del programa FLASHLDR
VARIABLE
baud=
board=
file=
group=
log=
memory=
name=
port=
prompt=
system=
vector=
version=
vtbaud=
vtport
DESCRIPCIÓN
Establece baudios del target (9600 por defecto)
Establece cual es la placa target.
Establece el fichero que se va a programar
Nombre del grupo (a elección del usuario)
Establece nivel lógico (terse/normal/verbose)
Tipo de memoria válida (none/RAM/flash/boot)
Nombre del programa (a elección del usuario)
Establece el puerto al que está conectado el cable serie en tu
ordenador
Fija apariencia del prompt (FLASH CMD:)
Fija el nombre del target (a elección del usuario)
Fija el vector de arranque (para tu programa)
Versión del programa (lo fija el usuario)
Baudios del emulador de terminal
Fija el puerto serie con el que se comunica el emulador de terminal
Página 16
Curso 96-97
Usad esta numeración:
357 de 357
MÁQUINAS ELECTRÓNICAS
Práctica 15
Página 17
Curso 96-97