Différences

Cette page vous donne les différences entre la révision choisie et la version actuelle de la page.

iassem:kernel 2015/01/10 09:26 iassem:kernel 2021/01/30 12:34 version actuelle
Ligne 2: Ligne 2:
====== Programmation système ====== ====== Programmation système ======
-<fc #ffa500>--- // Cet article serait à enrichir avec de nouveaux sujets ; nous n'avions traité que peu traité du système dans Quasar CPC. Avis aux amateurs.//</fc>+<fc #ffa500>--- // Cet article serait à enrichir avec de nouveaux sujets ; nous n'avions que peu traité du système dans Quasar CPC. Avis aux amateurs.//</fc>
{{:coccpetittrain_q21.png?direct&150  |Content !}} {{:coccpetittrain_q21.png?direct&150  |Content !}}
Nous allons ici traiter de la programmation utilisant le système en assembleur. Il s'agit de vous présenter divers packs de routines offertes par le firmware du CPC qui peuvent être fort utiles lors de la programmation de logiciels ou de petits jeux. En effet, le système du CPC n'est pas si mal fait et il est parfois judicieux de l'utiliser plutôt que de réinventer la roue. Nous allons ici traiter de la programmation utilisant le système en assembleur. Il s'agit de vous présenter divers packs de routines offertes par le firmware du CPC qui peuvent être fort utiles lors de la programmation de logiciels ou de petits jeux. En effet, le système du CPC n'est pas si mal fait et il est parfois judicieux de l'utiliser plutôt que de réinventer la roue.
 +
====== Les RSX ====== ====== Les RSX ======
Ligne 92: Ligne 93:
Dans cette section, je vais tenter de vous expliquer la gestion des interruptions par le noyau (kernel) du firmware du CPC. Dans cette section, je vais tenter de vous expliquer la gestion des interruptions par le noyau (kernel) du firmware du CPC.
-Je vous préviens tout de suite, certaines subtilités m'échappent encore et si vous avez des compléments à m'apporter (notamment sur les vecteurs ''&BCFE'' et ''&BD01''), je vous serai reconnaissant de m'en faire part. J'espère que la crédibilité de cet article n'en souffira pas trop !+Je vous préviens tout de suite, certaines subtilités m'échappent encore et si vous avez des compléments à m'apporter (notamment sur les vecteurs ''&BCFE'' et ''&BD01''), je vous serais reconnaissant de m'en faire part. J'espère que la crédibilité de cet article n'en souffrira pas trop !
===== Le concept ===== ===== Le concept =====
Ligne 105: Ligne 106:
  * le "ticker" : il est multiple du "fast ticker" et apparaît tous les 50èmes de secondes.   * le "ticker" : il est multiple du "fast ticker" et apparaît tous les 50èmes de secondes.
-Donc, vous pouvez demander au système d'exploitation de lancer vos routines en définissant la fréquence d'exécution et si elle doit avoir lieu au moment du "frame flyback"((au début du balaye vidéo)), "ticker"((au milieu du balayage vidéo)) ou "fast ticker"((6 fois par balayage vidéo)). Cette demande se fait par l'intermédiaire d'un "event block" (bloc d'événement). Chaque routine dispose donc de son "event block", qui donne toutes les informations qui la concerne (l'emplacement, la fréquence d'appel, la priorité, etc.).+Donc, vous pouvez demander au système d'exploitation de lancer vos routines en définissant la fréquence d'exécution et si elle doit avoir lieu au moment du "frame flyback"((au début du balayage vidéo)), "ticker"((au milieu du balayage vidéo)) ou "fast ticker"((6 fois par balayage vidéo)). Cette demande se fait par l'intermédiaire d'un "event block" (bloc d'événement). Chaque routine dispose donc de son "event block", qui donne toutes les informations qui la concerne (l'emplacement, la fréquence d'appel, la priorité, etc.).
Certains octets du bloc sont réservés au système d'exploitation qui y place des pointeurs qui lui permettent de passer d'un bloc à l'autre. Un bloc est forcément placé dans les 32ko centraux de la mémoire, car c'est la seule zone directement accessible indépendamment des ROMs connectées. Méfiez-vous cependant [[:assem:gate_array#Le dernier : le registre MMR|des banks]]. Certains octets du bloc sont réservés au système d'exploitation qui y place des pointeurs qui lui permettent de passer d'un bloc à l'autre. Un bloc est forcément placé dans les 32ko centraux de la mémoire, car c'est la seule zone directement accessible indépendamment des ROMs connectées. Méfiez-vous cependant [[:assem:gate_array#Le dernier : le registre MMR|des banks]].
Ligne 113: Ligne 114:
==== Structure d'un bloc ==== ==== Structure d'un bloc ====
-Voici la structure de la partie commune à toute sorte d'événement (appelé bloc d'événement)+Voici la structure de la partie commune à toute sorte d'événement (appelé bloc d'événement) :
  * **octet 0+1** : adresse de la chaîne pour la "pending queue". Réservé au système : pas touche !   * **octet 0+1** : adresse de la chaîne pour la "pending queue". Réservé au système : pas touche !
  * **octet 2** : compteur   * **octet 2** : compteur
