Programación de librerías
ILBM (3ª Parte)
Por Vicente Santos
Las librerías y su programación siguen ocupando un espacio importante de nuestra publicación. Con esta segunda entrega se profundiza en la generación de cabeceras y ficheros. |
|
CAPÍTULO 3.
Al principio de esta serie vimos que para poder utilizar una librería run-time es nuestros programas era necesario servirse de diversos ficheros especiales, según el lenguaje que queramos emplear.
En los más habituales estos ficheros son: ".bmap" en AmigaBASIC, "pragma" en C y librerías explorables en C -si no se usan "pragma"- y (al menos con las de usuario) Ensamblador.
Los tres se obtienen, más o menos directamente, de unos ficheros que acompañan a todas las run-time, llamados "file description" -descripción de fichero-, mas popularmente conocidos por ".fd".
FICHEROS ".FD"
Un ".fd" es un simple fichero ASCII formado por una serie de directivas, comentarios y entradas a la librería organizadas de la forma siguiente:
- una línea que empiece con los caracteres "##" es una directiva.
- Una línea que empiece con el carácter "*" es un comentario.
- Cualquier otra línea es una entrada a la librería -cada entrada corresponde a una función, aunque representada en un formato especial-.
Las directivas son:
- NombreBase: Es la variable global de tipo puntero en que se guarda la dirección en memoria donde estará la Base.
- bias n: Indica el valor del primer _LVO de la Base ("n" suele ser 30).
- public: Indica que las entradas a la librería que van tras ella son accesibles a nuestros programas -es decir, podemos utilizar las funciones representadas por cada una-.
- private: Indica que las entradas que van tras ella no son accesibles a nuestros programas.
- end: Señala el fin del fichero.
las entradas a la librería tienen el siguiente formato:
nombre función (lista de argumentos) (lista de registros). Tanto los argumentos como los registros deben estar separados entre coman (",") aunque estos últimos también pueden separarse con barras inclinadas ("/").
El fichero ".fd" de nuestra librería, 2ilbm_lib.fd", lo tienen, como ya es habitual, acompañando a este capítulo.
Como verán en él aparecen cada una de las directivas comentadas excepto "private", pues "ilbm.library" -al menos en esta su primera versión- no contiene funciones que no deban ser utilizadas. observarán además que las entradas están en el mismo orden en que aparecen las funciones en el programa principal que desarrollamos en el capítulo anterior. Esto debe ser así siempre.
Y ahora que ya tenemos ".fd" vamos a ver como nos las ingeniamos para convertirlo en algo que pueda servir para sacarle partido a la librería.
GENERANDO FICHEROS
Vamos a dividir esta sección en tres apartados, uno para cada tipo de fichero empleado con los lenguajes de uso más común.
FICHEROS ".BMAP": Son los necesarios para AmigaBASIC y aunque nuestra librería no está pensada para ser utilizada con este lenguaje -sinceramente, como yo hace siglos que no lo uso no he probado si funciona o no- vamos a analizarlo y ver la forma de obtenerlos por si alguno de vosotros esta interesado en emplear ilbm.library en programas BASIC.
Un fichero ".bmap" tiene la siguiente estructura: nombre de función en caracteres ASCII, terminado en un byte nulo (Hex. 00), vector de desplazamiento (_LVO) dado en una palabra (16 bits) con signo negativo y una "tabal de registros" de los argumentos (un byte por registro) terminando con un byte 0. Los registros se especifican de acuerdo a unas convenciones de codificación preestablecidas que pueden encontrar en el manual del AmigaBASIC (apéndice F, pág. A-18). En el caso de funciones que tomen sus argumentos de la pila, al estilo del C, esta tabla estará vacía.
Como ven son un tanto complejos pero afortunadamente para obtener los contamos con la ayuda del propio AmigaBASIC -aunque los valientes que se atrevan pueden intentar obtener el ".bmap" de ilbm.library a mano-. La ayuda comentada está en forma de programa en el disco Extras de la versión 1.2 y 1.3 del Workbench, se llama "ConvertFD" y genera ficheros ".bmap" a partir de ficheros ".fd" automáticamente. Al ejecutarlo nos informará sobre su uso.
FICHEROS "PRAGMA": Se utilizan como ficheros de cabecera en lenguaje C para llamar a las funciones de librerías run-time directamente, lo que evita enlazar con librerías explorables de rutinas puente a las funciones originales -más adelante veremos estas-. Esto, además hace que los programas que usen "pragmas2 se ejecuten más rápidamente.
Hasta hace poco sólo podían usarse en programas creados con SAS/Lattice, pero desde la versión 5.0 de Aztec C también pueden emplearse con este compilador -una ventaja añadida es que los "pragma" para ambos paquetes son idénticos-.
Estos ficheros tienen la composición siguiente:
- #pragma: es una instrucción que forma parte de una rutina especial llamada a la función,
- libcall: ídem,
- base: es el puntero a la Base de la librería,
- rutina: es el nombre de la función a la que se quiere llamar, -> offset: es el _LVO a la función desde la Base y -> magic: contiene la descripción de los registros de los argumentos y del registro en el que la función devuelve su resultado, codificados según unas convenciones predefinidas (¿dónde habré oído esto?) que podéis ver en el manual de vuestro compilador.
Esta información se completa con un indicador del número total de argumentos pasados a la función. Al igual que los ".bmap" estos "pragma" también pueden generarse de forma manual pero esto es todavía más complicado que con los del BASIC. Una vez más el compilador vendrá en nuestra ayuda. Tanto Lattice como Aztec incluyen programas que generan ficheros "pragma" a partir de ".fd".
El de SAS/Lattice es "fd2pragma" -desconozco el de Azted- y funciona así: (desde Shell o CLI) fd2pragma ilbm_lib.fd ilbm.h. Una vez generado el fichero y para completarlo al estilo de los ya incluidos en el directorio ":include/proto7" del compilador podemos añadir, inmediatamente antes de las sentencias progama, la declaración del puntero a ILBMBase y los prototipos de las funciones.
Tal vez alguno se pregunte porqué no incluyo el listado del fichero de cabecera correspondiente al "pragma". Pues por una razón muy sencilla: aunque no lo parezca este tipo de fichero puede presentar problemas según la versión de "fd2pragma" con la que se cree.
Por ejemplo, la incluida en Lattice 4.0 genera sentencias incorrectas si las funciones tienen más de cuatro argumentos. Los que poseáis SAC C 5.10 y Aztec C 5.0 no tendrán problemas pero es mejor que lo comprueben leyendo atentamente el manual y analizando alguno de los que incluye cada compilador para las librerías oficiales.
Si hay sentencias "pragma" entre caracteres de comentario habrá que llamar a las funciones correspondientes en la forma tradicional, o sea, enlazando con librerías explorables. Y hablando del rey de Roma...
LIBRERÍAS EXPLORABLES
Pues dicho queda: son el tipo de ficheros necesarios para programas en C que no utilicen "pragma" (a la antigua usanza) y para Ensamblador, al menos con run-time de usuario. Su estructura es la típica de las librerías de compilador, es decir, una colección de módulos de código objeto organizados de tal forma que pueda extraer los necesarios para ponerlos dentro de nuestros programas. Cada módulo es en realidad una rutina especial, llamada "stub routine" -algo así como "rutina de anclaje"- que se encarga de poner los argumentos en los registros apropiados para, a continuación, llamar a la auténtica función utilizando su vector de salto (_LVO). La información se completa con la definición de los _LVO de cada función de la librería run-time -esto es además muy útil para los programadores en Ensamblador, pues evita los famosos "_LVOFunción EQU -XX", ya que basta con escribir en su lugar "XREF _LVOFuncisn" y dejar que sea el linker quien se ocupe de averiguar el adecuado a tiempo de enlace-. El problema de estas librerías explorables es su generación. Para ello no podemos utilizar directamente los ficheros ".fd". Estos solo servirán como referencia para conocer los registros utilizados por cada función y el orden de estas en la run-time, orden que debemos respetar a rajatabla. La librería debemos escribirla a mano utilizando -de nuevo- Ensamblador.
Junto a este capítulo tienen dos programas: "ilbm_stubs.s", en el que se desarrollan las rutinas "stub" para cada función de "ilbm.library" y "ilbm_lvos.s" que contiene las definiciones de los vectores de salto. Para generar la librería explorable, a la que llamaremos "ilbm.lib", siga los siguientes pasos con máximo cuidado y atención:
1) Ensamble cada programa exactamente igual que el del capítulo anterior.
2) Una el código objeto de ambos, usando el comando JOIN del AmigaDOS de esta forma: JOIN ilbm_stubs.o ilbm_lvos.o AS ilbm.lib. Naturalmente JOIN y AS pueden teclearse en minúsculas (yo he usado mayúsculas sólo para resaltarlos).
3) Instale "ilbm.lib" en el directorio ":lib/" de vuestro compilador, ensamblador o mejor de ambos (no confundir con el ":Libs/" del Workbench).
Una librería explorable es la típica de las librerías de compilador,
es decir, una colección de módulos de código objeto, organizados
de tal forma que se puedan extraer los necesarios para ponerlos
dentro de nuestros programas |
|
Ahora ya podemos utilizar "ilbm.library" en nuestro programas. Para ello basta con indicar al linker que enlace con "ilbm.lib". Por cierto... ¿saben por qué en los compiladores no hay, aparentemente, librerías explorables con rutinas "stub" para las run-time oficiales?.
Se supone que tendrían que existir una "exec.lib", "intuition.lib", etc. Pues es fácil: las "stubs" correspondientes están TODAS en "Amiga.lib". Y con esto concluimos por ahora. Ya sólo nos queda escribir algunos ficheros de cabecera para C y Ensamblador y ver algunos ejemplos sencillos sobre como utilizar nuestra flamante run-time, pero eso lo dejaremos para el próximo (y último) capítulo. Además, los listados están completos en los discos de la revista.
##base _ILBMBase ##bias 30 ##public CheckILBM(fich)(d0) GetBMHeader(fich,bmhd)(d0,a0) GetGfxMode(fich)(d0) GetColMap(fich,colores,planos)(d0,a0,d1) DisplayData(fich,bitmap,mascara,compresion)(d0,a0,d1,d2) PutILBM(fich)(d0) PutBMHeader(fich.bitmap,mascara,compresio,ancho_p,alto_p)(d0,a0,d1,d2,d3,d4) PutGFxMode(fich.modografico)(d0,d1) PutColMap(fich,viewport)(d0,a0) Savedata(fich,bitmap,mascara,compresion,bufmem)(d0,a0,d1,d2,a1) PutILBMSize(fich,fsize)(d0,d1) ##end ************************************************************** * MODULO : ilbm.lvos.s * * DESCRIPCION : definicion de LVOs (Library Vector Offsets). * * DESARROLLO : 19-06-92 - Creacion (V. Santos) * * Correspondiente a version 1.0 de la libreria * ************************************************************** * FICHEROS DE CABECERA ============================================================== INCDIR "sys:devpac/include.cbm/" INCLUDE "exec/types.i" INCLUDE "exec/libraries.i" SECTION_LVO,DATA * DEFINICION DE _LVOs ============================================================== ; LIBINIT inicializa un _LVO igual a -30 para saltar los primeros vectores ; de 6 bytes de las funciones estandar del sistema (Open/Close/Expunge/ExtFunc). LIBINIT ; LIBDEF define el valor actual de _LVO de una etiqueta y salta -6 bytes ; para preparar el siguiente _LVO. Cada etiqueta corresponde a una funcion de ilbm.library ;(el orden de asignaciones debe ser el mismo en que aparecen las funciones "ilbm.s"). LIBDEF _LVOCheckILBM ; asigna valor -30 XDEF _LVOCheckILBM LIBDEF _LVOGetBMHeader ; asigna valor -36 XDEF _LVOGetBMHeader LIBDEF _LVOGetGfxMode : -42 XDEF _LVOGetGfxMode LIBDEF _LVOGetColMap ; -48 XDEF _LVOGetColMap LIBDEF _LVODisplayData ; -54 XDEF _LVODisplayData LIBDEF _LVOPutILBM ; -60 XDEF _LVOPutILBM LIBDEF _LVOPutBMHeader ; -66 XDEF _LVOPutBMHeader LIBDEF _LVOPutGfxMode ; -72 XDEF _LVOPutGfxMode LIBDEF _LVOPutColMap ; -78 XDEF _LVOPutColMap LIBDEF _LVOSaveData ; -84 XDEF _LVOSaveData LIBDEF _LVOPutILBMSize ; -90 XDEF _LVOPutILBMSize END ******************************************************************* * MODULO : ilbm.stubs.s * * DESCRIPCION : rutinas de enlace con funciones de "ilbm.library" * * para programas en C y Ensamblador * * DESARROLLO : 19-06-92 - Creacion (V. Santos) * * Correspondientes a version 1.0 de la libreria * ******************************************************************* * FICHEROS DE CABECERA =================================================================== INCDIR "sys:devpac/include.cbm/" INCLUDE "exec/types.i" INCLUDE "exec/libraries.i" SECTION CSTUB,CODE * REFERENCIAS EXTERNAS =================================================================== XREF _ILBMBase ; a base de libreria XREF _LVOCheckILBM ; a vectores definidos en ilbm_lvos.s XREF _LVOGetBMHeader XREF _LVOGetGfxMode XREF _LVOGetColMap XREF _LVODisplayData XREF _LVOPutILBM XREF _LVOPutBMHeader XREF _LVOPutGfxMode XREF _LVOPutColMap XREF _LVOSaveData XREF _LVOPutILBMSize * DEFINICIONES EXTERNAS =================================================================== ; necesarias para hacer acesibles las rutinas stubs al programa del usuario XDEF _CheckILBM XDEF _GetBMHeader XDEF _GetGfxMode XDEF _GetColMap XDEF _DisplayData XDEF _PutILBM XDEF _PutBMHeader XDEF _PutGfxMode XDEF _PutColMap XDEF _SaveData XDEF _PutILBMSize * RUTINAS STUB ==================================================================== ; Estas rutinas toman los argumentos de la pila, los ponen en los ; registros apropiados y llaman a las funciones de ilbm.library ; a traves de sus vectores (_LVOs). El resultado se devuelve en d0. _CheckILBM: move.l a6,-(sp) ; preservar a6 move.l 8(sp),d0 ; poner argumentos en sus registros move.l _ILBMBase,a6 ; ptro a base de libreria en a6 jsr _LVOCheckILBM(a6) ; llamar a la funcion move.l (sp)+,a6 ; restaurar a6 rts ; volver con resultado en d0 _GetBMHeader: move.l a6,-(sp) move.l 8(sp),d0 move.l 12(sp),a0 move.l _ILBMBase,a6 jsr _LVOGetBMHeader(a6) move.l (sp)+,a6 rts _GetGfxMode: move.l a6,-(sp) move.l 8(sp),d0 move.l _ILBMBase,a6 jsr _LVOGetGfxMode(a6) move.l (sp)+,a6 rts _GetColMap: move.l a6,-(sp) move.l 8(sp),d0 move.l 12(sp),a0 move.l 16(sp),d1 move.l _ILBMBase,a6 jsr _LVOGetColMap(a6) move.l (sp)+,a6 rts _DisplayData: move.l a6,-(sp) move.l 8(sp),d0 move.l 12(sp),a0
|
|