Obligement - L'Amiga au maximum

Mardi 16 octobre 2018 - 02:23  

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 - Guru Interceptor
(Article écrit par Frédéric Mazué et extrait d'Amiga News Tech - novembre 1990)


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 :
  • Les instructions dites "normales" peuvent être utilisées indépendamment du mode de fonctionnement du microprocesseur, c'est-à-dire aussi bien en mode User qu'en mode Superviseur.
  • Les instructions dites "priviligiées" ne peuvent être utilisées qu'en mode Superviseur.
Bien. L'un des secrets de l'informatique est de tout prévoir (ou tout au moins d'essayer !) avant de concevoir ou de programmer quelque chose. Les concepteurs du 68000 n'ont bien sûr pas manqué de se conformer à cette règle et ont prévu les cas suivants :
  • Le 68000 est mal connecté (au niveau matériel) et une erreur de bus est engendrée.
  • Le 68000 n'est pas conçu comme ayant la capacité de transférer un mot ou un long mot à une adresse impaire. Si on le force, il y a erreur d'adresse.
  • Le microprocesseur rencontre une instruction qui n'existe pas. Il y a alors erreur dite d'instruction illégale.
  • Le microprocesseur rencontre une instruction non implémentée, c'est-à-dire dont le code binaire commence par 1111 ou 1010. Ceci est légèrement différent de l'instruction illégale.
  • On demande au 68000 d'effectuer une division par zéro. Il y a impossibilité.
  • Un programme tournant en mode User veut utiliser une instruction "privilégiée", il y a erreur de violation de privilège.
A propos, voici l'énumération des instructions privilégiées : STOP, RESET, RTE, MOVE to SR, ANDI to SR, EORI to SR, ORI to SR, MOVE USP.

Peut être est-il intéressant ici d'apporter quelques explications :
  • Les instructions STOP et RESET sont assez hermétiques, prochainement je vous donnerai une application avec RESET.
  • RTE sera expliquée en détail plus loin.
  • Les quatre instructions suivantes montrent qu'il est interdit en mode User d'accéder au registre SR. Par contre, il est permis d'en lire le contenu avec une instruction du type MOVE SR to. Le registre SR, dit registre d'état, est un registre 16 bits qui peut en fait être considéré comme un double registre 8 bits. C'est la partie poids fort qui est intouchable, la partie poids faible, qui contient des drapeaux genre carry, overflow et autres, peut, elle, être manipulée sans problème. Cette partie poids faible est sous-nommée registre CCR. Donc une instruction de type MOVE to CCR peut être employée en mode User.
  • Enfin, l'instruction MOVE USP est une instruction qui peut être très appréciée par les fins bidouilleurs que vous êtes. Elle permet, lorsque le 68000 est en mode superviseur, et donc lorsqu'il utilise le registre A7' comme pointeur de pile, d'accéder au pointeur de pile qui sera utilisé lors du retour en mode User (l'inverse n'est pas possible). Ceci permet de se construire des routines bien surprenantes pour celui qui voudrait jeter des regards indiscrets dans vos programmes. Je laisse libre cours à votre imagination...
Venons-en au fait

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 :
  1. Il passe en mode Superviseur.
  2. Il examine de quel type d'erreur il s'agit.
  3. Il empile (dans la pile Superviseur), d'abord le contenu du PC, c'est-à-dire l'adresse du programme à laquelle s'est produite l'erreur sur un long mot, puis le contenu du registre SR au moment de l'erreur. Les très fins bidouilleurs auront peut-être besoin de savoir que le registre SR est également conservé dans une mémoire interne au 68000. Je viens de dire plus haut que le 68000 examine de quelle erreur il s'agit : dans le cas d'une erreur d'adresse, le contenu de la pile sera un peu plus compliqué même si le principe reste le même. Je n'entre pas ici dans le détail car ce serait inutile. Vous pouvez toujours vous reporter aux livres spécialisés.
  4. Enfin le 68000 saute dans un vecteur de routine dite routine de traitement d'exception. Il doit exister en effet une routine de traitement pour chaque type d'erreur. Les concepteurs du 68000 appelle ça des exceptions tout simplement parce qu'elles doivent se produire exceptionnellement (si si !).
  5. Les adresses des routines de traitement doivent figurer en bas de la mémoire (voir tableau).
  6. A la fin de la routine de traitement doit figurer l'instruction RTE qui provoque le dépilement inverse à l'empilement précédent (RTE dépile le registre SR), remet le 68000 en mode User et le programme repart à l'instruction trouvée derrière celle qui a provoqué l'erreur.
Voici maintenant un petit tableau :

assembleur

J'en vois déjà qui, à la vue de ce tableau, sentent d'où vient le vent : un programme comportait une erreur d'adresse et il y a eu un gourou n°3 : c'est le vecteur correspondant à l'erreur adresse. Ceux qui ont essayé de diviser par 0 ont eu un gourou n°5 : c'est à nouveau le vecteur correspondant à cette erreur. Mais patience nous allons entrer dans ces détails un peu plus tard.

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.
  • CHK sert à faire un test d'encadrement. Si le résultat est vrai, le programme continue normalement son cours, sinon l'exception de vecteur 6 est déclenchée.
  • TRAPV. Si le drapeau V (overflow) dans le registre d'état est faux le programme se poursuit comme si de rien n'était. Si le drapeau V est vrai, l'exception de vecteur 7 est déclenchée.
Aussi étonnant que cela puisse paraître, l'utilisation de ces deux instructions dans des programmes "mathématisants" peut parfois considérablement simplifier la vie. Aussi ne faut-il pas les négliger.

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 :

assembleur

Il est maintenant possible de faire le point. D'une part j'ai parlé d'exception à propos du 68000 et d'autre part j'ai dit qu'exec examinait si la tâche disposait d'un convertisseur de Trap. Y aurait-il de ma part une incohérence ? Eh bien non car la terminologie Motorola (pour le 68000) n'est pas la même que la terminologie Commodore. Ce que Motorola appelle une exception, Commodore appelle ça un Trap. Il ne faut donc pas se tromper de champ dans la structure Task. On pourrait être tenté d'y introduire un pointeur sur une routine personnelle en tc_ExceptCode mais on aurait tout faux car l'état d'exception d'une tâche est quelque chose qui n'a rien à voir avec le sujet de cet article.

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 :

main
 ILLEGAL

...pour obtenir un gourou n°4.

main
 lea $1000,a0
 add.l #$1,a0
 move.l (a0),d0

...pour obtenir un gourou n°3.

main
 move.w #$ffff,sr

...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.

assembleur
assembleur
assembleur
assembleur


[Retour en haut] / [Retour aux articles]