Ligne 119: Ligne 120:
    * s'il est inférieur à 0 (supérieur à 127) le bloc reste dans la chaîne ; le "kicking" ne conduit pas à l'exécution de la routine.     * s'il est inférieur à 0 (supérieur à 127) le bloc reste dans la chaîne ; le "kicking" ne conduit pas à l'exécution de la routine.
  * **octet 3** : classe   * **octet 3** : classe
-    * //bit 0// : si 1, l'adresse de saut est une "near address" (RAM centrale ou ROM inférieure) ; si 0, l'adresse de saut est une "far address" (ROM supérieure). +    * //bit 0// : si 1, l'adresse de saut est une "near address" (RAM hors zone de ROM (32k centraux) ou ROM inférieure) ; si 0, l'adresse de saut est une "far address" (peut-être n'importe où en RAM ou en ROM). 
-    * //bits 1 à 4// : déterminent la priorité (''%0000'' est la priorité la plus faible).+    * //bits 1 à 4// : déterminent la priorité (''%0000'' est la priorité la plus faible). Uniquement utilisé pour les événements synchrones.
    * //bit 5// : doit toujours valoir 0 !     * //bit 5// : doit toujours valoir 0 !
    * //bit 6// : si 1, c'est un "express event". Sa priorité est supérieure à celle des events normaux de priorité max.     * //bit 6// : si 1, c'est un "express event". Sa priorité est supérieure à celle des events normaux de priorité max.
-    * //bit 7// : si 1, c'est un "asynchron event" : pas de file d'attente, ils sont rangés immédiatement dans "l'interrupt pending queue" lors du "kicking" (KL EVENT). Si l'event est en plus "express", la routine est exécutée immédiatement et pas en fin de la routine d'interruption système comme normalement. Attention : la routine d'un "asynchron event" est forcément en RAM centrale (bit 0 ignoré).+    * //bit 7// : si 1, c'est un "asynchronous event" : pas de file d'attente, ils sont rangés immédiatement dans "l'interrupt pending queue" lors du "kicking" (KL EVENT). Si l'event est en plus "express", la routine est exécutée immédiatement et pas en fin de la routine d'interruption système comme normalement. Attention : la routine d'un "asynchronous event" est forcément en RAM centrale (bit 0 ignoré).
  * **octet 4+5** : adresse de la routine.   * **octet 4+5** : adresse de la routine.
-  * **octet 6** : ROM select (inutilisé si l'adresse n'est pas une "far address").+  * **octet 6** : ROM select (utilisé uniquement pour une "far address").
  * **octet 7** : début du champ utilisateur qui peut servir à transmettre des paramètres à votre routine. Lors de l'appel d'une event routine, HL contient l'adresse de l'octet 5 de l'event block s'il s'agit d'une "near address", sinon l'adresse du 6.   * **octet 7** : début du champ utilisateur qui peut servir à transmettre des paramètres à votre routine. Lors de l'appel d'une event routine, HL contient l'adresse de l'octet 5 de l'event block s'il s'agit d'une "near address", sinon l'adresse du 6.
