CODINGCLASSEURS WEKA ★ Comment exploiter toutes les ressources et augmenter les performances de votre AMSTRAD CPC ★

4/2.11 III - Les interruptions logicielles (SOFT) (16e Complément)Coding Classeurs Weka
4/2 - Assembleur Z80 : Définitions et rappels de base

4/2.11 Les interruptions sur Amstrad

III - Les interruptions logicielles (SOFT)

A partir du signal INT qui branche sur un programme de traitement (voir Partie 9, Chap. 8.12, pages 6 à 8), sont gérées ce que l'on appellera des interruptions logicielles. Cela signifie que ce sont des interruptions qui sont installées par le programmeur, et qui sont placées à des endroits bien déterminés pour pouvoir les utiliser.

Il existe trois interruptions logicielles que l'on appelle dans l'ordre de leurs priorités :

  • FAST TICKER la plus prioritaire,
  • FRAME FLY,
  • TICKER de priorité moindre, et programmable.

Dans chacune de ces interruptions il est possible à vous, utilisateur, d'insérer vos propres routines de traitement, en plus de celles déjà existantes ; certaines pouvant même être programmables.

Dans la suite de notre exposé, nous appellerons, un peu abusivement, mais pour utiliser le même langage que beaucoup de programmeurs sur CPC, chaque traitement que nous pourrons ajouter, une interruption.

Avant d'entrer dans l'étude de la gestion de ces trois types d'interruptions, nous allons définir ici les termes que vous rencontrerez souvent dans nos explications et dans tout ce qui concernera les interruptions en langage machine :

  • Le mot EVENEMENT (EVENT en anglais) correspond à ce que l'on peut appeler une interruption logicielle, branchant à un traitement particulier, et désiré bien sûr.
  • Un BLOC d'EVENEMENT (EVENTBLOCK) est une zone de la mémoire RAM dans laquelle sont placées les indications concernant la nature de l'événement à traiter : la fréquence du traitement, la priorité sur d'autres événements, l'endroit où se trouve la routine de traitement, le type d'interruption, et éventuellement un compteur permettant de programmer l'interruption.
  • La FILE D'ATTENTE (PENDING QUEUE) est une sorte de pile dans laquelle se trouvent les événements à traiter. Cette file est gérée de façon relativement complexe, réalisée à l'aide d'un pointeur, se déplaçant en gérant à la fois l'ordre d'insertion de l'interruption et sa priorité.

Heureusement pour le programmeur, la gestion de ce pointeur est totalement transparente, car effectuée par le logiciel de base.

Nous allons pouvoir maintenant étudier une à une les différentes interruptions programmables en langage machine, et nous verrons comment insérer soi-même sa propre routine d'interruption dans chacune d'elle.

L'interruption fast ticker

FAST TICKER est l'interruption la plus rapide conçue sur l'AMSTRAD-CPC. Elle est générée grâce au VGA sur la broche INT du microprocesseur Z80.

Nous avons étudié, Partie 9, Chapitres 8.11 et 8.12 dans la création d'un programme de sauvegarde écran et d'un utilitaire de CAPS LOCK interactif, comment détourner le traitement de cette interruption, et par là même insérer d'autres traitements.

Cette façon de procéder, même si elle est particulièrement simple et adaptée dans le programme proposé alors, est peu orthodoxe car elle rend la routine ajoutée comme étant la plus prioritaire, ce qui n'est pas toujours le cas, et dans l'éventualité de traitements longs elle perturberait le fonctionnement de votre CPC.

Il a été en effet prévu des vecteurs en RAM permettant d'ajouter et d'ini-tialiser un nouvel événement, de le désactiver et le réactiver à volonté. Il est même possible d'ajouter d'autres événements ultérieurement.

Ajouter et initialiser un événement

Nous vous proposons d'étudier la façon dont est ajouté un nouvel événement de type FAST TICKER.

Premièrement, il nous faut connaître le vecteur placé en RAM qui permet cet ajout. Vous trouverez cette adresse Partie 4, Chap. 2.7, page 55 : il s'agit de &BCE0 qui est le vecteur accédant à la routine dénommée KL-NEW-FAST-TICKER.

Cette routine initialise et positionne un bloc dans la file d'attente d'interruption rapide.

Avant d'étudier comment nous allons pouvoir placer un bloc à l'aide de KL-NEW-FAST-TICKER, nous vous proposons d'étudier la façon dont le logiciel de base insère le bloc. Précisons auparavant que cette étude a été effectuée sur CPC-6128, que les adresses que nous vous fournirons sont identiques sur CPC-664, mais qu'elles sont peut-être légèrement différentes sur CPC-464. Par contre la façon dont la routine insère le bloc doit être identique, ce qui ne change rien à notre étude.

