|
||||||||||||||||||||||||||||||||||||||||||||||
|
Oui, vous avez bien lu : interruptions. Je sais, ça fait le troisième article sur ce sujet dans l'ANT, et vous vous attendez sûrement à du rabâchage... Eh bien non, car les interruptions ont été traitées seulement sous Exec. Moi, je vous propose d'aborder les interruptions sans passer par la moindre bibliothèque : on va faire ça à la main comme des grands. De plus, nous allons voir en détail les sources d'interruptions et leur utilisation. Enfin, nous jetterons un coup d'oeil sur les exceptions et les instructions TRAP. Allez, entrons dans le vif du sujet... Les interruptions matérielles Ces interruptions permettent soit de se synchroniser avec le matériel, soit d'effectuer des actions à des moments bien précis (ni avant, ni après). De plus, l'utilisation d'interruptions permet un gain de temps non négligeable : pendant que l'on attend un événement, on peut effectuer d'autres tâches (qui ne dépendent pas du résultat de cet événement, bien entendu). Étudions donc les registres de contrôle d'interruptions afin d'en mieux connaître la provenance et l'utilisation.
Registres modifiés par l'utilisateur pour spécifier ses choix.
Registres utilisés par le système pour lancer le processus d'interruption. Également modifiés par l'utilisateur une fois l'interruption effectuée (effaçage du bit de déclenchement de l'interruption). Le format de ces quatre registres (en fait, seulement deux, mais ayant chacun une adresse en lecture et une en écriture) est identique. La description qui suit est donc générale. Bit 15 : SET/CLR Lors d'une écriture dans INTENA ou INTREQ, si le bit 15 est mis, tous les autres bits à 1 du mot transféré sont également mis à 1 dans la destination et ceux qui étaient à 0 dans la source resteront inchangés. Si le bit 15 est à 0, tous les bits à 1 de la source seront effacés dans la destination et ceux qui étaient à 0 dans la source resteront inchangés. Exemples : Pour effacer les bits 0 à 7 d'INTREQ : move.w #$00ff,$DFF09C. Pour placer les bit 0 à 7 d'INTENA : move.w #$80ff,$DFF09A. Bit 14 : INTEN (Master Interrupt Enable) Les interruptions ne peuvent avoir lieu que si ce bit est mis à un dans INTENA. En le mettant à 0, vous supprimez toutes les tentatives d'interruption. Bit 13 : EXTER (IRQ niveau 6) Ce bit concerne l'interruption externe à travers le CIA B (voir la note ci-dessous). Bit 12 : DSKSYN (IRQ niveau 5) Ce bit concerne l'interruption ayant lieu lorsque la lecture des données d'un lecteur de disquette est synchronisée avec le contenu du registre DSKSYNC ($DFF07E). Pour que cette interruption soit déclenchée, il faut que la demande de synchronisation ait été faîte (bit 9 de ADKCON mis à 1). Bit 11 : RBF (Receive Buffer Full, IRQ niveau 5) Ce bit concerne l'interruption pour le port série 1. Elle sera exécutée si le tampon mémoire d'entrée (SERDATR, $DFF018) est plein et peut être lu par l'utilisateur (voir aussi le bit 0). Bits 10-7 : AUD3-0 (IRQ niveau 4) Ces quatre bits concernent les interruptions pour les canaux audio 3 à 0. En mode automatique, elles ont lieu dès que Paula a pris en compte le contenu des registres audio (que l'on peut alors changer). En mode manuel, elles ont lieu à la fin de chaque transmission d'un mot de donnée sonore (on charge alors le prochain mot). Pour plus de renseignements sur ces interruptions, reportez-vous à cet article. Bit 6 : BLIT (Blitter Finished, IRQ niveau 3) Ce bit concerne l'interruption ayant lieu dès que le Blitter a fini sa tâche. Notez que le bit 14 de DMACONR ($DFF002) permet aussi de savoir si le Blitter a fini son travail. L'utilisation de l'une ou l'autre méthode dépend du contexte. Mais, en règle générale, la seconde est plus souvent utilisée, surtout quand le Blitter est beaucoup sollicité (sinon, on perd trop de temps lors des interruptions). La première peut rendre de grands services quand deux transferts importants se suivent : le second transfert sera déclenché par interruption dès la fin du premier. Ceci permettra d'effectuer de nombreuses tâches dès l'envoi du premier transfert, sans se soucier du deuxième. Bit 5 : VERTB (Vertical Blank, IRQ niveau 3) Ce bit concerne l'interruption de retour du spot de balayage vidéo. Elle a lieu dès que celui-ci est arrivé en bas du moniteur et qu'il s'apprête à remonter. Bit 4 : COPPER (IRQ niveau 3) Ce bit concerne l'interruption... Copper (gagné !). C'est l'utilisateur qui décide de la déclencher, à l'aide d'une liste Copper appropriée. Bit 3 : PORTS (IRQ niveau 2) Ce bit concerne l'interruption externe à travers le CIA A (voir la note ci-dessous). Bit 2 : SOFT (IRQ niveau 1) Ce bit concerne les interruptions logicielles (voir article sur les interruptions sous Exec). Bit 1 : DSKBLK (IRQ niveau 1) Ce bit concerne l'interruption ayant lieu lorsque la transmission des données à travers le canal DMA disque est achevée (fin d'une lecture ou d'une écriture). Cette interruption est le seul moyen de savoir quand le lecteur a fini son travail. Bit 0 : TBE (Transmit Buffer Empty, IRQ niveau 1) Ce bit concerne l'interruption pour le port série 1. Elle est déclenchée lorsque le tampon mémoire de sortie (SERDAT, $DFF030) est près à recevoir de nouvelles données (voir aussi le bit 11). Note concernant les interruptions CIA : tout d'abord, notons que les CIA ont été traités dans ces articles : 1, 2 et 3. Vous savez sans doute qu'il existe plusieurs sources d'interruptions pour chaque CIA (A et B) : timers, clavier, etc. Donc, lorsqu'une interruption de niveau 2 ou 6 survient, il faut être capable de différencier la source de cette interruption au sein du CIA concerné. De plus, il faut pouvoir contrôler les interruptions (autorisation, interdiction) des CIA. Pour cela, et aussi bizarre que cela puisse paraître, nous ne disposons que d'un registre de contrôle par CIA ! Le fonctionnement est en fait très simple : lors de l'écriture, on fixe les interruptions autorisées et interdites à l'aide d'un masque (même fonctionnement que pour INTENA) et lors de la lecture, on se renseigne sur les interruptions survenues. En gros, c'est comme si nous avions une sorte d'INTENA (écriture) et d'INTREQR (lecture) dans le même registre. A chaque lecture de ces registres le contenu en est effacé : c'était le seul moyen d'indiquer qu'une interruption avait été traitée puisqu'une écriture n'agit que sur les autorisations d'IT. C'est bien pratique, mais attention à bien traiter toutes les interruptions survenues : il ne peut y avoir qu'une lecture, donc chargez le contenu du registre voulu dans un registre de donnée et effectuez ensuite vos tests, sinon, certaines interruptions risqueraient de ne jamais être traitées. Un autre problème se pose du fait de la structure de ces registres : comment savoir quelles interruptions sont autorisées avant de les traiter ? A la lecture, le bit 7 permet de savoir si au moins l'une des interruptions survenues est autorisée. Il faudra se contenter de cela, mais dans la plupart des cas ce sera suffisant (il est rare que deux interruptions aient lieu simultanément).
Attention, seul l'octet de poids faible est utilisé. Bit 7 : SET/CLR (écriture) - IR (lecture) En écriture, ce bit a les mêmes fonctions que le bit 15 d'INTENA (voir plus haut). En lecture, il permet de déterminer quelle interruption est survenue, d'après la table suivante :
Inutilisés. Bit 4 : FLG Interruption par drapeau (flag). Bit 3 : SP Interruption due au port série plein ou vide. Dans le cas du CIA A, cela signifie qu'une donnée émanant du clavier est arrivée. Bit 2 : ALRM Interruption de TOD alarme. Bit 1 : TB TIMER B arrivé en dessous de 0. Bit 0 : TA TIMER A arrivé en dessous de 0. Interruption, me voici ! Je ne reprendrai pas l'explication parfaite accomplie dans cet article. Je prends le relais juste au moment où le 68000, une fois passé en mode superviseur, saute au vecteur correspondant au niveau de l'interruption. Rappelons tout de même que ces vecteurs sont situés de $64 à $7C pour les interruptions de niveau 1 à 7. Voyons donc comment se déroule une routine de traitement d'interruption typique.
Voici maintenant une méthode permettant, dans certains cas, d'éviter la perte de temps causée par l'exécution d'une interruption et de simplifier les choses : primo, ne pas autoriser les interruptions ; secundo, faire une boucle d'attente testant INTREQR ; tertio, vider INTREQ. Cela est intéressant dans des cas d'attente du lecteur de disquette, d'un chronomètre, etc. Exemples d'application Les routines suivantes ne sont bien sûr pas directement exécutables, mais vous donneront un ordre d'idée de ce qu'il faut faire : c'est à mon avis le principal (un long source est moins intéressant). Bref, il s'agit d'une synchronisation toute bête avec l'écran. Première méthode : interruption simple ![]() ![]() ![]() Les interruptions n'étant finalement rien d'autre qu'un type particulier d'exception, il était normal de terminer cet article par leur étude. Pour définir simplement les exceptions, on pourrait dire qu'il s'agit d'interruptions système servant à la détection des erreurs et, dans le cas de l'Amiga, débouchant parfois sur les terribles Guru Mediations. Vous comprenez aisément l'intérêt de tout ceci : orienter les erreurs du système vers ses propres routines de traitement. C'est notamment ainsi que les débogueurs procèdent pour que vos programmes ne plantent pas l'ordinateur à la moindre erreur de votre part. Il existe deux types d'exceptions : celles causées volontairement par le programmeur (diverses instructions du 68000 sont spécialement prévues à cet effet), et celles causées involontairement, suite à un bogue du programme. Nous commencerons cette étude par la deuxième catégorie. La chasse aux bogues N'oublions pas que l'exception n'est qu'une interruption : il se passe exactement la même chose pour les deux et la programmation est aussi la même (RTE à la fin de la routine). La seule chose qu'il reste à savoir est : où mettre l'adresse de sa routine ? Cadeau, voici la liste des vecteurs d'exceptions, classés par adresses.
Comme vous l'avez deviné, nous allons maintenant étudier les instructions TRAP. Il en existe deux sortes : TRAP #vecteur Le vecteur est exprimé sur 4 bits ; il désigne donc un chiffre entre 0 et 15. Cette instruction a pour effet d'exécuter l'exception de numéro de vecteur indiqué (vecteur 0=$80->vecteur 15=$BC) après suivi du processus habituel d'interruption (passage en mode superviseur, etc.). La routine sur laquelle se branche le 68000 est programmée de la même manière qu'une interruption quelconque (sauvegarde des registres utilisés, fin par un RTE). L'intérêt d'une telle instruction est que l'on n'a pas besoin de connaître l'adresse de la routine pour l'exécuter ; on peut réaliser un système à morphologie variable assez facilement. Par exemple, le système d'exploitation complet de l'Atari ST (TOS et GemDOS) est entièrement basé sur les TRAP. ![]() TRAPV Si le bit V (bit 1) du mot d'état SR est mis lors de l'exécution de cette instruction, il se produit une exception, sinon rien ne se passe. Rappelons la signification de ce bit : il est utilisé pour indiquer à l'utilisateur que l'intervalle numérique ouvert a été dépassé lors d'une opération arithmétique. Par exemple : ![]() Instruction CHK CHK <adresse effective>,Dn Cette instruction vérifie que le mot de poids faible du registre spécifié Dn est compris entre 0 et une limite supérieure donnée. Si ce n'est pas le cas, c'est-à-dire si Dn.W<0 ou Dn.W><adresse effective>, alors l'exception CHK est déclenchée.
|