Le bloc d'événement est précédé d'un bloc de contrôle qui comporte 2 octets pour un "frame fly" ou un "fast ticker", ce sont deux octets de chaînage (à ne pas modifier !) dans la "frame fly list" ou la "fast ticker list". Pour un "ticker" il compte 6 octets : Le bloc d'événement est précédé d'un bloc de contrôle qui comporte 2 octets pour un "frame fly" ou un "fast ticker", ce sont deux octets de chaînage (à ne pas modifier !) dans la "frame fly list" ou la "fast ticker list". Pour un "ticker" il compte 6 octets :
- 
  * **octet 0+1** : chaînage pour "ticker list" (on vous a dit de pas toucher !)   * **octet 0+1** : chaînage pour "ticker list" (on vous a dit de pas toucher !)
  * **octet 2+3** : "tick count", nombre d'apparitions du "ticker" avant que le bloc ne soit "kické" une fois.   * **octet 2+3** : "tick count", nombre d'apparitions du "ticker" avant que le bloc ne soit "kické" une fois.
  * **octet 4+5** : "reload count", indique la valeur à charger dans le "ticker count" après son écoulement.   * **octet 4+5** : "reload count", indique la valeur à charger dans le "ticker count" après son écoulement.
 +\\
 +\\
 +L'octet **ROM select** pour une "far address" se compose comme suit :
 +  * une valeur de 0 à 251 sélectionne et connecte la ROM haute indiquée. La ROM basse est déconnectée.
 +  * les valeurs 252 à 255 ne changent pas la ROM haute sélectionnée et :
 +    * 252 connecte les ROMs haute et basse
 +    * 253 connecte la ROM haute, déconnecte la ROM basse
 +    * 254 déconnecte la ROM haute, connecte la ROM basse
 +    * 255 déconnecte les ROMs haute et basse
==== Les vecteurs ==== ==== Les vecteurs ====
Ligne 155: Ligne 164:
**''BCDA'' : KL Add Frame Fly** **''BCDA'' : KL Add Frame Fly**
-Ajouter un bloc d'évenement dans la liste.+Ajouter un bloc d’événement dans la liste.
  * __CA :__   * __CA :__
    * HL=adresse du bloc de contrôle.     * HL=adresse du bloc de contrôle.
Ligne 177: Ligne 186:
**''BCE0'' : KL New Fast Ticker** **''BCE0'' : KL New Fast Ticker**
-Créer et ajouter un bloc d'évenement à la liste de ceux à exécuter tous les 300èmes de seconde.+Créer et ajouter un bloc d’événement à la liste de ceux à exécuter tous les 300èmes de seconde.
  * __CA :__   * __CA :__
    * HL=adresse du bloc de contrôle,     * HL=adresse du bloc de contrôle,
-    * B=classe de l'évenement,+    * B=classe de l’événement,
    * C=adresse de sélection de ROM,     * C=adresse de sélection de ROM,
    * DE=adresse de la routine à exécuter.     * DE=adresse de la routine à exécuter.
Ligne 216: Ligne 225:
    * HL=adresse du bloc de contrôle,     * HL=adresse du bloc de contrôle,
    * DE=valeur initiale du compteur,     * DE=valeur initiale du compteur,
-    * BC=valeur de recherge du compteur (lorsqu'il atteint 0).+    * BC=valeur de recharge du compteur (lorsqu'il atteint 0).
  * __CF :__   * __CF :__
    * AF, BC, DE et HL modifiés.     * AF, BC, DE et HL modifiés.