Pour étudier la routine, comme pour toute routine Assembleur que vous désirez extraire de la ROM pour la décortiquer, il nous faut en connaître les conditions d'entrée, sous peine de devoir les rechercher, ce qui n'est pas impossible, mais demande une grande compétence et un travail acharné sur l'Assembleur et l'organisation de la mémoire du CPC.

Les conditions d'entrée portent sur 6 registres du Z80 : le registre double HL doit contenir l'adresse du bloc d'interruption, le registre B contient le type d'interruption, C contient le numéro de la ROM à sélectionner pour traiter l'interruption, et le registre double DE contient l'adresse de la routine exécutant la procédure d'interruption.

Une question se pose : Que doit, ou va contenir le bloc d'interruption, et quelle taille doit-il atteindre ? (i.e. combien d'adresses mémoire doit-il occuper ?). L'étude qui suit à pour but de donner une réponse à ces questions.

Le vecteur de KL-NEW-FAST-TICKER, par l'intermédiaire d'une instruction RESTART, notée RST, effectue une commutation de la ROM inférieure, la ROM du système d'exploitation, et se branche directement à l'adresse &0176.

A partir de cette adresse, nous trouvons le programme suivant :

0176 E5 KLNFT: PUSH HL
0177 23 INC HL
0178 23 INC HL
0179 CD D2 01 CALL INIT
017C E1 POP HL
017D 11BBB8 LD DE,LISTFT
0180 C3 79 03 JP CHAINE

A l'adresse &0176, l'adresse du bloc est sauvegardée dans la pile système (PUSH HL), puis incrémentée pour pointer deux octets supérieurs (INC HL deux fois).

Un appel au sous-programme que nous avons dénommé INIT (JP INIT), situé à l'adresse &01D2, que nous désassemblerons plus loin, permet de créer le bloc d'événement.

