ARQUITECTURA DE SISTEMAS PARALELOS (3º ING. TÉC. INFORMATICA DE SISTEMAS) PRACTICA (CURSO 2004/05) ENTRADA/SALIDA. OBJETIVOS: En esta práctica, se pretenden ilustrar las diferencias entre las distintas técnicas para las operaciones de entrada/salida, así como programar algunos de los dispositivos E/S básicos. Para simplificar, usaremos como dispositivos algunos de los que se encuentran en cualquier PC convencional, en particular teclado, altavoz, etc. La programación se realizará en lenguaje C. Aunque puede usarse ensamblador, resulta mucho más engorroso y eso dificulta centrarnos en los conceptos básicos. En cuanto al sistema operativo usado, usaremos una ventana en modo MS-DOS para simular el hecho de que los programas de usuario accedan tan a bajo nivel al sistema. Tenga en cuenta que el acceso a entrada/salida en Windows o Unix debe hacerse a base de llamadas al sistema operativo y no directamente, como pretendemos nosotros. DESCRIPCION: Como dispositivo de entrada utilizaremos el teclado del PC, mientras que el dispositivo de salida será el altavoz. En efecto, todos los PCs disponen de un pequeño altavoz muy simple (el que suena al arrancar, o cuando se llena el buffer de teclado) conectado como muestra la figura. El propósito de la práctica es que suene un pitido en el altavoz cada vez que pulsemos una tecla del teclado. Se utilizan para controlar el altavoz los dos bits menos significativos del puerto 97 (61h) de E/S (en los PC/XT se trataba del puerto B de un PPI 8255). El bit 0 va a la entrada Gate que activa el contador 2 de un 8253/4, un temporizador que genera una señal periódica de una frecuencia concreta, programable (ver apéndice al final de este boletín). El bit 1 simplemente conecta la señal periódica de salida del temporizador (Out) al altavoz, permitiendo que suene. En las dos primeras partes de la práctica, no alteraremos el estado del temporizador, es decir, mantendremos siempre una frecuencia constante. Por tanto, cuando queramos que suene un pitido en el altavoz, simplemente activaremos durante un tiempo los bits 1 y 0 del puerto 61h. PARTE 1. En esta primera parte, se utilizará para las operaciones de E/S el método de E/S programada o de encuesta (polling). Se trata del método más simple, que consiste en que la CPU controla en todo momento la operación E/S: testeo de bits de status, activación de los bits de control, transferencia de datos, etc. Así, nuestro programa testeará continuamente el teclado, y en cuanto se pulse cualquier tecla sonará el pitido. En principio, la forma de leer caracteres de teclado en C bajo DOS es a través de funciones como kbhit() que detecta si alguna tecla ha sido pulsada- o getch() que devuelve un char con el código ASCII de la tecla pulsada. Así, un posible código sería:
main() { long int i; char a; for(;;) { while(!kbhit()) /* Espera típica en E/S programada. La CPU */ ; /* está ociosa hasta que se pulsa una tecla.*/ getch(); /* permite abortar con Control C */ a=inportb(97); /* lee la dirección E/S 97 */ a=a 0x03; /* activa los bits 0 y 1 */ outportb(97,a); for (i=0;i<1000000;i++) ; /* espera el tiempo suficiente para */ /* que el pitido sea audible. */ a=inportb(97); a=a&0xfd; /* desactiva los bits 0 y 1 */ outportb(97,a); exit(0); PARTE 2. En esta segunda parte se usará el método de E/S por interrupciones. Aquí la CPU también lleva el control, pero queda liberada mientras el dispositivo externo (en este caso el teclado) tarda en responder. En realidad en este tipo de sistemas el teclado siempre se trata de esta forma, por interrupciones. En concreto, en los PCs cada pulsación de una tecla hace que se active la interrupción número 9 de la BIOS. Entonces se produce la respuesta automática ante interrupciones, es decir, salvar el estado en pila, y tomar la dirección de la rutina de servicio (handler) de la tabla de vectores, para posteriormente saltar a dicha rutina. El propósito de esta segunda parte de la práctica es cambiar la rutina original del sistema y sustituirla por una escrita por nosotros. Más correctamente, nuestra rutina no sustituye totalmente a la original. En realidad, sólo activará el altavoz para que suene el pitido, y finalmente saltará a la rutina original para que se procese correctamente la tecla pulsada, y así el sistema pueda seguir funcionando normalmente. A este proceso se denomina Interceptar Interrupciones. Un posible código sería: #include <dos.h> #define INTR 0x09 void interrupt (*oldhandler)(); void interrupt handler(); main() { oldhandler=getvect(intr); setvect(intr, handler); return 0; Nota importante: dentro de la rutina de servicio, no se debe llamar a funciones del DOS. void interrupt handler() { /* Aquí vendría la activación del altavoz durante un tiempo */ oldhandler(); PARTE 3. Finalmente, si el pitido le sabe a poco, podría programarse el temporizador para variar la frecuencia (tono), y así hacer que suene una nota distinta, una melodía o algo parecido. Por ejemplo, una de las octavas sería (en herzios): NOTA DO RE MI FA SOL LA SI Hz 262 294 330 349 392 440 494 La dirección de E/S del registro de control del temporizador es la 43h, y la del contador 2 es la 42h. Debe configurar este contador 2 para que funcione en modo 3 (genera una onda cuadrada en la señal out), e inicializarlo a un valor tal que la frecuencia de la onda de salida sea una de las de la tabla anterior. Tenga en cuenta que la frecuencia de la señal clk es 1.19318MHz. Por ejemplo, para una nota LA sería 1193180/440=2712=0A98h, que habría que escribir en dos partes: primero 98h y después 0Ah. Una ampliación de esto consistiría en hacer que el sonido dependiera de la tecla pulsada. Para ello, podría accederse directamente al controlador de teclado a través de los puertos 60h y 64h. Una lectura del puerto
64h lee el registro de status, del que sólo nos interesa el bit 0 que indica si está a 1 que el buffer de teclado está lleno. En ese caso, el valor de la tecla puede leerse directamente del puerto 60h, obteniendo el scan code que identifica la tecla pulsada (ver figura).
APENDICE: Contador ó temporizador programable (Timer) En numerosas ocasiones, necesitamos un dispositivo capaz de generar una señal de reloj con un periodo concreto, que nos sirva de referencia o base para otros sistemas, como por ejemplo para generación de ondas periódicas (por ejemplo, para generar sonidos), contador de sucesos, retardos, generación de interrupciones periódicas, envío de mensajes por vía serie síncrona, etc. Un ejemplo de estos dispositivos es el timer i8254. A la CPU WR RD A 1 A 0 8 CONTROL Contador 0 Contador 1 Contador 2 Clk Gate Out El i8254 es un dispositivo destinado a la familia 8086, con las siguientes características: - Tiene un bus de datos de 8 bits - Tiene cuatro posiciones internas: Un registro de control y tres contadores independientes entre sí. Se elige la posición mediante las señales de dirección A 1,A 0. - Siempre la cuenta es hacia atrás. - Cada contador lleva tres señales de control: Clk (de entrada): Señal de reloj externa, que no tiene por qué ser el de la CPU. Gate(de entrada): Permite o no la cuenta. Out (de salida): Se activa al llegar al final de la cuenta. El funcionamiento es a grandes rasgos como sigue, aunque varía según el modo de funcionamiento. Los contadores funcionan de forma independiente. Primero se configura el modo de funcionamiento, y a continuación se inicializan los contadores. Nada más activarse la señal Gate, comienza la cuenta atrás de los contadores al ritmo que indique la señal Clk (un avance por cada flanco de bajada). Cuando se llegue al final de la cuenta, se activa la señal Out. El valor de los contadores puede leerse, pero el proceso es un poco complicado y no será necesario realizarlo en esta práctica. El tamaño de los contadores es de 16 bits, por lo que, al ser el bus de datos de 8 bits, para cargar un nuevo valor en ellos hay que hacerlo en dos fases, o bien contentarnos con escribir sólo una de las dos mitades (LSB ó MSB). El significado de los bits del registro de control, que es de 8 bits, es el siguiente: Bit 7, 6 5, 4 3, 2, 1 0 Función Indica el contador al que se refiere el resto de bits: 00 Cont. 0 01 Cont. 1 10 Cont. 2 un posterior acceso a dicho cont. escribirá: 01: sólo el LSB 10: sólo el MSB 11: 1º el LSB y 2º el MSB, en dos accesos consecutivos. Elige el Modo: 0-5 0 binario 1 BCD A continuación describimos brevemente los distintos modos de funcionamiento: Modo 0: Interrupción al final de cuenta. Se utiliza típicamente para bucles de espera, sin el inconveniente de depender de la velocidad del procesador. La cuenta comienza a la primera bajada del reloj que sigue a la inicializalización del contador. Al final de la cuenta Out pasa de cero a uno. Modo 1: one-shot redisparable por hardware. Un "one-shot" es un dispositivo que al recibir un flanco de subida produce un pulso a la salida, cuya duración depende de los valores de una resistencia y un condensador. Se llama redisparable si al recibir un flanco de entrada durante el tiempo en que se está produciendo el pulso de salida, éste se vuelve a iniciar. El Modo1 simula este dispositivo. Gate será el disparador y Out la salida. La duración del pulso será la introducida en el contador. Modo 2: Generador de frecuencias. Cuando transcurren los ciclos indicados, se produce un pulso de Out, de un ciclo de duración y vuelve a empezar el proceso. Así se obtiene una onda periódica mientras Gate se mantenga activa. Modo 3: Generador de onda cuadrada. Si el número cargado en el contador es N, se generará una onda cuadrada de N/2 ciclos arriba y N/2 ciclos abajo. Gate inicia la onda. Modo 4: Strobe disparable por Software. Al cargarse el contador, comienza la cuenta y al final se produce un pulso en Out de un ciclo. Modo 5: Strobe redisparable por Hardware. Como el modo anterior pero controla y se repite el proceso a cada flanco de subida de Gate.
ARQUITECTURA DE SISTEMAS PARALELOS 3º ING. TÉC. INFORMÁTICA DE SISTEMAS PRÁCTICA E/S. NUM GRUPO: ALUMNOS: Responda brevemente a las siguientes preguntas: a) Por qué las rutinas de servicio necesitan en C el adjetivo interrupt? b) Si hubiéramos escrito la rutina de servicio en lenguaje ensamblador, la llamada final a la rutina original hubiera tenido que hacerse de la siguiente forma: pushf; salva en pila el registro de banderas. call oldhandler; iret; Explique brevemente qué significa cada una de esas instrucciones y porqué no puede hacerse con un simple call. c) Para dejar el sistema intacto después de nuestra prueba, podemos usar un contador y cuando se realice un cierto número de pulsaciones volver a grabar en la tabla de vectores la dirección original. Cómo deberá definirse la variable contador? Por otro lado, la int 9 detecta tanto la pulsación como la liberación de la tecla. Use la variable contador para que sólo suene el pitido una vez cada pulsación. d) Escriba la secuencia de código necesaria para generar un sonido de frecuencia 2093 herzios al pulsar la tecla ESCAPE.