Suivez-nous sur X

|
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
ALL
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
A propos d'Obligement
|
|
David Brunet
|
|
|
|
Programmation : C - Un silencieux pour lecteur de disquette
(Article écrit par Frédéric Mazué et extrait d'Amiga News Tech - octobre 1990)
|
|
Heureux de vous retrouver, je vous propose aujourd'hui de mettre un silencieux dans vos lecteurs de disquette.
Ce n'est pas plus bête que de vouloir mettre un tigre dans son moteur, c'est moins dangereux et c'est
si facile alors pourquoi à l'essence privée ?
Murmures...
Pourquoi aller s'en priver disais-je donc, or voilà que j'entends des murmures dans le fond de la classe !
Comment ? On veut nous donner un programme qui empêche de cliqueter les lecteurs, alors qu'il existe un
programme du domaine public nommé "NoClick" qui remplit déjà cet office ! Seraient-ils à court d'idées chez
Commodore Revue ?
Honni soit qui mal y pense !
Et le maulubec vous trousque ! Car il est intéressant de reconsidérer le problème et je m'en explique.
Le programme NoClick travaille de la manière suivante : il fait une recherche d'octets en ROM afin de
localiser le trackdisk.device (pour que le programme puisse fonctionner selon les versions connues du
Kickstart). Ceci fait, le trackdisk.device en question est entièrement recopié en mémoire,
puis toutes les adresses de saut sont modifiées afin d'obtenir à nouveau un programme susceptible de
fonctionner, puis ledit nouveau trackdisk est modifié afin que les lecteurs ne cliquettent plus.
Il suffit en fait de changer un seul octet pour obtenir ce résultat. Ils auraient quand même pu
faire un effort chez Commodore !
Enfin l'Amiga est astucieusement leurré afin que ce nouveau trackdisk.device soit utilisé. Bien.
L'avantage de cette méthode est qu'aucune tâche n'est ajoutée donc l'Amiga n'est ralenti en rien.
Par contre, il y a à mon avis quelques inconvénients :
- Occupation d'une grande quantité de mémoire.
- Je n'aime pas ce genre de programmes qui utilisent directement des bouts de ROM sans passer par
les bibliothèques car il y a toujours le risque que Commodore modifie (et c'est son droit)
légèrement l'organisation du Kickstart sur une nouvelle série d'ordinateurs sans prévenir personne.
Et on se retrouve ainsi avec un programme qui ne marche plus ou pire : qui invoque le gourou au
moment le plus inopportun.
- Enfin, NoClick comporte un bogue. Bon j'avoue tout de suite que je suis allé le chercher loin
celui-là, mais c'est dans ma nature de grand pourfendeur de bogues devant l'Éternel. En effet, sur
un Amiga 500, en amorçant sur disque dur A590, avec une disquette quelconque en DF1: et aucune
disquette en DF0: et si NoClick est placé en début du startup-sequence, il se passe la chose suivante :
DF1: ne cliquettera effectivement plus si vous retirez la disquette mais DF0:, lui, cliquettera
Ad Vitam Eternam.
Une autre solution
Un lecteur de disquette ne cliquette que quand il est vide. Le cliquetis est généré par une
routine d'interruption qui vérifie régulièrement si une disquette est insérée dans le lecteur,
mais cette routine a le malheur de faire déplacer la tête du lecteur à chaque test, ce test
s'effectuant à travers le trackdisk.device en ROM.
Quand un lecteur contient une disquette,
il ne cliquette plus car la routine d'interruption considère à juste titre que le lecteur est occupé
et ne fait plus son test (en fait je simplifie un peu). Ceci considéré, il faut savoir qu'AmigaDOS
traite chaque lecteur de disquette en tant que processus à son plus haut niveau, sans se préoccuper
le moins du monde de ce que fait le trackdisk.device. Pour être plus précis, ce qui se passe quand un
accès disque est nécessaire est qu'AmigaDOS envoie un message particulier appelé DosPacket
à un processus DOS, par exemple DF0:. Lorsque le processus DF0: a reçu ce message, il s'occupe tout seul
(vive les ordinateurs multitâches !) de lancer le trackdisk.device et d'y écrire ou lire des données.
Ce qui est intéressant, c'est que ces fameux DosPacket permettent une quantité de commandes différentes
dont une (ACTION_ INHIBIT) qui, lorsqu'elle est envoyée, a pour effet "d'inhiber" le processus qui la
reçoit. C'est-à-dire qu'il deviendra impossible d'accéder au processus par d'autres commandes DOS tant
que dure cet état de fait.
Il faut maintenant s'intéresser au niveau le plus bas. La routine d'interruption dont nous avons parlé
tout à l'heure accède au trackdisk, mais pas au lecteur de disquette directement. Ça c'est le trackdisk
qui le fait en envoyant un message au port du lecteur de disquette et ceci uniquement si le drapeau
de disponibilité du port lecteur le permet. J'espère être suffisamment clair ! Maintenant que nous
savons tout ça, voilà le plan suivi par le programme que je vous propose :
- On inhibe le processus correspondant.
- On force le drapeau de disponibilité et le lecteur ne cliquette plus.
- Le programme ouvre ensuite une toute petite fenêtre dans un coin du Workbench.
Cliquer sur le gadget de fermeture de cette fenêtre provoquera l'effet inverse du précédent.
- On rétablit le drapeau de disponibilité.
- On "réveille" le processus précédemment inhibé et le lecteur revient à la vie.
Cette solution comporte un léger inconvénient : ajouter des tâches à l'état d'attente (waiting),
ce qui ralentit légèrement l'Amiga. Je dis légèrement car il ne faut pas oublier que pour compenser,
un processus est inhibé. De plus, une tâche d'attente ne ralentit que peu l'ordinateur. Des esprits
chagrins m'objecteront également que NoClick n'empêche pas l'accès aux lecteurs
contrairement à mon programme. A ceux-là je réponds que si l'on met une disquette dans le lecteur :
- Le lecteur ne cliquette pas.
- Que c'est pour travailler sur la disquette.
- L'utilisation de mon programme ou celle de NoClick ne se justifie donc pas.
Par contre, si le lecteur reste vide, c'est qu'on n'en a pas besoin et dans ce cas l'utilisation
de l'attente (alias "busy", c'est comme ça que j'appelle mon programme) est justifiée. De plus,
si l'on change d'avis, il suffit d'insérer une disquette et de "réveiller" le processus. La
disquette sera bien sûr reconnue comme il se doit. Par contre, il y a des avantages :
- Peu de mémoire occupée.
- Pas d'accès directs en ROM donc possibilité de fonctionnement avec tout Kickstart passé,
présent et à venir.
- Oserais-je le dire ? Pas de bogue.
Pourquoi en C ?
Le programme que je vous propose cette fois est écrit en C alors que je vous avais habitué jusqu'alors
à des listings en assembleur. Ceci s'explique très simplement : je suis paresseux. En effet,
un tel programme est beaucoup plus facile (à mon avis en tout cas) à écrire en C plutôt
qu'en assembleur.
Vous verrez par vous même très bientôt. Normalement, un périphérique logique est, pour ce qui est des décalages négatifs,
structuré comme une bibliothèque. Pour les décalages positifs du trackdisk.device, on trouve d'abord un
mot nul afin de pointer sur une adresse multiple de 4, puis quatre longs mots. Ces quatre longs mots sont des pointeurs
sur les ports des lecteurs de disquette. Si un lecteur n'est pas connecté, le long mot correspondant
est nul. Comme les autres aspects du trackdisk.device ne m'intéressent pas aujourd'hui, je me contente
de construire une petite structure trackdisk à ma convenance comme ceci :
Ainsi est défini un tableau de pointeur sur les ports lecteurs. Il suffira de tester si un élément
du tableau est nul pour savoir que le lecteur correspondant n'est pas connecté. L'adresse de base du
trackdisk.device est trouvée puis castée sur notre propre structure comme ceci :
Je passe sur les parties du programme qui s'occupent des fenêtres ou des gadgets car ce n'est pas
l'objet de cet article. Remarquez bien que le nom "Unit" choisi dans la structure n'est pas innocent.
En effet, le terme générique de tout périphérique géré par un périphérique logique (device) est Unit. Cette structure est
parfois étendue suivant le cas, mais pour ce que nous avons à faire, la structure minima Unit
sera largement suffisante.
Maintenant, il s'agit de vérifier si le numéro du lecteur entré est correct, si le lecteur de ce numéro
existe bien, si le processus correspondant n'a pas déjà été inhibé une fois. Il faut également prévoir
que le programme peut être lancé avec tous les lecteurs inhibés, auquel cas il faut pouvoir sortir.
Ici ce sera par l'appui sur la touche X. Tout ceci est testé en une seule boucle do-while ! Que celui
qui est capable d'écrire en assembleur une telle boucle de tests avec aussi peu de lignes se fasse
connaître immédiatement au journal ! Ceci montre bien la puissance et l'efficacité du langage C.
Et encore ! Il n'est pas impossible qu'un petit malin trouve le moyen de raccourcir encore le programme
avec quelques astuces.
Le gros morceau
Bien. Me voilà prêt pour attaquer la grosse difficulté de ce programme. Il s'agit de trouver le moyen
d'envoyer un de ces fameux DosPacket à un processus. Dans un petit livre trépelu (comme aurait dit
mon bon maître Rabelais), La Bible De L'Amiga pour ne pas la nommer, il est fait mention de deux
routines de la dos.library : GetPacket et QueuePacket qui permettraient de recevoir ou d'envoyer un
DosPacket. Mais il y a de gros problèmes :
- La structure d'un DosPacket n'est pas donnée donc on ne sait pas ce qu'elle doit contenir !
C'est un tantinet gênant.
- GetPacket permettrait de recevoir un DosPacket. Moi je veux bien, mais comme dans notre cas
le receveur du paquet est un processus AmigaDOS, je vois mal comment utiliser cette routine. En
se déguisant en lecteur de disquette peut-être.
- QueuePacket permettrait d'envoyer un DosPacket mais on ne sait pas à quel processus !
Dans le genre renseignements inutilisables, j'ai rarement vu mieux ! Si mon bon maître Rabelais
avait vu ça, il aurait encore dit que : "cil qui oncques ne peult, rien entreprendre ne doibt."
Il faut donc se débrouiller autrement. En fouinant dans les fichiers include d'un compilateur C
ou dans l'Amiga ROM Kernel Manual "Includes And Autodocs", on trouve les structures suivantes :
Je ne saurais encore dire à quoi servent les "dp_Arg2" jusqu'à "dp_Arg7", mais par contre "dp_Arg1"
est un drapeau qui a l'effet suivant :
- dp_Arg 1 = -1, le DosPacket est pris en compte par le processus.
- dp_arg 1 = 0, l'effet du DosPacket est annulé.
On trouve ensuite dans les fichiers includes une autre structure :
Voilà qui va nous permettre de réfléchir sainement : finalement nous voulons à partir de notre programme
envoyer un DosPacket à un autre programme, mais nous ne savons pas comment faire. Par contre, ce que
nous savons faire, c'est envoyer un message à un autre programme grâce aux routines PutMsg()
et consorts de exec.library. Ce n'est donc pas pour rien que les structures que nous venons de découvrir
contiennent des structures Message, et c'est ce procédé qu'il va falloir utiliser.
Disk_Busy
Nous allons maintenant expliquer en détail la fonction Disk_Busy du programme. C'est elle
qui permet d'inhiber un processus.
Ceci permet à notre programme de trouver l'adresse de sa propre structure Task qui est
immédiatement castée en structure Process. Nous avons le droit de faire ça.
Il faut en effet savoir que tout programme lancé depuis le CLI ou le Workbench n'est pas une "Task", mais
un "Process". Une Task est en fait très limitée, elle n'a pas le droit d'accéder à une fonction de
la dos.library. Un Process n'est rien d'autre qu'une structure Task très étendue qui comporte (et
ça c'est très important) d'office un port message. Pour plus de renseignements, vous pouvez consulter
les fichiers include. Ensuite, de la mémoire est réservée pour une structure StandardPacket,
puis :
Euh pas évidente cette ligne. Elle a pour fonction de placer l'adresse du DosPacket dans
le nom du noeud du message du StandardPacket (NDLR : oh que c'est lourd !). Ceci est
nécessaire afin qu'exec.library puisse retrouver à quoi correspond le message qu'on va lui
demander de transmettre. Le cast (UBYTE *) est indispensable car d'habitude en In_Name
on a un pointeur sur une chaîne de caractères :
Ainsi, dp_Link qui doit être un pointeur sur une structure message est initialisé sur l'adresse de la
structure Message du StandardPacket. Les deux structures DosPacket et Standard Packet sont maintenant
intimement liées (comme l'indique le nom dp_Link) :
Ainsi, dp_Port qui doit être un pointeur sur la structure Port qui va envoyer le message est initialisé
sur l'adresse du Port Message de notre programme. Je me permets de vous rappeler que ce Port existe
et qu'il n'est pas besoin de le créer puisque notre programme dépend d'une structure Process. Je passe
les deux lignes suivantes qui parlent d'elles-mêmes.
Ensuite nous avons :
Comment cela peut-il marcher ? Je pose cette question car d'après La Bible De L'Amiga, la fonction
DeviceProc renvoie le canal I/O utilisé par le processus. Ceci m'a autrefois valu quelques gourous
car bien entendu ce renseignement est encore faux ! Si vous consultez le fichier include "libraries/dosextens.h"
vous verrez que DeviceProc renvoie en fait un pointeur sur le Port Message du processus recherché,
ce qui change tout et qui explique que la ligne de programme envoie bien un message StandardPacket sur
le Port Message du Processus correspondant au lecteur de disquette.
Le reste ne mérite pas de grosses explications : on attend le message de retour (WaitPort), puis
on le retire (GetMsg), on libère ma mémoire et c'est tout. J'espère avoir été suffisamment clair
sur une chose qui n'était pas vraiment évidente au premier abord. Il me reste à vous dire que le
programme est conçu pour être lancé du workbench en cliquant sur une icône de type outils
(à vous de vous fabriquer votre icône). Si vous tenez absolument à lancer ce programme sous CLI,
vous pouvez le faire, mais par l'intermédiaire de la commande "run".
|