|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Un dernier pour la route ! Dans la foulée des précédentes séries d'articles prenant pour prétexte la programmation d'une cracktro (1 et 2) et celle d'un défilement sinusoïdal (1, 2, 3, 4 et 5), pour présenter dans le détail la manière d'attaquer le matériel de l'Amiga 500 en assembleur 68000 après avoir court-circuité le système d'exploitation, il convient de revenir sur un point délicat qui a été soulevé, à savoir celui de la gestion des "interruptions matérielles". Le problème plus particulièrement étudié est le suivant : comment mettre sous "interruption VERTB" un bout de code dont l'unique fonction est de modifier la couleur de fond (COLOR00) ? Pour bien en visualiser les effets, nous allons élaborer un scénario au fil duquel la couleur de fond sera notamment modifiée par un tel bout de code quand le faisceau d'électrons atteindra certaines positions verticales : ![]() Changement de COLOR00 par le Copper, le programme principal et un gestionnaire d'interruption VERTB
Comme cela a été expliqué dans un précédent article, le matériel de l'Amiga génère des événements qui peuvent entraîner des interruptions de niveau 1 à 6 du processeur. Par exemple, l'événement VERTB, qui survient quand le faisceau d'électrons ayant atteint le bas de l'écran entame son retour vers le haut de ce dernier pour afficher la trame suivante, peut entraîner une interruption de niveau 3 du processeur. Le processeur cesse alors d'exécuter le programme principal pour exécuter le gestionnaire d'interruption dont l'adresse est stockée dans une table des vecteurs d'interruptions. S'agissant d'une interruption de niveau 3, le M68000 8-/16-/32-Bit Microprocessors User's Manual nous apprend qu'il s'agit du vecteur 27, qui renvoie au gestionnaire d'interruption logé à l'adresse $6C : ![]() Appel du gestionnaire de l'interruption Level 3 Interrupt Autovector en cas d'événement VERTB La liste des événements signalés le matériel qui sont susceptibles d'entraîner une interruption de niveau 1 à 6 du processeur figure dans la documentation du registre INTENA dans l'Amiga Hardware Reference Manual :
Le matériel signale toujours les événements énumérés en positionnant les bits correspondants dans un autre registre, INTREQ. Par contre, il n'interrompt pas toujours le processeur dans la foulée. En effet, tout dépend de la gestion que le registre INTENA permet de configurer. Lorsqu'une valeur 16 bits X est écrite dans INTENA, l'état du bit SET/CLR dans X indique si un bit d'INTENA désigné par un bit positionné dans X doit être positionné ou effacé. Par exemple, pour inhiber le déclenchement d'une interruption niveau 3 du processeur sur un événement VERTB, nous devons écrire $0020 dans INTENA. A l'inverse, pour l'activer, nous devons écrire $8020 dans ce registre. Toutefois, il faut encore compter avec le bit INTEN. Le bit INTEN est une sorte d'interrupteur général :
Pour cette raison, seule une lecture de INTENAR, le registre homologue de INTENA en lecture seule - INTENA est en écriture seule -, permet d'être exactement renseigné sur la gestion des interruptions matérielles à un instant donné. Couper les interruptions matérielles Nous n'allons pas rentrer dans le détail des opérations requises pour mettre en place un environnement de développement en utilisant WinUAE pour Windows et pour écrire un programme minimal en assembleur 68000 qui prend le contrôle total du matériel de l'Amiga. Tout cela a déjà été soigneusement exposé dans cet article. D'emblée, concentrons-nous donc sur ce que concerne la gestion des interruptions matérielles. Après avoir court-circuité le système d'exploitation, il s'agit de prendre le contrôle des six vecteurs d'interruption du processeur qui, parmi les 255 dont ce dernier dispose, correspondent aux interruptions de niveau 1 à 6. Autrement dit, les vecteurs 25 à 30. Tout d'abord, nous devons sauvegarder l'état de la gestion des interruptions matérielles pour la restaurer à la fin. Pour cela, nous lisons et stockons dans des variables intena et intreq le contenu des registres INTENAR et INTREQR, versions en lecture seule des registres INTENA et INTREQ :
Nous pouvons alors couper toutes les interruptions matérielles :
Dans la foulée, nous acquittons tous les événements que le matériel pouvait avoir signalés. Certes, le matériel va continuer à en signaler, mais du moins saurons-nous à partir de cet instant que si le matériel positionne un bit dans INTREQ, ce sera pour signaler un événement survenu après que nous ayons pris la main :
Enfin, nous détournons les vecteurs des interruptions de niveau 1 à 6 en les renvoyant sur un sous-programme de type gestionnaire d'interruption qui nous appartient. Ces vecteurs pointent sur les adresses $64 à $78 :
Plus loin, après le RTS final du programme principal, nous programmons le gestionnaire d'interruption _rte en question :
Ce gestionnaire ne fait donc strictement rien, pas même acquitter la notification de l'événement matériel qui est à l'origine de son appel - nous reviendrons sur ce point. Un scénario à base d'interruptions VERTB Comme expliqué, INTREQ fonctionne exactement comme INTENA, sauf qu'il sert à signaler des événements plutôt qu'à activer/inhiber le déclenchement d'interruptions du processeur associées aux événements en question. Il faut rajouter deux choses sur INTREQ :
Commençons la liste Copper. Cette dernière se résume à une instruction WAIT pour attendre le faisceau d'électrons atteigne ou dépasse la mi-hauteur de l'écran, suivie d'une instruction MOVE pour stocker $0F00 (rouge) dans COLOR00 :
Bien évidemment, nous n'oublions pas de terminer la liste Copper par le traditionnel WAIT impossible :
Programmons maintenant le programme principal. Tant que l'utilisateur n'a pas pressé le bouton gauche de la souris, ce programme attend en boucle le faisceau d'électrons au premier quart de hauteur de l'écran. Il stocke alors $00F0 (vert) dans une variable color dans laquelle le gestionnaire d'interruption VERTB trouvera la couleur à stocker dans COLOR00. Enfin, il provoque l'appel à ce gestionnaire en générant une interruption VERTB par une écriture dans INTREQ :
Attention ! Ce code doit être complété par une boucle _wait1 venant immédiatement après la boucle _wait0. Son objet est d'attendre le faisceau d'électrons à la ligne 0 :
En effet, l'exécution du code de notre programme prend moins de temps qu'il n'en faut au faisceau d'électrons pour terminer de balayer la ligne DISPLAY_Y+(DISPLAY_DY>>2). A défaut d'attendre une ligne suivante - en l'occurrence, la ligne 0, mais cela aurait parfaitement pu être la ligne du dessous DISPLAY_Y+(DISPLAY_DY>>2)+1 -, le code du programme ne serait donc pas exécuté une fois par trame, mais plusieurs fois, ne produisant alors pas l'effet désiré. Enfin, il nous reste à programmer le gestionnaire d'interruption. Avant toute chose, il faut se rappeler qu'un gestionnaire d'interruption est un sous-programme exécuté alors qu'un programme était en cours d'exécution. Partant, il faut au moins sauvegarder le contenu des registres qui seront utilisés par le gestionnaire pour être en mesure de les restaurer quand ce dernier se terminera. Cela s'effectue généralement à l'aide de l'instruction MOVEM pour stocker et lire le contenu de registres dans la pile. Ensuite, il faut savoir que tant que l'événement n'est pas acquitté en effaçant son bit dans INTREQ, le matériel génère l'interruption processeur associée. Pour s'en convaincre, il suffit d'oublier d'acquitter l'événement VERTB dans le gestionnaire d'interruption et de modifier le programme principal pour lui demander de passer en boucle COLOR00 à $0FF0 (jaune) :
L'écran est totalement bleu ou presque, n'étant ponctué de jaune que lors des rares cycles d'exécution que la fin du gestionnaire d'interruption permet au processeur de récupérer pour continuer à exécuter le programme, avant qu'il ne doive exécuter de nouveau le gestionnaire d'interruption : ![]() Le programme est presque court-circuité par le gestionnaire d'interruption qui n'acquitte pas VERTB Dès lors, le code de notre gestionnaire d'interruption doit au moins être le suivant :
En l'espèce, son code complet est le suivant :
La liste Copper, le programme principal et le gestionnaire d'interruption étant programmés, il ne reste plus qu'à activer l'ensemble. Après avoir activé le Copper et lui avoir demandé d'exécuter en boucle la liste Copper, il suffit de stocker l'adresse de notre gestionnaire d'interruption à l'adresse contenue dans le vecteur de l'interruption de niveau 3 du processeur, c'est-à-dire $6C... :
...avant d'autoriser de nouveau le matériel à générer cette interruption de niveau 3 quand il détecte l'événement VERTB :
Le résultat produit est celui présenté dans l'image du début de cet article. Restaurer les interruptions matérielles Lorsque l'utilisateur clique sur le bouton gauche de la souris, le programme se termine en rendant aussi proprement que possible la main au système d'exploitation. Sans revenir sur les autres tâches qui nous incombent - elles sont détaillées dans cet article -, concentrons-nous donc encore sur ce que concerne la gestion des interruptions matérielles. Nous commençons donc pas couper de nouveau la génération d'interruptions processeur par le matériel... :
...avant de restaurer les vecteurs d'interruption... :
...et de rétablir la génération des interruptions processeur par le matériel :
C'est fini ! Une subtilité pour conclure... Noter que le Copper a accès en écriture à INTREQ. Par conséquent, il est parfaitement possible de déclencher une interruption VERTB à n'importe quelle position du faisceau d'électrons que le Copper peut repérer - comme cela a été expliqué dans cet article, ses capacités en la matière sont limitées, la granularité verticale étant certes d'une ligne, mais la granularité horizontale n'étant que de 4 pixels en basse résolution. Il suffit donc d'utiliser un MOVE dans la liste Copper :
A titre, d'exemple, voici le résultat produit par une variante du programme présenté plus tôt. Ici, le Copper déclenche une interruption VERTB quand le faisceau d'électrons atteint ou dépasse le dernier quart de la hauteur de l'écran, avec pour effet de passer COLOR00 à $0000 (noir) : ![]() Version plus complexe où le Copper déclenche une interruption VERTB
|