Obligement - L'Amiga au maximum

Lundi 25 juin 2018 - 04:29  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

 · Accueil
 · A Propos
 · Articles
 · Galeries
 · Glossaire
 · Liens
 · Liste jeux Amiga
 · Quizz
 · Téléchargements
 · Trucs et astuces


Articles

 · Actualité (récente)
 · Actualité (archive)
 · Comparatifs
 · Dossiers
 · Entrevues
 · Matériel (tests)
 · Matériel (bidouilles)
 · Points de vue
 · En pratique
 · Programmation
 · Reportages
 · Tests de jeux
 · Tests de logiciels
 · Tests de compilations
 · Articles divers

 · Articles in english
 · Articles in other languages


Twitter

Suivez-nous sur Twitter




Liens

 · Sites de téléchargements
 · Associations
 · Pages Personnelles
 · Matériel
 · Réparateurs
 · Revendeurs
 · Presse et médias
 · Programmation
 · Logiciels
 · Jeux
 · Scène démo
 · Divers


Jeux Amiga

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


Trucs et astuces

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


Glossaire

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


Partenaires

Annuaire Amiga

Amedia Computer

Relec

Hit Parade


Contact

David Brunet

Courriel

 


Programmation : Assembleur - Fichiers et répertoires en AmigaDOS
(Article écrit par Max et extrait d'Amiga News Tech - janvier 1990)


Voici une nouvelle série qui commence au sein de notre initiation à l'assembleur sur Amiga, dédiée à AmigaDOS. On va commencer doucement par quelques généralités... heu... générales.

Tu fichier !

Alors puisqu'il faut bien commencer par quelque chose, et que jusqu'à preuve du contraire, c'est à moi que revient la décision de commencer par ce dont j'ai envie, on va causer un peu des fichiers.

La première chose à faire, on s'en doute, c'est d'ouvrir la dos.library. On distingue alors deux possibilités : soit le fichier existe déjà, soit on le crée. Dans les deux cas, on utilise qu'une seule et même fonction, baptisée Open() et d'offset -30. Elle attend deux paramètres : dans D1, l'adresse du nom du fichier, et dans D2, le mode d'ouverture :

assembleur

Comme on peut s'en apercevoir, Open() renvoie un handle de fichier dans D0, handle dont on se servira par la suite pour tout accès à ce fichier. Si ce handle vaut 0, c'est que la fonction a échoué, auquel cas il convient de traiter l'erreur de manière appropriée.

Une fois le fichier ouvert, on va essayer de lire son contenu. On utilise pour cela la fonction Read(), d'offset -42. Les paramètres sont aussi logiques qu'attendus : le handle du fichier ouvert dans D1, l'adresse où les données lues doivent être logées dans D2, et le nombre d'octets à lire dans D3. Ce qui nous donne en gros :

assembleur

Read() retourne dans D0 le nombre d'octets réellement lus, 0 si la fin du fichier a été atteinte (EOF), ou -1 si une erreur (autre que EOF, donc) est survenue.

L'inverse de Read() est Write(), qui permet d'écrire des données dans un fichier. Son offset est -48 et les paramètres qu'elle attend sont exactement identiques à ceux de Read() :

assembleur

Tout comme Read(), Write() renvoie dans D0 le nombre d'octets réellement écrits, 0 en cas d'EOF ou -1 en cas d'erreur.

Pour se déplacer dans le fichier, ce qui permet par exemple de relire une information qu'on a écrite quelques instants auparavant, ou au contraire pour aller lire le énième octet précisément, on dispose de la fonction Seek(), d'offset -66. Elle attend comme paramètre dans D1, le handle du fichier, dans D2, l'intervalle de déplacement et dans D3, le mode de déplacement.

assembleur

Le nombre retourné dans D0 indique la position actuelle du pointeur de fichier s'il est positif, un EOF s'il est nul et une erreur s'il est négatif. Les offsets indiquent à partir d'où le pointeur devra être déplacé.

Spécifier le début du fichier, la position actuelle du pointeur et la fin du fichier. L'intervalle, quant à lui, peut-être soit positif (déplacement "vers l'avant") soit négatif (déplacement "vers l'arrière").

Reste à fermer le fichier, ce qui se fait tout naturellement grâce à la fonction Close() d'offset -36. Le seul paramètre à lui fournir est le handle du fichier, dans D1 toujours :

assembleur

Toutes ces fonctions, à l'exception de Close(), renvoient un éventuel code d'erreur négatif dans le registre D0. Seulement voilà, il existe tellement d'erreurs possibles, qu'il peut souvent être judicieux d'informer l'utilisateur de votre programme qu'erreur il y a. Le meilleur moyen d'y parvenir est encore d'appeler la fonction IoErr(), d'offset -132. Elle n'attend aucun paramètre mais renvoie dans D0 un code similaire à ceux utilisés par les commandes CLI (par exemple, 221 pour Disk Full). A titre d'information, voici un tableau des codes d'erreur AmigaDOS existant et leur signification.

Code Message correspondant
103 insufficient free store
105 task table full
120 argument line invalid or too long
121 file is not an object module
122 invalid resident library during load
202 object is use
203 object already exist
204 directory not found
205 object not found
206 invalid window description
209 packet request type unknown
211 invalid object lock
212 object not of required type
213 disk not validated
214 disk write protected
215 rename across devices attempted
216 directory not empty
218 device (or volume) not mounted
219 seek failure
220 comment too long
221 disk full
222 file is protected from deletion
223 file is write protected
224 file is read protected
225 not a valid DOS disk
226 no disk in drive
232 no more entries in directory

Tout ça ne vous rappelle rien ? Bien sûr que si : la commande Why du CLI.

La plupart de ces erreurs sont signalées par un "system requester" qu'AmigaDOS affiche sur l'écran du Workbench.

Mais voilà qui ne conclut pas pour autant les fonctions dédiées à la gestion des fichiers. Vous n'êtes pas sans savoir qu'il est possible d'ouvrir une fenêtre CON:, RAW: ou NewCON: de la même manière qu'on ouvre un fichier standard. Dans l'exemple ci-dessous :

assembleur

...on ouvre une fenêtre NewCON: (Workbench 1.3) aux coordonnées (0,11), de largeur 640 et de hauteur 200. Notez bien qu'on spécifie MODE-OLDFILE comme si la fenêtre existait déjà - alors qu'à l'évidence, ce n'est pas le cas. Moi, j'ai renoncé à comprendre pourquoi.

Pour écrire un texte dans cette fenêtre, on emploie encore la fonction Write() :

assembleur

Ici, le nombre d'octets à écrire est calculé par l'assembleur par soustraction de l'adresse courante (désignée par l'étoile) à celle du début du texte. Amis fainéants, bonjour.