Au retour, l'adresse du bloc est récupérée dans HL (POP HL), l'adresse du premier bloc de la chaîne existante est chargée dans le registre DE (LD DE, LISTFT — cette adresse est l'adresse RAM &B8BB), puis on saute à la routine que nous avons appelée CHAINE (JP CHAINE) en &0379, qui permet de valider le bloc dans la file d'attente, ce qui rendra l'interruption active.

Nous allons maintenant étudier le sous-programme appelé INIT. Rappelons ses conditions d'entrée : ce sont les mêmes que pour KLNFT, sauf le registre double HL qui pointe sur l'adresse du bloc plus deux octets.

Voici le désassemblage de INIT :

01D2 F3 INIT: DI
01D3 23 INC HL
01D4 23 INC HL
01D5 36 00 LD (HL), 0
01D7 23 INC HL
01D8 70 LD (HL), B
01D9 23 INC HL
01DA 73 LD (HL), E
01DB 23 INC HL
01DC 72 LD (HL),D
01DD 23 INC HL
01 DE 71 LD (HL),C
01DF 23 INC HL
01E0 FB EI
01E1 C9 RET

Ce sous-programme inhibe d'abord les interruptions (Dl) pour éviter une perturbation dans l'installation, incrémente deux fois HL (INC HL deux fois) afin de réserver deux octets en mémoire pour gérer la file d'attente.

Ensuite sont rangées dans l'ordre croissant des adresses : l'octet nul 0 pour initialiser un compteur, puis les caractéristiques de l'interruption (B, E, D, C).

HL est une dernière fois incrémenté pour pointer l'adresse la plus haute du bloc d'événement (ceci est particulièrement utile pour ajouter un nouveau bloc, si l'on avait travaillé avec TICKER.

Enfin, DI revalide les interruptions, et on retourne à KLNFT par RET.

On trouve ainsi, en fin de sous-programme, la zone mémoire suivante occupée :

adresse BLOC 8 C numéro de la ROM sélectionnée
haute BLOC 7 D adresse haute routine
BLOC 6 E adresse basse routine
BLOC 5 B type d'interruption
BLOC 4 0 réservé pour compteur
BLOC 3 X réservé
BLOC 2 X réservé
adresse BLOC 1 X réservé
basse BLOC 0 X réservé

BLOC 0 est l'adresse pointée initialement par HL.

Les adresses BLOC 0 et BLOC 1 sont réservées par le système pour chaîner l'interruption, ce que nous étudierons ultérieurement.

BLOC 2 et BLOC 3 sont aussi deux adresses qui sont réservées par le système, pour gérer la file d'attente d'interruptions rapides.

Nous nous apercevons déjà que la longueur à réserver pour un bloc d'interruptions rapides est de 9 octets. Ce bloc ainsi créé, il faut maintenant l'activer, ce que réalise la routine CHAINE.

Les conditions d'entrée sont : HL contient l'adresse basse du bloc d'interruptions. DE contient la première adresse de la liste de chaînage des blocs.

0379 EB CHAINE: EX DE, HL
037A F3 DI
037B CD 69 03 CALL EXAMIN
037E 38 06 JR C,FIN
0380 73 LD (HL),E
0381 23 INC HL
0382 72 LD (HL),D
0383 13 INC DE
0384 AF XOR A
0385 12 LD (DE), A
0386 FB FIN: El
0387 C9 RET

EX DE, HL permet d'utiliser la première adresse de la liste comme pointeur dans HL, l'adresse du bloc devenant la valeur à traiter (à sauvegarder).

On inhibe les interruptions (Dl), puis on accède à un sous-programme (EXAMIN étudié plus loin), qui permet de vérifier si le bloc d'interruption est déjà chaîné, afin d'éviter d'avoir deux fois la même interruption.

Au retour de EXAMIN, HL contient l'adresse basse du bloc identifiant l'interruption précédente, et l'indicateur C précisant si l'interruption est déjà chaînée (nous verrons cela ci-dessous).

Si le bloc est déjà chaîné (carry = 1), alors JR C, FIN envoie à la fin de la routine, sinon l'adresse basse du bloc est sauvegardée à l'adresse basse du bloc précédent (LD (HL), E), puis la même chose est réalisée pour les adresses hautes (INC HL - LD (HL), D), enfin la valeur 0 (XOR A) est placée dans l'adresse basse plus un du bloc d'interruptions que l'on vient de chaîner (INC DE - LD (DE), A), afin de l'identifier comme étant le dernier bloc installé.

Il nous reste à étudier le sous-programme EXAMIN qui vérifie si l'événement n'est pas déjà chaîné dans la file d'attente.

0369 79 EXAMIN: LD A, (HL)
036A BB CP E
036B 23 INC HL
036C 7E LD A,(HL)
036D 2B DEC HL
036E 20 03 JR NZ,EXAM1
0370 BA CP D
0371 37 SCF
0372 C8 RET Z
0373 B7 EXAM1 : OR A
0374 C8 RET Z
0375 6E LD L,(HL)
0376 67 LD H,A
0377 18 F0 JR EXAMIN

Même connaissant les conditions d'entrée (HL contient la première adresse de bloc pour le chaînage, et DE contient l'adresse du bloc à installer), il est relativement compliqué d'étudier ce programme au vu des différents sauts effectués et du non respect des structures algorithmiques (il nous a donné quelques octets à retordre).

Nous fûmes obligés d'en rechercher l'ordinogramme, que nous vous proposons ci-dessous.

Dans cette routine, difficilement explicable car truffée d'astuces de programmation, il est d'abord testé si l'adresse du bloc à ajouter est présente (LD A, (HL) - CP E - INC HL - LD A,(HL) - CP D - RET Z). De plus, juste avant le retour, le carry est placé à 1 (SCF) ; le retour est conditionné par la double égalité entre E et (HL) et D et (HL + 1). S'il n'y a pas égalité, on effectue un OU de A avec lui-même (OR A), ce qui conserve à A sa valeur, mais annule le carry et modifie l'indicateur de zéro.

Si A est égal à zéro, alors on sort de la routine (RET Z), sinon il faut examiner le bloc suivant (LD L, (HL) - LD H,A - JR EXAMIN).

Tout ce processus est répété jusqu'à ce que l'on trouve une place libre pour chaîner l'événement, s'il n'est pas encore chaîné. Cette place sera dans le dernier bloc précédemment installé. Le chaînage s'effectuera dans la routine CHAINE, car en sortie, HL contiendra l'adresse du précédent bloc.

Le chaînage, une fois effectué, chacun des blocs sera constitué d'une zone de 9 octets en mémoire RAM, sauf pour le bloc initial qui ne comportera que deux octets.

Dans le cas de l'initialisation et le chaînage de 3 blocs, la disposition du chaînage sera représentée telle la figure 1.


Fig. 1

L'adresse du bloc initial se trouve en &B8BB - &B8BC .(fixée par le logiciel du système d'exploitation du CPC). A cette adresse on trouve l'adresse de base du bloc numéro 1. A partir de celle-ci, se trouve l'adresse de base du bloc numéro 2, où l'on trouve celle du bloc numéro 3. Dans le bloc numéro 3, à l'adresse de base plus un, on trouve la valeur zéro qui signifie que le bloc numéro 3 est le dernier bloc de la chaîne d'interruptions.

