ETC II - Practica 3

 

Estructura del proyecto:

El proyecto esta compuesto de tres partes:

  1. Un programa en C que representa los datos en modo gráfico
  2. Una librería de funciones de interfaz en ensamblador que contiene las funciones de interfaz entre el programa C y la interfaz del dispositivo.
  3. Un programa residente (TSR) que maneja el dispositivo.

La conexión entre las partes esta representada esquemáticamente en la siguiente figura:



 

Llamadas

Funciones INT 61h


Resultados

de f-nes Registros



Var. globales


 

 

 

 

 

La separación del funcionamiento del sistema entre las tres partes lo deciden los alumnos. La separación de la figura es solo con fines ilustrativos. Es obligatorio:

  1. Que las partes del programa estén como ficheros de C, una librería y un TSR.
  2. También es obligatorio la presencia de un fichero make que compile el proyecto.

La parte de C por si misma no tendrá gran peso. Los que si se valora es la interfaz entre las partes.

 

Conceptos de compilación de un programa.

Segmentos de memoria

Independientemente del sistema operativo o el compilador un programa de alto nivel se compila de manera parecida en todas las plataformas. El código del programa se compila y forma un segmento llamado TEXT. Los datos forman distintos segmentos según su alcance y según si están inicializados o no.

En general:

Los datos inicializados forman el segmento(s) BSS.

Los datos no inicializados forman segmentos(s) DATA.

Los datos constantes forman segmento(s) CONST.

Los argumentos de las funciones y los variables locales automáticos (sin 'static') forman el segmento STACK.

En un entorno de programación moderno los segmentos tienen propiedades distintas. Es normal que el segmento TEXT esté execute only (ni siquiera podemos leer). El segmento CONST normalmente es de solo lectura. Los segmentos BSS y DATA son R/W y normalmente se unen en un solo espacio (un segmento). El segmento STACK es r/w. Si el sistema operativo no asigna los permisos de los segmentos correctamente pueden producirse fallos del programa y problemas serios de seguridad del entorno.

Por ejemplo si STACK es también un segmento ejecutable al manipular las variables locales podemos escribir programas en la pila y ejecutarlos, lo que permite una infracción grave de la seguridad del sistema.

Los compiladores son libres de unir los segmentos. Por ejemplo en entorno DOS/TC modelo SMALL BSS,DATA,CONST y STACK son un solo segmento. El ejemplo de abajo da una idea de cómo un compilador distribuye el resultado de una compilación.

#define C 23 // el compilador consume C.

int a; // a va en el segmento BSS (el valor inicial es 0).

char *p= // p va al segmento BSS

"ABCDEF"; // "ABCDEF" va al segmento CONST. Seria incorrecto p[2]='x';

char p1[23]; // p1 y el espacio de 23 chars de p1 van a BSS. p1[3]='x' es correcto.

static b=5; // b va a BSS.

static fuu() {return 5;}

int foo( // foo esta en TEXT

int arg) { // STACK

int d; // STACK

static char *q= // BSS -- q no es una variable automática.

"ABCDEF"; // CONST. El compilador es libre de utilizar el mismo espacio

// que utiliza "ABCDEF" de p=… o puede alocar otro espacio.

d=c+1+strlen(q)+fuu(); // el código que se genera va a TEXT

return d; // TEXT

}

/*

Al generar código para DOS el compilador genera:

Public _a

Public _p

Public _p1

Public _foo

Notar que el compilador NO genera Public _fuu, public _b, public _q o public _d

También el compilador genera

Extern _strlen

Pero NO extern _fuu.

*/

Implicaciones practicas en DOS -- al copiar memoria de clases distintas hay que utilizar los registros de segmentos. En DOS no existe CONST porque el CPU se utiliza en real mode.

Hay que suponer restricciones máximas sobre los segmentos que genera el compilador si queremos que el programa sea fácil de aplicar en otro entorno de programación.

 

Contexto de ejecución

Al compilar un programa de alto nivel el compilador supone que existe consistencia de los recursos del sistema al llamar a una función. En particular el compilador supone que unos registros no cambian antes y después de la llamada. El conjunto de recursos que deberían permanecer constantes al finalizar la llamada de una función se llama contexto de ejecución de este compilador.

Si los registros que forman el contexto se utilizan en la función de alto nivel el compilador tiene la responsabilidad de guardar estos registros (en la pila) y de recuperarlos al salir de la función. Si programamos en bajo nivel (ensamblador) o utilizamos funciones incompatibles con el contexto de ejecución (de otros compiladores) necesitamos de guardar el contexto 'a mano' y recuperarlo al salir de la función. Lo que exactamente compone el contexto de ejecución es algo particular para cada entorno (SO) y cada compilador. Se consigue esta información de los manuales del compilador o al compilar código excesivo con opción de generación de código de ensamblador.

En DOS y TC el contexto del compilador (sin 80x87) es:

SS,SP,BP,SI,DI, El flag I, DS -- dependiente del modelo de memoria. Los otros registros no forman parte del contexto y pueden cambiar al ejecutar una función. El registro SP puede cambiar si la función es PASCAL.

 

Distribución de registros, pila, cambios de nombres de funciones/variables, etc.

La manera mas universal de utilizar funciones de bajo nivel, que existe en prácticamente todos los SO es llamando a funciones de bajo nivel y utilizando variables globales compartidas. Existen otras maneras de utilizar el compilador (inline ASM code, procedimientos especiales de captura y proceso de interrupciones y excepciones, peek/poke, llamadas a estructuras de datos etc.) que son muy particulares para cada sistema operativo y incluso para una plataforma de hardware.

Mirar http://www.ii.uam.es/~kostadin/etc2/interlng.doc

 

 

 

http://www.ii.uam.es/~kostadin/etc2/interlng.htm