De la même manière, on peut lire dans cette fenêtre, grâce à la fonction Read() :

assembleur

Cette méthode présente un avantage pour l'utilisateur : même s'il entre cent cinquante, douze mille caractères au clavier, ils seront effectivement affichés, mais lors de l'appui sur la touche "Entrée", seuls les 10 premiers seront transmis au programme.

Enfin, on ferme la fenêtre, encore une fois de la même manière qu'un fichier standard, avec la fonction Close() :

assembleur

Oui, je sais ce que vous pensez : "et si je veux afficher des données directement dans la fenêtre CLI, sans passer par l'ouverture d'une fenêtre à moi, hein ?". Rien de plus simple, réponds-je alors, utilisez la fonction Output() d'offset -60. Son rôle est de fournir au programme la handle de la fenêtre CLI depuis laquelle il a été lancé, comme s'il l'avait lui-même ouverte :

assembleur

Même chose dans l'autre sens : pour lire des données depuis la fenêtre CLI, on utilise la fonction Input(), d'offset -54 :

assembleur

Plutôt sympa, non ? Bon, imaginez maintenant que vous ayez besoin de lire des caractères sur l'interface série, parce que par exemple, vous avez un modem branché dessus (ce sont des choses qui arrivent). Rien ne vous permet à priori de vous assurer que le modem est bel et bien branché (à moins que vous ne développiez ce programme pour vous-même et dans un but strictement personnel, auquel cas vous n'avez peut-être pas besoin de telles procédures de test d'erreur). Pourtant, il existe une fonction plutôt sympathique baptisée WaitForChar() d'offset -204, qui permet d'attendre un caractère sur un canal ouvert en entrée pendant un laps de temps déterminé :