Une question se pose encore : nous connaissons les registres à utiliser en tant que conditions d'entrée et la façon dont ils sont insérés dans le bloc d'interruptions (voir Fig. 2), mais quelles valeurs doit-on leur affecter ?


Fig. 2 : Structure d'un bloc.

Le contenu du registre HL n'est pas inséré dans le bloc, mais dans le bloc précédent (adresse de chaînage).

Le registre C contiendra le numéro de la ROM supérieure où se trouve la routine d'interruption à installer. Le contenu de ce registre est important si vous avez créé vos propres ROMs et qu'elles contiennent des routines fonctionnant sous interruption logicielle. Dans le cas le plus probable qui est celui où nous ajouterons nos routines en mémoire vive, cette valeur pourra être quelconque.

Le registre double DE contient l'adresse à laquelle débute la routine de traitement de l'interruption.

Le registre B contient ce que l'on peut appeler la classe de l'événement, c'est-à-dire l'importance qu'il peut avoir vis-à-vis d'autres événements.

La valeur contenue dans ce registre possède une signification bit par bit, aussi nous vous l'avons décomposé en figure 3.

  • le bit b0 permet de signaler l'emplacement de la routine, soit en RAM, soit en ROM. Si ce bit est positionné à 0, l'adresse se situe dans une ROM supérieure (adresse comprise entre &C000 et &FFFF, et dans ce cas le registre C prend toute son importance), sinon, cela signifie qu'elle se trouve soit en ROM inférieure (adresse comprise entre &0000 et &3FFF), soit en RAM centrale (entre &4000 et &BFFF, à partir de &C000 on se trouve dans la mémoire écran). Dans les applications que nous vous donnerons, on se placera toujours en RAM centrale, donc b0 égal à 1.


Fig. 3 : Contenu du registre B.

  • Les bits b4, b3, b2, b1 permettent de définir la priorité de l'interruption, sachant que la combinaison 0000 est la moins prioritaire, et que 1111 correspond à un événement très prioritaire, toutes les combinaisons intermédiaires étant possibles.
  • Le bit b5 sera toujours placé à 0.
  • Le bit b6 permet de déterminer si l'interruption est une interruption expresse ou non. S'il est positionné au niveau 1, l'événement est considéré comme express et sera traité prioritairement par rapport aux événements dits classiques, pour lesquels ce bit aura été placé à 0.
  • Le bit b7 permet de rendre un événement asynchrone en le positionnant à 1, c'est-à-dire qu'il sera exécuté immédiatement, sans être placé dans la file d'attente, s'il a été déclaré comme express (b6 = 1). Si b7 est positionné à 0, l'interruption est considérée comme synchrone, et baisse en priorité.

Ainsi, si l'on veut placer une interruption expresse synchrone, de priorité 3 en RAM centrale, les valeurs de b7b6b5b4b3b2b1 seront : 01001111, ce qui donnera pour B une valeur hexadécimale égale à &4F.

Lorsque l'on désire qu'un événement soit réalisé en mode programmation sous Basic, il faut placer l'événement dans le mode asynchrone (b7 = 1).

Nous vous conseillons de ne pas rendre vos événements de type FAST TICKER trop prioritaires, sous peine de ne plus laisser de temps au logiciel de base pour se préoccuper des tâches vitales au bon fonctionnement de votre CPC : une combinaison b7b6b5b4b3b2b1 = 110111111 = & DF assure un blocage de votre CPC et une réinitialisation pour reprendre la main.

Retirer un événement FAST TICKER

Pour retirer un événement de type FAST TICKER, nous avons à notre disposition le vecteur situé en RAM à l'adresse &BCE6, et appelé : KL-DEL-FAST-TICKER dont les caractéristiques sont énoncées brièvement Partie 4, Chap. 2.7, page 56.

Ce vecteur effectue un saut en ROM inférieure à l'adresse &0183 pour dévalider le bloc d'interruption spécifier.

Les conditions d'entrée de cette routine sont l'adresse du bloc d'interruptions correspondant qu'il faut placer dans le registre HL.

0183 11 BB B8 KLDFT: LD DE,LISTFT
0186 C3 88 03 JP RETIRE

L'adresse initiale de chaînage des blocs d'interruptions FAST TICKER (&B8BB) est chargée dans DE (LD DE, LISTFT), puis on effectue un saut à la routine retirant le bloc en &0388 (JP RETIRE).

