Ampliación 4. Overlays.
Curso: Curso de Pascal, por Nacho Cabanes
Si trabajamos con MsDos y su famoso límite de 640 Kb, nos podemos encontrar con datos que ocupen más espacio que la memoria que tenemos disponible en nuestro ordenador.
Esto puede parecer un problema en un principio, pero una vez que se sabe cómo usar el acceso aleatorio a un fichero, la solución es fácil: leemos sólo los datos que necesitemos en cada momento.
Pero ¿qué ocurre si es nuestro programa el que necesita más memoria de la que tenemos? ¿O simplemente si necesitamos más memoria libre? Es habitual que, igual que ocurre con los datos, muchas partes de un programa se usan con poca frecuencia. En cambio, en un programa no existe nada parecido al acceso aleatorio para tener en memoria sólo las rutinas que nos interesan en un momento concreto.
¿Nada? Eso no es cierto del todo. En Turbo Pascal
tenemos una forma de conseguirlo: empleando overlays (En Free Pascal todo esto
no es necesario: tenemos acceso a toda la memoria del sistema sin necesidad
de estos "trucos", así que podemos manejar programas enormes de forma transparente).
La palabra "overlay" sale a querer decir algo parecido a "solapamiento". Y justo esa es la idea: al igual que hacíamos con los datos, podremos tener procedimientos o funciones que se "solapen" en memoria: en cada momento sólo tenemos uno de ellos en la memoria central (RAM), y cuando hace falta otro, éste sale y le deja su hueco en memoria.
Imaginemos que nuestro programa presenta un mensaje al principio de bienvenida (que sólo se emplea una vez en cada ejecución), otro de despedida al final (pasa lo mismo), y hay una rutina de ordenación de los datos, que sólo usamos una vez por semana. Lo que nos interesa es, en vez de tener las 3 cosas en memoria, cargarlas sólo cuando las necesitemos. Y aun hay más: como no tienen por qué ser simultáneas (no se ordena a la vez que se saluda, por ejemplo), podríamos liberar la memoria que ha usado una de ellas antes de cargar la otra.
Eso es lo que nos permite hacer el sistema de "overlays", que Turbo
Pascal incorpora como estándar.
Ahora que vamos sabiendo por donde van los tiros, vamos a empezar a aplicarlo.
El programa debe empezar por incluir la unidad Overlay:
uses overlay, ...
También debemos incluir las unidades que queremos solapar:
uses overlay, crt, dos, miUnit1, miUnit2 ...
Ahora tenemos que emplear 3 directivas
de compilación:
- $F+ para indicar al compilador que queremos llamadas lejanas (far, fuera del segmento actual).
- $O+ para decirle que permita usar overlays.
- $O UNIDAD para indicar qué unidades queremos solapar.
Con todo esto, nuestro programa estaría quedando:
{$F+,O+} program PruebaOverlays; uses overlay, crt, dos, miUnit1, miUnit2; {$O miUnit1} {$O miUnit2} begin [...]
¿Más cosas? Apenas un par de ellas. Ya en el cuerpo del programa, usamos OvrInit para inicializar el sistema de overlays e indicar a nuestra aplicación en qué fichero se encuentran éstos:
OvrInit ( 'MIPROG.OVR' );
Ahora podemos comprobar que todo ha ido bien, mirando el valor de OvrResult. Este será 0 si no ha habido problemas, o un número distinto si no ha podido inicializar todo correctamente.
Hay definidas unas constantes simbólicas para simplificarnos esas comparaciones:
ovrOk = 0; { Todo correcto } ovrError = -1; { Error del manejador de overlays; normalmente será que no hemos creado bien los overlays } ovrNotFound = -2; { Fichero de overlays no encontrado. No ocurrirá al compilar, pero puede pasar después, si no distribuimos el .OVR junto al .EXE } ovrNoMemory = -3; { No hay memoria suficiente para la zona de solapamiento } ovrIOError = -4; { Error de E/S con el fichero OVR } ovrNoEMSDriver = -5; { No existe memoria EMS (expandida) } ovrNoEMSMemory = -6; { No hay suficiente memoria EMS }
¿Por qué habla por ahí de EMS? Pues porque podemos decirle además al compilador que queremos que nuestro programa intente cargar los overlays en memoria expandida (EMS). Si lo consigue, será más rápido leer los procedimientos desde ahí que desde el disco. Si no lo consigue, tampoco hay problema, porque entonces trabajaría con el disco como si nada.
¿Y cómo le pedimos que lo intente cargar en EMS? Pues simplemente escribiendo
OvrInitEMS;
La última consideración, pero no menos importante, es que para que todo esto funcione debemos compilar a disco.
¿Las unidades a solapar? Son unidades normales y corrientes, en las que debemos indicar al principio {$F+,O+}
Vamos ya a ver un ejemplo de cómo se aplica todo esto: El programa
principal sería
{--------------------------} { Ejemplo en Pascal: } { } { Programa que usa } { Overlays } { MIOVR.PAS } { } { Este fuente procede de } { CUPAS, curso de Pascal } { por Nacho Cabanes } { } { Comprobado con: } { - Turbo Pascal 7.0 } {--------------------------} {$F+,O+} { Necesario } program MiOvr; uses Overlay, Crt, { Unidades que empleamos } MiUnit1, MiUnit2; {$O MiUnit1} { Indicamos cuales queremos solapar } {$O MiUnit2} var tecla: char; begin ClrScr; OvrInit('MIOVR.OVR'); { Inicializar y reservar memoria } {OvrInitEMS;} if OvrResult <> 0 then { Si hay algún error } begin WriteLn('Error en el sistema de overlays: ', OvrResult); Halt(1); end; repeat Proc1; { De la unidad 1 } Proc2; { De la unidad 2 } WriteLn; { Para que se lea mejor } delay(50); { Y esperamos un poco } until KeyPressed; { Así hasta que se pulse una tecla } tecla := ReadKey; { Absorbemos la tecla pulsada } end.
La primera unidad podría ser:
{--------------------------} { Ejemplo en Pascal: } { } { Primera unidad sola- } { para para MIOVR } { MIUNIT1.PAS } { } { Este fuente procede de } { CUPAS, curso de Pascal } { por Nacho Cabanes } { } { Comprobado con: } { - Turbo Pascal 7.0 } {--------------------------} {$O+,F+} unit MiUnit1; interface procedure Proc1; implementation procedure Proc1; begin Writeln('Estoy en la primera unidad.'); end; end.
{--------------------------} { Ejemplo en Pascal: } { } { Segunda unidad sola- } { para para MIOVR } { MIUNIT2.PAS } { } { Este fuente procede de } { CUPAS, curso de Pascal } { por Nacho Cabanes } { } { Comprobado con: } { - Turbo Pascal 7.0 } {--------------------------} {$O+,F+} unit MiUnit2; interface procedure Proc2; implementation procedure Proc2; begin Writeln('Estoy en la segunda unidad.'); end; end.
Y si queremos emplear EMS, las unidades no cambian, y el programa principal
podría ser:
{--------------------------} { Ejemplo en Pascal: } { } { Programa que usa } { Overlays y los guarda } { en memoria EMS } { MIOVR2.PAS } { } { Este fuente procede de } { CUPAS, curso de Pascal } { por Nacho Cabanes } { } { Comprobado con: } { - Turbo Pascal 7.0 } {--------------------------} {$F+,O+} { Necesario } program MiOvr2; uses Overlay, Crt, { Unidades que empleamos } MiUnit1, MiUnit2; {$O MiUnit1} { Indicamos cuales queremos solapar } {$O MiUnit2} var tecla: char; begin ClrScr; OvrInit('MIOVR.OVR'); { Inicializar y reservar memoria } OvrInitEMS; if (OvrResult = -5) or (OvrResult = -6) then { Si hay problemas con la EMS } begin WriteLn('Error con la EMS. Usando el disco...'); Delay(1000); { Avisamos y seguimos } end else if OvrResult <> 0 then { Si hay algún error "serio" } begin WriteLn('Error en el sistema de overlays: ', OvrResult); Halt(1); end; repeat Proc1; { De la unidad 1 } Proc2; { De la unidad 2 } WriteLn; { Para que se lea mejor } delay(50); { Y esperamos un poco } until KeyPressed; { Así hasta que se pulse una tecla } tecla := ReadKey; { Absorbemos la tecla pulsada } end.
Como último comentario: al buffer de solapamiento se le asigna
el tamaño más pequeño que haga falta, y que coincide
con el Overlay más grande. Si queremos un buffer mayor, para intentar
que el programa haga menos lecturas de disco y sea más rápido,
podemos leer el tamaño actual del buffer con OvrGetBuf, y
cambiarlo con OvrSetBuf.
Actualizado el: 20-07-2013 11:20