martes, 25 de febrero de 2014

Puertos y memoria en el Lenguaje C


Para intercambiar información con dispositivos exteriores, el computador utiliza los puertos de entrada y salida (E/S). Estos puertos se caracterizan por la dirección que tienen asignada en el mapa de memoria, y es posible la comunicación con los mismos enviando o recogiendo bytes a/en dicha dirección.

Las funciones de biblioteca que proporciona C para la manipulación de estos puertos son fuertemente dependientes del hardware y del sistema operativo y, por tanto, no demasiado estándar. Así, nos centraremos ahora en las funciones de acceso a los puertos hardware que proporciona TurboC bajo entornos DOS.

Al igual que en determinadas circunstancias es preciso comunicarse con dispositivos externos, también es necesario intercambiar información con el propio PC. Ello se realiza con frecuencia leyendo y escribiendo valores en/a determinadas posiciones de la memoria del mismo. Por ejemplo, podemos conocer el estado en el que se encuentra el teclado del PC sin más que acceder al valor almacenado en el segmento 0x40,offset 0x17 de la memoria. Con él podemos discernir si la tecla "bloq mayús" está pulsada o no, si la tecla "Alt" está pulsada o no, etc. Asimismo, podemos recoger información del hardware actualmente instalado en el PC y, entre otras cosas, determinar en qué direcciones de E/S se hallan los puertos hardwaremencionados en el párrafo anterior.

Como antes, las funciones de biblioteca que proporciona C para el acceso a la memoria no siguen un estándar determinado, y nos concentraremos en las que proporciona TurboC bajo entorno DOS.

Acceso a los puertos hardware
  • Lectura del puerto
  • Escritura en el puerto
Lectura del puerto

Para recoger información procedente de un puerto hardware (lectura del puerto), TurboC proporciona las funciones inport e inportb. Son muy similares; la única diferencia radica en que inportb lee un byte cada vez mientras que inport lee una palabra (2 bytes).

Estas funciones se declaran de la siguiente manera:
int inport(int puerto_dir);
nsigned char inportb(int puerto_dir);
u
donde:
puerto_dir es la dirección del puerto hardware de la cual se quiere leer.
y devuelven un dato de tipo int (2 bytes) o de tipo unsigned char (1 byte), respectivamente..

NOTAS:
  • inport trabaja como la instrucción IN del microprocesador 80x86. Lee el byte bajo de la palabra desde puerto_dir y el byte alto desde puerto_dir+2.
  • inportb es una macro que lee un byte.
Estas funciones se hallan definidas en el archivo de cabecera DOS.H. Si se hace una llamada a inportbdesde programa habiendo incluido este archivo, es tratada como macro y expandida como código inline. Si no se incluye DOS.H, se obtiene la función del mismo nombre.
En el siguiente ejemplo vamos a obtener un byte del puerto paralelo (bidireccional) instalado en el PC (en la dirección de E/S 378h):

#include <stdio.h>
#include <dos.h>

main()
{
unsigned char valor;
int puerto = 0x378; /* dirección del puerto paralelo */

valor = inportb(puerto);
printf("Byte leído del puerto %d = 0x%X\n", puerto, valor);
}

Escritura en el puerto

Para enviar información a un puerto hardware (escritura en el puerto), TurboC proporciona las funcionesoutport e outportb. Son muy similares; la única diferencia radica en que outportb envía un byte cada vez mientras que outport envía una palabra (2 bytes).

Estas funciones se declaran de la siguiente manera:
void outport(int puerto_dir, int valor);

void outportb(int puerto_dir, unsigned char valor);
donde:
  • puerto_dir es la dirección del puerto hardware en la cual se quiere escribir;
  • valor es el byte o la palabra que se envía al puerto.
y no retornan ningún valor.

NOTAS:
  • outport trabaja como la instrucción OUT del microprocesador 80x86. Escribe el byte bajo de valoren puerto_dir y el byte alto en puerto_dir+2.
  • outportb es una macro que lee un byte.