0388 EB RETIRE: EX DE,HL
0389 F3 DI
038A CD 69 03 CALL EXAMIN
038D 30 06 JR NC,RETIR1
038F 1A LD A,(DE)
0390 77 LD (HL),A
0391 13 INC DE
0392 23 INC HL
0393 1A LD A,(DE)
0394 77 LD (HL),A
0395 FB RETIR1 : EI
0396 C9 RET

Il est d'abord procédé à un échange entre DE et HL pour se servir de l'adresse de base comme pointeur (EX DE, HL), puis on inhibe les interruptions (DI) pour effectuer un saut au sous-programme EXAMIN (étudié précédemment).

Au retour, l'indicateur carry est affecté de la valeur 1 si le bloc a été trouvé (zéro sinon) et HL pointe sur le bloc précédent le bloc à retirer.

Si le bloc n'existe pas, on saute à la fin (JR NC, RETIR1), sinon, par le jeu des instructions qui suivent le test, l'adresse pointant le bloc suivant le bloc concerné (placée dans les cases mémoires BLOC 0 et BLOC 1) est transféré dans le bloc précédant. On « shuntera » (sautera) ainsi le bloc que l'on désire retirer de la liste. Si le bloc à éliminer se trouvait être le dernier, l'octet 0 de BLOC 1 se trouve transféré dans le bloc précédent à la même position, ce qui indiquera qu'il est devenu le dernier.

Le bloc retiré de la file d'attente, s'il n'est plus utilisé ultérieurement, permettra de récupérer des adresses en mémoire vive, ainsi que la place utilisée par la routine de traitement, si elle se trouvait en mémoire centrale.

Insérer une interruption

Supposons, qu'après avoir installé et initialisé une interruption par KL-NEW-FAST-TICKER, vous l'ayez otée pour une raison quelconque par KL-DEL-FAST-TICKER, mais que cet écart du fonctionnement ne soit que momentané.

Vous devez donc chaîner à nouveau le bloc, mais cette fois-ci sans réinstaller ses caractéristiques (à moins qu'elles ne soient devenues différentes). Un vecteur en RAM permet ce travail : il s'agit de KL-ADD-FAST-TICKER placé à l'adresse &BCE3 (Voir Partie 4, Chap. 2.7, page 56).

Ce vecteur effectue un saut en ROM inférieure à l'adresse &017D, avec une condition d'entrée concernant le registre double HL, qui doit contenir l'adresse du bloc d'interruption à chaîner.

017D 11 BB B8 KLADFT: LD DE,LISTFT
0180 C3 79 03 JP CHAINE

Après avoir chargé DE avec l'adresse de base de la file d'attente (LD DE,LISTFT), il est effectué un saut à la routine CHAINE étudiée précédemment.

On remarquera qu'il est possible de préparer soi-même le bloc d'interruptions, selon la description donnée au paragraphe ci-dessus intitulé Ajouter et initialiser un événement, et uniquement la chaîner à l'aide de KL-ADD-FAST-TICKER (pourquoi faire simple, si l'on peut faire compliqué ?).

Exemples d'utilisation de FAST TICKER

Pour illustrer nos explications, nous vous proposons deux exemples d'utilisation des vecteurs de FAST TICKER, afin d'installer nos propres routines en mémoire centrale.

La première utilise le vecteur KL-NEW-FAST-TICKER pour initialiser une fonction simple, la deuxième vous permettra d'afficher une horloge dynamique dans le coin supérieur droit de l'écran, avec possibilité d'inhiber l'affichage et de le réautoriser.

  • Avec cette routine on ne s'endort pas

Nous vous proposons un petit programme qui émet un bip toutes les trois minutes et demi environ, pour signaler que votre CPC est toujours sous tension, utile si vous avez du vous absenter précipitamment de l'écran, et éventuellement pour réveiller un programmeur endormi lors de la frappe tardive d'un programme relativement conséquent.

Voici l'algorithme général de fonctionnement :

  • DEBUT : Initialisation de l'interruption
    • Initialiser l'interruption
  • FIN
  • DEBUT : Traitement de l'interruption
    • Décrémenter le compteur
    • SI le compteur a atteint la valeur nulle
      • ALORS
        • Emettre un BIP
    • FIN SI
  • FIN

A partir de cet algorithme nous pouvons détailler les opérations et par référence aux instructions de l'Assembleur Z80, nous tirons ci-après l'ordinogramme du traitement de l'interruption.

