General
Los registros del procesador se emplean para controlar instrucciones en ejecución, manejar direccionamiento de memoria y proporcionar capacidad aritmética. Los registros son espacios físicos dentro del microprocesador con capacidad de 4 bits hasta 64 bits dependiendo del microprocesador que se emplee. Los registros son direccionables por medio de una viñeta, que es una dirección de memoria. Los bits, por conveniencia, se numeran de derecha a izquierda (15,14,13…. 3,2,1,0), los registros están divididos en seis grupos los cuales tienen un fin especifico. Los registros se dividen en:
· Registros de segmento
· Registros de apuntadores de instrucciones
· Registros apuntadores
· Registros de propósitos generales
· Registro índice
· Registro de bandera.
TIPOS DE REGISTROS
FUNCION
Registros de Segmento:
Un registro de segmento Un registro de segmento tiene 16 bits de longitud y facilita un área de memoria para el direccionamiento conocida como el segmento actual Registros de Apuntador de Instrucciones Este registro esta compuesto por 16 bits y contiene el desplazamiento de la siguiente instrucción que se va a ejecutar. Los procesadores 80386 y posteriores tienen un IP ampliado de 32 bits llamado EIP.
Registros Apuntadores:
Permiten al sistema accesar datos al segmento de la pila. Los procesadores 80386 tienen un apuntador de pila de 32 bits llamado ESP. El sistema maneja de manera automática estos registros.
Registros de Propósito:
GeneralSon los caballos de batalla del sistema y pueden ser direccionados como una palabra o como una parte de un bytes. Los procesadores 80386 y posteriores permiten el uso de todos los registros de propósitos general mas sus versiones ampliadas de 32 bits llamados EAX, EBX, ECX y EDX.
Registros Indices :
Sirven para el direccionamiento de indexado y para las operaciones de sumas y restas.
Registros de Banderas
Sirven para indicar el estado actual de la maquina y el resultado del procesamiento. De los 16 bits de registro de bandera 9 son comunes a toda la familia de los procesadores 8086.
Un concepto más específico
Es un tipo que almacena varios campos de tipos distintos y que se identifican por su nombre. En C/C++ se llaman struct y en Pascal y Ada record
type T_Sensor
isrecord Magnitud : Natural;
Alarma : Boolean;
end record;
Acceso a los campos
Para acceder a los campos de un registro se usa la clásica notación registro.
componente:
procedure Medir_temperatura is
Sensor_temperatura : T_Sensor;
begin
Sensor_temperatura.Magnitud := 23;
Sensor_temperatura.Alarma := False;
-- ...
if Sensor_temperatura.Alarma then
Put_Line ("Alarma de temperatura");
end if;
end;
Para asignar valores a todos los componentes se puede utilizar un agregado, lo cual es útil para asegurarnos de que no dejamos ninguno sin darle un valor:
procedure Asignar_temperatura is S
ensor_temperatura : T_Sensor;
begin
Sensor_temperatura :=
(Magnitud => 23, Alarma => False);
end;
También es posible dar un valor inicial a algunos o todos los componentes de un registro de modo que todos los objetos de ese tipo se inicialicen automáticamente con esos valores:
type T_Sensor isrecord
Magnitud : Natural := 0;
Alarma : Boolean := False;
end record;
Registro nulo
El registro nulo se puede usar cuando se necesita un tipo que no almacene ningún dato (aunque parezca raro hay casos en los que es útil, como instanciar un genérico que pide un tipo, para el cual no tenemos ningún dato que pasarle). Hay dos maneras de declarar un registro nulo:
type Registro_Nuloi
s record null;
end record;
type Registro_Nulo is null record;
Ambas definiciones son semánticamente idénticas, la segunda es simplemente una sintaxis abreviada.La declaración de una variable o constante de este tipo sería:Nulo :
constant Registro_Nulo := (null record);
Tipos especiales de registros
Registros con discriminante
En un tipo registro discriminado, a algunos de los componentes se los conoce como discriminantes y el resto pueden depender de ellos. Los discriminantes tienen que ser de tipo discreto o puntero. Por ejemplo:type T_Matriz is array (Integer range <>, Integer range <>) of Float;type T_Matriz_Cuadrada (Orden: Positive) is record Matriz: T_Matriz(1..Orden, 1..Orden); end record;
Registros con parte variante
Puede interesar que un registro contenga partes de su estructura que puedan variar en función de otras. Para ello se definen los registros variantes que son un tipo especial de registros discriminados en los que la existencia de algunos campos depende del valor del discriminante.Por ejemplo:
declare type TTipoVuelo is (MILITAR, CIVIL, ENEMIGO, DESCONOCIDO); type TRegistroVuelo (Clasif: TTipoVuelo) is record Velocidad: Float; Detalles: String(1..100); case Clasif is when CIVIL => null; when MILITAR => Origen, Destino: String(1..10); when ENEMIGO DESCONOCIDO => NivelAmenaza: Integer; end case; end record; Vuelo1: TRegistroVuelo (MILITAR);begin Vuelo1.Origen := "Francia ";end;Ilegal sería Vuelo1.NivelAmenaza := 1; pues dicho campo sólo es válido para
DESCONOCIDO o ENEMIGO.
Si el discriminante lleva un valor por defecto entonces el registro es variante durante su ciclo de vida (es mutable), si no, el valor del discriminante se fija en su declaración y no varía durante la vida del objeto.Los registros variantes en cierto modo están sobrepasados por la extensión de los tipos etiquetados, sin embargo aún son útiles en casos simples como aquellos en los que se conocen todas las posibilidades y no se desea que alguien añada una derivación a posteriori.
Registros etiquetados, dan soporte de la programación orientada a objetos en Ada 95. Los tipos etiquetados de Ada 95 son los que permiten realizar el polimorfismo. A menudo se asimila la combinación de un tipo etiquetado y el paquete que lo contiene a una clase en otros lenguajes de programación también orientados a objetos. Sin embargo hay algunas diferencias de sintaxis, que no de filosofía general, que veremos en este apartado.Un tipo etiquetado puede ser un registro de modo que su estructura es pública, o puede ser un tipo privado, aunque en la parte privada siempre se termina definiendo como un registro.
type T_Base is tagged record ... end record;type T_Base is tagged private;Un tipo etiquetado se puede extender. type T_Derivado is new T_Base with record ... end record; type T_Derivado is new T_Base with private;El nuevo tipo hereda todos las operaciones primitivas del tipo base y las puede redefinir.Todo objeto de un tipo etiquetado contiene una
Manejo de archivos
Programación en C/Manejo de archivosAsí como hemos revisado la salida y entrada por pantalla y teclado respectivamente, veremos ahora la entrada y/o salida de datos utilizando ficheros, lo cual será imprescindible para un gran número de aplicaciones que deseemos desarrollar.
El estándar de C contiene funciones varias para la edición de ficheros, estas están definidas en la cabecera stdio.h y por lo general empiezan con la letra f, haciendo referencia a file. Adicionalmente se agrega un tipo FILE, el cual se usará como apuntador a la información del fichero. La secuencia que usaremos para realizar operaciones será la siguiente:
- Crear un apuntador del tipo FILE *
- Abrir el archivo utilizando la función fopen y asignándole el resultado de la llamada a nuestro apuntador.
- Hacer las diversas operaciones (lectura, escritura, etc).
- Cerrar el archivo utilizando la función fclose.
Los prototipos correspondientes de fopen y fclose son:
FILE * fopen (const char *filename, const char *opentype);
int fclose (FILE *stream);Un ejemplo pequeño para abrir y cerrar el archivo llamado fichero.in en modo lectura:
#include
int main ( int argc, chComo vemos, en el ejemplo se utilizó el opentype "r", que es para la lectura, aquí hay una lista de ellos:
"r" : abrir un archivo para lectura
"w" : abrir un archivo para escritura
"a" : abrir un archivo para escritura al final del contenido
"r+" : abrir un archivo para lectura y escritura
"w+" : crear un archivo para lectura y escritura
"a+" : abrir/crear un archivo para lectura y escritura al final del contenido
Adicionalmente hay tipos utilizando "b" (binary) los cuales no serán mostrados por ahora y que solo se usan en los sistemas operativos que no pertenecen a la familia de unix.Un archivo generalmente debe verse como un string (una cadena de caracteres) que esta guardado en el disco duro. Para trabajar con los archivos existen diferentes formas y diferentes funciones. Las funciones que podríamos usar para leer un archivo son:
int fgetc(FILE *archivo) char *fgets(char *buffer, int tamano, FILE *archivo) size_t fread(void *puntero, size_t tamano, size_t cantidad, FILE *archivo);
Las primeras dos de estas funciones son muy parecidas entre si. Pero la tercera, por el numero y el tipo de parámetros, nos podemos dar cuenta de que es muy diferente, por eso la trataremos en una sección aparte junto al fwrite que es su contraparte para escritura.
La función fgetc es la más simple de las tres. Esta función lee un caracter a la vez del archivo que esta siendo señalado con el puntero *archivo. En caso de que la lectura sea exitosa devuelve el caracter leído y en caso de que no lo sea o de encontrar el final del archivo devuelve EOF. Esta función se usa generalmente para recorrer archivos de texto. A manera de ejemplo vamos a suponer que tenemos un archivo de texto llamado "prueba.txt" en el mismo directorio en que se encuentra el fuente de nuestro programa. Un pequeño programa que lea ese archivo será: #include
int main()
{ FILE *archivo;
char caracter;
archivo = fopen("prueba.txt","r");
if (archivo == NULL) exit(1);
printf("\nEl contenido del archivo de prueba es \n\n");
while (feof(archivo) == 0){
caracter = fgetc(archivo);
printf("%c",caracter);
}
return 0;
}
En este ejemplo lo que vemos ya ha sido explicado excepto por la función feof. Esta función sirve para determinar si el cursor dentro del archivo encontró el final (end of file). Existe otra forma de verificar el final del archivo que es comparar el caracter que trae fgetc del archivo con el macro EOF declarado dentro de stdio.h, pero este método no ofrece la misma seguridad (en especial altratar con los archivos "binarios"). La función feof siempre devolverá 0 si no se encuentra al final del archivo.
Otra cosa importante es que el lenguaje C no tiene dentro de si una estructura para el manejo de excepciones o de errores, por eso es necesario comprobar que el archivo fue abierto con éxito "if (archivo == NULL)". Si fopen pudo abrir el archivo con éxito devuelve la referencia al archivo (*FILE), de lo contrario devuelve NULL y en este caso se debera revisar la direccion del archivo o los permisos del mismo. En estos ejemplos solo vamos a dar una salida con un retorno de 1 que sirve para señalar que el programa termino por un error.
La otra función de la que hablábamos fue fgets que sirve para el mismo propósito. Existen algunas diferencias por supuesto y podemos empezar con sus tres parámetros. El primer parámetro buffer lo hemos llamado así porque es un puntero a un espacio de memoria del tipo char (podríamos usar un arreglo de char). El segundo parámetro es tamano que es el limite en cantidad de caracteres a leer para la funcion fgets. Y por ultimo el puntero del archivo por supuesto que es la forma en que fgets sabra a que archivo debe escribir.
#include
int main()
{ FILE *archivo;
char caracteres[100];
archivo = fopen("prueba.txt","r");
if (archivo == NULL) exit(1);
printf("\nEl contenido del archivo de prueba es \n\n");
while (feof(archivo) == 0){ fgets(caracteres,100,archivo);
printf("%s",caracteres);
}
return 0;
}
Este es el mismo ejemplo de antes con la diferencia de que este hace uso de fgets en lugar de fgetc. La función fgets se comporta de la siguiente manera, leerá del archivo apuntado por archivo los caracteres que encuentre y a ponerlos en buffer hasta que lea un caracter menos que la cantidad de caracteres especificada en tamano o hasta que encuentre el final de una linea (\n) o hasta que encuentre el final del archivo (EOF). En este ejemplo no vamos a profundizar más que para decir que caracteres es un buffer, los pormenores seran explicados en la sección de manejo dinámico de memoria.
El beneficio de esta función es que se puede obtener una linea completa a la vez. Y resulta muy útil para algunos fines como la construcción de un parser de algún tipo de archivo de texto.
Su prototipo es el siguiente:
size_t fread (void *data, size_t size, size_t count, FILE *stream);En estas definiciones se usa el tipo size_t, el cuál está definidio en stddef.h y sirve para definir tamaños de objetos.Lo que recibe esta función es un puntero donde almacenaremos los datos leídos (comunmente llamado buffer), el tamaño de los datos a leer, la cantidad de esos datos a leer y el apuntador al fichero.Aquí hay un ejemplo simple para leer los primeros 100 caracteres de un fichero y almacenarlos en un buffer:
#include
FILE *fp;
fp = fopen ( "fichero.in", "r" );
fclose ( fp );
return 0;
}