Estas funciones se hallan definidas en el archivo de cabecera DOS.H. Si se hace una llamada a outportbdesde programa habiendo incluido este archivo, es tratada como macro y expandida como código inline. Si no se incluye DOS.H, se obtiene la función del mismo nombre.
En el siguiente ejemplo vamos a enviar la letra C al primer puerto paralelo bidireccional instalado en el PC (en la dirección de E/S 378h):

#include <stdio.h>
#include <dos.h>

main()
{
unsigned char valor = 'C' ;
int puerto = 0x378; /* dirección del puerto paralelo */

outportb(puerto, valor);
printf("Valor %c enviado al puerto %d\n", valor, puerto);
}

Acceso a la memoria:
  • Lectura de la memoria
  • Escritura en la memoria
Lectura de la memoria

Para leer la información almacenada en la memoria, TurboC proporciona las funciones peek y peekb. Ambas devuelven el valor almacenado en la posición designada por segmento:offset (direccionamiento segmentado de la memoria). La única diferencia radica en que peekb devuelve un byte, mientras que peek devuelve una palabra (2 bytes).

Estas funciones se declaran de la siguiente manera:

int peek(unsigned segmento, unsigned offset);

char peekb(unsigned segmento, unsigned offset);

y devuelven un dato de tipo int (2 bytes) o de tipo char (1 byte), respectivamente..
NOTA: Estas funciones se hallan definidas en el archivo de cabecera DOS.H. Si se hace una llamada apeek o a peekb desde programa habiendo incluido este archivo, son tratadas como macros y expandidas como código inline. Si no se incluye DOS.H, se obtienen las funciones del mismo nombre.
Como ejemplo, veamos cómo se determina cuántos puertos paralelo se hallan instalados en un PC, y qué direcciones de E/S tienen asignadas. Para ello, es preciso leer en la posición de memoria 40h:8h una tabla de 3 palabras de 16 bits que contienen las direcciones de los puertos paralelo presentes en el sistema:

#include <stdio.h>
#include <dos.h>

main()
{
 unsigned int puerto;  /* número de puerto paralelo: 
     0 (LPT1), 1 (LPT2), 2 (LPT3)*/
 unsigned int puerto_dir; /* dirección del puerto */

 /* Determina los puertos paralelo instalados y sus direcciones E/S */
 for (puerto=0; puerto < 3; puerto++) {
puerto_dir = peek(0x0040,0x0008 + puerto*2);
if (puerto_dir == 0)
   printf("No hay puerto asignado a LPT%d \n", puerto+1);
  else
   printf("La dirección de LPT%d es 0x%X\n", puerto+1, puerto_dir);
 }
}

Escritura en la memoria

Para almacenar un dato en la posición de memoria designada por segmento:offset, TurboC proporciona las funciones poke y pokeb. pokeb almacena un carácter (1 byte) mientras que poke almacena un entero (2 bytes).

Estas funciones no retornan ningún valor y se declaran de la siguiente manera:
void poke(unsigned segmento, unsigned offset, int valor);

void pokeb(unsigned segmento, unsigned offset, char valor);
NOTA: Estas funciones se hallan definidas en el archivo de cabecera DOS.H. Si se hace una llamada apoke o a pokeb desde programa habiendo incluido este archivo, son tratadas como macros y expandidas como código inline. Si no se incluye DOS.H, se obtienen las funciones del mismo nombre.
Como ejemplo, veamos cómo se fuerza por software la activación de "Scroll Lock". Para ello hay que almacenar el valor 16 en la posición 40h:17h, lugar de las variables de la BIOS donde residen los banderines de estado del teclado:

#include <stdio.h>
#include <conio.h>
#include <dos.h>

main()
{
 printf("Asegúrese de que \"Scroll Lock\" está desactivado \
  y pulse cualquier tecla\n");
 getch(); /* toma un carácter de la consola sin hacer eco */

 pokeb(0x0040, 0x0017, 16); /* escribe 16 en 40h:17h */

printf("\"Scroll Lock\" está ahora activo\n");
}

0 comentarios:

Publicar un comentario

Aprende a Programar tus propias aplicaciones