On remarquera que le compteur est inscrit sur deux octets et qu'il est décrémenté par l'intermédiaire de HL. Le test permettant de savoir si le compteur a atteint la valeur nulle s'effectuera en opérant un OU logique entre le registre L (placé dans A) et le registre H. L'indicateur de zéro ne sera positionné à 1 que si le résultat de cette opération est nulle, donc si les contenus de H et L sont tous deux nuls. Dans ce cas, le BIP sera émis en fournissant la valeur &07 à la routine TXT-OUTPUT, qui affiche le caractère qu'on lui transmet dans le registre A ; ici &07 correspond à la valeur ASCII du BIP sonore (BELL).

De l'ordinogramme précédent, on tire le programme suivant :


L'initialisation du bloc s'effectue par chargement de HL avec l'adresse du bloc d'événement (BLOCEV) de 9 octets réservé (DEFS 9).

Le registre DE est chargé avec l'adresse de la routine (DEPART).

Le registre B est chargé avec la valeur &81 qui correspond à la valeur binaire &B10000001, ce qui signifie que l'événement est asynchrone (b7 = 1 ), donc il en est tenu compte même en mode programmation, il n'est pas express (b6 = 0), et ne possède aucune priorité par rapport à d'autres événements (b4b3b2b1 = 0000) (à quoi bon ?). On signale par bO = 1 que la routine de traitement est en RAM centrale.

Le registre C n'est pas affecté puisque la routine est en RAM.

Dans la routine de traitement, il nous est possible de calculer la période d'apparition des BIPs. Nous ne tiendrons pas compte des temps d'exécutions des instructions (de l'ordre de quelques microsecondes par rapport à 3,3 millisecondes qui est la période d'appel de la routine). Le compteur est initialisé à la valeur &0000 (COMPT: DEFB 00,00), et ne repassera à zéro qu'après un tour complet, c'est-à-dire 65536 appels plus tard. Le temps est donc de 1/300 x 65536 = 3,3 x 10-3 x 65536 = 219 secondes, ce qui nous donne un temps approximatif de 3 mn 39 s.

Pour ceux qui désirent expérimenter cette routine et qui ne possèdent pas de logiciel d'assemblage, nous fournissons ci-dessous le chargeur Basic correspondant.


Les explications pour son utilisation sont insérées dans le programme, on conseillera cependant de sauvegarder celui-ci et de retirer la disquette du lecteur avant de lancer son exécution.

  • Une horloge dynamique

Pour créer une horloge s'affichant régulièrement, nous avons tout de suite pensé à la placer sous interruption à l'aide de KL-NEW-FAST-TICKER. il nous fallait aussi trouver une façon simple de la mettre à l'heure, nous avons donc créé une instruction résidente au Basic (RSX) que l'on a appelé ÙHEURE (ou ! HEURE selon le type de CPC que vous possédez) à laquelle nous fournirons la valeur des heures et des minutes par la syntaxe suivante : ùHEURE,hh,mm.