Ligne 373: Ligne 382:
Oui, je vous ai parlé plus haut de compteur 16 bits et 8 bits sans vraiment vous dire leur utilité et leur fonctionnement, voici donc. Oui, je vous ai parlé plus haut de compteur 16 bits et 8 bits sans vraiment vous dire leur utilité et leur fonctionnement, voici donc.
-Le compteur de l'octet 2 du bloc d'événement est sur 8 bits. S'il contient un valeur positive (de ''&00'' à ''&7F'') il indique le nombre d'interruptions "ratées" et qui seront rattrappées à la prochaine occasion. Alors, la routine "en retard" sera exécutée à chaque traitement des routines d'interruption (donc pas à la fréquence normale) afin de se remettre à jour. Ce phénomène est illustré par le programme BASIC ([[#Programme 1|Prog 1]]) avec en plus une démonstration de la gestion des priorités. J'y reviendrai plus bas.+Le compteur de l'octet 2 du bloc d'événement est sur 8 bits. S'il contient un valeur positive (de ''&00'' à ''&7F'') il indique le nombre d'interruptions "ratées" et qui seront rattrapées à la prochaine occasion. Alors, la routine "en retard" sera exécutée à chaque traitement des routines d'interruption (donc pas à la fréquence normale) afin de se remettre à jour. Ce phénomène est illustré par le programme BASIC ([[#Programme 1|Prog 1]]) avec en plus une démonstration de la gestion des priorités. J'y reviendrai plus bas.
Le plus important est de remarquer que, grâce à ce compteur, le système s'arrange pour que globalement la routine que vous lui avez confiée soit exécutée à la fréquence demandée. Remarquez aussi que le problème de rattrapage d'interruptions ne se pose pas pour les événements asynchrones puisqu'ils sont forcément traités à chaque interruption. Le plus important est de remarquer que, grâce à ce compteur, le système s'arrange pour que globalement la routine que vous lui avez confiée soit exécutée à la fréquence demandée. Remarquez aussi que le problème de rattrapage d'interruptions ne se pose pas pour les événements asynchrones puisqu'ils sont forcément traités à chaque interruption.
Ligne 398: Ligne 407:
Les commandes ''EVERY'' et ''AFTER'' du BASIC créent des "tickers" synchrones (non express). Le BASIC vous donne accès à 4 "tickers" de priorités différentes (le plus prioritaire est le numéro 3). Les commandes ''EVERY'' et ''AFTER'' du BASIC créent des "tickers" synchrones (non express). Le BASIC vous donne accès à 4 "tickers" de priorités différentes (le plus prioritaire est le numéro 3).
-C'est la boucle de l'interprêteur du BASIC qui s'occupe de lancer les événements synchrones via les vecteurs du kernel. En effet, le terme "synchrone" signifie que les événements ne sont exécutés qu'à votre demande, lorsque vous le décidez. Contrairement aux événements asynchrones qui s'exécutent forcément à la fréquence définie. Voyons maintenant comment demander le lancement des événements synchrones.+C'est la boucle de l’interpréteur du BASIC qui s'occupe de lancer les événements synchrones via les vecteurs du kernel. En effet, le terme "synchrone" signifie que les événements ne sont exécutés qu'à votre demande, lorsque vous le décidez. Contrairement aux événements asynchrones qui s'exécutent forcément à la fréquence définie. Voyons maintenant comment demander le lancement des événements synchrones.
Il faut pour cela utiliser les trois vecteurs ''&BCFB'', ''&BCFE'' et ''&BD01'' : Il faut pour cela utiliser les trois vecteurs ''&BCFB'', ''&BCFE'' et ''&BD01'' :
-{{  :newton_q18.png?direct&200|Ohé ! Les amiches !}}+{{  :newton_q18.png?direct&200|Ohé ! Les aminches !}}
  * Début   * Début
    * **''&BCFB''** : KL Next Synchronous     * **''&BCFB''** : KL Next Synchronous
Ligne 423: Ligne 432:
Dernier point : quand le Z80 entame l'exécution d'une routine d'interruption via le kernel, il est en EI (c'est-à-dire que [[:iassem:interruptions|les interruptions]] sont autorisées). Donc, si votre routine est longue, il vaut mieux faire un DI au début (le EI en fin est facultatif, le kernel s'en occupe) afin d'éviter les empilements intempestifs... mais attention, vous risquez alors de perturber l'ensemble des interruptions et événements ! Dernier point : quand le Z80 entame l'exécution d'une routine d'interruption via le kernel, il est en EI (c'est-à-dire que [[:iassem:interruptions|les interruptions]] sont autorisées). Donc, si votre routine est longue, il vaut mieux faire un DI au début (le EI en fin est facultatif, le kernel s'en occupe) afin d'éviter les empilements intempestifs... mais attention, vous risquez alors de perturber l'ensemble des interruptions et événements !
-Je vous ai rajouté en dernière minute le [[#Programme 4|Prog 4]] qui met en oeuvre un "fast ticker" et vous montre qu'on peut très bien construire soi-même le bloc d'événement sans passer par les vecteurs. Traine aussi une description à mon avis plus exacte du vecteur ''&B921'' que celle du chapitre précédent.+Je vous ai rajouté en dernière minute le [[#Programme 4|Prog 4]] qui met en oeuvre un "fast ticker" et vous montre qu'on peut très bien construire soi-même le bloc d'événement sans passer par les vecteurs. Traîne aussi une description à mon avis plus exacte du vecteur ''&B921'' que celle du chapitre précédent.
Voilà, c'en est fini de cette section sur la gestion d'interruptions par le kernel. J'espère que vous aurez compris à peu près ! Voilà, c'en est fini de cette section sur la gestion d'interruptions par le kernel. J'espère que vous aurez compris à peu près !
 
iassem/kernel.1420881968.txt.gz · Dernière modification: 2017/10/09 10:22 (édition externe)