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