assembleur

Si, au bout du temps spécifié dans D2 (exprimé en 1/50e de seconde) aucun caractère n'est disponible sur le canal, D0 contient en retour la valeur 0 (FALSE). Dans le cas contraire, D0 contient la valeur -1 (TRUE) et on peut aller lire le(s) caractère(s) avec Read(). Pratique, non ?

Chance de répertoire !

Si vous n'êtes pas bêtes, vous aurez remarqué que les fichiers ne sont pas les seuls éléments que l'on trouve sur une disquette. Il existe aussi les répertoires. Il me semble logique que l'on n'ouvre pas un répertoire comme on ouvre un fichier normal ; il y a certaines règles à respecter.

Commençons par le commencement, si vous voulez bien, et imaginons que nous voulions créer un nouveau répertoire sur la disquette placée en DF0: (ce que fait la commande CLI MakeDir, soit dit en passant). Nous appelons pour ce faire la fonction CreateDir d'offset -120, qui attend dans D1 l'adresse du nom du nouveau répertoire :

assembleur

Précision utile pour les fanas du Workbench : tout comme MakeDir, cette fonction ne crée pas d'icône associée au répertoire ; à vous de vous débrouiller !

CreateDir() renvoie dans D0 un pointeur sur ce qu'on appelle une structure Lock (verrou), ce que l'on pourrait traduire par "clé d'accès au répertoire". De la même manière que le handle des fichiers, le verrou sera utilisé par la suite pour tout accès au répertoire auquel il est associé. Cette structure est longue de 20 octets, et se présente comme suit :

Offset Nom Signification
* NextLock Prochain verrou ou 0
4 DiskBlock Numéro du premier bloc du répertoire
8 Access Type Type d'accès
12 Proceed ID Numéro d'identification
16 VolNode Pointeur sur les informations disquette

Le "Type d'accès" peut prendre les valeurs -1 (accès exclusif, c'est-à-dire qu'un seul programme peut avoir accès au répertoire à ce moment donné) ou -2 (accès général, toutes les applications ont accès au répertoire).

Dans le cas où le répertoire existe déjà sur la disquette, il est inutile de le créer avec CreateDir(), qui d'ailleurs échouerait lamentablement. On utilise dans ce cas la fonction Lock() d'offset -84, qui attend deux paramètres, à savoir dans D1, l'adresse du nom du répertoire et dans D2, le type d'accès :

assembleur

"ACCESS_READ" signifie qu'on va accéder au répertoire en lecture uniquement, donc qu'on ne va pas modifier son contenu, donc que les autres applications peuvent également y accéder sans danger.

"ACCESS_WRITE" signifie au contraire qu'on y accède en écriture, donc qu'on est susceptible d'en modifier le contenu, et donc que les autres applications ne peuvent y accéder également (imaginez Deluxe Paint en train de charger une image et vous l'effaçant avant qu'il ait terminé...).

Le contraire de Lock(), c'est UnLock(). Son offset est -90 et le seul paramètre qu'elle attend est le verrou à libérer dans D1:

assembleur

A partir de là, la mémoire utilisée par la structure Lock est libérée, et le répertoire devient à nouveau accessible à tous.

