|
||||||||||||||||||||||||||||||||||||||||||||||
|
Devinette : quel est le moyen à la fois le plus simple, le plus efficace et le plus sûr d'accéder à toute la puissance du système d'exploitation de l'Amiga ? Tous ceux qui ont répondu "les bibliothèques" ont le droit de lire la suite de cet article. Bien sûr, on peut s'en passer, des bibliothèques. Il existe même certains cas où c'est obligatoire, notamment lorsque l'on se fiche du multitâche comme de l'an 40, et que tout ce que l'on souhaite, c'est faire de jolis effets avec le Copper et/ou afficher des énormes BOB avec le Blitter. Messieurs les créateurs de démos et de jeux, vous m'aurez compris. Mais dès que l'on se penche un peu plus avant sur la cohabitation de plusieurs tâches au même instant dans la même machine (subtil résumé du terme "multitâche"), les choses deviennent un peu plus ardues, voire franchement impossibles. Heureusement, l'Amiga met à notre disposition plusieurs séries de fonctions regroupées par thèmes dans ce que l'on appelle, justement, les bibliothèques ("libraries" en anglais). Ce qu'il faut savoir Qu'est-ce qu'une bibliothèque, concrètement ? Il ne s'agit ni plus ni moins que d'un ensemble de fonctions, auxquelles on accède via une adresse de base. Pour clarifier les choses, l'adresse de base représente l'adresse d'une table de pointeurs sur les fonctions de la bibliothèque. Ces pointeurs sont relatifs à l'adresse de base et négatifs, car en fait l'adresse de base pointe sur la fin de la table. Pour accéder à une fonction particulière, il faut en connaître son numéro de décalage (ou "offset", c'est-à-dire sa position dans la table) ainsi bien sûr que l'adresse de base de la bibliothèque. L'appel ne peut se faire qu'à partir de l'assembleur, via l'instruction JSR. La règle veut que ce soit le registre A6 qui contienne l'adresse de base de la bibliothèque (règle d'ailleurs obligatoire et immuable, étant donné que la majorité des fonctions accède à des données propres à la bibliothèque qui les contient, en utilisant A6 comme index). Un exemple typique d'appel d'une fonction serait :
Pour qu'un programme puisse accéder aux fonctions d'une bibliothèque donnée, il doit d'abord lui en demander la permission. En jargon de programmeurs, on appelle ça "ouvrir la bibliothèque". Et comment fait-on pour ouvrir une bibliothèque ? On appelle une fonction d'une bibliothèque, tout simplement et paradoxalement en même temps. Tout cela mérite une explication. Il existe en fait une et une seule bibliothèque à laquelle on peut accéder sans rien demander à personne : il s'agit d'exec.library ; c'est elle qui se charge de la gestion du multitâche au sein de l'Amiga (partage du temps du microprocesseur, allocations de mémoire, communication entre tâches, etc.). L'adresse de base d'exec.library est la seule adresse "fixe" du système d'exploitation, garantie par Commodore-Amiga de le rester jusqu'à la saint Glin-Glin. On la trouve à l'adresse 4 :
Partant de là, on peut ouvrir toutes les autres bibliothèques que l'on souhaite. Y'a quelqu'un ? Les conventions mises au point par les gens de chez Commodore-Amiga indiquent que le passage de paramètres aux fonctions des bibliothèques se fait par l'intermédiaire des registres du microprocesseur. C'est en effet la méthode la plus rapide, mais également la plus gourmande en mémoire, puisqu'elle oblige à sauvegarder les registres utilisés par les fonctions. Heureusement, ces mêmes conventions limitent les dégâts, en garantissant que seuls les registres a0, a1, d0 et d1 risquent d'être modifiés par les fonctions. Tous les autres sont préservés. Enfin, un usage "intelligent" des registres est fait, puisque toutes les adresses sont transmises par l'intermédiaire des registres d'adresse a0 à a5 (a6 étant occupé par l'adresse de base de la bibliothèque) et toutes les autres données, par l'intermédiaire des registres... de données, oui. Une exception à cette règle : la dos.library, qui n'utilise que les registres de données. Enfin, dernière règle, quand une fonction doit retourner une valeur, c'est le registre d0 qui la contient. Un exemple ! Pour illustrer cela, nous allons utiliser l'exec.library pour ouvrir l'intuition.library (celle qui contient toutes les fonctions de gestion des fenêtres, menus déroulants et autres requêtes). Le code présenté ici est écrit en assembleur avec Devpac 2, et en C avec le compilateur Lattice. Jugez vous-même de la différence entre les deux langages. ![]() ![]()
Mise à jour de mai 2025 : une archive contenant le listing adapté à vbcc, et avec l'exécutable compilé par vbcc, a été réalisée par Yann-Gaël Guéhéneuc et est disponible sur obligement.free.fr/files/antpubliclibraries.lha. Reste encore un point à éclaircir : où trouve-t-on les numéros de décalages des fonctions et comment sait-on quels paramètres elles attendent et, pour les programmeurs en assembleur, dans quels registres les passer ? Il exige heureusement de la documentation spécialisée, notamment les fameux ROM Kernel Manuals, dont Frédéric Mazué vous dit du bien chaque fois qu'il le peut. Il s'agit en effet de la documentation officielle de Commodore-Amiga sur les routines du système d'exploitation. Mais l'on peut également en trouver une liste (sans aucune explication, hélas) dans la Bible De L'Amiga, dont Frédéric Mazué vous dit du mal chaque fois qu'il le peut. Spécial assembleur Alors que le langage C fournit un appel direct aux fonctions des bibliothèques (les références étant résolues lors du reliage), les programmeurs en langage machine doivent en définir les décalages dans chacun de leurs programmes. Commodore - et HiSoft dans le cas de Devpac 2 - vient fort heureusement à leur rescousse, en incluant dans leur assembleur un lot de fichiers baptisés "fichiers include", qu'il suffit d'inclure dans le source pour pouvoir les utiliser. Ces fichiers contiennent la liste des décalages de toutes les bibliothèques ainsi que des macros facilitant leur utilisation. Pour reprendre l'exemple précédent, la macro CALLEXEC est définie comme suit : ![]() ![]() ![]() ![]() Dernier point important Et même très important : lorsque l'on ouvre une bibliothèque dans le but d'utiliser ses fonctions, il convient de la refermer avant de terminer le programme et de retourner au CLI ou au Workbench. Pour vous en convaincre, examinons de plus près le fonctionnement de la fonction OpenLibrary(). 1. Dans un premier temps, elle recherche dans la mémoire si la bibliothèque demandée n'a pas été déjà ouverte par une autre tâche (c'est souvent le cas, puisque le Workbench et AmigaDOS utilisent eux-mêmes plusieurs bibliothèques). Si c'est le cas, elle passe directement au point 5. 2. Dans le cas contraire, elle recherche si la bibliothèque demandée est située en ROM et si oui, elle saute au point 5. 3. Sinon, c'est que la bibliothèque se trouve sur disque (généralement dans le tiroir Libs: de la disquette Workbench), auquel cas elle la charge en mémoire et passe au point 5. 4. Si on arrive là, c'est que la bibliothèque demandée n'existe pas, et OpenLibrary retourne une valeur indiquant l'erreur (en l'occurrence, il s'agit de la valeur NULL). 5. Une fois la bibliothèque trouvée (ou chargée depuis le disque). OpenLibrary saute à une routine interne à la bibliothèque, qui s'occupe d'initialiser différents paramètres qui lui sont propres. Cela peut fort naturellement inclure des réservations de mémoire. De plus, une même bibliothèque pouvant être utilisée par plusieurs tâches "en même temps", un compteur interne est incrémenté, qui comptabilise le nombre de "clients" de la bibliothèque. 6. Enfin, l'adresse de base de la bibliothèque nouvellement ouverte et initialisées est renvoyée au programme appelant dans le registre d0. Lorsque l'on ferme une bibliothèque via CloseLibrary(), ce sont les opérations inverses qui sont effectuées : 1. Saut à une routine interne à la bibliothèque, qui libère la mémoire allouée lors de l'ouverture et décrémente le nombre de "clients". 2. Si le nombre de "clients" est égal à zéro, la bibliothèque est tout bonnement effacée de la mémoire, il faudra la recopier depuis la ROM ou la recharger depuis le disque lors d'un prochain appel à OpenLibrary(). On voit donc qu'oublier de fermer une bibliothèque conduit immanquablement à des "pertes de mémoire", qui peuvent tôt ou tard s'avérer critiques pour le système. A noter que si vous avez chargé le Workbench en incluant la commande "LoadWB -debug" dans votre startup-sequence, le menu "invisible" supplémentaire propose l'option "flushlibs", qui permet de remédier à une "oubli" de fermeture. Adresses Les adresses de base des bibliothèques portent des noms particuliers, qui sont utilisés par l'éditeur de liens dans le cas du langage C, et par les macros dans le cas de l'assembleur. Voici un mémento des principales bibliothèques avec le nom symbolique de leur adresse de base. ![]() Utiliser les bibliothèques présente deux avantages : dans un premier temps, on s'évite toutes les galères propres au multitâche que d'autres ont déjà résolues pour nous. Et dans un deuxième temps, on est sûr de rester compatible avec toutes les versions futures et à venir du système d'exploitation... à condition toutefois de respecter les règles imposées par Commodore-Amiga et de ne pas taper "n'importe comment n'importe où". Mais cela est une autre histoire...
|