|
|||||||||||||||||||||||||||||||||||||||||||
|
Vous avez sans doute remarqué qu'un programme lancé depuis une fenêtre CLI ou Shell ne rend pas la main à celle-ci tant que son exécution n'est pas terminée, ce qui a pour effet d'immobiliser ladite fenêtre. Pour remédier à cela, je vous propose ce mois-ci un fichier include qui permettra à n'importe quel programme assembleur de démarrer en tâche de fond, c'est-à-dire en rendant immédiatement la main au CLI tout en poursuivant sa propre exécution. Afin de réaliser ce petit miracle, il nous faudra faire une petite exploration des arcanes de la très ésotérique, nébuleuse et entortillée dos.library. A tout hasard, gardez votre aspirine à portée de la main. Effectivement, le problème majeur de la dos.library est qu'elle n'est absolument pas documentée. Monsieur Commodore n'est pas vraiment enclin à donner des renseignements quand il ne recommande pas purement et simplement de ne pas toucher à ce qu'il considère comme un domaine privé. Par voie de conséquence, les remarquables Amiga ROM Kernel Manual sont hélas muets sur ce sujet même si les fichiers "include" correspondants sont fournis. Une raison En fait, il y a, à défaut d'une justification valable, une raison à cette situation : la dos.library n'a absolument rien de standard par rapport aux autres bibliothèques du système d'exploitation de l'Amiga. Au lieu d'être écrite en C comme les autres bibliothèques, la dos.library a été écrite (surtout ne demandez pas pourquoi !) avec le bizarre autant qu'étrange langage BCPL. Cette situation est modifiée sur le Kickstart 2.0 des A3000 où la dos.library est enfin standard, mais en attendant, sur un A500 ou un A2000, le BCPL apporte les contraintes suivantes : 1. Incroyable mais vrai, un pointeur BCPL ne pointe pas ! Qu'est-ce à dire ? Que la valeur d'un pointeur BCPL est divisée par 4. Rendons ceci plus clair par un exemple : un pointeur "désignant" l'adresse $20000 vaut $5000. Ce qui signifie que pour utiliser humainement un pointeur BCPL, il faut d'abord multiplier sa valeur par 4. 2. Une chaîne de caractères BCPL est constituée d'un octet représentant la longueur de la chaîne, cet octet n'étant pas compté dans ladite longueur, les octets "caractères", et pas de zéro final. Cela signifie que si vous allez pêcher une chaîne de caractères dans une structure relevant du DOS, vous devrez modifier la chaîne si vous voulez la transmettre à une structure relevant d'une autre bibliothèque. 3. La routine "SetFunction" d'exec.library ne permet pas de modifier une fonction de la dos.library, cette dernière n'étant pas standard (c'est sans doute une des raisons du mutisme de Commodore). Petit préliminaire Quelle que soit la façon dont démarre un programme, il faut savoir que celui-ci répondra à une structure Process et non à une structure Task. En effet, une Task ne peut-être qu'un sous-programme très élémentaire car elle n'a pas le droit d'accéder au DOS et par voie de conséquence à aucune entrées/sorties. Voici la structure Process : Depuis le Workbench Lorsqu'on clique sur une icône, le Workbench charge le programme (ou l'outil par défaut) correspondant, puis crée un Process avec CreateProc de la dos.library. Tout Process étant muni d'un port message, le Workbench envoie un message à ce port. Le Process tournant déjà, il est en mesure de retirer le message de la file d'attente, ce qu'il doit faire le plus rapidement possible et théoriquement avant d'ouvrir la dos.library si l'on respecte les directives de programmation fournies par Commodore. J'en profite pour vous informer que les startups du Lattice C font exactement le contraire ! Certes, cela marche quand même car le message est retiré tout de suite après l'ouverture du DOS. Quoi qu'il en soit, ce n'est absolument pas un exemple à suivre, on a déjà vu des gourous réveillés pour moins que ça... Pour terminer son exécution, un programme lancé du Workbench doit retourner à l'expéditeur (la Workbench Task) le message de départ ce qui permettra au système de s'y retrouver pour supprimer le Process et libérer les mémoires proprement. Bien sûr, un programme lancé depuis le Workbench est de fait une tâche de fond, donc point n'est besoin dans ce cas de réinventer la poudre : on utilisera une routine de démarrage classique. Ceux qui désirent en savoir plus peuvent se reporter au "RKM Libraries And Devices" page 579. Depuis le CLI Là, le problème est plus compliqué faute de documentation. Au péril de ma santé mentale, j'ai exploré pour vous la dos.library et en ait rapporté les informations suivantes. Que l'on utilise un CLI ou un Shell, on a à faire avec le Command Line Interface (d'où le nom de CLI à l'origine) de la dos.library. Pour simplifier à l'extrême, on peut se contenter de dire que ce n'est finalement qu'une routine de la dos.library. Cette routine est en étroite communication avec intuition.library et avec plusieurs périphériques logiques dont le (pauvre) CON: dans le cas du CLI et le NEWCON: dans le cas du Shell. Dans la suite on appellera CLI le Command Line Interface, sans se soucier du (ou des) périphérique(s) logique(s) associé(s). Voyons d'abord la structure CLI :
Le CLI "s'attache", pour ce qui concerne notre sujet, à la structure Process par les éléments "pr_CLI" qui est un pointeur BCPL sur la structure CLI dans laquelle on vient d'initialiser "cli_Module", "pr_CurrentDir" pour le directory courant et l'élément "pr_SegList" est initialisé avec la même valeur que "cli_Module". Enfin, sachons que le programme (pr_StakBase) utilisera la pile du CLI. Bien sûr, d'autres éléments de la structure Process sont initialisés, mais cela ne nous intéresse pas aujourd'hui. Les registres 68000 sont initialisés comme suit : 1. D0 contient le nombre de caractères de la chaîne de paramètres suivant le nom du programme lors de son appel (l'espace entre le nom du programme et les paramètres ne fait pas partie de la chaîne). 2. A0 contient l'adresse de la chaîne de paramètres. Attention : cette chaîne n'est pas au format C car il n'y a pas de zéro final mais le caractère $0A, ni au format BCPL car la valeur contenue dans D0 ne fait pas partie de la chaîne. Enfin, la chaîne ne comporte plus le nom du programme alors que c'est le cas pour la chaîne de "cli_CommandName". 3. A1 pointe sur la pile. 4. A2 pointe sur la dos.library, mais en plein milieu des décalages de fonction (ça me rappelle un de mes amis Shadoks qui disait : "Mais pourquoi faire simple quand on peut faire compliqué"). 5. A3, selon la Bible de l'Amiga, devrait pointer sur la taille de la pile. Mes propres recherches montrent que ceci est faux (comme d'habitude !) car A3 est tout simplement un pointeur de segments et pointe donc sur le premier segment du programme. Attention car très étrangement, ce pointeur, bien qu'il pointe un segment n'est pas un pointeur BCPL mais un pointeur normal (toujours la théorie de Shadoks). 6. A4, selon la Bible de l'Amiga, devrait pointer sur le début du programme. C'est encore faux. A4 pointe en fait cette fois sur la valeur de la taille de la pile. 7. A5 pointe sur une routine de la dos.library telle qu'il est possible de s'en servir pour appeler des fonctions de cette dos.library, mais à condition de donner le décalage de saut dans A4, la valeur du décalage étant exprimée par rapport à la "base" définie dans A2 (très simple n'est-il pas ?). 8. A6 pointe sur une routine de la dos.library qui permet de rendre la main au CLI mais sa manipulation est plutôt du genre délicate et la place disponible pour cet article n'étant pas illimitée... L'utilisation de ces registres peut être très intéressante mais le moins que l'on puisse dire est qu'ils sont à manier avec précaution. De plus, ceci ne devrait pas être utilisable sur Kickstart 2.0 sauf en ce qui concerne D0 et A0. Tout ceci étant préparé, le CLI "saute" dans le début du programme, qui ne se situe pas à la valeur contenue dans A3 mais en fait, de par la constitution des segments, à la valeur A3+4. Il nous reste maintenant à voir comment sont constitués ces fameux segments, et nous pourront ensuite (enfin ?) voir comment parvenir au but proposé. Quèsako segment ? Si vous avez lu attentivement l'article sur l'installateur d'amorce, vous savez qu'un programme exécutable sur disquette est constitué d'un ou plusieurs segments. Eh bien lorsqu'un programme exécutable est chargé en mémoire, ses segments "codes" et "datas" sont légèrement modifiés pour devenir des segments chaînés entre eux. Un segment est constitué comme suit :
Enfin, si vous essayez de charger un programme avec LoadSeg de la dos.library, vous obtiendrez en retour dans D1 un pointeur BCPL (cette fois) sur le premier segment. En multipliant cette valeur par quatre, vous aurez donc "l'adresse" du premier segment, cette adresse contenant le pointeur BCPL sur le prochain segment (ou zéro s'il y en a pas). En examinant la mémoire avec un utilitaire quelconque, vous pourrez vous rendre compte que le long mot de longueur existe bien même s'il n'est jamais pointé. Le plan de bataille Pour parvenir au but que nous nous sommes fixé, il suffira de faire en sorte d'avoir un programme d'au moins deux segments, de sauvegarder certains paramètres utiles dans le deuxième segment tant qu'il fait partie du premier programme. Une fois ces paramètres transmis, couper adroitement (beaucoup plus adroitement que le CBACK du Lattice C s'il vous plaît !) le premier segment de la chaîne afin d'obtenir deux programmes distincts. Enfin démarrer à partir du premier programme le deuxième programme à l'aide de CreateProc puis de rendre la main au CLI. Un tel programme doit être écrit avec une certaine rigueur, voyons tout cela point par point. Le programme Il se présente comme un fichier include écrit pour Devpac 2 qui sera à utiliser en lieu et place de easystart.i (répertoire "Misc") qui permet seulement de démarrer à partir du Workbench. Les utilisateurs de macro-assembleurs dignes de ce nom tels que l'assembleur du Lattice, de l'Aztec, MetaComCo, etc. pourront adapter ce programme sans aucune difficulté. Les obstinés de K-Seka n'ont que leurs yeux pour pleurer. Les labels commencent par un souligné afin d'éviter des redondances avec les labels de vos propres programmes. Un assemblage conditionnel assure la présence des autres fichiers include nécessaires. Il est supposé que votre programme comportera au moins la directive include "exec/exec_lib.i". Le programme commence par "section backstart0,code". La directive d'assemblage section assure la formation d'un segment chaque fois qu'elle est utilisée. Donc le programme sera constitué d'autant de segments qu'il y aura de "section" dans le source. Sur beaucoup d'autres assembleurs la directive d'assemblage équivalente est CSECT ou encore DSEG. Ensuite, on sauvegarde dans le deuxième segment le nombre de caractères de la chaîne de paramètres et les registres transmis par le CLI puisque nous avons vu que tout cela pouvait être utilisé. Le programme recherche l'adresse de sa propre tâche afin de pouvoir tester en "pr_CLI" si le programme a été lancé depuis le CLI ou le Workbench. Le cas Workbench ne sera pas expliqué ici : ce n'est que du très classique. De la mémoire est réservée. On y recopie la chaîne de paramètres. L'adresse de la mémoire réservée est sauvée dans le deuxième segment. On ouvre la dos.library, l'adresse de base de celle-ci sera sauvée dans le deuxième segment. Ceci vous offre donc la possibilité d'appeler la dos.library dans votre programme sans avoir à l'ouvrir auparavant puisque c'est déjà fait. Voyez dans le programme d'exemple. Mais plus encore, mieux vaut ne pas ouvrir une deuxième fois cette bibliothèque et surtout il ne faut pas la fermer, que vous l'ayez ouverte ou non, sous peine d'un gourou au moment de rendre la main. La raison à cela ? C'est assez compliqué et je n'ai pas la place de fournir une explication exhaustive qui satisfasse les puristes. Disons simplement que le programme ne devant plus dépendre ni du CLI ni du Workbench, le système considérera qu'il utilise la dos.library "interne" elle-même ouverte en permanence. Si vous fermez le DOS, c'est cette bibliothèque "interne" qui sera fermée et le système ne saura plus comment rendre la main en supprimant le tâche lorsque votre programme arrivera à sa fin. Suivant les déclarations faites dans votre propre source (voyez à la fin du programme d'exemple, mais dans tous les cas, ces déclarations sont obligatoires), on ouvre ou non la console courante. Ceci afin de permettre la sortie éventuelle de messages dans le CLI. Attention : si vous avez demandé l'ouverture, c'est votre programme qui devra s'occuper de la fermeture. Ne pas fermer la console courante empêcherait la fermeture de la fenêtre par un autre programme comme "endcli" par exemple et tout simplement. L'instruction "lea _start_backstart-4(pc),a0" (à vos souhaits !) permet de récupérer dans le premier segment du programme, le pointeur BCPL sur le deuxième segment. Ce pointeur BCPL est rangé ensuite dans D3 car c'est là une condition d'entrée de la routine CreateProc. Ensuite, on prend dans "pr_CLI" le pointeur BCPL sur la structure Command Line Interface, on obtient l'adresse véritable en multipliant ce pointeur par 4, ce qui permet d'aller chercher en "cli_Module" le pointeur BCPL sur la chaîne de segments. Bien sûr, ce pointeur doit lui aussi être multiplié par 4. On a alors l'adresse véritable du pointeur BCPL sur le deuxième segment. Ce pointeur est effacé par "clr.l (a2)" et à ce moment le système considérera que notre programme n'est constitué que d'un seul segment. Enfin, on initialise les registres de façon à satisfaire aux autres conditions d'entrées de CreateProc et ainsi le processus peut être créé. Il reste à rendre la main. A ce moment, le système libérera la mémoire occupée par le premier segment et par lui seulement. La mémoire occupée par le reste du programme est toujours réservée, comme cela doit être sur un Amiga. Voyons maintenant la fin du programme. La directive "section backstart1,code" permet de définir un deuxième segment. On récupère ensuite la chaîne de paramètres, sa longueur, et le contenu des registres normalement transmis par le CLI. On saute au programme principal. Lorsque l'exécution de celui-ci est terminée, on libère la mémoire réservée pour la chaîne de paramètres, puis on libère la mémoire occupée par le programme par UnLoadSeg. Je ne peux pas m'empêcher de vous signaler une autre énormité de la Bible de l'Amiga : celle-ci nous dit qu'un Process peut être arrêté par UnLoadSeg, c'est faux ! UnLoadSeg ne fait que libérer la mémoire utilisée par le Process pile et structure comprise, mais le programme n'est pas arrêté ni supprimé de la liste des tâches pour autant. Ceci devant tout simplement se produire, que UnLoadSeg ait été employée ou pas, lorsqu'une instruction rts dépilera l'adresse de la routine de traitement prévue à cet effet et placée sur la pile par le système lors du CreateProc. Conclusion Je me permets de vous rappeler quelles sont les contraintes pour utiliser ce fichier include.
Voilà, tout est dit. Il me reste à vous quitter en attendant d'avoir la joie de vous retrouver dans les pages de la nouvelle formule de l'ANT qui, tout comme un programme peut se décrocher du CLI, s'est décrochée de Commodore Revue.
|