|
||||||||||||||||||||||||||||||||||||||||||||||
|
Supportastes vous oncques gourou ? Caisgne ! Réduisez à mémoire la contenance qu'aviez. Tapait-il sur vos nerfs, ce gourou plein de mystères ? Cela suffit, jetons-lui un sort, carissant de mon chapeau magique, voici programme pantagruélique : le très renommé Guru Interceptor. Ciblons le problème Vous l'avez compris, il est question aujourd'hui de ne plus se laisser embêter par des méditations du gourou tout à fait intempestives. Mais entendons nous bien car tout comme il y a programmeur et programmeur, il y a gourou et gourou. Je m'explique par un exemple : un amateur veut modifier une bibliothèque et pour cela change arbitrairement une adresse de saut sans prévenir le système. Eh bien si le système d'exploitation, pour une raison qui lui appartient, vérifie la somme de contrôle de la bibliothèque ainsi estropiée, il se fera une joie de renvoyer à notre amateur une méditation du gourou de n°81000003 bien méritée. Il n'est pas question d'intercepter de telles "lamers failures", un programmeur digne de ce nom n'encourant pas ce type de risques car il sait qu'un système d'exploitation d'ordinateur, ça se respecte et pour notre exemple, aurait employé la routine SetFunction. Ceci étant précisé, il faut bien reconnaître qu'il est permis à tout le monde de se tromper en écrivant un programme. Que celui qui n'a jamais transféré un mot long à une adresse impaire me jette le premier octet ! Je vous propose donc aujourd'hui le moyen d'éviter les Guru Meditation de n°3 à n°B, erreurs qui peuvent toujours survenir, ne serait-ce qu'à cause d'une simple faute de frappe. Ceci nous évitera d'avoir à redémarrer le système et nous permettra grâce à un débogueur d'aller immédiatement désassembler l'endroit litigieux sans perdre de temps. Tout ceci sera bien utile pour la mise au point de programmes intraçables du genre démos ou autres... Petit préambule Avant de commencer, il faut que je vous précise que tout ce qui va être dit dans cet article concerne les Amiga équipés de microprocesseur 68000. Ceux qui auraient gonflé leur ordinateur à grand renfort de 68020 ou 68030 devront se reporter aux livres adaptés pour faire les corrections nécessaires. Néanmoins, le principe général de l'article restera valable. Pour ceux qui veulent tout connaître du microprocesseur 68000 et approfondir ce qui est dit dans cet article, je me permets de recommander l'excellent livre Mise En Oeuvre Du 68000 de C. Vieillefond aux éditions Sybex. Particularités du 68000 Le microprocesseur 68000 est un de ceux qui possède le jeu d'instructions le plus complet qui soit. Parmi toutes les instructions existantes, certaines sont dites "normales" et d'autres sont dites "privilégiées". En dehors des sauts d'interruptions communs à tous les microprocesseurs, le 68000 se distingue par une particularité supplémentaire : il dispose de deux modes de fonctionnement, le mode User ou mode Utilisateur et le mode Superviseur. Il est également nécessaire de savoir que le 68000 dispose de deux "stack pointers", c'est-à-dire de deux registres de pile. Le premier de ces registres est le registre A7, il est employé lorsque le 68000 tourne en mode User. Le second est le registre A7' qui est évidemment employé quand le 68000 tourne en mode Superviseur. Maintenant, voyons d'un peu plus près les différences entre les instructions :
Peut être est-il intéressant ici d'apporter quelques explications :
Puisqu'il est prévu que le 68000 rencontre des instructions qui n'existent pas ou autres bêtises, Il est donc conçu pour ne pas "coincer" bêtement dans de tels cas. Généralement, un 68000 est utilisé en mode User sur un ordinateur, le mode Superviseur étant en principe réservé au système d'exploitation de l'ordinateur. C'est le cas de notre cher ami gars. Vous avez donc votre programme qui tourne en mode User et tout d'un coup, une erreur est rencontrée. Voici comment réagi le 68000 :
Auparavant, je voudrais apporter encore quelques compléments afin d'être le plus précis possible. J'ai dit plus haut qu'en cas d'erreur, le 68000 "saute" dans le vecteur correspondant. Prenons un exemple : erreur de division par 0 : le 68000 analyse l'erreur et s'aperçoit qu'elle concerne le vecteur 5. Il charge alors son PC avec le contenu de l'adresse correspondante. Dans notre exemple, le contenu de l'adresse $014. Qu'y a-t-il dans ces adresses ? Patience j'y viens. Je voudrais d'abord vous expliquer pourquoi figurent dans le tableau les instructions CHK et TRAPV alors que je ne vous en avais pas parlé lors du recensement des erreurs. Ces deux instructions sont en quelque sorte bâtardes car elles ne déclenchent pas systématiquement (contrairement aux autres du tableau) un état d'exception.
Pour en terminer avec le 68000, une dernière précision est nécessaire. J'ai dit plus haut que lors d'une exception le 68000 empilait le PC et donc l'adresse à laquelle s'est produite l'erreur. Ceci n'est exactement vrai que dans le cas suivant : l'instruction litigieuse est codée sur deux octets. C'est le cas de l'instruction ILLEGAL $4AFC. Dans le cas d'une erreur d'adresse lors d'un adressage très compliqué, la valeur empilée peut être incrémentée de 2 à 12 octets proportionnellement à la longueur du codage machine de l'instruction. Ce phénomène propre au 68000 est appelé par ses concepteurs le phénomène d'anticipation. Voilà. Maintenant que nous avons ces quelques succinctes explications (si !) sur le 68000 nous pouvons nous intéresser à notre cher ordinateur. L'Amiga Si nous exceptons le cas très particulier de la réinitialisation (reset), nous voyons d'après le tableau que le 68000 peut avoir besoin du contenu des adresses $008 à $02C. Le contenu de ces adresses ne dépend bien sûr pas du 68000 mais est particulier à chaque ordinateur. Dans le cas de l'Amiga le contenu de ces adresses est lié à exec.library. C'est elle qui charge les bonnes valeurs lors de l'initialisation de la machine. Ces valeurs sont du genre $FCxxxx donc des sauts en ROM. Résumons : si le 68000 saute à des vecteurs d'exceptions, ce qui est fait lors de ces sauts ne dépend pas du 68000 mais de l'ordinateur. Voyons sommairement comment l'Amiga (en fait exec.library) traite les exceptions. D'abord, à son tour, exec détermine de quel vecteur d'exception il s'agit et ceci d'une manière très simple. Il lui suffit au début de la routine de traitement que cette routine regarde elle-même à quelle adresse elle tourne pour en déduire le numéro du vecteur. Ce numéro de vecteur est ensuite empilé lui aussi par exec sur la pile superviseur du 68000. Ce numéro de vecteur est au format long mot. On a donc finalement sur la pile un long mot pour le vecteur, un mot pour le registre SR, un long mot pour l'adresse de l'erreur. Encore une fois, le traitement de l'erreur d'adresse est beaucoup plus compliqué à cause du phénomène d'anticipation et exec elle-même est contrainte d'empiler quantité d'informations supplémentaires pour être en mesure de s'y retrouver par la suite. Ensuite exec recherche la tâche qui a provoqué l'exception 68000 et examine si cette tâche dispose d'un convertisseur de trap. Comme ce n'est généralement pas le cas, exec use de son convertisseur par défaut. Celui-ci nous envoie d'abord le message "Task Held Software Error. Finish ALL disk activity". Lorsque l'on clique sur "Cancel", exec essaie de lancer son propre débogueur appelé ROM-WACK. Celui-ci ne tournera que s'il y a un terminal branché sur le port série de l'Amiga. Comme ça n'est généralement pas non plus le cas, exec charge en D6 le numéro de vecteur de tout à l'heure, en D7 l'adresse de la tâche qui a planté, et appelle la très subtile routine Alert qui nous donne le très redouté message tout de rouge clignotant "guru méditation #0000000x,xxxxxxxx". Et comme l'alerte en question est une DEADEND_ALERT, l'Amiga fait une réinitialisation et tout est fichu. Deux remèdes Le bon et le mauvais. Il est possible d'envisager de modifier le contenu des adresses $008 à $02C afin de détourner le 68000 sur des routines de traitements personnelles. Ceci est évidemment faisable, mais c'est une mauvaise solution car l'aspect multitâche de l'Amiga ne sera plus respecté puisque le traitement sera le même quelle que soit la tâche qui plante. Si vous voulez un exemple de cette méthode, vous pouvez désassembler le très connu X-Copy. Ce programme a besoin de tourner en mode Superviseur afin de s'assurer d'être le seul à pouvoir accéder aux drives. Si vous désassemblez ce programme vous verrez au tout début qu'il modifie l'adresse de saut de la violation de privilège sur lui-même, fait un monstrueux MOVE to SR afin de déclencher l'exception et de continuer ainsi à s'exécuter en mode Superviseur. Voilà qui est bien bestial et délirant car ça ne trompe personne et il aurait été bien plus simple d'appeler la routine SuperState d'exec.library qui met l'Amiga en mode Superviseur on ne peut plus naturellement. Encore une fois, sur un Amiga, il est possible de tout programmer sans pour autant contrarier le système d'exploitation. Pour ceux qui voudraient faire tourner un programme en mode Superviseur, veillez bien à ne pas confondre la routine SuperState avec la routine Supervisor de la même exec.library car cette dernière est beaucoup plus chatouilleuse. Voyons maintenant la bonne solution. Pour cela, il faut se remémorer la structure Task : En conclusion, il faut bien fournir à la tâche un pointeur sur une routine personnelle dans le champ tc_TrapCode afin d'obtenir un convertisseur de trap spécifique à la tâche. Le pointeur tc_TrapData est tout simplement un pointeur dont on peut se servir pour pointer sur certaines données. Son initialisation n'est pas indispensable. Le programme Maintenant que tout cela est dit, parvenir au but que nous nous sommes fixés est d'une facilité sans nom. Le programme a été écrit sur Devpac 2. Les utilisateurs de Seka sont punis et devront faire l'adaptation eux-mêmes. Le programme, avant toute chose, commence par sauvegarder le pointeur de pile (User) et son contenu dès le départ. Tout simplement parce que la pile pointe à ce moment sur la routine de traitement de retour, que le programme soit lancé depuis Devpac ou depuis un CLI ou depuis ailleurs (ne le lancez pas par la fenêtre tout de même !). Le CLI utilise une pile "profonde", c'est pour cela que le pointeur de pile est sauvegardé en plus du contenu. Ensuite le programme recherche l'adresse de sa propre tâche afin de pouvoir initialiser le pointeur sur le convertisseur de trap. A toutes fins utiles et pour assurer, on sauvegarde également l'état du matériel au départ et le tour est joué. Il ne reste qu'à sauter au programme qui risque de planter. Le convertisseur Trap Qu'y fait-on ? A nouveau que des choses très simples. D'abord on sauvegarde en mémoire le contenu des registres afin de pouvoir savoir ce qu'ils contenaient au moment du plantage. Vous remarquerez que A7 n'est pas sauvegardé. En effet, ceci serait une faute car à ce moment nous sommes en mode Superviseur et que le pointeur de pile Superviseur est totalement indépendant du plantage. De plus, l'adresse mise sur la pile par le 68000 lors du plantage nous suffit pour connaître l'adresse litigieuse. On sauvegarde également en mémoire le registre SR. Ça peut être très utile de pouvoir connaître l'état des drapeaux au moment du plantage. Vous remarquerez également que l'on teste s'il s'agit d'une erreur d'adresse par cmp.l #$3,(sp) (je me permets de vous rappeler que c'est exec qui a mis le numéro de vecteur sur la pile. C'est bien pratique et voilà une raison de plus pour ne pas modifier les adresses de saut en bas de mémoire) auquel cas le traitement est légèrement différent à cause du format de la pile bien que le principe soit le même. Sont sauvergardés ensuite en mémoire le numéro de vecteur et l'adresse du plantage (il suffit d'aller les pêcher dans la pile Superviseur.). Maintenant que nous avons tout le nécessaire, nous allons quitter le mode Superviseur avec l'instruction RTE. Si nous le faisons tout de suite, le programme reprendra son cours derrière l'erreur précédente mais à nos risques et périls. Nous allons donc, avant de quitter le mode Superviseur par RTE, modifier le contenu de l'adresse de retour de la pile Superviseur afin de se retrouver dans un programme dont nous sommes sûrs en mode User. Ceci est réalisé par move.l #exit,2(sp). Exit Comme son nom l'indique, il s'agit maintenant de quitter le programme proprement. On ouvre graphics.library et intuition.library. Ensuite, on transforme les données numériques des registres précédemment sauvegardées en chaînes de caractères tout en les insérant en même temps dans une structure AlertMsg, ceci grâce à l'utilisation de RawDoFmt (exec.library). Comme je suis méchant, je ne peux pas m'empêcher de me rouler par terre de rire en voyant les efforts de programmations que certains font pour leur sortie de texte ! Je n'ai pratiquement jamais vu cette routine utilisée (correctement cela s'entend) dans des programmes (toujours cette manie de négliger le système d'exploitation ô combien puissant de notre Amiga). Moi, je suis un grand paresseux, vous commencez à le savoir, alors j'utilise RawDoFmt qui fait tout le travail pour moi, plutôt que programmer des routines (boguées ?) des quelques kilo-octets. Ensuite, à partir de l'adresse de base de la graphics.library, on va chercher l'adresse de l'ancienne liste Copper et on la réinitialise (de manière très paresseuse, cela s'entend, mais néanmoins efficace), puis on remet le matériel (DMA, Blitter, Raster...) dans l'état où il était avant de lancer le programme. Ainsi, si le programme testé est une mégadémo avec une liste Copper d'enfer, il est malgré tout possible d'en revenir sans casse. On envoie ensuite une RECOVERY_ALERT avec l'AlertMsg précédemment constitué de façon à simuler un Guru Meditation, avec en plus la possibilité de visualiser le contenu des registres. On ramène enfin l'écran du Workbench au premier plan (sait-on jamais...), on ferme les bibliothèques et on restaure la pile initiale et on s'en va, le tour est joué. Essayons le programme Celui-ci saute au label main. Si vous assemblez tel quel, sauter au label main, c'est sauter n'importe où, donc c'est un gourou assuré et un excellent (et paresseux) moyen d'essayer le programme. Vous pouvez aussi essayer :
...pour obtenir un gourou n°4.
...pour obtenir un gourou n°3.
...pour obtenir un gourou n°8. Et ainsi de suite à votre bon choix messieurs-dames ! Mais le mieux est d'insérer en "main" un source que l'on vient d'écrire, d'assembler le tout et d'essayer immédiatement pour voir si quelque chose ne va pas. Si c'est le cas, comme le programme vous rend la main, vous pouvez aller immédiatement désassembler le corps du délit avec un débogueur quelconque. Vous pouvez, pour les experts, relier ce programme avec un autre. Quelques conseils Je me permets de vous recommander de ne pas faire de faute de frappe dans ce programme. En effet, s'il y avait une erreur d'adresse pendant le traitement d'exception "erreur d'adresse", tout serait bloqué irrémédiablement dans le 68000 ! Il faudrait alors faire une réinitialisation. Ce programme ne peut évidemment pas servir en quoi que ce soit si vous programmez une routine d'interruption. En effet, il serait impossible de reprendre la main car l'interruption reviendrait tout le temps déclencher l'alerte. Il faut dans ce cas essayer votre routine indépendamment, puis ne la placer sous interruption qu'après être sûr que tout va bien. Si vous lancez votre programme depuis Devpac, ce dernier considère le programme comme un prolongement de lui-même. Dans ce cas, le convertisseur de trap est donc installé sur la tâche de Devpac. Ainsi, tout autre programme lancé plus tard continuera à bénéficier du convertisseur. Amusant, non ? Enfin, une dernière remarque. Le Guru Interceptor est utile pour déboguer les programmes intraçables mais n'est pas traçable lui-même. Le traceur planterait dès l'installation du convertisseur de trap.
|