Dans ce Chapitre, nous verrons à quoi correspond le terme
de "Librairie Dynamique", puis nous travaillerons sur la démarche
qu'il faut adopter afin d'en créer une en C sous TIGCC, et,
naturellement, nous étudierons comment il nous sera possible de l'utiliser, à
notre avantage...
Ne vous inquiétez pas : le Chapitre parait long au premier
abord, mais, en fait, il n'occupe une telle place que parce que j'ai essayé de
détailler au maximum mes explications... et parce que j'ai pris des exemples
qui ne sont pas particulièrement courts :-) !
I:\ Généralités sur les librairies dynamiques :
Avant tout, je vais vous demander d'excuser une petite "erreur de traduction" de ma part... En effet, ce que nous appelons ici librairie porte, en anglais, le nom de "library" (Et "libraries" au pluriel). La traduction exacte de ce terme, adaptée à la programmation serait "Bibliothèque partagée". Mais, par (mauvaise) habitude, nous n'emploierons jamais le terme de "bibliothèque" : nous utiliserons, comme nous l'avons déjà fait, celui de "librairie", qui est plus répandu dans la communauté française des TI-89 et TI-92plus !
Sans doute avez vous déjà installé un Kernel sur votre
calculatrice, ne serait-ce que pour utiliser certains jeux. Si tel est le cas,
vous avez pu remarquer la présence programmes présentés comme étant de type
ASM dans le VAR-LINK, mais apparaissant avec l'extension LIB dans un
Explorer (Tels, pour ne citer que les plus connus et les plus utilisés,
Doors-Explorer, ou PCT...).
Les librairies dynamiques qu'il est le plus courant de
rencontrer sur nos TIs sont GraphLib (Qui regroupe de
nombreuses fonctions graphiqes), Gray4Lib (Qui
permet d'utiliser des graphismes en 4 niveaux de gris, et se base principalement
sur GraphLib), FileLib (Qui permet la
manipulation de fichiers de la VAT), Userlib (Qui
permet d'utiliser bon nombre de diverses fonctions, telles attendre un appui sur
une touche, ou encore demander un mot de passe), ZipLib (Qui
permet de compresser/décompresser des variables)... et bien d'autre
encore !
A titre informatif, vous pouvez retenir que ce type de
librairies correspond aux fichiers comportant l'extension .DLL sous MS Windows.
Il vous faut tout d'abord savoir que ce qui a donné à ce
type de librairies leur qualificatif de "dynamiques" est le fait
qu'elles sont appelées par vos programmes, via le Kernel, qui effectue une
relocation des librairies. (Nous ne nous intéresserons pas ici à cette partie
théorique, qui n'est d'aucune influence sur leur fonctionnement, et à laquelle
je n'ai jamais vraiment prêté attention... !).
Donc, pour pouvoir utiliser ce type de librairie à partir
d'un de vos programmes écrit en C, il sera impératif que celui-ci ait
été écrit en mode Kernel, et non pas en mode Nostub. (Enfin, en général...
En effet, le FAT-Engine de la TICT
fonctionne en mode Nostub, mais je ne sais pas du tout comment !).
Si
vous souhaitez apprendre plus de détails sur la façon dont le Kernel traite
les librairies lors de leur utilisation par un programme, vous pouvez visiter
cette page écrite par François REVOL, a.k.a mmu_man sur le forum de TI-Fr : http://clapcrest.free.fr/revol/ti68k/tuto_libs.html.
Je le remercie d'ailleurs de l'autorisation qu'il m'a donné de placer ce lien
ici.
Nous allons à présent voir en quoi nous pouvons considérer que les librairies dynamiques sont utiles, c'est-à-dire que nous essayerons d'énumérer leurs avantages, puis nous tenterons de voir quels sont leurs inconvénients :
II:\ Création de la librairie dynamique sous TIGCC :
A: Opérations préliminaires :
Pour créer une librairie dynamique avec TIGCC, il vous
faut créer un nouveau projet. Ici, nous utiliserons un projet nommé "tcilib1",
qui donnera, comme pour les programmes normaux, le nom du fichier qui sera
présent sur la TI.
Ensuite, il vous faut créer un nouveau fichier comportant
l'extension .c, exactement de la même façon que vous pourriez créer un
nouveau code source vierge.
Maintenant, il vous faut effacer les diverses informations
ou fonctionnalités que TIGCC a placé par défaut dans ce nouveau ficher. Pour
cela, sélectionnez tout, et appuyez sur Del !
Vous avez donc une page entièrement vide devant vous... Nous
allons pouvoir commencer à la remplir :-) !
Pour commencer, je vous conseille fortement d'inclure les
librairies de TIGCC, qui vous permettront d'utiliser des fonctions déjà
écrites. De plus, il faut que vous précisiez un mode de compilation.
Naturellement, ne compilez pas votre librairie en mode nostub ! Incluez donc les
deux lignes de code suivantes dans votre source :
#define USE_KERNEL
// Permet la
compilation du programme en mode Kernel
#include <tigcclib.h>
// Inclut toutes
les librairies de TIGCC.
Pour déclarer un fichier .c comme une future
librairie une fois sur la TI, il faut placer à l'intérieur de celui-ci cette
ligne de code :
char
_library[] = "Un commentaire quelconque...";
Comme vous pouvez le voir, cela ressemble fortement au
type de commentaire qu'il est possible d'inclure avec tout programme écrit en
mode Kernel !
Ensuite, il vous faut déclarer au compilateur pour quelle
calculatrice il devra créer la libraire. cela se déroule exactement de la
même façon que pour les programmes normaux, c'est-à-dire qu'il vous faudra
inclure ce type de ligne de code :
short
_ti92plus, _ti89;
B: Le code de la librairie en lui-même :
Maintenant que nous avons vu comment faire pour déclarer une librairie, nous pouvons commencer à nous intéresser à la partie qui concerne le code de la librairie à proprement parler...
Chaque fonction de la librairire doit être introduite par
ce type d'instruction :
#define nom_de_la_fonction nomlib__numérodelafonction
Ainsi, puisque nous prendrons l'exemple d'une
librairie nommée "tcilib1", il faudra déclarer chaque instruction
ainsi :
#define nom_de_la_fonction tcilib1__xxxx
Attention : la
numérotation de xxxx pour les fonctions ou variables commence à 0000,
et croit ensuite : 0001 puis 0002, et ainsi de suite...
Il convient ensuite de placer le code de la fonction, exactement de la même façon que nous le ferions dans un programme normal en C. Naturellement, les fonctions utilisées peuvent être de tous types (void, short, long, ...), et prendre autant d'arguments que vous le souhaitez !
Je peux vous conseiller de définir la première fonction
de la librairie comme son numéro de version, ce qui peut éventuellement
apporter une aide par la suite, si vous en faites plusieurs versions
successives... Ainsi, dans notre exemple, la première fonction, ou plutôt, le
numéro de version serait défini de cette façon :
#define tcilib_version tcilib1__0000
long tcilib1_version = 0b0000000000000001;
Ensuite, il vous suffit de définir les autres fonctions
de la librairies de la même façon. Dans notre cas, la seconde fonction que
nous utiliserons est un exemple que j'ai extrait de la FAQ de TIGCC. Il s'agit
d'une fonction donnée par Zeljko Juric pour tracer des lignes plus rapidement
qu'avec DrawLine. Je l'ai un peu
modifiée, afin qu'elle ai plus de possibilités, mais son mode de
fonctionnement est équivalent, ou presque, même si cela a entraîné une
baisse de vitesse. Voilà comment nous inclurions cette fonctions dans la
librairie :
#define DrawLineFast
tcilib1__0001
void DrawLineFast(short
x1, short y1,
short x2, short
y2, short mode)
{
/* Liste des Arguments :
x1 : coordonnées en X du point de départ.
y1 : coordonnées en Y du point de départ.
x2 : coordonnées en X du point d'arrivée.
y2 : coordonnées en Y du point d'arrivée.
mode : Le mode selon lequel la ligne sera tracée. mode peut prendre pour
valeurs : A_NORMAL, A_REVERSE, A_XOR.
*/
short x =
x1, y = y1;
short dx =
abs (x2 - x1),
dy = abs (y2 -
y1);
short ystep =
(y1 < y2) ? 1
: -1,
pystep = 30 *
ystep;
short mov =
dx ? 0 :
-1;
unsigned char *ptr
= (char*)(void
*)LCD_MEM +
30 * y +
(x >> 3);
short mask =
1 << (~x &
7);
if (x1
< x2)
while (x
!= x2 || y !=
y2)
{
if(mode
== A_NORMAL)
*ptr |= mask;
if(mode
== A_REVERSE)
*ptr &=
mask;
if(mode
== A_XOR)
*ptr ^= mask;
if (mov
< 0)
y += ystep, ptr
+= pystep, mov +=
dx;
else
{
mov -= dy;
if (++x
& 7)
mask >>= 1;
else ptr++,
mask = 0x80;
}
}
else
while (x !=
x2 || y != y2)
{
if(mode
== A_NORMAL)
*ptr |= mask;
if(mode
== A_REVERSE)
*ptr &=
mask;
if(mode
== A_XOR)
*ptr ^= mask;
if (mov
< 0)
y += ystep, ptr
+= pystep, mov +=
dx;
else
{
mov -= dy;
if (x--
& 7) mask <<=
1;
else ptr--,
mask = 1;
}
}
}
Il est aussi possible d'utiliser une librairie dynamique
pour retenir le nom du détenteur d'un record, par exemple. pour cela, il faut
déclarer dans cette librairie une chaîne de caractères qui le contiendra.
Voilà comment nous pourrions effectuer cette opération :
#define tcilib1_chaine1 tcilib1__0002
char
tcilib1_chaine1[15] = "";
Ce faisant, nous avons déclaré dans notre librairie
une chaîne de caractères nommée tcilib1_chaine1, et comportant 15
caractères. Attention : Remarquez
bien la présence des guillemets et du signe d'égalité dans lé déclaration
de la chaîne, qui indiquent au compilateur et au programme que la chaîne ne
doit pas être allouée sur le Stack (ou sa durée de vie serait courte !), mais
à l'intérieur de la librairie !
N'oubliez pas que, pour que la chaîne de caractères puisse
être modifiée, il faut que la librairie soit désarchivée... Dans le cas
contraire, le propriétaire du record, par exemple, ne verra pas son nom
mémorisé...
Nous utiliserons aussi une autre fonction, elle aussi
issue de la FAQ de TIGCC, et je lui ai apporté de petites modifications (Mon
but n'est pas ici de développer de nouvelles fonctions, mais de vous montrer
comment les libraires dynamiques fonctionnent => je ne prends pas la peine
d'en créer !). Celle-ci sera celle qui est donnée par Zeljko Juric pour
permettre à l'utilisateur d'entrer une chaîne de caractères dans le
programme. Nous la définirons, bien entendu, de la même façon que la fonction
DrawFastLine, c'est-à-dire de la
façon suivante :
#define InputStrXY
tcilib1__0003
void InputStrXY(short
x, short y,
char *buffer,
short maxlen)
{
/* Liste des Arguments :
x : coordonnées en X du point où la demande apparaîtra à l'écran.
y : coordonnées en Y du point où la demande apparaîtra à l'écran.
buffer : la chaîne de caractères où le résultat sera stocké.
maxlen : le nombre maximal de caractères qu'il sera possible d'entrer.
*/
SCR_STATE ss;
short key,
captured, i=0;
void CaptureHandler (EVENT
*ev)
{
if(ev->Type
== CM_STRING)
captured = *(ev->extra.pasteText);
}
MoveTo(x, y);
buffer[0]
= 0;
SaveScrState(&ss);
do
{
MoveTo(ss.CurX,
ss.CurY);
printf("%s_
", buffer);
// Note that two
spaces are required if F_4x6 font is used
key = ngetchx();
if (key
== KEY_CHAR && i
< maxlen)
{
EVENT ev;
ev.Type =
CM_KEYPRESS;
ev.extra.Key.Code
= key;
EV_captureEvents(CaptureHandler);
EV_defaultHandler(&ev);
EV_captureEvents(NULL);
buffer[i++]
= captured;
}
if (key
>= ' ' &&
key <= '~' &&
i < maxlen) buffer[i++]
= key;
if (key
== KEY_BACKSPACE && i)
i--;
buffer[i]
= 0;
}while(key
!= KEY_ENTER);
}
C: Opérations finales :
Voilà, vous avez terminé l'écriture du source de votre librairie dynamique. A présent, il ne vous reste plus qu'à la compiler, afin d'en faire un fichier que vous pourrez par la suite envoyer sur la calculatrice. Pour cela, il vous faudra agir exactement de la même façon que pour tous les autres programmes écrit en C que vous ayez jusqu'à présent compilé : le fait que ce soit une librairie ne change rien : TIGCC va créer un fichier comportant l'extension .9xz ou .89z, et vous pourrez ensuite l'envoyer à votre TI-92plus ou TI-89.
Si vous souhaitez consulter le code de la librairie dans son intégralité plutôt que sous forme de multiples "petits morceaux" comme ici, je vous conseille de cliquer sur CE lien.
III:\ Le fichier Header qui permettra d'utiliser la librairie :
Pour utiliser une libraire dynamique, il vous faut, comme pour toutes les librairies de TIGCC, un fichier header (comportant l'extension .h). Celui-ci devra par la suite être inclus dans le fichier comportant le code source du programme utilisant la librairie. (Nous verrons comment faire au IV de ce Chapitre).
Ce que je vous conseille de faire est, pour des raisons de
facilités de navigation, de placer directement de fichier header dans votre
projet TIGCC en cours (celui que vous utiliserez pour le programme.). ATTENTION
: il ne doit en aucun cas s'agir du projet correspondant au code
de la librairie !
Il vous est aussi possible d'enregistrer ce fichier header
dans le répertoire Include\C de TIGCC...
Nous verrons dans la partie suivante de ce Chapitre comment
faire pour l'utiliser dans ces deux cas, mais sachez que je préfère la
première solution, car elle permet de regarder le code du header de temps en
temps pendant l'écriture du programme.
A: Les
bases communes à tous les headers :
Le fichier Header doit comporter des instructions qui
serviront au préprossesseur. Étant donné qu'il n'est pas vraiment nécessaire
de connaître leur mode de fonctionnement, nous ne l'étudierons pas en détails
ici : nous nous contenterons de le commenter légèrement, afin que vous
compreniez tout de même à quoi sert ce que vous écrivez !
Voici ce qu'il ne vous pas utile de connaître, mais qui est
tout de même nécessaire dans le Header :
#ifdef DOORS
// Si le programme est créé en mode Kernel...
#ifndef __H_testlib //
Si le fichier Header n'est pas défini
#define __H_testlib //
Il faut le définir => exécution des actions du code du Header...
// Ici, vous devrez insérer ce que nous appellerons dorénavant le code du
Header...
#endif //
Fin du test de la condition "Si le Header n'est pas défini".
#else //
Si le programme n'est pas créé en mode Kernel...
#error: Pour utiliser des librairies
dynamiques, vous devez être en mode KERNEL !!! //
Affiche un message d'erreur...
#endif //
Fin du test de la condition "Programme en mode Kernel"
Je vais tout de même vous fournir quelques explications, même si elle ne seront que assez superficielles :
B:
Le "code" du Header :
Dans celui-ci, il faut, pour chaque fonction, définir son nom, en le faisant correspondre au numéro auquel la fonction correspond dans la librairie. Il convient ensuite de donner le prototype de la fonction.
Une particularité qu'il ne faut pas oublier, c'est que les fonctions ou variables déclarée dans la librairie n'appartiennent pas au programme de la même façon que toutes celles que nous avons jusqu'à présent utilisé... Il sera donc impératif de les déclarer de type extern, et incluant ce modificateur de type devant celui que nous leur avons attribué dans le code de la librairie.
Dans les cas où il ne s'agit pas d'une fonction, mais
d'une variable; il convient de faire de même. Ainsi, dans le cas du numéro de
version de notre librairie, nous aurons ceci :
#define tcilib1_version tcilib1__0000
extern
long tcilib1_version;
En effet, cette variable correspond à la première
"fonction" de la librairie, et porte donc le numéro 0000. De plus, il
d'agit d'une variable de type long.
Pour les fonctions que nous avons déclaré dans notre
librairie, il conviendra d'utiliser (dans le cas de la fonction DrawFastLine)
:
#define DrawFastLine tcilib1__0001
extern
void DrawFastLine(short, short,
short, short,
short);
En effet, cette fonction correspond à la
fonction portant le numéro 0001 dans la librairie, et elle prend en arguments
quatre shorts.
De la même façon que pour le numéro de version, la
chaîne de caractères intégrée à la librairie doit être définie comme ceci
:
#define tcilib1_chaine1 tcilib1__0002
extern
char tcilib1_chaine1[];
En effet, cette variable porte le numéro 0003 dans la
librairie, et il s'agit bien d'une variable de type tableau de chars.
Enfin, pour la dernière fonction que nous avons intégré
à notre librairie, il faudra, puisque c'est celle qui porte le numéro 0003, et
qu'elle prend en arguments deux shorts,
un pointeur vers un char,
et un autre short,
écrire ceci :
#define InputStrXY tcilib1__0003
extern
void InputStrXY(short, short,
char *, short);
De la même façon que pour le source de la librairie, si vous souhaitez consulter le code du fichier header dans son intégralité plutôt que sous forme de multiples "petits morceaux" comme ici, je vous conseille de cliquer sur CE lien.
IV:\ Le code
du programme utilisant la librairie :
A: Utilisation du fichier Header :
Nous avons dit précédemment qu'il était possible d'enregistrer le fichier Header de deux manières différentes :
B: Utilisation des fonctions de la librairie :
C'est la dernière opération à effectuer : Utiliser les fonctions et variables que nous avons défini dans la librairie ! :-)
Pour cela, il suffit d'agir EXACTEMENT de la même façon
que pour toutes les fonctions intégrées aux librairies de TIGCC. En effet, le
fichier Header que nous avons créé précédemment correspond au type de
headers utilisés par TIGCC pour ses librairies internes.
Nous pourrons donc, par exemple, effecteur le manipulation
suivante pour entrer une chaîne de caractères dans la variable correspondante
de la librairie :
InputStrXY(20, 20, tcilib1_chaine1, 14);
Et, naturellement, il est aussi possible d'afficher le
contenu de celle-ci, en utilisant des instructions "normales" de TIGCC
:
DrawStrXY(20, 20, tcilib1_chaine1, A_NORMAL);
Pour utiliser la fonction de dessin rapide de lignes,
il conviendra de faire quelque chose ressemblant à ceci :
DrawFastLine(10, 20, 50, 75, A_NORMAL);
Enfin, voici le programme que j'ai choisi d'utiliser comme
exemple. Je vous conseille de l'exécuter au moins deux fois sur votre
calculatrice, avec la librairie désarchivée, et, pour voir la différence,
d'essayer aussi en ayant archivé la librairie...
#define USE_KERNEL
#define NO_EXIT_SUPPORT
#include <tigcclib.h>
#include "testlib.h"
char _comment[]
= "Programme de Démo pour TIClib1";
short _ti92plus,
_ti89;
void _main(void)
{
short i=0;
short lcd_width =
LCD_WIDTH;
short lcd_height =
LCD_HEIGHT;
ClrScr();
FontSetSys(F_6x8);
DrawStrXY(10,
10, "Record
1 ::", A_NORMAL);
DrawStrXY(20,
20, tcilib1_chaine1,
A_NORMAL);
ngetchx();
for(i=0
; i<lcd_height
; i++)
{
DrawFastLine(0,
0, lcd_width-1,
lcd_height-i, A_NORMAL);
DrawFastLine(lcd_width-1,
0, 0,
lcd_height-i, A_NORMAL);
DrawFastLine(0,
lcd_height, lcd_width-1,
i, A_NORMAL);
DrawFastLine(lcd_width-1,
lcd_height, 0,
i, A_NORMAL);
}
ClrScr();
for(i=0
; i<lcd_height
; i++)
{
DrawFastLine(0,
0, lcd_width-1,
lcd_height-i, A_XOR);
DrawFastLine(lcd_width-1,
0, 0,
lcd_height-i, A_XOR);
DrawFastLine(0,
lcd_height, lcd_width-1,
i, A_XOR);
DrawFastLine(lcd_width-1,
lcd_height, 0,
i, A_XOR);
}
for(i=0
; i<lcd_height
; i++)
{
DrawFastLine(0,
0, lcd_width-1,
lcd_height-i, A_REVERSE);
DrawFastLine(lcd_width-1,
0, 0,
lcd_height-i, A_REVERSE);
DrawFastLine(0,
lcd_height, lcd_width-1,
i, A_REVERSE);
DrawFastLine(lcd_width-1,
lcd_height, 0,
i, A_REVERSE);
}
ClrScr();
DrawStrXY(10,
10, "Nouveau
record ?", A_NORMAL);
InputStrXY(20,
20, tcilib1_chaine1,
14);
}
Le code de ce programme étant des plus banals, mis à part le fait qu'il fait appel à des fonctions non incluses d'origine dans TIGCC, je ne le commenterai pas... Tous son contenu a déjà été étudié précédemment dans ce tutorial. Si jamais vous ne vous souvenez pas de quelques chose, je vous encourage fortement à revenir un peu en arrière, ça ne fait jamais de mal...
Bon, voilà, j'espère que, maintenant, vous avez compris
comment faire pour créer et utiliser vos propres librairies dynamiques en C...
A partir du C, il peut être possible d'utiliser des
librairies dynamiques que vous n'avez pas défini vous-même, et qui ont été
développées pour l'ASM (Comme ZipLib, par exemple), mais ça demande
une bonne compréhension du langage Assembleur, parce qu'il vous faudra
quasiment toujours passer par de l'ASM Inline... Désolé, mais c'est
comme ça... (Vous pourrez un exemple d'utilisation de ZipLib différent de
celui de la FAQ de TIGCC avec une macro ASM dans la FAQ
de ce tutorial).
Copyright Squale92 & Verstand. Logo et sigle TCI® réservés. Développé pour MS Internet Explorer 5.0 en 1024x768 pixels. Page modifiée le