Nous n'avons pas tenu compte des secondes dans la mise à l'heure, ni dans l'affichage, car cela prendrait un temps non négligeable dans l'exécution (la fonction d'affichage est celle qui occupe le plus de temps). Cet affichage se présentera sous la forme suivante, par exemple pour 13 heures 56 minutes : 13:56 ou encore 13 56.

L'affichage du caractère deux points sera intermittent, toutes les secondes nous afficherons soit l'une des possibilités, soit l'autre, et ce au même endroit, ce qui fera croire à un clignotement du caractère.

Nous avons choisi cette possibilité d'affichage, premièrement pour signaler, lors d'un arrêt de la frappe au clavier, que l'horloge est toujours en fonctionnement, deuxièmement, pour réafficher l'horloge lors d'un scrol-ling vertical montant.

Lors de l'exécution de certains logiciels, il est possible que l'affichage de l'heure perturbe l'écran, aussi, avons-nous prévu l'inhibition de celui-ci, mais sans arrêt du comptage, ainsi vous pourrez restaurer l'affichage, sans perdre l'heure

ùHON restaure l'affichage de l'heure,
ùHOFF inhibe l'affichage de l'heure.

Un dernier point à notre cahier des charges était de respecter l'emplacement de l'heure quel que soit le mode d'affichage (MODE 0, 1 ou 2). Ceci fut effectué par l'utilisation d'un vecteur fournissant les dimensions de l'écran en caractères, il s'agit de SCR-CHAR-LIMITS qui, sans condition d'entrée, fournit dans B le numéro de la dernière colonne de l'écran, et dans C le numéro de la dernière ligne. Connaissant le nombre de colonnes, il nous est possible de déterminer l'emplacement de la dizaine des heures (4 caractères devant, l'unité des minutes se trouvant sur la dernière colonne). Ainsi, quel que soit le mode d'affichage, l'affichage de l'heure occupera toujours les 5 caractères en haut à droite de l'écran (il n'y a aucun problème pour le nombre de lignes, car celui-ci est identique dans les deux modes).

L'ordinogramme de traitement de l'interruption se présente ainsi :





Ce qui donne le programme Assembleur suivant :





Adresses &A000 à &A00B : les instructions créées en RSX, pointées par la table « VECTEU », sont insérées au Basic avec le vecteur KLOGTXT (KL-LOG-TEXT) qui demande un bloc réservé aux adresses &A01A à &A01D.

Adresses &A00E à &A019 : le bloc d'événement est installé et initialisé par KLNEWT (KL-NEW-FAST-TICKER), le bloc est réservé aux adresses &A036 à &A03E.

La routine mettant à jour le compteur horaire débute en &A03F. A partir de cette adresse, il est effectué un décompte du contenu de l'adresse (COMPT), qui est initialisée à chaque annulation avec la valeur &012C. Ce qui nous donne un temps à peu près égal à 1 seconde compte tenu des temps pris pour l'exécution de la routine.

Ainsi toutes les secondes, la variable SECOND est remise à jour, MINUTE et HEURE le sont aussi, si cela est utile.

A chaque seconde écoulée, le contenu de (DPOINT) est testé pour afficher soit un espace, soit le caractère deux points (Adresses &A050 à &A05D).

Le programme d'affichage débute en &A099. A l'initialisation, il est placé le code RET (&C9), qui permet le retour au Basic sans effectuer d'affichage. Lors de l'autorisation de l'affichage (par ùHON, voir aux adresses &A118 à &A11D), ce code est remplacé par 00 (NOP) qui n'effectue aucune opération, ce qui permet au programme de continuer à l'adresse suivante. Si on demande l'inhibition de l'affichage (ùHOFF aux adresses &A11E à &A123), le code RET est de nouveau installé en &A099.

L'affichage est effectué chiffre par chiffre, en intercalant en plus le séparateur contenu dans DPOINT. Les chiffres des dizaines sont « récupérés » par une rotation du nombre quatre fois et un masquage par la valeur &0F. En outre, le chiffre obtenu doit être transformé en caractère ASCII pour pouvoir être fourni à TXTWRC, ce qui est effectué en lui ajoutant la valeur &30.

Après affichage de tous les chiffres, le programme redonne la main au Basic.

Adresses &A0EF à &A117 : à ces adresses se trouve la routine permettant de mettre à jour l'horloge à l'aide de la commande ùHEURE,hh,mm. Cette remise à jour effectue automatiquement une initialisation des secondes à la valeur 0. Le procédé de mise à jour est à la fois simple et un peu « tordu ». Les valeurs fournies, en décimal, sont récupérées dans la pile des paramètres pointée par le registre IX. A partir de ces valeurs qui sont en hexadécimal (le microprocesseur travaillant dans cette base, il est effectué une incrémentation de 1 avec un ajustement décimal pour obtenir un nombre BCD (Binaire Codé Décimal) qui sera rangé à l'adresse appropriée.

Afin de ne pas défavoriser le programmeur Basic ne possédant pas de logiciel d'assemblage, nous fournissons ci-dessous le chargeur Basic.



Les directives de sauvegarde sont une fois encore incluses dans le programme, ainsi qu'une décomposition en quatre blocs des codes machine Z80, pour permettre une gestion des erreurs plus facile.

Nous vous proposons aussi ce petit programme permettant de comparer cette horloge nouvellement créée, par rapport à une horloge utilisant les interruptions logicielles du Basic.

Après avoir initialisé et visualisé l'horloge précédente, vous lancerez le programme suivant :

L'INTERRUPTION FRAME FLY

Comme son nom l'indique, cette interruption va concerner le balayage écran (FRAME).

Elle est générée à chaque retour du faisceau cathodique de balayage de l'écran de votre CPC, c'est-à-dire tous les 50* de secondes.

Comme pour FAST TICKER, il existe trois vecteurs en RAM pour initiali-ser et positionner un bloc FRAME FLY dans la file d'attente (KL-NEW-FRAME-FLY), retirer un bloc de cette file (KL-DEL-FRAME-FLY), ou le rajouter (KL-ADD-FRAME-FLY), dont vous trouverez les caractéristiques Partie 4, Chap. 2.7, page 55.

KL-NEW-FRAME-FLY

Pour ce vecteur placé à l'adresse RAM &BCD7, les conditions d'entrée sont identiques à celles de KL-NEW-FAST-TICKER.

Le programme appelé en ROM inférieure se situe à partir de l'adresse &0163 et le voici désassemblé :

0163 E5 KLNFF: PUSH HL
0164 23 INC HL
0165 23 INC HL
0166 CD D2 01 CALL INIT
0169 E1 POP HL
016A 11B9B8 LD DE,LISTFF
016D C3 79 03 JP CHAINE

On remarquera que ce programme est identique à celui de KL-NEW-FAST-TICKER, aux adresses et à la variable LISTFF près, aussi nous ne sommes pas obligés de l'étudier à nouveau.

L'interruption nécessitera donc un bloc défini de façon identique au précédent, de 9 octets, et initialisé par les registres en entrées comportant le même type de paramètres.

Son chaînage par contre sera effectué à partir de l'adresse de base &B8B9 (au lieu de &B8BB), puis se propagera dans les blocs installés.

Retirer un bloc

Encore une fois nous avons un procédé identique à celui de KL-DEL-FAST-TICKER, mais cette fois-ci pour KL-DEL-FRAME-FLY situé à l'adresse &BCDD, seule l'adresse du bloc de base est différente.

Nous vous donnons pour indication des adresses le programme appelé :

0170 11 B9 B8 KLDEFF: LD DE,LISTFF
0173 C3 88 03 JP RETIRE

Ajouter un bloc

De procédé identique à KL-ADD-FAST-TICKER, KL-ADD-FRAME-FLY situé en &BCDA renvoi à l'adresse &016A.

016A 11 B9 B8 KLADFF LD DE,LISTFF
016D C3 79 03 JP CHAINE

Exemple d'utilisation

Afin d'utiliser FRAME FLY, nous vous proposons l'étude d'un programme générant un curseur clignotant qui vous signalera que votre CPC est toujours actif, et que vous avez la main sous Basic.

Le procédé d'installation est le même que pour FAST TICKER, ainsi nous ne reviendrons pas dessus. Signalons tout de même que nous avons donné à ce programme une priorité identique en chargeant le registre B avec la valeur &81, mais qu'il n'est traité que tout les 50e de secondes, c'est-à-dire toutes les 20 millisecondes.

  • L'ordinogramme
    L'ordinogramme principal :

La procédure ALLUMER le curseur :

La procédure ETEINT le curseur :

  • Le programme Assembleur



Adresses &A000 à &A00A : initialisation et chaînage du bloc événement situé en &A017.

Adresses &A00B et &A013 : on initialise les variables de temps d'allumage et d'extinction du curseur : &19 fois 0.02 seconde, ce qui donne 25 x 0.02 = 0.5 seconde pour chaque temps, ce qui donne un cycle de 1 seconde. Si vous jugez ce temps trop rapide, ou trop long, il faut modifier la valeur de l'adresse &A00C. TEMP correspond à la valeur servant à l'initialisation des deux autres variables. TEMPVI est la variable contenant le compteur de temps qu'il reste au curseur à être visible, tandis que TEMPET est celui pour lequel le curseur sera éteint.

La routine de traitement de l'interruption de type KL-NEW-FRAME-FLV se trouve à partir de l'adresse &A020.

On sauvegarde d'abord les registres, puis un test sur le temps restant au curseur à être visible est effectué. Si ce temps est nul, on va éteindre le curseur en &A046.

Sinon le curseur est allumé en décrémentant le temps restant.

Page précédente : 4/2.11 II - Les interruptions matérielles (Hard)
Je participe au site:

» Vous avez remarqué une erreur dans ce texte ?
» Aidez-nous à améliorer cette page : en nous contactant via le forum ou par email.

CPCrulez[Content Management System] v8.7-desktop/c
Page créée en 369 millisecondes et consultée 442 fois

L'Amstrad CPC est une machine 8 bits à base d'un Z80 à 4MHz. Le premier de la gamme fut le CPC 464 en 1984, équipé d'un lecteur de cassettes intégré il se plaçait en concurrent  du Commodore C64 beaucoup plus compliqué à utiliser et plus cher. Ce fut un réel succès et sorti cette même années le CPC 664 équipé d'un lecteur de disquettes trois pouces intégré. Sa vie fut de courte durée puisqu'en 1985 il fut remplacé par le CPC 6128 qui était plus compact, plus soigné et surtout qui avait 128Ko de RAM au lieu de 64Ko.