On peut également examiner le contenu d'un répertoire grâce aux fonctions Examine() et ExNext(). Leurs offsets respectifs sont -102 et -108, et elles réclament les mêmes paramètres, c'est-à-dire dans D1 le pointeur sur la structure Lock et dans D2, le pointeur sur une zone de mémoire de 260 octets qui sera remplie d'informations toutes plus utiles les unes que les autres et qu'on appelle FileInfoBlock. Ces deux fonctions permettent d'examiner dans le détail le contenu d'un répertoire, en d'autres termes, d'en obtenir la liste (commande List du CLI). Examine() sert à se brancher sur la première entrée du répertoire (en fait, elle renvoie le nom du répertoire lui-même), ExNext() recherhe les suivantes, une à une. Pour illustrer cela, voici une routine semblable à la commande List, qui se contente toutefois d'afficher le nom des fichiers qu'elle trouve, donc sans leur taille, attribut, commentaire, etc. Son utilisation - sous CLI ou Shell uniquement - est très simple puisqu'il suffit d'entrer :

Examine Nom-du-répertoire

...pour qu'une liste de son contenu soit affichée.

Tant qu'on y est et avant que j'oublie, voici tout de suite le contenu et l'organisation de ce fameux FileInfoBlock :

Offset Nom Signification
0 Diskkey Numéro de la disquette
§ DirEntryType Type d'entrée ()0: (0 = fichier)
8 FileName Nom du fichier ou répertoire (108 caractères)
116 Protection Attributs (A, R, E, W, D)
120 EntryType Type d'entrée
124 Size Taille du fichier
128 NumBlocks Nombre de blocs occupés
132 Days Date de création
136 Minutes Heure de création (minutes)
140 Ticks Heure de création (secondes)
144 Comment Commentaire (80 octets)

Comme on le voit, 108 octets sont réservés au nom du fichier, alors qu'AmigaDOS n'en permet que 32, et 116 au commentaire, alors qu'on n'a droit qu'à 80. Les futures versions du système d'exploitation permettront peut-être d'exploiter tous ces espaces.

assembleur
assembleur

Je ne pense pas que ce programme requiert plus d'explications, sinon peut-être la routine Print, chargée de sortir le nom du fichier dans la fenêtre CLI.

Car l'inconvénient de la fonction Write() lorsqu'on l'utilise pour écrire du texte, est qu'elle a besoin de connaître le nombre de caractères à sortir. Les noms de fichiers sous AmigaDOS pouvant être de longueur variable, il faut compter un à un tous les caractères jusqu'au 0 final. On initialise donc le registre D3 - celui qui doit justement indiquer la longueur du texte à Write() - à 0, et on l'incrémente au fur et à mesure que l'on teste les caractères composant le nom du fichier. A ce test s'ajoute le fait qu'un nom de fichier ne peut, dans la version actuelle d'AmigaDOS, excéder 32 caractères de long. On utilise donc le registre D0 comme compteur de boucle dans l'instruction DBcc. En gros, cela se résume à : "tant que l'on n'a pas testé 32 caractères au maximum ou que l'un des caractères testés n'est pas 0, incrémente D3". Puissant, comme instruction, non ?

Par la suite, on insère un Line Feed (code ASCII $0a) à la suite du nom du fichier, histoire que le prochain aille s'afficher une ligne plus bas et non collé au premier, avant d'appeler Write() et de boucler sur ExNext().

Les plus pointilleux d'entre vous trouveront certainement que je me suis embêté pour pas grand-chose en écrivant ce programme (emploi de l'instruction Link, etc,) ; c'est vrai, mais ainsi il est entièrement relogeable, c'est-à-dire que vous pouvez, par exemple, l'inclure tel quel dans un programme en GFA Basic avec l'instruction Inline. Et puis après tout, c'est un de ces exercices de style dont je suis assez friand.

Un dernier mot pour en terminer avec ce programme : rien ne vous empêche de l'améliorer en y insérant une routine de tri alphabétique sur les noms, de sortie de la longueur des fichiers (en hexa ou, plus difficile, en décimal) et de leur commentaire, etc. Si ces routines vous intéressent, je vous les proposerai un de ces quatre.

En attendant, on se retrouve le mois prochain pour la suite de nos investigations au sein (oh oui !) d'AmigaDOS.


[Retour en haut] / [Retour aux articles]