— Basé sur les articles publiés dans Quasar CPC numéros 12, 13, 14, 15 et 16, Assembleur : Hardware, par OffseT.
— Décrire les quelques instructions secondaires restantes et rajouter des informations sur les timings.
— Rajouter l'explication sur les autres gaps présents sur le disque et leur influence sur le calcul du gap#3.
Vous voici en tête à tête avec l'article dédié au FDC (en français : contrôleur de disquettes). Nous allons ici étudier comment fonctionne ce grand gourou du pilotage de lecteur de disquettes, sa logique de fonctionnement, sa programmation, ses travers, etc..
En conclusion, vous aurez même droit à une bibliothèque de routines complète qui vous permettra de tester la quasi-totalité des fonctions du FDC sans perdre vos neuronnes.
Nous allons commencer timidement par une petite présentation. En effet, notre brave FDC est une bestiole assez réticente qu'il faut manipuler avec des pincettes… Il vaut donc mieux bien la connaître avant de la triturer.
Tel qu'il est câblé sur le CPC, le FDC (Ferronnerie Du Cantal ?) nous permet de gérer deux drives simple ou double densité. Sauf bidouilles, le lecteur interne est obligatoirement un lecteur simple face alors que le lecteur externe peut être un double face. En ce qui concerne le nombre de pistes, le FDC peut en gérer jusqu'à 256 mais, en pratique, on en aura au maximum 82 à 84 pour les lecteurs 3”1/2 et les 5”1/4 dits 80 pistes ; nos braves lecteurs 3” étant eux limités à 42 pistes. La densité ne change quant à elle que le nombre de secteurs par piste. C'est ainsi que sur CPC, le format standard compte 9 secteurs par piste (on est en double densité) alors qu'il y en a 18 sur les disquettes PC (haute densité). La gestion des drives haute densité étant impossible sur CPC et le mode simple densité ne présentant aucun intérêt, nous raisonnerons uniquement en double densité.
Puisque c'est le thème principal de cette première section sur le FDC (Fête De Cannes ?), allons y, parlons de la structure de nos petites disquettes. Comme vous le savez certainement, une disquette est coupée en pistes, elles-mêmes partagées en plages que l'on appelle secteurs. Les pistes sont figées, elles dépendent du lecteur de disquettes. C'est ainsi qu'on a des lecteurs 40 pistes et 80 pistes ; et on ne peut rien faire de plus que de tenter de grapiller deux ou trois pistes de plus avant la butée de fin de course. En revanche, dans une piste, on fait à peu près ce qu'on veut comme découpage dans la limite de 6250 octets. Ces quelques octets devront être partagés entre les données proprement dites et des zones d'informations (nom, taille des secteurs) et de temporisation (GAP).
Le FDC (Flamberge De Cancrelat ?) nous propose sept tailles de secteurs différentes :
Taille | Capacité |
---|---|
0 | 128 |
1 | 256 |
2 | 512 |
3 | 1024 |
4 | 2048 |
5 | 4096 |
6 | 8192 |
Bien entendu, la capacité est indiquée en octets. Oui, il est toujours là ! Le petit blond à lunettes vient de lancer sa première critique de l'article ! Un secteur de taille 6 est plus grand qu'une piste ! En fait, ce type de secteur sert à déformater les pistes… ou à créer des formats spéciaux dits à “arrêt moteur”1).
Maintenant, libre à nous de remplir nos petites pistes avec des secteurs dans la limite des 6250 octets disponibles. Mais attention, comme je vous l'ai déjà dit, il faut également prendre en compte les octets de GAP ainsi que quelques octets d'identification. Voici donc la structure exacte d'un secteur :
&00
)&A1
)&FE
&4E
)&00
)&A1
)&FB
&4E
)Les structures de données en italique sont invariables quels que soient les secteurs. Ensuite, les 4 octets d'ID secteur sont respectivement le numéro de piste, le numéro de tête, le nom du secteur et sa taille. Les n octets de données sont bien évidemment le contenu de notre secteur et les n octets de GAP sont en fait la zone de séparation entre deux secteurs consécutifs.
Le tout est d'optimiser le nombre de secteurs par piste et leur taille de façon à avoir un GAP le plus faible possible. Mais attention, il ne faut pas avoir un GAP trop faible car sinon, compte tenu de la variation de la vitesse de rotation des différents lecteurs, votre disquette aura du mal à passer chez certains. Un GAP minimum de 32 octets vous assure que votre disquette sera lisible même sur un lecteur un peu mollasson. Par contre, même si la pratique n'est pas courante, il est tout à fait possible d'avoir des secteurs de tailles différentes sur une même piste. À titre d'exemple, une disquette au format DATA compte 9 secteurs de taille 2 par piste, ce qui nous donne donc un GAP de &78
octets ! Autant vous dire que le jour où votre lecteur n'arrivera plus à lire ce genre de format, il sera vraiment fatigué ! Si on prend le format 206K de Format II (utilitaire du défunt CPC-Infos), on a 10 secteurs de taille 2 par piste, soit un GAP de 51 octets ce qui est beaucoup plus optimisé.
On a en effet la possibilité de faire des formats batards, formats écrasés, formats avec “arrêt moteur”, et autres… Mais si ces formats ont connu leur heure de gloire avec le “plombage”, on ne se penchera pas dessus car on cherchera plutôt à optimiser les paramètres “capacité disque” et “vitesse de chargement”2).
Tout dépend de ce que l'on veut… Si les secteurs sont lus en séquentiel (c'est le cas de l'Amsdos), il est intéressant de les entrelacer sur la piste ; je m'explique. En format DATA, les secteurs portent les noms &C1
à &C9
et sont formatés comme suit sur la piste : &C1
, &C6
, &C2
, &C7
, &C3
, &C8
, &C4
, &C9
, &C5
. Pourquoi ? Eh bien simplement car quand l'Amsdos va avoir à lire les secteurs &C1
et &C2
, il va lancer un ordre de lecture du secteur &C1
, récupérer les données, puis lancer l'ordre de lecture du secteur &C2
; oui mais voilà, le disque tourne en permanence et pendant l'envoi de l'ordre de lecture du deuxième secteur, le début du secteur suivant le secteur &C1
est déjà passé devant la tête de lecture. Or, si ce secteur était &C2
, c'est raté et il va falloir refaire un tour complet : perte de temps ! Avec le format entrelacé, le secteur &C2
est en fait le premier qui sera effectivement rencontré puisque c'est le &C6
qu'on aura raté.
Dans le cas où l'on désire lire nos secteurs par bloc, par exemple &C2
à &C7
(les instructions du FDC permettent effectivement de commander la lecture de x secteurs consécutifs du moment où l'on reste sur la même piste), vous l'aurez compris, exit le mode entrelacé, on met tout à la suite. Je ne vous cache pas que c'est avec ce principe là que l'on obtient les meilleurs résultats (plus de 12K par seconde sur un lecteur 3”), mais ça n'est clairement pas adapté à la gestion d'un système de fichiers qui a besoin de travailler avec de petites zones.
Dans cette section, nous allons voir comment il faut s'y prendre pour communiquer avec le FDC. À la base, c'est plutôt simple puisque tout se fait via deux registres. Le premier, situé à l'adresse &FB7E
, est le registre d'état principal. Le second, situé à l'adresse &FB7F
, est le registre de données. Nous disposons également d'un troisième port (&FA7E
) qui nous permettra d'allumer et d'éteindre les moteurs de tous les lecteurs de disquettes connectés au CPC.
Voyons à présent en détail ce qui se cache derrière chacun de ces ports après quoi, nous enchaînerons sur le protocole de communication à proprement parler et nous serons armés pour étudier quelques unes des 15 instructions que connait notre brave FDC (Face De Cloporte ?).
Comme sus-cité, le port &FB7E
est le registre d'état principal du FDC (Fan De Chichoune ?). Ah, je vois que le petit blond à lunettes va poser une question ? Paf ! Une bonne giffle pour qu'il la ferme ! Non mais !
Reprenons. Voilà un port qui est très simple ; tout d'abord, il ne fonctionne qu'en lecture et nous renvoie des informations d'ordre général sur l'état du FDC (Fils de Contrôleur ?). On peut aller lire ce brave petit registre principal quand bon nous semble, il n'y a aucune restriction d'accès ! C'est bien beau ça, mais quels renseignements nous donne-t-il exactement ?
Hum… je vous demande un instant, le petit blond à lunettes est en train de ramener sa fraise… Et vlam, un coup de clavier PC dans la tronche ! J'espère que je l'ai pas trop abîmé quand même… (le petit blond à lunettes pas le PC !).
Or donc, voici la description de l'octet lu en &FB7E :
Je me doute que ce schéma doit pour le moment vous laisser sceptique, mais tout va s'éclaircir en abordant la partie sur le protocole de communication et les phases de fonctionnement du FDC (Fan De CPC ?).
Mais pour l'heure, passons au port &FB7F
, le port des données. Celui-ci fonctionne à double sens (vous l'aviez sans doute déjà deviné si vous avez bien étudiez la structure du registre d'état principal). C'est grâce à ce port que le FDC (Fou De Chocolat ?) va être programmé, qu'il va recevoir toutes les informations nécessaires aux diverses instructions et qu'il va retourner sa copie (les registres d'état secondaires dont nous parlerons plus bas). Autant vous dire que c'est le port le plus important ! Nous allons en reparler tout de suite avec le protocole de communication.
Et puis enfin, il y a le petit dernier, le port &FA7E
, qui va nous permettre, lorsqu'on lui met gentiment son petit bit 0 à 1, d'allumer le moteur de tous les lecteurs de disquettes. Naturellement, on éteint tout en remettant ce bit à 0. Toutefois, deux restrictions, ce port ne marche qu'en écriture et ne permet pas de choisir quel lecteur on allume : c'est tous ou aucun !
Il est en fait assez simple mais il faut être très rigoureux et ne jamais sauter d'étape… Pour arriver à faire cracher le morceau au FDC (Fond De Compensation ?), il faut passer impérativement par trois phases bien distinctes.
La première phase est la phase d'instruction. Concrêtement il s'agit ici d'envoyer sur le port des données (&FB7F
) un code d'instruction suivi de ses éventuels paramètres (de 0 à 9 selon le cas). Attention toutefois, une phase d'instruction ne sera acceptée par le FDC (Faute De Calcul ?) que si le bit 4 du registre d'état principal est à 0. Ah, pour une fois le petit blond à lunettes bougonne une remarque intéressante : “que se passe-t-il pour les bits 7, 6 et 5 ?” Eh bien, si on considère une instruction “normale” (qui a donc des paramètres), sitôt le code d'instruction entré, le bit 7 sera mis à 1 (données à transférer par le port &FB7F
) et le bit 6 sera mis à 0 (c'est au Z80 d'envoyer ses paramètres). En outre, le bit 5 restera à 0 jusqu'à ce que tous les paramètres soient envoyés. Si tout s'est bien passé, une fois tous les paramètres communiqués, le bit 7 passera à 0 (histoire de faire comprendre au Z80 qu'on veut rester tranquille pour digérer toutes les infos). Et puis enfin, lorsque l'instructions aura été interprêtée, le bit 5 sera placé à 1. On enchaîne alors sur la phase d'exécution.
Comme suggéré ci-dessus, la phrase suivant la phase d'instruction est la phase d'exécution. Celle-ci sera plus ou moins longue suivant l'instruction. C'est pour les instructions de lecture et d'écriture que cette phase est la plus significative puisqu'il faudra alors envoyer ou recevoir les octets relatifs au secteur en cours. Durant cette phase, les bits 6 et 7 du registre d'état principal sont encore actifs et permettent de savoir si le FDC et le Z80 sont en phase… Le bit 5, quant à lui, sera remis à 0 lorsque la phase d'instruction est achevée afin d'indiquer le début de la toute dernière phase : la phase de résultat.
On va enfin pouvoir savoir si le FDC (Fromage Du Cantal ?) a tout compris ou s'il n'en a fait qu'à sa tête. Durant la phase de résultat, le FDC va nous renvoyer tout un tas d'octets et les registres d'état secondaires. On va être servis ! En effet, selon l'instruction, on va recevoir jusqu'à 7 (argh !) octets qui nous donnent des foules d'infos sur ce qui s'est passé durant la phase d'exécution. En pratique, 50% de ces infos sont inutiles, mais pour le fun nous les verrons toutes en détail. Il est pas d'accord le petit blond à lunettes ? Et crac ! Une carte mère PC explosée sur la tête (ça fait du bien vous savez). Nous verrons ces valeurs plus en détail avec la liste des instructions…
Voici tout de même une rapide présentation de tous les registres d'état secondaires qui nous seront très utiles par la suite. Il sont au nombre de 4 et répondent aux doux noms de ST0, ST1, ST2 et ST3. ST0 renvoie des informations sur l'interruption d'une instruction, ST1 et ST2 vont ensemble et vous informerons sur tout ce qui s'est passé durant la phase d'exécution. Le registre ST3 est en revanche un peu à part car il n'est renvoyé par le FDC (Faucon De Colombie ?) qu'à la demande du Z80 ; celui-ci donne des informations sur l'état des lecteurs. Nous verrons en détail le contenu de ces registres dans la section qui leur est consacrée un peu plus bas.
Je vais vous donner ici la liste des instructions FDC sans toutefois entrer dans le détail puisque nous les reprendons toutes unes à unes dans les sections suivantes.
On a tout d'abord les instructions de lecture/écriture. Celles-ci sont au nombre de 5 ! Ah ! Je vois que le petit blond à lunettes se gratte la tête d'un air interrogatif… En effet, 5 est un nombre assez curieux. En fait, on a 2 instructions d'écriture et 3 instructions de lecture. On peut lire ou écrire des secteurs classiques (ça fait 2), de même on peut lire et écrire des secteur effacés (ça fait 2+2=4) et lire des blocs (4+1=5, le compte est bon).
Je crois que quelques éclaircissement sont nécessaires. On entend ici par secteur effacés des secteurs qui ont été marqués de façon particulière et qui sont sautés lors d'une lecture classique. C'est juste une façon de “classer” ses secteurs sur un disc. Nous n'utiliserons pas la notion de secteurs effacés par la suite car elle est en fait sans grand intérêt. Ensuite, l'instruction de lecture de blocs ne diffère de l'instruction de lecture de secteurs que par le fait qu'elle permet de lire d'un seul coup les n premiers secteurs successifs sur une même piste. Elle est intéressante dans le cadre de formats sectoriels car rapide mais est difficilement gérable lorqu'on est dans le cas de fichiers qui peuvent être éparpillés un peu partout sur le disc.
On a ensuite l'incontournable instruction de formatage. Puis l'instruction de lecture d'ID qui permet de savoir quel est le prochain secteur qu'on va rencontrer (on n'est pas sensé connaître par coeur son disc). On a ensuite trois instructions qui permettent de vérifier la validité d'un secteur. L'instruction de recalibrage permet quant à elle de renvoyer la tête de lecture en butée (piste 0) ; l'instruction seek placera cette même tête sur la piste désirée.
Enfin, on pourra utiliser 2 instructions d'état qui permettent d'interroger les registres ST0 et ST3 et une instruction de paramétrage pour régler divers paramètres des lecteurs dont le seul utile sur CPC est le temps de changement de piste.
Les bases étant posées, nous allons à partir de maintenant apprendre à mettre en œuvre le FDC en rentrant petit à petit dans les routines de la bibliothèque FDC fournie ici. Ces routines sont déjà largement commentées dans le listing lui-même, aussi nous nous focaliserons plutôt sur leur fonctionnement global dans les explications qui suivent.
Comme je vous l'ai déjà dit précédemment, sur CPC, le contrôle de l'allumage des moteurs des lecteurs se fait via le port &FA7E
. Si on y met 1, on allume et si on y envoie 0, on coupe tout ! La seule chose à laquelle il faut penser, c'est de mettre une petite temporisation après l'ordre d'allumage pour que les moteurs aient le temps d'arriver à leur vitesse de croisière.
Voici à présent la liste des instructions du FDC qui sont utilisées dans notre premier petit programme d'exemple ci-dessous. Nous allons en tout et pour tout en voir quatre dans cette section.
La première instruction que vous allez rencontrer dans l'exemple est l'instruction de recalibrage. Celle-ci permet, comme son nom l'indique, de recalibrer le lecteur de disquettes. Pratiquement, il s'agit d'un ordre de retour en piste 0 de la tete de lecture ; mais pas tout à fait puisque le FDC n'envoie au maximum que 77 impulsions de retour arrière de la tête… Sur un lecteur 3” 40 pistes, ça ne pose aucun problème, mais sur un lecteur 3”1/2, cela peut ne pas suffir si notre tête était au-delà de la piste 77. Pour y remédier, on a deux solutions : être radical et envoyer toujours les ordres de recalibrage par paire (si la tête est déjà en piste 0, il ne se passe rien de toute façon) ou se servir d'informations que le FDC peut nous renvoyer si on lui demande gentiment… comme le numéro de piste en fin d'instruction (c'est celle pour laquelle j'ai opté). Voici l'instruction FDC :
Nom d'instruction : recalibrage de tête Code d'instruction : %00000111 Paramètre : IDlecteur Retour : Rien
Bon, je crois que quelques précisions s'imposent à propos de ce que j'ai appelé IDLecteur
. Il s'agit en fait d'un code précisant à la fois le numéro du lecteur et la tête concernée par l'instruction. Ce paramètre est demandé par la plupart des instructions et est stockée dans la variable Lecteur
de la bibliothèque FDC. Voici sa structure :
Le petit blond à lunettes, toujours fidèle au poste, pose sa question sournoise : “à quoi sert de donner le numéro de tête alors que sur les lecteurs double face les deux têtes sont liées et se déplacent forcément ensemble sur le disc ?” Eh bien ça sert à rien du tout ! Z'avez vu comme je l'ai remballé le trouble-fête ! En effet, c'est, dans ce cas, sans aucun intérêt, mais bon, c'est comme ça (des lecteurs double face aux têtes indépendantes étaient peut-être prévus…).
La suite logique de l'instruction de recalibrage est l'instruction d'interrogation d'état d'interruption. En effet, il s'agit de l'instruction à utiliser pour tester ce qu'il advient de l'instruction qu'on vient de faire exécuter au FDC. Celle-ci nous renvoie le registre d'état ST0 (dont nous avons déjà parlé plus haut et dont nous reparlerons aussi plus bas) qui va nous permettre d'attendre la fin de l'instruction programmée précédemment. Pour les sceptiques, jetez un coup d'oeil à la routine WaitIns
de la bibliothèque de routines… vous constaterez que cette instruction nous renvoie également le numéro de la piste en cours, paramètre que j'utilise dans la routine de recalibrage pour vérifier si je suis effectivement arrivé en piste 0 à la fin de celle-ci. Voici le protocôle complet de l'instruction d'interrogration de l'état des interruptions :
Nom d'instruction : interrogation de l'état des interruptions Code d'instruction : %00001000 Paramètre : Aucun Retour : ST0 Numéro de piste en cours
L'instruction qui va nous intéresser à présent est celle nous permettant de positionner la tête de lecture où on le désire. Elle répond au doux nom d'instruction de recherche de piste et fonctionne comme suit :
Nom d'instruction : recherche de piste Code d'instruction : %00001111 Paramètre : IDLecteur Numéro de piste Retour : Rien
Comme vous pouvez le constater, cette instruction est élémentaire ; elle est utilisée dans la routine GoTo
de la bibliothèque.
Et puis pour finir, une instruction extrêmement pratique qui permet d'interroger l'état des lecteurs en nous renvoyant le registre d'état ST3 qui nous dit tout sur ce qui se passe dans leur ventre (disquette manquante ou protégée, lecteur défectueux, type de lecteur, etc.). Alors pour récupérer ce registre, il n'y a pas plus simple :
Nom d'instruction : interrogation des lecteurs Code d'instruction : %00000100 Paramètre : IDLecteur Retour : ST3
En voilà une instruction qu'elle est pratique ! Elle est utilisée dans la routine ReadST3
, pas de surprise. Nous reviendrons plus en détail sur ce registre un peu plus bas.
Voici un petit exemple utilisant les quelques routines de base mettant en jeu les instructions vues ci-dessus.
; Exemple d'utilisation des routines Exemple di ex af,af' push af call moton ; Réveil ld a,1 ; On sélectionne call sellect ; le drive B ld a,0 ; et call seltete ; la face A call piste0 ; Recalibrage le e,81 ; Et paf, on call goto ; fonce en piste 81 ! call piste0 ; Vlam retour en piste 0 call motoff ; Dodo Restore pop af ex af,af' ei ret
Les quelques exemples qui précèdent ont pour seul objet de titiller la mécanique du lecteur de disquettes. Que ce soit clair : que la disquette présente dans le lecteur soit formatée, non formatée, découpée au cutter (porte-lame pour les puristes) ou je ne sais quoi d'autre, du moment qu'il y a un semblant de disque qui tourne dans le lecteur, on peut faire un recalibrage, déplacer la tête en piste X ou demander l'état du lecteur. À aucun moment, il n'y a de tentative de lecture lors de ces opérations.
Plus simplement, le lecteur n'a pas besoin de la disquette pour se rendre en piste 0 (il y a une butée de début de course) ni pour pour aller en piste X (il compte les pas de son moteur à partir de la piste 0). Ce qui m'amène à répondre à une question d'ailleurs très pertinente de Madram : sans relalibrage le lecteur ne peut pas se rendre sur une piste précise. Toutefois, en temps normal, le recalibrage est à effectuer une seule fois lors de la première utilisation du FDC après allumage de l'ordinateur. Notez tout de même que l'Amsdos fait un recalibrage à chaque fois qu'il rencontre une erreur de lecture avant de retenter sa chance.
Pour en finir avec les remarques sur le listing, comme l'a justement fait remarquer Madram (décidément il va devenir pire que le petit blond à lunettes), la boucle WaitIns
qui consiste à attendre que le FDC soit prêt, peut être sans fin s'il est complètement planté (par exemple lors d'une tentative d'accès à un lecteur mal connecté). Il faudrait donc normalement mettre une sécurité ; à titre d'exemple l'Amsdos abandonne tout accès au FDC au bout de quelques secondes et renvoie un “Bad Command”.
Avant de nous attaquer aux instructions élaborées, voyons en détail les registres d'état ST0 à ST3 qui vont très vite nous devenir indispensables. En effet, tant que l'on se contente d'ordres simples, le FDC n'a pas grand chose à nous dire, mais dès qu'on lui parle d'opérations de lecture/écriture/formatage, il devient plus bavard et prend un malin plaisir à montrer du doigt la moindre anomalie.
Le registre d'état ST0 est l'un des plus importants car il nous renseigne directement sur ce qui s'est passé durant l'instruction.
Le registre d'état ST1 est moins dense que ST0 en informations mais il demeure très intéressant.
Registre ST1 :
Les informations fournies par le registre ST2 sont un complément direct du registre ST1. La plupart de ses bits précisent d'éventuelles erreur indiquées par le registre ST1.
Registre ST2 :
Ah ! Le petit blond à lunettes est de retour ! Pourquoi n'y a-t-il pas de commentaire face aux bits 2 et 3 du registre ST2 ? Eh bien tout simplement car ces bits sont sans intérêt pour le moment car il sont spécifiques à l'instruction “Scan” du FDC que nous verrons plus loin. Pour l'instant, ignorez-les.
Enfin, il y a le fameux registre ST3 qui ne peut être obtenu que par l'instruction d'interrogation de l'état d'un lecteur que nous avons vu la dernière fois.
Registre ST3 :
Nous passons maintenant aux choses sérieuses avec le formatage de nos chers discs. Un petit rappel : le formatage d'une disquette consiste à y réserver les zones de données et de repérage pour pouvoir ensuite l'utiliser. Le FDC dispose d'une instruction de formatage qui est à la fois puissante et très simple à utiliser. En voici les caractéristiques
Nom d'instruction : formatage de piste Code d'instruction : %01001101 Paramètres : IDlecteur Taille de secteur Nombre de secteur par piste Octet de séparation ID/données Octet de remplissage du secteur Exécution : le Z80 envoie au FDC les ID des secteurs à formater dans l'ordre souhaité. Retour : ST0 ST1 ST2 Numéro de piste en cours Tête en cours Nom du secteur en cours Taille du secteur en cours
Les plus observateurs d'entre vous auront constaté qu'il y a une phase de plus dans cette instruction par rapport à celles vues précedemment. En fait, non, mais pour toutes les autres instructions déjà vues le FDC n'attendait rien du Z80 pour s'exécuter. Ici - et il en sera d'ailleurs de même pour toutes les opérations de lecture/écriture - la phase d'exécution nécessite un échange de données entre les deux compères. Pour plus de détails, relisez cette section.
Penchons nous à présent sur les paramètres de cette instruction. L'IDLecteur n'a en principe plus de secrets pour vous. La taille des secteurs non plus puisque nous en avons déjà parlé ici. Le nombre de secteurs par piste est bien sûr fonction de la taille des dits secteurs mais, au risque de me répéter, nous avons encore vu tout celà ici. L'octet de séparation ID/données est celui qui servira à remplir le vide entre les champs d'ID et les champs de données dont nous avons parlé dans… oui ! Vous avez deviné, ici ! Et puis pour finir, il y a l'octet de remplissage qui est la valeur que va mettre le FDC dans nos secteurs encore vides (non, nous n'en avons pas parlé dans ici).
Vient ensuite la phase d'exécution durant laquelle on va envoyer successivement les ID des secteurs à formater. C'est à ce moment que l'on choisit le nom et l'ordre de nos secteurs sur la piste. Et devinez où nous avons déjà parlé de l'optimisation des l'ordre des secteurs sur une pistes ? ici bien sûr ! Sinon, en ce qui concerne les noms à proprement parler, vous êtes complètement libres.
Après la théorie, un peu de pratique ! Nous allons ici étudier trois des routines de la bibliothèque FDC. Figurent également quelques datas que je vous propose d'utiliser pour tester ces routines. Dans tous les cas, n'oubliez pas d'envoyer l'ordre de démarrage du moteur avant tout…
; Exemple 1 d'utilisation des routines Exemple1 di ex af,af' push af call moton ld a,1 call sellect ld a,0 call seltete call piste0 ld hl,secta ld ix,typpist ld e,0 call fmtpist call motoff Restore pop af ex af,af' ei ret SectA db &11,&12,&13,&14,&15,&16,&17,&18,&19,&1a TypPist db 2,10,&20,&e5
Le premier exemple est le plus élémentaire : il permet de formater une piste homogène sur le lecteur et la face en cours. Comme prévu, il n'est fait aucun accès direct au FDC et tout passe entièrement par les routines élémentaires vues précedemment. La routine ”FmtPist
” que nous utilisons a besoin de trois paramètres en entrée : la liste des noms des secteurs (par exemple HL=SectA), les paramètres de formatage (au hasard mettez IX=TypPist), et bien sûr, le numéro de la piste à formater. Vous récupérez en sortie les registres d'état S0, ST1 et S2 qui vous permettront de savoir si tout s'est bien passé. Ah ! Il n'a pas pu s'en empêcher ! Le petit blond à lunettes se demande pourquoi je parle de piste homogène… Eh bien tout simplètement car tous les secteurs d'une même piste sont, avec cette routine, de tailles identiques. Il est en effet tout à fait envisageable de créer une piste avec des secteurs de tailles différentes en transférant des ID de secteurs variables lors de la phase d'exécution. Mais attention, calculez bien vos longueurs de secteurs et vos GAP pour ne pas déborder (cf. cette section).
; Exemple 2 d'utilisation des routines Exemple2 di ex af,af' push af call moton ld a,1 call sellect ld a,0 call seltete call piste0 ld hl,secta ld ix,typpist ld e,0 ld d,2 call fmtside call motoff Restore pop af ex af,af' ei ret SectA db &11,&12,&13,&14,&15,&16,&17,&18,&19,&1a TypPist db 2,10,&20,&e5
La deuxième routine que nous utilisons ici, ”FmtSide
” est en quelque sortie une macro-routine qui utilise la première pour lancer en boucle le formatage de plusieurs pistes. Il n'y a rien de particulier à signaler à son propos sinon qu'elle ne tient absolument pas compte des éventuelles erreurs renvoyées par ”FmtPist
”. Libre à vous d'écrire une routine plus performante.
; Exemple 3 d'utilisation des routines Exemple3 di ex af,af' push af call moton ld a,1 call sellect ld a,0 call seltete call piste0 ld hl,secta ld iy,sectb ld ix,typpist ld e,0 ld d,2 call fmtfull call motoff Restore pop af ex af,af' ei ret SectA db &11,&12,&13,&14,&15,&16,&17,&18,&19,&1a SectB db &01,&02,&03,&04,&05,&06,&07,&08,&09,&0a TypPist db 2,10,&20,&e5
Derrière le label ”FmtFull
” se cache une autre routine d'exploitation de ”FmtPist
”. Celle-ci est similaire en tout point à ”FmtSide
” hormis le fait qu'elle ne tient pas compte de la tête en cours puisqu'elle formate en double face. Il vous faudra donc la tester avec un lecteur double face. De même, au niveau des paramètres à fournir en entrée, on différencie la liste des noms des secteurs des faces A et B. Attention, c'est facultatif ! Vous pouvez tout à fait avoir les mêmes noms sur les deux faces… ce qui permet d'utiliser le mode mulitrack en lecture ou écriture de secteurs.
Comme je vous l'ai déjà dit à plusieurs reprises, le FDC dispose en lecture comme en écriture d'une instruction permettant de traiter plusieurs secteurs sur une même piste d'un seul coup. L'exploitation de ce mode de fonctionnement est très importante car elle augmente considérablement les taux de transfert. Eh bien, le mode multitrack permet d'oublier la notion de face pour ces instructions.
Je m'explique. Imaginons un disc formaté en utilisant les datas proposés ci-dessous. On a donc des faces A avec des secteurs numérotés de 0 à 9 et des faces B avec des secteurs numérotés de 0 à 9 également. Si on demande au FDC de nous lire les secteurs 0 à 9 d'une piste en mode multitrack, ça marche directement sans avoir à se préoccuper de la face ; les deux seront lues consécutivement. C'est pas une belle option ça ? Un seul ordre de lecture (ou d'écriture) au lieu de deux en mode normal !
; Datas compatibles pour des lectures/écritures en mode multitrack SectA db &00,&01,&02,&03,&04,&05,&06,&07,&08,&09 SectB db &00,&01,&02,&03,&04,&05,&06,&07,&08,&09 TypPist db 2,10,&20,&e5
Nous avons à notre disposition trois instructions de lecture. La lecture de secteurs, la lecture de secteurs effacés, et la lecture de piste. Comme je vous l'ai déjà fait remarquer il y a quelques temps, les secteurs dits effacés n'ont qu'un intéret limité et ne représentent absolument pas des données effectivement effacées du disque mais simplement des secteurs “marqués” comme tels. Relisez les sections précédentes si, comme le petit blond à lunettes, vous n'avez pas encore assimilé toutes les notions relatives aux pistes et aux secteurs.
Mais revenons-en à ce qui nous intéresse : le descriptif de ces instructions. Tout d'abord, voici la lecture de secteurs :
Nom d'instruction : lecture de secteurs Code d'instruction : %t1000110 Paramètres : IDlecteur Numéro de piste Numéro de tête Nom du premier secteur à lire Taille de secteur Nom du dernier secteur à lire Octet de séparation ID/données Longueur effective de secteur (si taille de secteur=0) Exécution : le FDC envoie au Z80 le contenu des secteurs demandés Retour : ST0 ST1 ST2 Numéro de piste en cours Tête en cours Nom du secteur en cours Taille du secteur en cours
Je rassure tout de suite le petit blond à lunettes : le petit t
en bit 7 du code d'instruction n'est pas une faute de frappe ! C'est un paramètre ; s'il est mis à 1 alors on passe en mode multitrack (cf. cette section) !
Et puis enfin, je dois vous mettre en garde sur le fait que le numéro de piste, le numéro de tête et les informations sur la taille des secteurs ne sont là que pour vérification de la validité du header des secteurs. La tête doit avoir préalablement été déplacée sur la piste où l'on désire lire les secteurs et la tête de lecture (lorsque le mode multitrack est désactivé) est choisie via l'IDLecteur.
Vient ensuite l'instruction de lecture des secteurs effacés. Celle-ci a exactement les mêmes paramètres et se comporte de la même manière. Je me contente donc de vous donner son code d'instruction : %t1001100.
Et puis pour en finir avec la lecture de nos discs, nous avons à notre disposition l'instruction de lecture de piste. Celle-ci est similaire à l'instruction de lecture de secteurs utilisée avec le paramètre “Nom du premier secteur à lire” égal au premier secteur de la piste et a comme avantage d'être plus rapide. Concrêtement elle permet de lire le contenu des X premiers secteurs de la piste. Toutefois, si le secteur indique comme “Nom du dernier secteur à lire” n'existe pas, c'est tous les secteurs de la piste qui seront lus. En outre, les quatre premiers paramètres à fournir après l'IDLecteur ne sont là que pour permettre au FDC de positionner certains flags des registres d'état et n'ont donc pas d'influence sur le déroulement de l'instruction. Le petit blond à lunettes s'impatiente, voici donc son descriptif :
Nom d'instruction : lecture de piste Code d'instruction : %01000010 Paramètres : IDlecteur Numéro de piste Numéro de tête Nom de secteur Taille de secteur Nom du dernier secteur à lire Octet de séparation ID/données Longueur effective de secteur (si taille de secteur=0) Exécution : le FDC envoie au Z80 le contenu des secteurs demandés Retour : ST0 ST1 ST2 Numéro de piste en cours Numéro de tête en cours Nom du secteur en cours Taille du secteur en cours
Attention : pas de mode multitrack ici !
Les instructions d'écriture sont au nombre de deux : l'écriture de secteurs et l'écriture de secteurs effacés. Ces deux instructions sont très similaires à leurs équivalents en lecture. Mais attention, comme je vous l'ai déjà précisé précédemment, on ne peut écrire que dans un secteur existant sur la piste en cours ! Toute instruction d'écriture de secteurs sera mise en échec si le secteur demandé n'a pas été préalablement formaté sur la piste ; les instructions d'écriture ne peuvent en aucun cas créer un secteur. Celles-ci ne sont là que pour “remplir” ceux-ci.
Voici donc le descriptif de nos deux dernières instructions FDC ; l'écriture de secteurs :
Nom d'instruction : écriture de secteurs Code d'instruction : %t1000101 Paramètres : IDlecteur Numéro de piste Numéro de tête Nom du premier secteur à écrire Taille de secteur Nom du dernier secteur à écrire Octet de séparation ID/données Longueur effective de secteur (si taille de secteur=0) Exécution : le Z80 envoie au FDC les données à écrire sur le disque Retour : ST0 ST1 ST2 Numéro de piste en cours Numéro de tête en cours Nom du secteur en cours Taille du secteur en cours
Tout comme pour les instructions de lecteur, le petit t
permet d'activer le mode multitrack s'il est mis à 1. Mais dans ce cas, le disque doit être dans un format adéquat comme vu précédemment.
Et puis pour finir, nous avons l'instruction d'écriture de secteurs effacés. Comme celle-ci fonctionne de la même manière que pour les secteurs “normaux”, je vous donne juste son code d'instruction : %t1001001
.
Mais le FDC a encore un bon nombre d'autres instructions… je vais très rapidement vous en décrire certaines qui pourront peut-être vous êtres utiles mais sachez qu'avec tout ce que nous avons déjà vu, vous avez tous les éléments en main pour créer et gérer vos formats de disque.
Une instruction assez pratique si vous avez oublié le nom de vos secteurs sur la piste en cours : la lecture des ID des secteurs. Grâce à cette instruction, vous pouvez demander au FDC de vous communiquer quelle est l'ID du prochain secteur rencontré.
Nom d'instruction : lecture de l'ID du prochain secteur Code d'instruction : %01001010 Paramètres : IDlecteur Exécution : Le FDC bosse, rien à lire ou envoyer. Retour : ST0 ST1 ST2 Numéro de piste en cours Numéro de tête en cours Nom du secteur en cours Taille du secteur en cours
Pour la première fois depuis le début de cet article, on a enfin une instruction qui nous donne des informations utiles dans les quatre derniers octets !
Et puis pour finir, je vous donner l'instruction permettant de régler le délai d'attente piste à piste. Plus vous avez un lecteur récent, plus ce délai peut être réduit (un lecteur 3” a besoin d'un plus grand délai qu'un 3”1/2).
Nom d'instruction : réglage du temps piste à piste Code d'instruction : %00000011 Paramètres : %dddd0000 %00000000
Les bits dddd
déterminent notre délai d'attente. Si vous mettez 1111
ce délais est minimum et un 0000
spécifie un délai maximum. Un délai trop rapide provoque des erreurs de lecture et un délai trop grand est un perte de performance notable. Les autres bits qui sont ici figés à 0 ont en fait également une signification, seulement pour des lecteurs au format 8”, aussi je pense que personne n'est concerné. Le bit 0 du deuxième paramètre permet quant à lui de sélectionner le mode non DMA qui est le seul disponible sur CPC.
Voilà, nous avons fait le tour du chip le plus délicat à programmer sur CPC ; j'espère que toutes ces informations vous seront utiles.
Télécharger le listing au format Maxam 1.5
; ; Routines de gestion du FDC en Hardware ; OffseT of Futurs' pour Quasar CPC (1997-1999) ; Version 6 ; Org &8000 Nolist ; Exemple d'utilisation des routines Init di ex af,af' push af call moton ld a,1 call sellect ld a,0 call seltete call piste0 ; ld e,82 ; call goto ; ld hl,sectb ; ld ix,typpist ; ld e,0 ; call fmtpist ld hl,secta ld iy,sectb ld ix,typpist ld e,0 ld d,2 call fmtfull ld de,&8000 ld ix,typpist ld iy,&c000 ld hl,&111a call sectr call motoff Restore pop af ex af,af' ei ret ; ; Utilitaires FDC en trackdisk ; ; ; Lecture de secteurs en double densité ; avec gestion des erreurs ; ; In ; E=Piste ; D=Mode multitrack (0=Off / 128=On) ; IX pointe sur les paramètres secteurs ; (Taille,Nombre par piste,octet GAP#3,octet de remplissage) ; H=Premier secteur à lire ; L=Dernier secteur à lire ; IY=Adresse du tampon ; Out ; FlagErr=Statut des erreurs ; ; IY=Fin du tampon SectR call litsecs call testerr ld (flagerr),a ret ; ; Écriture de secteurs en double densité ; ; In ; E=Piste ; D=Mode multitrack (0=Off / 128=On) ; IX pointe sur les paramètres secteurs ; (Taille,Nombre par piste,octet GAP#3,octet de remplissage) ; H=Premier secteur à lire ; L=Dernier secteur à lire ; IY=Adresse du tampon ; Out ; FlagErr=Statut des erreurs ; ; IY=Fin du tampon SectW call ecrsecs call testerr ld (flagerr),a ret ; ; Lecture de secteurs en double densité ; ; In ; E=Piste ; D=Mode multitrack (0=Off / 128=On) ; IX pointe sur les paramètres secteurs ; (Taille,Nombre par piste,octet GAP#3,octet de remplissage) ; H=Premier secteur à lire ; L=Dernier secteur à lire ; IY=Adresse du tampon ; Out ; E=ST0 ; D=ST1 ; L=ST2 ; H=Piste courante ; Modif ; AF, AF', BC=&FA7E, IY=fin du tampon LitSecS push de call goto pop de ld a,%01100110 ; Ordre de lecture en DD or d ; Bit multitrack call putfdc ld a,(lecteur) ld d,a ld a,(tete) sla a sla a or d call putfdc ; Lecteur/Tête ld a,e call putfdc ld a,(tete) ; Envoi de l'ID call putfdc ld a,h call putfdc ld a,(ix+0) call putfdc ld a,l call putfdc ld a,&20 call putfdc xor a call putfdc WaitDAT in a,(c) jp p,waitdat and 32 jr z,finlit inc c in a,(c) dec c ld (iy),a inc iy jr waitdat FinLit call getfdc ld e,a call getfdc ld d,a call getfdc ld l,a call getfdc ld h,a call getfdc call getfdc call getfdc ret ; ; Écriture de secteurs en double densité ; ; In ; E=Piste ; D=Mode multitrack (0=Off / 128=On) ; IX pointe sur les paramètres secteurs ; (Taille,Nombre par piste,octet GAP#3,octet de remplissage) ; H=Premier secteur à lire ; L=Dernier secteur à lire ; IY=Adresse du tampon ; Out ; E=ST0 ; D=ST1 ; L=ST2 ; H=Piste courante ; Modif ; AF, AF', BC=&FA7E, IY=fin du tampon EcrSecS push de call goto pop de ld a,%01000101 ; Ordre d'écrture en DD or d ; Bit multitrack call putfdc ld a,(lecteur) ld d,a ld a,(tete) sla a sla a or d call putfdc ; Lecteur/Tête ld a,e call putfdc ld a,(tete) ; Envoi de l'ID call putfdc ld a,h call putfdc ld a,(ix+0) call putfdc ld a,l call putfdc ld a,&20 call putfdc xor a call putfdc WaitECR in a,(c) jp p,waitecr and 32 jr z,finecr ld a,(iy) inc iy inc c out (c),a dec c jr waitecr Finecr call getfdc ld e,a call getfdc ld d,a call getfdc ld l,a call getfdc ld h,a call getfdc call getfdc call getfdc ret ; ; Formatage homogène d'un disc en simple face ; ; In ; HL pointe sur la liste des noms des secteurs ; IX pointe sur les paramètres secteurs ; (Taille,Nombre par piste,octet GAP#3,octet de remplissage) ; E=Piste de départ ; D=Piste d'arrivée ; Out ; FlagErr=Statut des erreurs ; D=Piste courante ; E=ST0 ; Modif ; AF, AF', BC=&FA7E, HL FmtSide xor a ld (flagerr),a ld a,d cp e ret c push de call piste0 pop de LoopSid push de push hl call fmtpist call testerr pop hl pop de cp 1 jr z,loopsid cp 3 jp putflagerrc cp 2 call z,putflagerri ld a,e inc e cp d jr nz,loopsid call piste0 or a ret ; ; Formatage homogène d'un disc en double face ; ; In ; HL=Pointe sur la liste des noms des secteurs face A ; IY pointe sur la liste des noms des secteurs face B ; IX pointe sur les paramètres secteurs ; (Taille,Nombre par piste,octet GAP#3,octet de remplissage) ; E=Piste de départ ; D=Piste d'arrivée ; Out ; C=Erreur dans les données ; D=Piste courante ; E=ST0 ; Modif ; AF, AF', BC=&FA7E, HL FmtFull xor a ld (flagerr),a ld a,(tete) push af ld a,d cp e ret c push de call piste0 pop de LoopFul push de push hl xor a call seltete push de push hl call fmtpist call testerr pop hl pop de pop hl pop de cp 1 jr z,loopful cp 3 jp z,putflagerrc cp 2 call z,putflagerri LoopFu2 push de push hl ld a,1 call seltete push iy pop hl call fmtpist call testerr pop hl pop de cp 1 jr z,loopfu2 cp 3 jp z,putflagerrc cp 2 call z,putflagerri ld a,e inc e cp d jr nz,loopful call piste0 pop af ld (tete),a or a ret ; ; Formatage d'une piste homogène ; In ; HL=Pointe sur la liste des noms des secteurs ; IX pointe sur les paramètres secteurs ; (Taille,Nombre par piste,octet GAP#3,octet de remplissage) ; E=piste ; Out ; E=ST0 ; D=ST1 ; L=ST2 ; Modif ; AF, AF', BC=&FA7E, H FmtPist call goto ld e,d ld a,%01001101 call putfdc ; Ordre de formatage ld a,(lecteur) ld d,a ld a,(tete) sla a sla a or d call putfdc ; Lecteur & Tête ld a,(ix+0) call putfdc ; Taille des secteurs ld a,(ix+1) call putfdc ; Nombre de secteurs ld a,(ix+2) call putfdc ; Octet GAP#3 ld a,(ix+3) call putfdc ; Octet remplissage ld d,(ix+1) ; Envoi des 4 octets LoopFmt ld a,e ; d'ID pour chaque secteur call putfdc ld a,(tete) call putfdc ld a,(hl) call putfdc inc hl ld a,(ix+0) call putfdc dec d jr nz,loopfmt call getfdc ld e,a call getfdc ld d,a call getfdc ld l,a ld h,4 LoopFm2 call getfdc dec h jr nz,loopfm2 ret ; ; Routines élémentaires FDC ; ; ; Choix du lecteur courant ; In ; A=numéro de lecteur ; Modif ; AF SelLect and 3 ld (lecteur),a ret ; ; Choix de la tête courante ; In ; A=numéro de tete ; Modif ; AF SelTete and 1 ld (tete),a ret ; ; Allumage des moteurs ; Modif ; AF, BC=&FA7E MotOn ld a,(moteurs) ; État des moteurs or a ; Retour direct ret nz ; si déjà allumés ld bc,&fa7e ; Sinon mise à inc a ; jour de l'état ld (moteurs),a ; des moteurs et out (c),a ; allumage ld bc,&f520 ; Attente de Tempo in a,(c) ; quelques VBL rra ; pour etre sur jr nc,tempo ; d'atteindre Wait in a,(c) rra jr c,wait dec c ; la vitesse jr nz,tempo ; de croisière ret ; Fini ! ; ; Arret des moteurs ; Modif ; AF, BC=&FA7E MotOff xor a ; Mise à zéro de ld bc,&fa7e ; l'état des moteurs ld (moteurs),a ; et arret des out (c),a ; moteurs ret ; Fini ! ; ; Recalibrage du lecteur courant ; Out ; D=piste courante ; E=ST0 ; Modif ; AF, AF', BC=&FB7E Piste0 ld bc,&fb7e Twice ld a,%00000111 ; Ordre de recalibrage call putfdc ; Envoi au FDC ld a,(lecteur) ; pour le lecteur call putfdc ; courant call waitins ld a,d or a ; calibrage jr nz,twice ; Sinon ret ; Fini ! ; ; Positonnement sur une piste ; In ; E=piste où aller ; Out ; D=piste courante ; E=ST0 ; Modif ; AF, AF', BC=&FB7E GoTo ld bc,&fb7e ld a,%00001111 call putfdc ld a,(lecteur) ld d,a ld a,(tete) sla a sla a or d call putfdc ld a,e call putfdc jp waitins ; ; Attente de fin d'instruction ; In ; BC=&FB7E ; Out ; D=piste courante ; E=ST0 ; Modif ; AF, AF' WaitIns ld a,%00001000 call putfdc call getfdc ld e,a call getfdc ld d,a ld a,e and %00100000 jr z,waitins ret ; ; Lecture du registre ST3 ; In ; BC=&FB7E ; Out ; A=ST3 ; Modif ; F, AF', E ReadST3 ld a,%00000100 call putfdc ld a,(lecteur) ld e,a ld a,(tete) sla a sla a or e call putfdc jp getfdc ; Envoi d'une valeur sur le port de données ; In ; BC=&FB7E ; A=valeur a envoyer ; Modif ; F, AF' PutFDC ex af,af' LoopPut in a,(c) jp p,loopput ex af,af' inc c out (c),a dec c ret ; ; Réception d'une valeur sur le port de données ; In ; BC=&FB7E ; Out ; A=valeur lue ; Modif ; F, AF' GetFDC ex af,af' LoopGet in a,(c) jp p,loopget ex af,af' inc c in a,(c) dec c ret ; ; Gestion des erreurs disque ; In ; E=ST0 ; D=ST1 ; L=ST2 ; Out ; A=Statut des erreurs ; 0 ; Pas d'erreur ; 1 ; Erreur signalée et Retry demandé ; 2 ; Erreur signalée et Ignore demandé ; 3 ; Erreur signalée et Cancel demandé TestErr ld a,e bit 3,a jr nz,discmis and 254 bit 7,a jr nz,unknow ld a,d and 127 jr z,ok cp 1 jr z,unform cp 2 jr z,discpro cp 4 jr z,secmis jr unknow Ok ld a,l or a jr nz,discerr xor a ret DiscMis ld hl,discmistext call putscr jr retcan Unknow ld hl,unknowtext call putscr jr ignretcan Unform ld hl,unformtext call putscr jr ignretcan DiscPro ld hl,discprotext call putscr jr retcan SecMis ld hl,secmistext call putscr jp ignretcan DiscErr ld hl,discerrtext call putscr jp ignretcan PutSCR ld a,(hl) or a ret z call &bb5a inc hl jr putscr RetCan ld hl,retcantext call putscr call &bb00 LoopRetCan xor a call &bb06 cp "r" jr z,retry cp "a" jr z,cancel jr loopretcan IgnRetCan ld hl,ignretcantext call putscr call &bb00 LoopIgnRetCan xor a call &bb06 cp "r" jr z,retry cp "i" jr z,ignore cp "a" jp z,cancel jr loopignretcan Retry call &bb5a ld a,1 ret Ignore call &bb5a ld a,2 ret Cancel call &bb5a ld a,3 ret ; PutFlagErrI ld a,2 ld (flagerr),a ret PutFlagErrC ld a,3 ld (flagerr),a ret ; Data programme ; Moteurs db 0 Lecteur db 0 Tete db 0 FlagErr db 0 DiscMisText db 13,10,"Disque manquant",13,10,0 UnknowText db 13,10,"Problème inconnu !",13,10,0 UnformText db 13,10,"Disque non formaté",13,10,0 DiscProText db 13,10,"Disque protégé contre l'écriture",13,10,0 SecMisText db 13,10,"Secteur non trouvé",13,10,0 DiscErrText db 13,10,"Erreur de données",13,10,0 RetCanText db 13,10,"Recommencer ou Abandonner ? ",0 IgnRetCanText db 13,10,"Recommencer, Abandonner ou Ignorer ? ",0 ; ; Data Test ; SectA db &11,&12,&13,&14,&15,&16,&17,&18,&19,&1a SectB db &01,&02,&03,&04,&05,&06,&07,&08,&09,&0a TypPist db 2,10,&20,&e5 ; ; Routine de traçage ; Debug pop af ex af,af' ei brk di ex af,af' push af ret