Obligement - L'Amiga au maximum

Samedi 17 novembre 2018 - 04:01  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

 · Accueil
 · A Propos
 · Articles
 · Galeries
 · Glossaire
 · Liens
 · Liste jeux Amiga
 · Quizz
 · Téléchargements
 · Trucs et astuces


Articles

 · Actualité (récente)
 · Actualité (archive)
 · Comparatifs
 · Dossiers
 · Entrevues
 · Matériel (tests)
 · Matériel (bidouilles)
 · Points de vue
 · En pratique
 · Programmation
 · Reportages
 · Tests de jeux
 · Tests de logiciels
 · Tests de compilations
 · Articles divers

 · Articles in english
 · Articles en d'autres langues


Twitter

Suivez-nous sur Twitter




Liens

 · Sites de téléchargements
 · Associations
 · Pages Personnelles
 · Matériel
 · Réparateurs
 · Revendeurs
 · Presse et médias
 · Programmation
 · Logiciels
 · Jeux
 · Scène démo
 · Divers


Jeux Amiga

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Trucs et astuces

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Glossaire

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Partenaires

Annuaire Amiga

Amedia Computer

Relec

Hit Parade


Contact

David Brunet

Courriel

 


Programmation : Assembleur - Backstart, démarrage d'un programme en tâche de fond
(Article écrit par Frédéric Mazué et extrait d'Amiga News Tech - janvier 1991)


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 :

assembleur
assembleur

Tous les éléments BPTR de cette structure sont des pointeurs BCPL comme expliqué plus haut. Ceci exposé, voyons chacune des deux façons de lancer un programme sur l'Amiga.

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 :

assembleur
assembleur

Le CLI fonctionne approximativement de la manière suivante : on peut frapper ce que l'on veut dans la fenêtre du CLI, celui-ci n'en tient pas compte tant que la touche "Entrée" n'est pas pressée et dort du sommeil le plus profond. Quand cette dernière est pressée, un signal est envoyé au CLI afin de le réveiller. A ce moment, ce dernier compte combien de caractères ont été tapés dans la ligne, range cette chaîne de caractères (attention, chaîne BCPL !) dans l'élément "cli_CommandName" de la structure du CLI puis essaie de charger le programme correspondant au premier mot de la chaîne. Le CLI charge un programme comme suit :
  • De la mémoire est réservée pour la création d'une structure Process.
  • Le CLI charge le programme avec la routine "LoadSeg" de la dos.library.
A ce moment, on a donc en mémoire un programme exécutable et relogé constitué d'un ou plusieurs segments, avec la mémoire correspondante réservée. Nous verrons comment sont constitués les segments plus loin, il suffit de savoir pour l'instant que l'élément "cli_Module" est initialisé avec un pointeur BCPL sur le premier segment du programme.

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 offsets 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 l'offset de saut dans A4, la valeur de l'offset é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 "hunks". Eh bien lorsqu'un programme exécutable est chargé en mémoire, ses hunks "codes" et "datas" sont légèrement modifiés pour devenir des segments chaînés entre eux. Un segment est constitué comme suit :
  • Un long mot contenant la longueur en octets du segment, ce long mot étant compris dans la longueur.
  • Un long mot qui est un pointeur BCPL sur le prochain segment si celui-ci existe (sinon le pointeur sera nul). Sachons que ce pointeur ne pointera pas sur le début du prochain segment, mais sur le début plus quatre octets, c'est-à-dire sur le pointeur "prochain segment" du prochain segment. Un jeu d'enfant n'est-ce pas ?
  • Les octets du programme (les voilà tout de même).
Une petite précision : j'ai dit plus haut que A3 contenait un pointeur de segment (non BCPL), donc A3 pointe sur le pointeur "prochain segment" du premier segment, donc il faut bien ajouter 4 pour arriver sur le programme.

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.
  • Votre programme doit commencer par le label "_main".
  • Quelque part dans votre programme, vous devez faire les déclarations suivantes :

    1. _BackgroundIO dc.l x
    Si x=0, la console courante ne sera pas ouverte. Si x<>0, la console courante sera ouverte, ce qui permet la sortie aisée de messages d'en-tête.

    2. _priorité dc.l x
    Ceci est la priorité que vous accordez à votre programme. En général on utilisera x=0.

    3. _stack dc.l x
    Ceci est la taille de la pile. La taille standard est pour x=4000.

    4. _progname dc.b 'xxxxxx',0
    Chaîne de caractères définissant à l'ordinateur le nom de votre programme, ce qui est indispensable puisque le nom transmis par le CLI a été "oublié".
Ce procédé fonctionne parfaitement sur Kickstart 1.2 et 1.3, comme vous pourrez vous en convaincre en appelant le programme d'exemple plusieurs fois depuis la même fenêtre CLI. Par contre, bien que je n'ai pas encore eu l'occasion d'essayer, je pense qu'il est probable que cela ne marchera pas sur le Kickstart 2.0 des A3000 puisque la dos.library a été réécrite en C, mais il suffira sans doute seulement de supprimer les "multiplications BCPL" pour obtenir le même résultat.

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.

assembleur
assembleur
assembleur
assembleur
assembleur


[Retour en haut] / [Retour aux articles]