l'assembleur  [livre #4602]

par  pierre maurette




Assembleurs autonomes

Un assembleur autonome est défini ainsi par opposition aux assembleurs intégrés à des langages de haut niveau, voire à des environnements de développement bâtis sur la base d'un langage de haut niveau, et que nous présenterons plus loin.

Un assembleur autonome permet d'obtenir des programmes, des bibliothèques ou toute forme d'exécutable ou de fichiers objets à partir de fichiers sources en assembleur. Bien que cette démarche puisse sembler la plus naturelle, nous verrons qu'elle est loin d'être la plus simple et qu'elle est même devenue marginale, voire inutilement lourde dans certains cas. Il existe néanmoins des situations où cette façon de travailler est la seule envisageable.

Le but des pages qui suivent n'est pas d'apprendre à programmer en assembleur, mais de se mettre en situation de le faire. D'une certaine façon, ce langage n'est pas très compliqué : aucun de ses éléments n'est réellement ésotérique, comme peut par exemple apparaître la Programmation Orientée Objet. Mais, tout comme pour un compilateur C/C++ en ligne de commande, la mise en œuvre de ce langage est pénible pour celui qui n'a jamais pratiqué. C'est souvent là que naît le découragement.

Définition

EDI

Un EDI est un Environnement de Développement Intégré . C'est un programme intégrateur qui permet au minimum d'éditer du code source, de gérer la notion de projet, de tester le programme dans le débogueur ou debugger . Le simple fait de sauvegarder les fichiers modifiés avant test est une prestation de l'EDI.

Un EDI peut être souple et évolutif, c'est à dire que l'utilisateur sera en mesure à l'aide d'un toolkit de créer ses propres outils et comportements. Un EDI n'est donc pas nécessairement lié à un langage.

Les EDI sous DOS peuvent être agréables et efficaces, même utilisés sous Windows. Certains éditeurs de texte très améliorés, comme Emacs, une fois paramétrés, sont des EDI.

Il est fréquent aujourd'hui que seul l'EDI soit considéré comme onéreux, les compilateurs, assembleurs et autres outils étant gratuits. Voir (Google) le projet Eclipse , ou le nouveau C++BuilderX si mal nommé. Les outils et environnements créés à partir d'un toolkit, parfois en langage Java, sont distribués ou commercialisés sous le nom de plugin .

En définitive, les choix de l'EDI et des assembleurs et autres compilateurs peuvent être indépendants.

Comme signalé dans l'introduction du livre, ceux d'entre vous qui ont déjà de bonnes notions de programmation peuvent commencer par ce chapitre, et même le lire en diagonale, c'est-à-dire en sautant les paragraphes consacrés aux outils dont ils n'ont pas décidé l'installation. Ils choisiront, si ce n'est déjà fait, un assembleur et une façon de travailler, feront l'installation et les tests, à l'aide du chapitre suivant s'il s'agit de MASM ou MASM32, et ensuite pourront sereinement passer à la suite.

C'est peut-être la seule partie de cet ouvrage où il est, dans un premier temps, plus important de faire que de comprendre. Cette position est pédagogiquement bizarre, mais il faut bien voir que tant que vous serez en difficulté pour saisir, assembler, lier, déboguer un code, aucun progrès ne sera possible. Alors qu'à l'inverse, si vous pouvez étudier sous débogueur une source qui vous semble opaque, mais qui se construit et qui tourne, ces mêmes progrès seront rapides.

Ajoutons qu'il ne nous semble même pas indispensable au départ de comprendre entièrement le processus de fabrication, bien qu'il ne faille pas que cet état se prolonge trop longtemps. En partant de quelque chose qui fonctionne, il est plus facile, par comparaison, de déterminer la cause d'un problème.

CD-ROM

Squelettes d'applications

Idéalement, il faudrait qu'avant de commencer l'étude du langage assembleur vous ayez à votre disposition une série de squelettes d'applications, dans toutes les configurations possibles : assembleur autonome ou intégré, environnement cible DOS, Windows, développement sous DOS, Windows 9X, 2000, XP, etc. En plus des exemples donnés à l'occasion de chaque chapitre, vous trouverez une collection de squelettes sur le CD-Rom, dans un dossier finement nommé cimetière . N'y figurent que les configurations réellement testées, un nombre déjà important. Les fichiers à modifier, pour tenir compte des dossiers d'installation des outils sur votre machine, sont au besoin donnés dans un fichier Lise&Moi.txt . Vous trouverez également sur le CD-Rom une description des produits installés sur la machine de test, si vous utilisez les mêmes dossiers les modifications à faire seront minimales, ou nulles. Il faut pour utiliser un squelette :

1          Copier le dossier sur le disque dur.

2          Rendre l'ensemble des fichiers modifiables, en décochant l'option Lecture seule dans les propriétés.

3          Modifier les fichiers qui doivent l'être en fonction de votre configuration.

4          Effacer s'il y a lieu les fichiers  .exe , .obj , . lst , etc., bien que cet effacement soit normalement prévu dans les fichiers de commandes fournis.

5          Impérativement, construire l'application squelette telle quelle avant de poursuivre.

Vous pouvez maintenant vous consacrer librement à l'expérimentation...

Si vous avez la curiosité de consulter FAQs et autres forums de discussion, et plus particulièrement les questions de débutants (newbies), vous constaterez que les difficultés tournent souvent autour de ce processus de fabrication, particulièrement du lieur. Pour être plus précis, un fichier  .obj (voir un peu plus loin) sera souvent généré par l'assembleur, mais rejeté par le lieur. Dans d'autres langages, servis par un solide EDI, les questions porteront plus souvent sur de sévères plantages à l'exécution. Rassurez-vous, une fois résolues les difficultés de fabrication, un programme assembleur vous offrira également de très beaux plantages à l'exécution !

Comment expliquer autant de difficultés ? Nous allons y revenir, mais donnons dès maintenant une hypothèse : un programme assembleur, comme tout programme, va tourner sur une plate-forme et un modèle mémoire déterminés. Il se trouve que, pour des raisons souvent historiques, le débutant programme en assembleur des programmes en mode segmenté 16 bits, ce qui est plus rare dans d'autres langages. Pour arriver à un résultat, il faut au minimum :

  Un code source.

  Un choix d'assembleur.

  Une volée de paramètres à transmettre à cet assembleur.

  Un choix de lieur.

  Une volée de paramètres à transmettre à ce lieur.

Tous ces éléments doivent être adaptés entre eux et au type de programme cible pour que le processus fonctionne. Et il existe des programmes différents portant le même nom, par exemple link.exe qui est chez Microsoft le nom unique pour le lieur segmenté à utiliser pour les programmes 16 bits et le lieur incrémental à utiliser pour les programmes 32 bits. Voilà qui explique en partie la réputation de difficulté autour du processus d'assemblage, difficulté qui, soit dit en passant, sera la même si nous utilisons un compilateur C ou Pascal pour faire à la fois du 16 et du 32 bits.

Un assembleur s'intègre dans un processus de développement qui ne lui appartient pas en propre, c’est-à-dire qu'il est commun à d'autres langages utilisés en ligne de commande. Nous allons donc, dans un premier temps, découvrir ce type de processus.

4.1 Fabrication d'un programme

Un processus de fabrication
figure 4.01 Un processus de fabrication [the .swf]

Commentons ce processus en essayant de mettre des noms sur les numéros. 1 a été saisi par le programmeur au clavier. C'est par exemple un fichier source en langage C, essai.c . Donc 3 est un fichier objet (nous y reviendrons) essai.obj . L'outil 1 permettant de passer de l'un à l'autre est un compilateur C, tc.exe .

2 est une bibliothèque de fonctions écrite en assembleur, utils.asm , que le programmeur préfère conserver sous forme de code source. Il obtient 4, utils.obj , via l'outil 2 tasm.exe , un assembleur.

Remarque

Compilateur et assembleur

Un compilateur comme tc.exe et un assembleur comme tasm.exe sont de la même essence. Les deux mots sont généralement interchangeables, dans le domaine du processus de fabrication. Un macro-assembleur n'est rien d'autre qu'un compilateur de langage assembleur.

La syntaxe de essai.c est liée à tc.exe , comme celle de utils.asm est associée à tasm.exe , pour produire essai.obj et utils.obj qui sont d'un même format normalisé ou du moins de formats compatibles.

5 est une bibliothèque graphique du commerce, graph.lib , dont nous ignorons le langage de développement. Pour l'utiliser, l'éditeur nous a fourni 6, graph.h , un fichier contenant les prototypes des fonctions de graph, des macros, etc. Ce fichier est utilisé par le compilateur C. Une fois la compilation terminée, il n'est plus nécessaire. Le format de graph.lib est comparable à celui de essai.obj et utils.obj .

Nous obtenons donc deux  .obj et un  .lib dont nous avons vérifié la compatibilité. L’outil important est un lieur, tlink.exe par exemple. Nous obtenons enfin 7, l'objet convoité. C'est un exécutable, par exemple essai.exe , mais cette famille cache plusieurs espèces.

Reste 8, qui est utilisé pour l'exécution mais n'a pas été nécessairement fabriqué pour la circonstance. Appelons-le func_sys.dll . C'est une bibliothèque liée dynamiquement, donc un exécutable. C'est de cette façon que le système d'exploitation met à la disposition du programmeur des bouquets de fonctions à réutiliser. Cette DLL, en toute rigueur, aurait demandé la représentation d'une bibliothèque d'import , un fichier décrivant le contenu de la DLL et la façon de l'utiliser, à l'attention des outils du processus, un peu à la manière de 6.

Nous avons donc fait apparaître trois catégories de fichiers, correspondant aux trois étapes fondamentales de la fabrication :

  1 et 2 sont les fichiers sources, éditables par le programmeur.

  3, 4 et 5 sont des fichiers intermédiaires, dits fichiers objets.

  7 et 8 sont des fichiers exécutables.

D'autres fichiers, comme 6, sont accessoires dans la description du processus. Ils sont néanmoins sources de problèmes potentiels.

Le but final du programmeur est très souvent d'obtenir un exécutable, ou plus exactement d'obtenir une exécution dans l'environnement de son choix, Windows ou DOS dans notre cas. Cette dernière nuance pour prendre en compte l'étape ultime, qui consiste par exemple à lancer un fichier  .exe ou  .com et qui sollicite le système d'exploitation, sous la forme d'un programme appelé loader . C'est le loader du DOS qui ajoute une zone de 256 octets, le PSP, devant un fichier  .com . Il y place les paramètres saisis par l'utilisateur, s'il y en a. Cette étape se déroulera chez l'utilisateur final à chaque lancement du produit. Le programmeur doit connaître suffisamment du loader pour, par exemple, être à même de lire les paramètres saisis par l'utilisateur.

Il existe, en plus de  .exe et  .com , une foule d'autres types de fichiers exécutables. Parmi ceux-ci, citons  .dll , .vxd , .ocx , .drv , .386 , etc. Un fichier exécutable est parfois appelé fichier image , pour bien montrer qu'il ne s'agit pas nécessairement d'un  .exe .

Astuce

À la chasse aux exécutables

Si vous disposez d'un désassembleur comme  W32Dasm , sélectionner le menu Open File to Disassemble . W32Dasm  désassemble les formats suivants : 32 bits PE, 32 bits LE et 16 bits NE. La liste déroulante de choix de type montre déjà un grand nombre d'extensions proposées pour des exécutables. Choisissez  *.* , et testez tout ce qui vous semble susceptible de contenir du code exécutable ; par exemple les plug-ins de nombreux programmes graphiques, d'extension  *.8* , compatibles PhotoShop. Il s'agit en fait de DLL. Vous serez surpris du nombre de fichiers qui se désassemblent parfaitement et qui sont donc bien des exécutables, normalisés qui plus est.

Profitez-en pour essayer les extensions  .obj et  .lib . Nous verrons que ces fichiers contiennent du code non exécutable.

Il existe d'autres méthodes. Par exemple, installer et utiliser l'option Aperçu rapide comme vu au tout début du chapitre intitulé DOS et DEBUG, premiers programmes .

Nous connaissons la fin du processus de base, l'exécution et le produit final : un fichier exécutable ou fichier image.

Le point de départ du processus est un simple fichier texte, le source, qui contiendra le code saisi à l'aide d'un éditeur. L'extension de ce fichier correspond au langage, asm pour l'assembleur. Nous passerons du source au fichier image par l' assembleur , ou le compilateur pour les langages de haut niveau.

Mais cela ne s’effectuera pas directement. Un fichier intermédiaire, le fichier objet , est créé. C'est le lieur, ou éditeur de liens ou encore linker, qui permettra d'obtenir le fichier image à partir d'un ou de plusieurs fichiers objets. Expliquer le rôle de cette étape dans le cas minimal d'un seul fichier source n'est pas si simple. Témoin cette phrase, issue de la documentation Microsoft :

« Object file: A file given as input to the linker. The linker produces an image file, which in turn is used as input by the loader »

Le fichier objet est donc défini comme entrée du linker. De là à ce que le linker soit défini comme le programme qui permet de passer du fichier objet à l'exécutable... Mais dans la suite du document Microsoft, les formats COFF (fichiers objets) et PE (fichiers images) sont définis octet par octet.

Très grossièrement, l'assembleur traduit, bloc par bloc, du code source en langage machine, c’est-à-dire en octets. Il laisse en blanc ce qu'il ne sait pas faire. Le linker se charge d'agglomérer le tout et de le placer en mémoire. Tout en sachant que c'est le loader qui va terminer le travail.

Le rôle de cette étape intermédiaire est plus évident dans le cas de plusieurs fichiers sources. C'est dans ce cas que les noms lieur, éditeur de liens ou linker se justifient pleinement.

Quelques avantages de cette étape intermédiaire :

  Les portions de code compilées une fois n'ont théoriquement pas besoin de l'être à nouveau.

  Le fichier objet ne connaît plus le langage qui lui a donné le jour. Son utilisation permet donc la programmation multilangage.

  L'utilisation des fichiers objets facilite le découpage du travail, la modularité, Donc le travail en équipe et la réutilisation du code. Dans ces deux derniers cas, il faut bien entendu transmettre et archiver le code source en plus des fichiers objets.

  Les fichiers objets, souvent sous leur forme  .lib , permettent de distribuer son travail sans divulguer son savoir-faire. Il suffit de conserver son code source et de distribuer, de façon gratuite ou onéreuse, peu importe, un fichier objet accompagné de sa documentation et souvent d'un fichier d'include.

Nous allons voir très superficiellement la structure d'un fichier objet et celle d'un fichier exécutable. Nous aurons ainsi une idée du travail de l'assembleur/compilateur et de celui du lieur. Une idée générale, mais suffisante pour aborder les diverses documentations.

Que connaît le compilateur ou assembleur quand il fait son travail, et surtout qu'ignore-t-il ?

Des fonctions et procédures, il ne connaît souvent que le fait qu'elles sont dans un autre module, autrement dit externes, et heureusement, leurs prototypes. Le prototype est important, puisqu'il permet de savoir quels types de paramètres envoyer à la fonction, quel retour en attendre, et à travers les conventions d'appel, comment se transmettent ces données. Il peut donc presque tout coder, à part quelques CALL . Il va noter ces difficultés.

Il connaît la taille des zones de données dont il a besoin, celles qu'il doit initialiser, et qui seront donc dans le fichier objet, et les autres, dont il lui suffit de transmettre la taille. Il serait loufoque de gonfler un fichier par des données dont la valeur n'importe pas.

Il ne connaît pas l'adresse à partir de laquelle le programme va être implanté pour fonctionner. À vrai dire, dans la plupart des cas cette donnée ne sera pas connue avant le lancement effectif du programme et est susceptible de changer à chaque utilisation. Il va donc soigneusement noter toutes les références absolues d'adresses, données ou sauts. Elles ne sont pas si nombreuses, l'adressage relatif étant favorisé par le jeu d'instructions.

Alerté par ses propres difficultés, il va lui-même préparer un genre de résumé facilitant l'accès aux données et routines qu'il veut bien partager.

Si telle est la demande, il va garder des traces du code source, des noms de variables, etc., pour faciliter le débogage.

Il va enfin assembler (ou compiler) pour obtenir un code machine. Un certain nombre de références, soigneusement notées, sont laissées en blanc. Ce code occupe une zone nommée zone texte.

Tout ceci donne une allure générale au fichier  .obj ressemblant à l’illustration.

Structure générale d'un fichier .obj
figure 4.02 Structure générale d'un fichier .obj [the .swf]

L'en-tête (file header) recense diverses informations dont l'offset auquel se trouvent les différentes zones, le début du code, la taille de la zone de données non initialisées, etc. La zone texte renferme le code machine à terminer. La table des symboles regroupe tous les symboles exportés du module avec leur offset. Enfin les informations de relogement recensent les points à corriger dans la zone texte.

Le lieur va maintenant faire le tour des fichiers objets à rassembler et consulter tout d'abord les en-têtes. Il va résoudre les inconnues dans le code machine, celles qui sont à sa portée, et créer une zone texte et une zone de données. Il va éventuellement conserver les tables de symboles et d'autres informations, à des fins d'échanges ou de débogage.

Ce que le lieur connaît que ne connaissait pas le compilateur, ce sont les positions relatives des données et points d'entrée des modules qui font partie du lien. Il ne peut pas toujours tout connaître de l'adresse réelle à partir de laquelle le programme va tourner.

Les offsets par rapport aux débuts des divers segments sont connus. Ce sont ces débuts de segments que peut ne pas maîtriser complètement le lieur.

En mode Protégé, le mode d'adressage linéaire actuel et les mécanismes de pagination peuvent permettre de donner à chaque application une adresse linéaire précise et de donner la même adresse linéaire à plusieurs codes simultanément en mémoire. Sous DOS, en mode segmenté 16 bits, le lieur peut au moins s'arranger pour solliciter du loader une zone mémoire qui lui permettra de connaître toutes les adresses de début de segment par rapport à l'adresse de base du segment de code (initial), indiquée par CS. Il va donc lier le projet en supposant CS = 0000h , puis localiser par une table un ensemble de références auxquelles il faudra que le loader ajoute la valeur réelle de CS au lancement.

Le lieur préparera donc des informations à destination du loader, afin que celui-ci puisse lancer le programme dans de bonnes conditions. C'est également le loader qui mettra à la disposition du programme la mémoire non initialisée dont il a besoin. C'est donc lui qui refusera le lancement si cette mémoire n'est pas disponible.

Nous n'irons pas plus loin dans l'étude de la structure des différents fichiers, mais il est certain que vous aurez besoin de documentation à ce sujet afin d’écrire de bons programmes. Notre travail en assembleur se situera donc entre les fichiers sources et les fichiers objets. Néanmoins, une partie importante de nos problèmes surgira lors de l'édition de liens, voire de l'utilisation d'utilitaires divers et variés, préprocesseurs, convertisseurs de formats de fichiers, compilateurs de ressources, etc.

Cette relative complexité, qui n'est pas qu'apparente, présente des aspects positifs :

  Apprendre ce type de processus de développement manuel, dit en ligne de commande , sera applicable à toutes sortes de langages, et pas seulement à l'assembleur. Les compilateurs bruts sont souvent gratuits, contrairement aux environnements intégrés. Citons Java, C++, C# par exemple.

  La programmation multilangage n'est pas beaucoup plus compliquée que la programmation en assembleur pur. Nous restons dans le domaine de l'assembleur autonome.

  Nous pouvons produire des fonctions qui seront invoquées depuis un autre langage. Le contraire, peut-être plus rare, est également possible.

  Nous pouvons mêler, dans un projet, par l’intermédiaire d’un fichier de type make au besoin, des sources dans différents langages, pour au final lier les fichiers objets  .obj obtenus. Parmi ces sources, les  .asm sont du ressort de l’assembleur autonome. Les gros environnements comme C++Builder ou Visual Studio font cela très bien : il suffit d'associer l'extension  .asm à un assembleur tel ml.exe ou tasm32.exe .

Les premiers compilateurs de langages évolués, parmi lesquels certainement le Fortran, traduisaient leur code source en code source assembleur relativement simplifié. Un assembleur faisait partie de la fourniture logicielle de base. Actuellement, le code, C ou Pascal par exemple, est directement traduit en code objet. Une option de compilation permet d'obtenir un code assembleur intermédiaire. Il n'est pas certain du tout qu'il existe réellement dans le processus de compilation une étape texte assembleur intermédiaire. Nous avons donc représenté une étape optionnelle correspondant à un désassemblage, mais ce n'est qu'indicatif d'un fonctionnement réel qui peut-être légèrement différent.

Obtenir un listing assembleur
figure 4.03 Obtenir un listing assembleur [the .swf]

L'assembleur nécessaire aux temps historiques pouvait être très simple, puisqu'il pouvait se contenter d'une traduction quasiment bijective d'un code source sans erreur issu du compilateur, la difficulté appartenant plus au lieur. Ceci explique la rusticité de certains produits, éloignés des macro-assembleurs que nous connaissons.

Cette façon de faire consistant à passer par un langage intermédiaire pour terminer le travail est encore courante. Particulièrement pour des langages à haut niveau d'abstraction et au moins en phase de développement. Le code généré, dans le domaine des générateurs d'application, de l'intelligence artificielle, de l'automatisme, sera soit du C, soit couramment du C++. La génération de code assembleur est plus rare.

Plus loin dans ce chapitre, nous traiterons de l’implantation de plusieurs environnements de développement. Environnement est un grand mot, pour désigner une façon de travailler, en s’aidant de quelques utilitaires, par exemple d'un éditeur amélioré configuré dans cette optique, et surtout des facilités offertes par Windows.

En effet, si les outils permettant de développer sont faciles à trouver, souvent gratuitement, il n’existe pas réellement d’environnements de développement indiscutables, bien que d’intéressantes tentatives soient actuellement proposées sur Internet.

Le seul composant incontournable pour travailler est justement l'assembleur, ml.exe ou tasm.exe par exemple, le fameux compilateur de langage assembleur qui permet de passer d'un fichier texte  .asm à un fichier objet  .obj . Nous ne pourrons pas nous passer non plus d'un lieur. En effet, si un appel à ml.exe suffit pour obtenir un  .exe , c'est parce que cet assembleur appelle le lieur  link.exe .

Ce qui importe, c’est qu’à l’issue de cet important chapitre, vous soyez libéré des problèmes de mise en œuvre, pour vous consacrer entièrement au code.

4.2 L'offre : quel assembleur ?

Tous les assembleurs qui justifient ce nom doivent être capables de produire le même code. Ce n'est pas forcément le cas des lieurs. L'argument du meilleur code, pertinent dans le cas des compilateurs C ou C++, n'a donc aucun sens dans le cas d'un assembleur.

Le choix se fera sur les critères suivants :

L'environnement, s'il en existe un. Il faudra tester l'efficacité réelle d'un EDI, son look & feel , et non seulement son apparence. Les anciens EDI sous DOS tirent bien leur épingle du jeu.

La puissance. Par exemple, les macro-assembleurs comme MASM ou TASM proposent un très grand nombre de directives, de facilités qui transfigurent la programmation. Il serait par exemple illusoire de tenter d'élaborer des programmes Windows sans macros de procédures et de contrôle de flux.

La fraîcheur. Il faudra considérer les jeux d'instructions supportés, ainsi que la disponibilité de mises à jour. Quand apparaît un nouveau jeu comme SSE3, il est possible d'ajouter des macros pour assembler ces nouvelles instructions. Néanmoins, il est plus efficace que ces instructions soient traitées nativement par l'assembleur, et que la définition de la cible soit la plus fine possible.

L'étendue des cibles proposées. 16 bits segmenté DOS, 32 bits linéaire Windows, DLL, VXD, etc. Il faudra couvrir l'ensemble de ses besoins, parfois à l'aide de plusieurs assembleurs et surtout de plusieurs lieurs.

La vitesse de construction. Ce ne sera un critère significatif que pour des projets importants.

La vitalité. C'est à dire essentiellement l'existence d'une communauté d'utilisateurs, de groupes de discussions et autres ressources Internet.

Pour points 3 et 4, le téléchargement sera un aide précieuse. Il se fera souvent sur les sites de Microsoft.

De l'offre du marché, en restant dans l'environnement DOS-Windows, nous retiendrons avant tout deux produits fondateurs : MASM  (Macro Assembler) de Microsoft, et TASM  (Turbo Assembler) de Borland.

MASM est souvent présent dans les autres produits. Au minimum, la documentation se réfère à MASM pour expliquer par différence une syntaxe propre. C'est le cas de TASM, qui est un assembleur original et non une copie, acceptant deux syntaxes différentes : un mode MASM, très largement compatible et un mode Ideal, corrigeant les défauts de MASM. TASM est un assembleur réellement Borland, mais qui répond à une attente précise : assembler (presque) sans modifications du code écrit pour MASM.

Effectivement, MASM est un programme incontournable, particulièrement pour qui souhaite coller à l'actualité et utiliser les ressources des SDK et DDK mis à disposition par Microsoft. C'est dans un autre ouvrage (collection "Référence") que nous traitons en profondeur ce macro-assembleur. Vous trouverez sur Internet (voir le CD-Rom) la documentation complète, mais en anglais, de la syntaxe de MASM.

MASM reproduit de versions en versions, pour respecter la compatibilité ascendante, quelques indiscutables incohérences. Ces défauts vont assez loin, le typage est approximatif, mais les utilisateurs habituels s'en accommodent, avec même, semble-t-il, un certain plaisir. C'est au moment de l'apprentissage que le bât blesse.

Un certain nombre de solutions, du domaine du logiciel libre, sont disponibles gratuitement. Elles vont du clone au produit original, en passant par l'ensemble cohérent bâti autour de  ml.exe ( MASM32 ).

Paradigm Assembler, PASM  pour rester dans la tradition, est un clone de TASM, donc compatible avec la syntaxe MASM. Sa documentation, sous forme de fichier  .pdf , est intéressante. Il semble que ce produit, documentation comprise, soit aujourd'hui 100 % commercial, intégré à la suite Paradigm C++.

NASM  (Netwide ASseMbler) est un produit multi-plate-forme en licence libre LGPL, dont le berceau est précisément Internet. Vous trouverez donc sur la toile toutes les ressources que vous pouvez souhaiter, souvent en langue anglaise. Une certaine activité est en devenir en français, autour de communautés dont vous trouverez les coordonnées sur le CD-Rom.

Un avantage des produits en licence libre est que, généralement, un site Internet rassemble tout ce qui est nécessaire, documentation, forum, exemples de code source et surtout téléchargement de l'assembleur.

Ces assembleurs sont des paquets de programmes, utilisables en ligne de commande, et souvent fournis avec soit un éditeur, soit un intégrateur de type atelier (workshop). Au centre de ces livraisons se trouve l'assembleur lui-même : tasm.exe , tasm32.exe chez Borland, ml.exe et anciennement masm.exe chez Microsoft, nasm.exe pour NASM par exemple, des bibliothèques et fichiers de macros. Une grande partie des autres programmes sont communs à l'assembleur et à d'autres langages de programmation.

Le paquet basé sur MASM version 6.1x, destiné au développement 32 bits sous Windows et nommé MASM32 doit retenir toute votre attention. De nombreux exemples sont fournis et une grosse activité existe sur Internet. Certainement une très bonne solution en 32 bits et une source unique pour obtenir en un seul téléchargement un grand nombre de fichiers, à compléter éventuellement pour, par exemple, travailler en mode DOS. Nous y revenons dans un chapitre ultérieur.

Une bonne solution est de télécharger un ensemble complet directement opérationnel, idéalement avec bibliothèques, utilitaires et EDI. Nous pensons évidemment à MASM32. Ensuite, il est toujours possible d'installer d'autres outils, assembleurs et lieurs en particulier, dans ce cadre. Nous proposerons ce type de démarche en présentant MASM32 au chapitre suivant.

Nous ne pouvons pas ne pas citer le phénomène HLA (High Level Assembly), un produit intéressant semble-t-il, tellement amélioré que d’aucuns prétendent qu’il ne s’agit plus d’assembleur. Son auteur, Randall Hyde, est extrêmement actif dans certains newsgroups, à tel point qu’il est judicieux de se poser la question de son unicité. Il vous répondra souvent par un lien vers son site, qui est une véritable mine d’or, de dimension phénoménale. Incontournable ! Il existe un groupe français ayant débuté la traduction de cette œuvre.

CD-ROM

Se procurer gratuitement un assembleur

Un chapitre est exclusivement consacré à ce sujet, sur le CD-Rom, actualisé sur la page personnelle de l'auteur. Vous y trouverez des liens et des fichiers. Vous devez impérativement vous y reporter si vous souhaitez expérimenter et n'êtes pas encore outillé. Sinon, Google, Google et encore Google.

 

4.3 MS-DOS : le problème du PATH

Reportez-vous aux paragraphes consacrés au sujet au chapitre intitulé DOS et DEBUG, premiers programmes et au CD-Rom, et n'hésitez pas à prendre les quelques minutes nécessaires pour configurer confortablement votre machine, si ce n'est déjà fait.

Nous allons ici nous attacher à faciliter l'accès aux divers fichiers.

Une fois installés, la plupart des paquets de développement proposent la même structure générale.

Les distributions MASM et TASM installées
figure 4.04 Les distributions MASM et TASM installées

Les dossiers visibles sur l’illustration ne sont que des exemples, ce ne sont pas ceux qui seront réellement utilisés pour traiter les exemples et qui sont donnés sur le CD-Rom.

Les lignes qui suivent s'appliquent aux assembleurs, mais également aux compilateurs en ligne de commande, aux RAD de type Delphi ou C++Builder, à Visual Studio...

Trois dossiers se retrouvent pratiquement dans chaque cas : bin , include et lib.

Vous pouvez également décider de n’utiliser dans un premier temps que l’assembleur et le lieur, et ne pas passer par une installation.

Si vous travaillez à partir d’une seule distribution, installée dans les règles de l’art, ce qui suit peut ne pas vous concerner. L'environnement aura été configuré lors de l'installation.

La variable d’environnement  PATH décrit un ensemble de chemins où MS-DOS, ou ce qui en tient lieu, va chercher les fichiers s’ils ne sont pas dans le dossier courant. Par exemple la commande suivante permettra de taper des commandes MS-DOS, qui sont dans C:\windows\command , depuis n’importe quel dossier :

SET PATH=C:\WINDOWS;C:\WINDOWS\COMMAND

La variable d’environnement  PATH  par défaut se situe, jusqu’à Windows 98, dans le fichier autoexec.bat . Pour 2000 et XP, elle est accessible via le Panneau de configuration, module Système . Cette variable est rechargée à partir de cette source à chaque lancement de session MS-DOS. Vous pouvez donc sans danger la modifier au cours d’une session, et ce pour une session particulière. Chaque processus, donc chaque fenêtre DOS, possède ses propres variables d'environnement. Rappelons que, sous Windows, une session MS-DOS est tout simplement une fenêtre et qu'il peut y en avoir plusieurs simultanément. Pour connaître en une seule commande l'ensemble des variables d'environnement, il suffit de taper la commande SET sans paramètre.

Résultat de la commande SET sous Windows 2000
figure 4.05 Résultat de la commande SET sous Windows 2000

Puisque nous utilisons la commande SET , tapons set /? . Nous obtenons :

C:\WINDOWS>set /?
Affiche, définit, ou supprime les variables d'environnement Windows.
 
SET [variable=[chaîne]]
 
  variable  Nom de la variable d'environnement.
  chaîne    Chaîne de caractères à affecter à la nouvelle variable.
 
SET sans paramètre affiche les variables d'environnement définies.

Chaque application utilisant un fonctionnement en ligne de commande va ajouter son propre chemin, qui, souvent, restera après une désinstallation. Il est vivement conseillé de faire le ménage, par exemple en réorganisant le tout en modules de ce type :

REM DELPHI 6 Perso
REM SET PATH=%PATH%;E:\DEVTOOLS\BORLAND\DELPHI6\BIN
REM SET PATH=%PATH%;E:\DEVTOOLS\BORLAND\DELPHI6\PROJECTS\BPL

Cette mise en  REM  ne pose aucun problème pour une utilisation standard de Delphi, qui n'exploite pas PATH en fonctionnement normal. Le danger est plutôt d’avoir plusieurs fois le même nom de fichier, link.exe par exemple, dans des chemins différents, que ce ne soit pas celui que nous attendons qui sera appelé lors de la construction et que nous ne comprenions pas l'origine de l'erreur. Nous allons vous proposer une méthode radicale pour éviter ce type de problèmes, sans avoir à modifier les variables d’environnement par défaut.

Notre machine d’essai comporte trois systèmes d’exploitation, en triple boot donc : Win98se, Win2000Pro et Windows XP édition Familiale. Nous nous sommes arrangés pour que notre partition de travail porte la même lettre, F, dans les trois systèmes. Les fichiers de TASM sont simplement copiés dans cette partition, comme visible un peu plus haut sur la capture d’écran. Nous travaillons dans un dossier F:\Try\ , source et résultats. Nous avons fabriqué deux fichiers  .bat , comme suit :

PATH = C:\WINDOWS;C:\WINDOWS\COMMAND
 
REM TASM 5.0
SET PATH=%PATH%;F:\TASM\BIN
SET PATH=%PATH%;F:\TASM\INCLUDE
SET PATH=%PATH%;F:\TASM\LIB
 
command.com
LEGENDE=TP98.BAT

Et :

PATH = %SystemRoot%\system32
 
REM TASM 5.0
SET PATH=%PATH%;F:\TASM\BIN
SET PATH=%PATH%;F:\TASM\INCLUDE
SET PATH=%PATH%;F:\TASM\LIB
 
%SystemRoot%\system32\cmd.exe
LEGENDE=TPXP.BAT et TP2000.BAT

TPXP.BAT et TP2000.BAT sont identiques, grâce à l’utilisation de la variable d’environnement SystemRoot . Nous écrivons de même trois fichiers MP98.BAT , MP2000.BAT et MPXP.BAT , qui font la même chose en configurant la session pour l’utilisation de MASM. Vous pouvez de cette façon tester diverses configurations de divers assembleurs.

Sous XP, il est possible de configurer la compatibilité. Nous n’avons pas pu mettre en évidence une utilisation de cette particularité.

Quand le fichier  .bat correspondant au système d’exploitation est lancé, par un double-clic, la variable  PATH est réinitialisée, et une session DOS (ou Invite de commandes) est créée, dans le dossier de travail.

Les fichiers  .bat ne sont pas spécifiques à ce dossier ; une fois écrits conformément à votre environnement, ils peuvent être copiés dans n’importe quel dossier de travail. Si, par un clic droit, vous créez un raccourci vers le fichier batch, vous pourrez de plus modifier les propriétés de la fenêtre DOS. Ce raccourci devra être recréé (ou édité) pour chaque nouveau dossier de travail.

Si vous utilisez un éditeur sous Windows, vous pouvez remplacer dans le batch la dernière ligne par :

del hello.obj
del hello.exe
del hello.map
tasm hello
if errorlevel 0 tlink hello
if errorlevel 0 hello

Ces lignes ne sont, bien entendu, qu’un exemple. Elles effacent les anciens fichiers résultats, puis compilent, lient et exécutent le programme.

Vous pouvez écrire un fichier batch générique, pour lequel le nom du projet à construire est passé en paramètre. Les paramètres passés à un fichier batch sont appelés dans le corps du fichier par les symboles %1 , %2 , etc. Voyons un exemple :

@echo off
 
if exist "%1.obj" del "%1.obj"
if exist "%1.dll" del "%1.dll"
if exist "%1.lst" del "%1.lst"
 
C:\masm32\bin\ml /c /coff /Cp /Fl "%1.asm"
if errorlevel 1 goto errasm
 
C:\masm32\bin\Link /DLL /SUBSYSTEM:WINDOWS /DEF:SK_DLL.DEF "%1.obj"
if errorlevel 1 goto errlink
dir "%1.*"
goto Fin
 
:errlink
echo _
echo Erreur Lieur
goto Fin
 
:errasm
echo _
echo Erreur Assembleur
goto Fin
 
:Fin
 
pause

Si ce fichier s'appelle build.bat , il faudra pour construire le projet "Hello" saisir build hello dans une fenêtre DOS. Si nous désirons procéder directement à partir de l'Explorateur, il faudra créer un raccourci vers build.bat , puis éditer les propriétés de ce raccourci.

Configuration pour construire Hello
figure 4.06 Configuration pour construire Hello

Il suffit d’ajouter les paramètres, ici hello , à la fin de la ligne de commande. Un double-clic lancera la construction. L'instruction pause en fin de fichier batch donne le temps de lire les messages à l'issue du processus de construction.

Vous pouvez, dans la fenêtre DOS, taper tout simplement edit hello.asm et saisir le code.

Travail sur deux fenêtres
figure 4.07 Travail sur deux fenêtres

 

Cet éditeur est une scorie de QuickBasic, comme QuickHelp ( qh ) que nous verrons un peu plus loin. Il ne présente pas d'avantage décisif par rapport au Bloc-notes de Windows, et surtout par rapport à l'éditeur à tout faire, ou orienté programmation, que vous ne manquerez pas de choisir.

Remarque

Comportement des fenêtres DOS ou Invite de commandes

Ceci est valable peu ou prou pour les trois versions de Windows abordées ici. La souris doit fonctionner sans autre installation dans les fenêtres DOS comme edit , ainsi que qh et pwb que nous allons voir avec MASM. Si elle ne fonctionne pas, c’est très souvent à cause de la validation de l’option d’édition rapide, qui permet de réaliser des copier-coller à l’aide de la souris dans toutes les fenêtres. Il vous faut impérativement invalider cette option.

Si vous avez lancé directement un exécutable DOS ou un batch, vous pouvez modifier ces options, Windows vous proposant éventuellement d’en faire l’option par défaut. Dans 98, il faut attendre de relancer une fenêtre pour observer l’effet de la modification.

Mais il suffit de quelques secondes pour créer un raccourci vers le batch ou le fichier . exe , en cliquant du bouton droit sur son nom dans l’Explorateur, puis en choisissant Créer un raccourci . Vous pouvez ensuite renommer ce raccourci et, encore, par un clic droit, explorer ses propriétés. En plus de l’option d’édition rapide qui concerne la souris, vous pouvez, à ce niveau, décider de démarrer cette fenêtre en mode Plein écran. Il y a un rapport direct, qui dépend de la version de Windows, entre ces raccourcis et les fichiers PIF (Program Information Files).

De même, vous devez pouvoir rappeler les commandes déjà saisies par les  Flèche haut  et  Flèche bas  . Si cette possibilité n’est pas installée, sous 95 ou 98, il suffit à priori de taper doskey , ou de l’intégrer au batch de lancement. Dans tous les cas, l’ensemble des propriétés de cette facilité s’obtient par doskey /? . N’oubliez pas que taper simplement  help fournit une aide sur l’ensemble des commandes du DOS.

Rappelons enfin que, dans une fenêtre DOS, il est normal de sortir d’un programme comme edit par la commande appropriée. Ensuite, la fenêtre se ferme en tapant exit au prompt du DOS.

Pour assembler, lier et exécuter le programme, il est inutile de quitter l’éditeur. Il suffit de lancer une autre session DOS et de laisser les deux fenêtres ouvertes pendant la durée du travail. Vous pouvez parfaitement lancer simultanément plusieurs fenêtres avec des variables  PATH différentes. En fait, vue depuis Windows, chaque fenêtre est un espace de travail complètement indépendant, qui n’échange avec les autres que par l’intermédiaire de fichiers. Il n'est pas impossible qu'un ancien programme DOS soit perturbé par le fait que des fichiers puissent être modifiés par un autre programme pendant son propre fonctionnement.

Remarque

Le format 8.3 des noms de fichiers et de dossiers sous DOS

Sous DOS, les noms de fichiers et de dossiers sont au format 8.3, c’est-à-dire de 1 à 8 caractères valides suivis éventuellement d’un point et de 1 à 3 caractères valides. Particulièrement pour travailler sur des applications 16 bits, nous allons utiliser des fenêtres DOS et des outils un peu anciens. Prenez autant que possible l’habitude d’appliquer ce format. Ce n’est pas toujours indispensable, mais c’est un facteur de confort. Dans le même état d’esprit, créez des dossiers de travail proches de la racine de vos partitions, pour éviter les arborescences interminables. Une fois le travail terminé, vous pourrez les archiver où bon vous semblera.

4.4 Application d'essai faire_boot, première étape

Avant de revenir en détail sur l’utilisation d'un éditeur en programmation, voyons ce que sera notre application d’essai, faire_boot  ; il s’agit d’un petit programme qui permet de fabriquer une image d’une disquette, et ensuite de refabriquer une disquette, clone de la première, à partir de cette image et d’une disquette formatée ; elle est évoquée sur un plan pratique au chapitre intitulé DOS et DEBUG, premiers programmes .

CD-ROM

Nouvelle version de Mak_Flop

Pour ne pas risquer de conflit entre listings et commentaires et pour ne pas utiliser trop de notions inconnues en début d'apprentissage, nous avons conservé intégralement les listings de la première édition. Ils figurent évidemment sur le CD-Rom. Mais nous y avons joint une version finalisée, réunissant les deux fonctionnalités (sauvegarde et restauration) dans un même exécutable. De plus, elle est proposée en deux versions, très proches il est vrai : MASM et TASM. Vous les trouverez dans le dossier Mak_Flop_Final , accompagné de son fichier Lise&Moi.txt contenant les paramètres de construction.

Nous voulons une application en ligne de commande, la plus universelle possible, c’est-à-dire pouvant s’exécuter sous DOS, qui effectue les tâches suivantes :

  Copier l’ensemble des secteurs d’une disquette HD (1,44 Mo), son image, dans un fichier.

  Recopier cette image sur une disquette préalablement formatée.

  Éviter de détruire les données du disque dur.

Ce dernier point affecte surtout l’auteur initial du programme : effacer les premiers secteurs de votre disque dur à cause d’une mauvaise interprétation de la numérotation des lecteurs ne serait pas bon du tout.

Deux petits programmes indépendants seront développés, sav_flop.exe pour sauvegarder l’image de la disquette dans le fichier disk.sav et rst_flop.exe pour l’opération inverse. Il serait ensuite très simple d’agglomérer le tout, au travers d’un passage de paramètres ou d’un menu rudimentaire, mais est-ce bien utile ?

Nous allons mener ce développement point par point ; les étapes seront de moins en moins détaillées. Nous travaillons, dans un premier temps, avec l'éditeur UltraEdit32 utilisé en mode Éditeur seul ; donc tout éditeur de texte, Notepad compris, convient.

La compilation, le lien et les essais se font dans une fenêtre DOS, ouverte comme expliqué précédemment.

En fin de première phase, nous verrons comment mieux utiliser UltraEdit ou un autre éditeur orienté programmation. En réalité, les essais ont été menés le plus longtemps possible sur deux fenêtres, une utilisant MASM 6.14 et l’autre TASM 4.1. Le système d’exploitation est d'abord Windows XP, ce qui garantit pratiquement le fonctionnement sous 98 et 2000.

Au sujet de XP, deux précisions s’imposent : si c’est aujourd’hui le système d’exploitation de Microsoft le plus protecteur, il ne doit pas entraîner de problèmes à la compilation ou assemblage, mais éventuellement à l’exécution. Dans notre petite application, les interruptions du BIOS sont peut-être interceptées par XP, mais cela ne change rien pour nous.

Remarque

Le format d’une disquette

Chaque face d’une disquette HD comprend 80 pistes concentriques, divisées chacune en 18 secteurs de 512 octets. La position d’une piste est appelée un cylindre. Cette image est plus parlante dans le cas du disque dur, qui est constitué de plusieurs plateaux empilés, chaque plateau étant assimilable à une grosse disquette. À ce moment-là, le mot désigne l’ensemble des pistes à la même position sur toutes les faces de tous les plateaux ; cela forme bien un cylindre. Le mot face est synonyme de tête ; à chaque face correspondant une tête de lecture/écriture. Donc :

2 têtes x 80 pistes x 18 secteurs x 512 octets = 1 474 560 octets.

Il n’y a pas plus d’informations sur les longues pistes extérieures que sur les petites pistes près du centre. Et il s’agit bien de pistes concentriques, et non d’un sillon hélicoïdal !

Si les propriétés de la disquette font état d’un nombre légèrement inférieur, la différence tient dans quelques secteurs de service. Peu importe, puisque notre but vise à tout cloner.

L’approche générale sera de créer un fichier, de lire un paquet d’octets, à définir, sur la disquette, de l’ajouter au fichier, et ainsi de suite jusqu’à la fin de la disquette. La restauration sera très symétrique de la sauvegarde. Il est logique de confier la gestion du fichier à des fonctions (interruptions) du DOS et celle de la disquette au BIOS.

Nous travaillons dans des fenêtres DOS et sur des assembleurs un peu anciens ; nous choisirons par précaution des noms de fichiers au format 8.3. Nous créons donc les deux fichiers sav_flop.asm et sav_flop.inc . Ce dernier est un fichier d’include, qui joue le même rôle que les fichiers  .h du C. Le parallèle est tel qu’il existe des utilitaires de conversion comme h2inc.exe ( .h to .inc ). Nous ne l’utilisons ici que pour expliciter la saisie de constantes DOS et BIOS. Voici son contenu final, une partie seulement étant utilisée dans ce premier jet :

NULL                    EQU     0
TailleSecteur           EQU     512
NbreSecteurs            EQU     18
NbreCylindres           EQU     80
 
INT21_CreateFile        EQU     3Ch
INT21_OpenFile          EQU     3Dh
INT21_CloseFile         EQU     3Eh
INT21_WriteFile         EQU     40h
open_read_write         EQU     02h
INT21_ReadFile          EQU     3Fh
 
INT13_ReadSectors       EQU     02h
INT13_WriteSectors      EQU     03h

Le seul rôle de ce fichier est de nous permettre de rendre le listing plus lisible en écrivant mov ah, INT21_CreateFile plutôt que mov ah, 3Ch . Un autre avantage est que, en phase de tests, NbreSecteurs et NbreCylindres pourront se voir affecter des valeurs plus faibles, pour gagner du temps d’essai. Il ne sera pas nécessaire de chercher puis de modifier toutes les occurrences de ces valeurs. Mais elles restent des constantes , puisque connues à la compilation. Nous avons utilisé la syntaxe la plus universelle possible, puisque la syntaxe n'est justement pas l'objet premier de ce chapitre. Il est vrai que la directive  = aurait été ici préférable à  EQU , toutes les valeurs étant numériques.

Pour les fonctions DOS et BIOS, il existe dans les distributions de MASM, TASM, etc., des fichiers d’include, contenant des noms plus ou moins normalisés par l’usage, communs avec la programmation en langage C, qu’il sera judicieux d’utiliser pour d’autres projets. Nous avons tenu à mener celui-ci ex nihilo.

Nous procédons par petites étapes. La première va consister à tester la possibilité de créer un fichier sur le disque.

Nous aurons plus tard besoin d’une zone mémoire pour recevoir le paquet élémentaire ; c’est cette zone tampon, ou buffer, élégamment nommée bouffeur, pour l'instant simplement initialisée (jusqu’à ce que nous ayons testé les fonctions de lecture sur disquette), qui nourrira le fichier d’essai. Pour les tests, la zone est initialisée à la valeur 32h, soit le caractère 2. Nous pourrons vérifier le fichier avec un simple éditeur comme Notepad ou UltraEdit. La taille du paquet élémentaire sera plus que certainement d’un ou plusieurs secteurs, en fait soit un secteur, soit une piste de 18 secteurs, soit les deux pistes d’un cylindre, ou pourquoi pas la totalité de la disquette. Nous ferons ces premiers essais sur un seul secteur de 512 octets. Ce qui nous donne le listing :

; Programme [autonome] Sauvegarde de l'image d'une disquette
; Essais - Etape 1 
 
INCLUDE sav_flop.inc
 
.MODEL small
 
.STACK 64
 
.DATA
bijor       db      "Bienvenue dans sav_flop ...$"
loupe       db      "Erreur ... $"        
fichier     db      "disk.sav", 0             ; Fichier image
handle      dw      NULL
bouffeur    db      TailleSecteur DUP (32h)   ; Buffer pour 1 secteur
 
.CODE
 
debut:   mov ax, @data             ; initialisation du data segment 
         mov ds, ax                                  
 
         mov ah, 09h               ; sortie chaine bienvenue
         mov dx, offset bijor
         int 21h
         
         ; création du fichier d'essai
         mov ah, INT21_CreateFile  
         xor cx, cx                ; attribut normal
         mov dx, offset fichier    ; fichier image
         int 21h
         jc  FinPrematuree
         mov handle, ax
         
         ; écriture dans le fichier d'essai
         mov ah, INT21_WriteFile
         mov bx, handle       
         mov cx, 200h           ; écriture 512 octets
         mov dx, offset bouffeur
         int 21h    
         jc  FinPrematuree              
                   
         ; fermeture fichier d'essai   
         mov ah, INT21_CloseFile
         mov bx, handle
         int 21h
         jc  FinPrematuree         
            
         mov ah, 00h             ; attend une touche
         int 16h
 
         mov AH, 4Ch             ;fin du programme
         int 21h                 ;et retour au DOS
         
FinPrematuree:
         mov ah, 09h             ; sortie chaine echec
         mov dx, offset loupe
         int 21h      
                        
         mov AH, 4Ch             ;fin du programme
         int 21h                 ;et retour au DOS
 
         END debut

 

Par la directive  .MODEL small , nous définissons un modèle de segmentation, donc le sens de .CODE, .DATA, .STACK . En dehors de la tuyauterie de l’assembleur, très simple ici, ce n’est qu’un enchaînement d’appels au DOS. La seule décision que prend le programme à l’issue de chaque appel est de cesser prématurément l’exécution en cas d’erreur, qui est détectable par le flag CF mis à 1. Dans ce cas, un registre contient le code de l’erreur, que le programme n’exploite pas ici. Il peut être lu lors de l’utilisation d’un débogueur. Il serait bien de retourner le code d’erreur au programme appelant, ici le DOS.

Vous tapez : ml /Zi sav_flop.asm. L’exécutable est généré sans problème. Testez-le d’abord directement en tapant sav_flop, voire en double-cliquant sur sav_flop.exe sous Windows. Vous constatez la création d’un fichier disk.sav , qui a la taille attendue de 512 octets. Vous pouvez le tester via le débogueur CodeView, en tapant cv sav_flop.exe . Ouvrez la fenêtre des registres, vous obtenez le résultat figurant sur l’illustration.

CodeView…
figure 4.08 CodeView…

La plupart des problèmes se résoudront en appuyant, dans CodeView, sur la touche de traçage   F8  et en observant les registres. Notons avec satisfaction une fenêtre d’observation de la FPU. Nous ne voyons pas de fenêtre spécifique pour la pile, mais il suffit d’aller dans le menu des options pour une des fenêtres  Memory et de taper SS:SP comme expression d’adresse. Nous pouvons également paramétrer le format d’affichage des données, du caractère au flottant (nombre réel) de 80 bits.

Tracez le programme par   F8  en découvrant les options des menus. Vous pouvez, par exemple, placer des points d’arrêt (menu Data ). La touche   F4  vous permet de permuter l’écran vers la fenêtre de sortie. Quand le programme attend une entrée clavier, le passage sur la console de sortie est automatique.

Pour tester de la même façon les possibilités de Turbo Debugger for DOS, de Borland, tapons : tasm /zi sav_flop , puis tlink /v sav_flop , sav_flop pour tester, et enfin : td sav_flop .

Turbo Debugger for DOS au travail…
figure 4.09 Turbo Debugger for DOS au travail…

Dans la fenêtre  CPU , sélectionnez un élément dans chacun des cinq rectangles, puis cliquez du bouton droit : un certain nombre de menus contextuels apparaissent, parfois à deux niveaux. Reportez-vous au paragraphe sur le débogueur dans la prise en main de Delphi, au chapitre sur l’assemblage en ligne. À la langue près, tous les avatars de Turbo Debugger possèdent le même comportement.

 

4.5 Utilisation d'un éditeur orienté programmation

D’abord, imaginons notre source dans le Bloc-notes ( notepad.exe ). Puis une fenêtre DOS, dont la variable  PATH a été convenablement configurée par un judicieux raccourci, dans laquelle a déjà été saisie la commande ml /Zi hello.asm .

Ml  lance link.exe , sauf option contraire. Puis encore une autre fenêtre DOS, dans laquelle hello.exe vient d’être testé. Isoler la fenêtre de test permet de ne relancer qu'elle seule en cas de blocage.

Souvenons-nous des possibilités de rappel et d’édition des commandes offertes par doskey . Nous sommes dans des conditions très correctes pour élaborer notre programme. Aucun risque de foulure de l’auriculaire. Le danger dans ces façons de travailler est d’oublier de sauvegarder le source modifié, avant de relancer la construction et de constater que votre modification ne change pas grand-chose ! Il n'est pas idiot de prévoir systématiquement en début de programme l'affichage d'un message et de modifier de temps en temps ce message (une lettre suffit) pour être certain que c'est bien la bonne version qui est assemblée et testée. Dans le même ordre d'idées, il est préférable d'effacer au moins les fichiers  .obj et  .exe avant de lancer une nouvelle construction. Modifier le code source tout en continuant à tester le même exécutable est plus fréquent qu'on ne le croit.

Mais nous voulons éditer simultanément plusieurs fichiers, ce qui peut se produire dans le plus simple des projets (un . asm , un . inc , une paire de batchs, et éventuellement un ou deux fichiers modèles, d’une précédente application), alors il nous faudra ouvrir autant de fenêtres du Bloc-notes. De plus, il faut admettre que le Bloc-notes reste rudimentaire.

Il existe un certain nombre d’éditeurs orientés programmation ; nous avons choisi d’installer UltraEdit32, qui est plus un éditeur à tout faire. Il est disponible sur Internet, où vous pouvez télécharger une version d’évaluation limitée dans le temps. Une fois sur le site de l’éditeur, pensez à charger également le manuel au format  .pdf , en anglais, ainsi que les fichiers de configuration qui vous intéressent. Vous trouverez sur le CD-Rom un fichier  wordfile.txt permettant la coloration syntaxique pour l’assembleur. Nous l'avons modifié pour qu'il applique la même coloration aux fichiers .inc . Vous pouvez également, à partir de là, aller sur le site de UeMake, un utilitaire destiné à augmenter les possibilités d’UltraEdit dans le domaine de la programmation, mais qui risque de vous embrouiller plus qu'autre chose, et que nous avons définitivement abandonné.

UltraEdit32 n’est certainement pas le meilleur dans une optique purement programmation. Il est pour cela trop riche en fonctions, qui vont jusqu’à la possibilité d’une correction orthographique. Nous allons voir que ses outils dédiés au développement ne sont rien de plus que le lancement de commandes DOS avec arguments. Il n’y a pas, par exemple, de possibilité de replier/déplier des procédures, l’option Affichage/Masquer/Afficher les lignes ne pouvant faire illusion ; mais cette possibilité est également très difficile à trouver sur d’autres éditeurs.

Adopter UltraEdit se justifie encore plus s’il est affecté à d’autres tâches. Sur le site de l’éditeur, une astuce est proposée pour qu’UltraEdit remplace totalement le Bloc-notes, à base d’un loader nommé… notepad.exe . Ce n’est pas indispensable. Il est en revanche important, lors de l'installation, d’intégrer UltraEdit dans le menu contextuel (clic droit), ce qui permettra de regarder n’importe quel fichier. UltraEdit est en effet également un éditeur hexadécimal honnête, malheureusement sans aucune fonction de lecture de secteur sur disque.

Une fois l’éditeur UltraEdit installé, prenons un moment pour le découvrir. Par exemple, ouvrez un fichier, modifiez-le par une autre application, comme le Bloc-notes et observez la réaction d’UltraEdit. Cette particularité de proposer de recharger les fichiers modifiés extérieurement est réellement très pratique. Il n'est malheureusement pas possible de configurer UltraEdit pour qu'il procède à cette synchronisation sans demander confirmation.

Pour tester cet éditeur, ou tout autre si tel est votre choix, nous utilisons le programme obtenu à l’issue de la première étape de notre application d’essai sav_boot, au paragraphe précédent. Dans le dossier F:\faire_boot\faire_boot_etape1\ , nous avons donc les fichiers suivants : sav_flop.asm , sav_flop.inc , ainsi que masmxp.bat et/ou tasmxp.bat . Les noms longs, hors format 8.3, ne sont pas là par erreur mais pour un test. Ils ne semblent pas poser problème dans cette configuration.

Nous allons commencer par vérifier que tout cela se compile sans problème :

Lancez donc masmxp.bat , puis à l’invite DOS :

ml /Fl /Fm /Z1 sav_flop.Asm pour MASM, ou tasm /l /Zi sav_flop puis tlink /v sav_flop pour TASM et enfin sav_flop , pour tester le fichier . exe .

Constatez que, outre le fichier résultat disk.sav , quatre fichiers sont créés : sav_flop.lst , sav_flop.map , sav_flop.obj et sav_flop.exe .

Avec la souris, sélectionnez sav-flop.asm et sav_flop.inc , puis d’un clic droit, ouvrez-les simultanément dans UltraEdit.

Si ces deux fichiers s’ouvrent dans deux fenêtres ou instances séparées, il faut, dans Avancé/Configuration/Général , décocher Autoriser des instances multiples , ou alors ouvrir un fichier, puis l’autre par le menu Fichier/Ouvrir . Le problème ne se posera plus une fois le projet créé, puisque c’est justement le rôle d’un projet que d’ouvrir de multiples fichiers dans une entité unique.

Dans le menu Projet/Nouveau Projet.Espace de travail , vérifiez que vous êtes dans F:\faire_boot\faire_boot_etape1\  ; sinon, positionnez-vous dans ce dossier, puis tapez faire_boot_etape1 comme nom de projet ; enfin validez.

Vous arrivez dans la fiche de configuration du projet, à laquelle vous pourrez revenir par Projet/Fichiers , quand le projet sera ouvert.

Configuration d’un projet
figure 4.10 Configuration d’un projet

Un fichier projet faire_boot_etape1.prj , dans le dossier du même nom, sera créé. En cochant Chemins relatifs au fichier projet , vous pourrez déplacer l’ensemble des fichiers de ce dossier, y compris le fichier projet, vers n’importe quel autre dossier. En réalité, nous commettons une erreur en ne cochant pas Chemins relatifs . Nous y reviendrons au moment d’aborder l’étape 2. Cliquer sur  + Tous fichiers ouverts ajoute au projet les deux fichiers actuellement ouverts. Ils sont pour l’instant dans le groupe par défaut, symbolisé par un point dans la liste déroulante Groupe .

Vous souhaitez séparer les fichiers sources, que sont les  .asm et  .inc , des futurs fichiers de services, fichiers batch et autres. Cliquez donc sur Nouveau groupe et saisissez le nom Sources . Tout nouveau groupe est rempli des fichiers présents à l’écran au moment de sa création. Il vous est ensuite loisible de lui en ajouter ou d’en enlever. Le groupe par défaut ne peut pas être supprimé ; il peut en revanche être vidé de ses éléments. Son rôle n’est pas très clair. À vous de lui en trouver un.

La possibilité de lier au projet un Wordfile peut servir à archiver un dossier projet en étant certain de retrouver le fichier de coloration syntaxique adéquat plus tard. Quant aux deux options concernant un index, elles intéressent C++ et les CTags.

Dans la liste des fichiers d’un groupe affichée à l’écran, vous pouvez en sélectionner un certain nombre et les ouvrir en cliquant sur… Ouvrir . Les fichiers déjà ouverts ne sont pas fermés, et si vous désirez ouvrir des fichiers appartenant à plusieurs groupes, il faudra faire la démarche Projet/Fichiers autant de fois qu’il y a de groupes. De plus, il ne semble pas possible d’ouvrir ni même de sélectionner tous les fichiers d’un groupe. Quittez la fiche par Fermer .

Une dernière expérience : ouvrez, par Fichiers/Ouvrir , un fichier n’ayant rien à voir avec le projet. Vous avez donc trois fichiers ouverts. Notez la position du curseur dans ces trois fenêtres, ainsi que la fenêtre active. Quittez UltraEdit, en fermant ou non le projet. Puis rouvrez celui-ci, en double-cliquant sur faire_boot_etape1.prj . Vous constatez que l’espace de travail se rouvre à l’identique, fichier hors projet compris.

Notre premier travail sera de nettoyer le dossier des fichiers générés lors des premiers tests en ligne de commande. Pour le fichier disk.sav , il s’agit d’une opération ponctuelle. Activez Avancé/Commande DOS ou appuyez sur   F9  . Il ne vous reste plus qu’à taper la commande del disk.sav et à valider. Dans l’Explorateur Windows, vous constatez l’effacement. Recréez le fichier en lançant sav_boot.exe . Puis dans UltraEdit,  Ctrl  +  F9  ou Avancé/Dernière commande DOS . disk.sav a à nouveau disparu.

Vous aimeriez pouvoir lire le contenu du dossier de travail dans UltraEdit. Créez une commande pour cela. Dans le menu Avancé , vous remarquez que vous pouvez configurer des outils persistants et des outils liés au projet. Votre commande de listage sera utile dans d’autres projets, choisissez donc Configuration des outils .

Configuration des outils
figure 4.11 Configuration des outils

Si vous saisissez les caractéristiques et le nom de l’outil puis cliquez sur OK, votre travail est perdu. Il faut, avant de quitter cette fiche, cliquer sur Insérer . De même, si vous modifiez un outil, vous le faites réapparaître en double-cliquant sur son nom dans la liste du bas ; puis, vous devez impérativement cliquer sur Remplacer pour le modifier ou sur Insérer pour un nouvel outil déduit du premier. Il aura, dans ce cas, été judicieux de modifier le nom. Ce comportement est assez crispant.

Pour notre commande  dir , la commande est simplement le dir du DOS. Le dossier n’est pas renseigné, il reste celui par défaut du projet.

Quelques précisions maintenant sur la sortie de la commande : les quatre premiers choix (case à cocher ronde) sont exclusifs. C’est l’endroit où la sortie de la commande sera affichée. Dans les trois premiers cas, un ou plusieurs documents sont créés pour recevoir cette sortie. Dans le dernier cas, une fenêtre de sortie va apparaître en bas de la fenêtre principale. Vous pouvez rendre cette fenêtre flottante ; pensez ensuite, par un clic droit, à invalider l’option Permettre l’encastrement  ; sinon la fenêtre sera perpétuellement dockée. Mais ce choix de la fenêtre de capture ne suffit pas, il faut encore penser à cocher l’option Capturer la sortie  ; sinon, il ne se passera rien.

Afficher la fenêtre DOS accomplit ce qui est annoncé ; cette option est indépendante des autres. La fenêtre DOS n’est pas maintenue ouverte ; il n’est donc très souvent pas possible de lire la sortie. Son utilité est de permettre de répondre à des invites. À ce sujet, le test de l’exécution de nos programmes, pour peu qu’ils soient interactifs, présente le danger d’accumuler des instances non fermées. Donc, pour tester un programme hors débogueur, soit vous devez ouvrir une simple fenêtre DOS totalement indépendante et lancer le programme en ligne de commande, soit vous devez utiliser un outil, mais celui-ci se contente d’afficher une fenêtre DOS et ne capture pas la sortie.

Écrivez (si ce n’est déjà fait) le fichier batch clean.bat suivant :

del *.obj
REM del *.exe
del *.lst
del *.map

Vous pouvez soit l’exécuter directement, soit faire un outil clean avec uniquement clean dans la case Ligne de commande et rien d’autre. Pourquoi le  REM  ? Pour réaliser un premier essai et nous assurer que c’est bien dans le dossier souhaité que les fichiers sont effacés ! Une fois cette vérification faite, modifiez votre batch, sauvez et fermez. Cette manipulation permet d’entrevoir la souplesse de travailler conjointement avec un outil et un batch. Bien entendu, clean.bat est édité avec UltraEdit mais ne fait pas partie du projet. Il est plutôt à sauvegarder avec les xasmxx.bat dans une boîte à outils.

Il est (heureusement) possible d’utiliser des variables dans les commandes pour désigner les fichiers en cours d’élaboration par leur nom et leur chemin. Ces variables se réfèrent toujours au fichier actif, c’est-à-dire celui qui est au premier plan dans UltraEdit. Depuis cette fenêtre de configuration, il vous suffit de cliquer sur Aide pour retrouver la définition de ces variables. En voici un extrait :

Les commandes suivantes peuvent être utilisées pour passer une portion
des chemins complets de noms de fichiers :
%P : Seulement le chemin ("C:\projet\essai\")
%N : Nom du fichier seulement ("essai")
%E : Extension seulement (".c")
%P%N%E : est équivalent à %F qui est "C:\project\essai\essai.c" dans
l'exemple cité ci-dessus.

 

Remarque

Noms courts et noms longs

Si %f , %p , %n , %e sont en minuscules, les noms de fichiers sont passés en tant que noms longs et devraient être mis entre guillemets, par exemple : "%f" ou "%p%n" , etc. Si %F , %P , %N , %E sont en majuscules, les noms de fichiers et dossiers sont convertis et passés en tant que noms courts avec la spécification "8.3" pour une comptabilité maximale avec les programmes DOS.

Chargez un fichier quelconque n’importe où, sur n’importe quelle partition. Ensuite, déduisez de la commande Dir local la commande Dir distant (double-clic, puis modification et Insérer ), en ajoutant  %p dans la case Répertoire de travail . Ainsi, cette commande permettra d’afficher le contenu du dossier contenant le fichier actif, quel qu’il soit. Pratique : dans la fenêtre de sortie des outils  dir , vous pouvez double-cliquer sur un nom de fichier : il va alors s’ouvrir dans UltraEdit.

Nous pouvons tester diverses commandes de confort pour nous familiariser avec les outils. Par exemple, c:\masm\bin\ml /? et une capture sur une nouvelle fenêtre vous permettra d’avoir la liste des options de  ml disponibles. Cela s’applique à n’importe quel programme sous DOS. Néanmoins, faites attention au fait que nous sommes limités à dix outils généraux et à dix autres par projet. Ces outils ne sont pas susceptibles d’être sauvegardés. Sachez néanmoins que leur définition est stockée sous une forme très simple, en texte, dans les fichiers uedit32.ini dans un dossier système et dans le fichier  .prj . Il n’est pas très compliqué de jouer du copier-coller. Si vous souhaitez lire un fichier  .prj dans UltraEdit, vous devrez le renommer ou alors utiliser le Bloc-notes.

Vous pouvez également tester par vous-même les modèles , pour par exemple insérer des en-têtes à vos propres normes, comportant date et heure.

Passons maintenant aux choses sérieuses, la construction de l’exécutable. Et tout d’abord, avec TASM.

Trois outils UltraEdit pour TASM
figure 4.12 Trois outils UltraEdit pour TASM

Les majuscules %P%N , qui transmettent des noms courts, sont, pour cette version au moins, nécessaires. Pour Tasm Debug, la sortie n’est pas capturée. Ce serait dangereux et n’aurait aucun sens. C’est simplement une fenêtre DOS qui est ouverte et qui est mise à la disposition de l’utilisateur.

Notons que nous avons codé en dur le chemin vers les exécutables Borland. Les variables système n’ont pas été modifiées ; pour le faire, il aurait fallu modifier leurs valeurs par défaut, ce que nous souhaitons éviter. Attention, modifier ces valeurs est la façon normale de travailler, et vous pouvez parfaitement y souscrire. Ce n’est qu’à partir du moment où vous utilisez de nombreux outils de développement différents que cela devient pénalisant.

Astuce

Messages d’erreurs

Quand l’assembleur génère des messages d’erreurs, il suffit de double-cliquer sur un message pour se retrouver sur la ligne concernée, prêt pour la correction.

Pour MASM, la première tentative nous montrera que ml.exe ne trouve pas link.exe . Nous avons donc décidé d’utiliser des fichiers batch, ce qui est une approche normale. De plus, ces fichiers sont aussi bien utilisables en ligne de commandes. Quand il faudra modifier souvent les paramètres passés à l’assembleur ou au lieur, il sera plus facile d’éditer un fichier que de modifier un outil d’UltraEdit. À un niveau encore supérieur de complexité, il sera temps de recourir à make , étudié en fin de chapitre. Ces arguments valent pour TASM, avec lequel il sera également intéressant d’utiliser cette technique.

Commencez par écrire les fichiers batch, les sauvegarder et les intégrer au projet, dans le groupe Batches. Ils sont intégrés au projet mais sont réutilisables sans modification dans d’autres projets. Donc, boîte à outils. Le batch de construction, que nous appelons m_c_d.bat , m valant pour MASM, c pour Construire et d pour DEBUG, puisque l’exécutable contiendra des informations de débogage et que des fichiers  .lst et  .map seront générés :

@echo off
PATH = %SystemRoot%\system32
SET PATH=C:\MASM\BIN;C:\MASM\BINR;%PATH%
SET LIB=C:\MASM\LIB
SET INCLUDE=C:\MASM\INCLUDE
del *.exe
del *.obj
del *.lst
del *.map
ml /Zi /Fl /Fm %1.asm >sortie_ml.txt

L’outil s’appelle Masm Construit D, sa commande est M_C_D %P%N , et très important, la propriété Sauvegarder d’abord tous les fichiers est cochée. Pas de capture ni de fenêtre DOS. %P%N est transmis en argument à m_c_d.bat dans lequel il devient %1 . La sortie de  ml n’est pas capturée, mais elle est redirigée vers un fichier texte. La suite des del *.xxx peut être remplacée par call clean (ne pas oublier le call ) si clean.bat est présent dans le dossier de travail. La sauvegarde préalable de tous les fichiers ouverts (du et hors projet) vous assurera de tester la version corrigée. Il reste un petit détail énervant : les noms %P et %N sont extraits du fichier actif au moment de la commande. En cours d’édition du fichier m_c_d.bat , il vous arrivera de lancer la commande, et bien entendu, elle ne fonctionnera pas. Il serait possible de remplacer %N par sav_flop , mais la portabilité serait diminuée. Les fichiers batch sont très pratiques, un peu capricieux quant à la capture de leur sortie dont il vaut mieux parfois s’abstenir. À partir de ces quelques exemples, vous devriez être à même de configurer UltraEdit et vraisemblablement d’autres éditeurs, pour un travail avec la plupart des assembleurs. Il restera à voir rapidement l’utilisation de l’environnement Visual Studio pour traiter l’assembleur 16 et 32 bits.

Voilà, les questions d’environnement de travail ont suffisamment de réponses pour que nous puissions terminer notre projet dans de bonnes conditions.

 

4.6 faire_boot, suite et fin

Pour continuer à progresser dans notre petit projet, nous allons copier l’ensemble du contenu du dossier F:\faire_boot\faire_boot_etape1\ dans F:\faire_boot\faire_boot_etape2\ pour continuer à y travailler. Mais auparavant, nous allons faire disparaître le contenu du premier dossier, en le compactant sous la forme d’une archive  .zip par exemple. Pourquoi ? Quand nous travaillons sur des projets et que nous les déplaçons, le risque existe que certains éléments continuent à être considérés comme appartenant au dossier d’origine, car des chemins absolus ont été mémorisés. Dans ce cas, masquer la première version ne résout rien, mais va au contraire empêcher le programme de fonctionner dès la première tentative. Nous pourrons ainsi régler le problème, ce qui en général n’est pas à ce stade très compliqué. Cela s’applique quel que soit le type de projet. En particulier quand nous partons systématiquement d'une application modèle, d'un squelette.

Le paragraphe précédent, réellement écrit avant de faire la manipulation, était utile. À l’ouverture du projet, tous les fichiers sont vides. Notre erreur avait été de ne pas cocher Chemins relatifs . Il est encore facile de se rattraper. Modifions le projet initial en cochant la bonne case et refaisons le transfert zippage. La situation est à peine meilleure. Tous les fichiers ouverts pointent sur leur ancienne copie, donc les fenêtres sont vides. Fermons-les toutes. Puis allons dans Projet/Fichiers . Il suffit de sélectionner les fichiers du groupe Sources pour que la bonne version s’ouvre. À priori, le fichier batch fonctionne, inutile de le rouvrir. Nous testons tous les outils, pas de problème.

Maintenant, nous savons, le passage à l’étape 3 se fera sur du velours. En restant dans cette configuration de travail, nous devrions pouvoir nous consacrer à la programmation. Enfin, espérons-le.

L’étape 2 va consister à lire quelques secteurs sur la disquette et à les placer dans la mémoire tampon bouffeur. Il nous faut donc quelques renseignements sur l’interruption du BIOS qui va nous permettre cette opération. Les opérations bas niveau sur les disques sont fournies par l’interruption  13h . Nous trouvons la fonction 02h de cette interruption, qui est nommée Read Sector(s) . Voici la signification de la valeur des registres en entrée :

  AH : 02h , le code de la fonction.

  AL : le nombre de secteurs à lire.

  CH : les 8 bits faibles du numéro de cylindre (valeur sur 10 bits).

  CL : les 6 bits faibles : le numéro du premier secteur à lire. Les 2 autres complètent CH.

  DH : numéro de tête.

  DL : numéro de lecteur.

  ES : BX : adresse de la zone devant recevoir les données lues.

En retour, la valeur la plus importante est le flag CF, qui est à 1 en cas d’erreur et à 0 sinon. AL contient le nombre d’octets réellement lus, et AH l'état (status) de l’opération, qui joue le rôle d’un code d’erreur.

Attention, les secteurs se comptent à partir de 1, et non de 0 comme les cylindres (pistes) et les têtes. Plusieurs secteurs (nombre dans AL) peuvent être lus en un seul appel, mais en conservant la même tête et le même cylindre, donc sur la même piste.

Les lecteurs de disquettes sont numérotés de 00h à 7Fh , les disques durs à partir de 80h et jusqu’à FFh . Pour nous, ce sera la disquette A:, donc 00h .

Enfin, vu la capacité d’une disquette, la subtilité du nombre de cylindres sur 10 bits ne va pas nous gêner : numéro du cylindre dans CH et numéro du premier secteur à lire dans CL.

Nous en profitons pour regarder parallèlement 03h , Write Sector(s) . C’est on ne peut plus parallèle, seul le sens du transfert change avec le code de la fonction dans AH.

Il nous suffit donc d’ajouter le bloc de code suivant entre création et écriture du fichier :

; lecture de N secteurs                    
mov ah, INT13_ReadSectors                  
mov al, NbreSecteurs; nbre secteurs à lire 
mov ch, 0          ; cylindre (8 bits bas) 
mov cl, 1          ; premier secteur       
mov dh, 0          ; tête 0                
mov dl, 0          ; disquette A:          
mov bx, offset bouffeur                    
int 13h                                    
jc  FinPrematuree                          

La taille du tampon mémoire sera déclarée en conséquence :

bouffeur    db      TailleSecteur*NbreSecteurs DUP (32h)

De plus, il faut initialiser ES à la même valeur que DS :

debut:   mov ax, @data
         mov ds, ax        ; initialisation du data segment
         mov es, ax        ; initialisation de extra segment

Il faudra tester le programme en initialisant la constante NbreSecteurs , dans le fichier sav_boot.inc , à diverses valeurs entre 1 et 18. La taille du fichier disk.sav sera une bonne indication du fonctionnement correct du programme. L’observation de ce fichier avec un éditeur hexadécimal comme UltraEdit permettra d’identifier le secteur de boot à quelques détails (nom du volume, derniers octets à 55h AAh par exemple). Mais l’idéal est l’éditeur WinHex, ou un équivalent, qui permet d’afficher simultanément le contenu de la disquette secteur par secteur, et celui du fichier. Il permet même, dans sa version enregistrée, de... sauvegarder et restaurer l’image d’une disquette.

Tel qu’il est à ce stade, le programme se construit et s’exécute avec TASM comme avec MASM. Passons à l’étape suivante, comme nous savons maintenant le faire : création de dossier, copie des fichiers, zippage des anciens fichiers et test. Cette fois-ci, aucun problème particulier.

Revenons sur les fonctions DOS de gestion de fichiers. Celles que nous utilisons impliquent un handle  (un entier unique à un instant donné) fourni par DOS pour désigner le fichier à partir de sa création ou de son ouverture. Elles sont simples à invoquer. À la création du fichier, qu’il existe déjà sur le disque ou non, le handle est créé et un curseur est initialisé par DOS à 0 soit en début de fichier. Les opérations d’écriture successives auront pour effet d’écrire dans le fichier n octets à partir de ce curseur, puis à l’incrémenter de n. Au moment de sa fermeture, le fichier sera écrit sur le disque de l’octet 0 à la position actuelle du curseur moins un et le handle sera libéré. D’autres traitements existent qui modifient le curseur, mais le fonctionnement nous satisfait tel quel : sans rien faire de particulier, nous écrivons le fichier au fur et à mesure, en mode Ajout ou Append.

Comment balayer l’ensemble du disque ?

D’abord, le temps passé par le programme ne le sera pas dans notre code, qui est de longueur dérisoire, mais dans les appels au BIOS, puisqu’ils font intervenir la mécanique du lecteur de disquettes.

Les appels DOS pour le fichier seront plus rapides et certainement se passeront en mémoire jusqu’à la fermeture.

Il est intuitif que les performances seront optimales si la quantité élémentaire de lecture est la piste, c’est-à-dire 18 secteurs. Imaginons le cas d’une lecture sur la base d’un secteur. Une fois la lecture d’un secteur et sa sauvegarde achevée, la tête de lecture peut aussi bien se trouver juste après le début du secteur suivant. Quoi qu’il en soit, le gestionnaire devra à nouveau vérifier sa position sur la piste ; il va donc laisser passer un tour complet pour se repositionner.

Il nous reste à savoir de quelle façon balayer le disque piste par piste. L’opération la plus lente est le passage d’un cylindre à un autre. Il est donc préférable de lire les deux pistes (tête 0 puis tête 1) d’un même cylindre, puis ensuite de passer au cylindre suivant. Nous aurons ainsi 80 mouvements, au lieu du double dans un autre algorithme. Le cheminement du programme sera :

  Création fichier ;

  Compteur de cylindres à 0 ;

  (Début boucle) ;

  Lecture 18 secteurs tête 0 ;

  Écriture du résultat dans le fichier ;

  Lecture 18 secteurs tête 1 ;

  Écriture du résultat dans le fichier ;

  Incrémenter compteur de cylindres ;

  Si pas égal à 80, reboucler ;

  Fermeture fichier.

De ce dernier choix d’algorithme dépend l’image sur disque de la disquette. Nous allons utiliser la même structure de programme pour la restauration ; nous sommes donc libres de notre décision. Il en serait allé autrement si les fichiers  .sav devaient être compatibles avec d’autres programmes du même type.

Précisons ce dernier point de détail : si nous fabriquons une image de la disquette en balayant chaque piste tête 0 et tête 1 avant de passer à la suivante, et si nous refabriquons la disquette en parcourant toutes les pistes tête 0, puis toutes les pistes tête 1, la disquette reconstituée ne sera pas conforme à l'originale.

Le compteur de cylindres sera une mémoire et non un registre ; nous nous affranchissons ainsi de toute contrainte présente et future dans l’utilisation des registres au sein de la boucle.

Un bloc élémentaire lecture/écriture existe déjà. Le second sera dupliqué par copier-coller. Il serait inutilement lourd de parcourir deux fois le même code en modifiant la valeur correspondant à la tête, alors que nous sommes au sein d’une boucle.

Vous trouverez sur le CD-Rom une version étape3 et une version finale, à l’interface très légèrement humanisée. Afin de ne pas multiplier les listings peu différents, voici directement la version finale :

; Programme [autonome] pour sauver l'image d'une disquette
 
INCLUDE sav_flop.inc
 
.MODEL small
 
.STACK 500
 
.DATA
bijor       db      "Bienvenue dans sav_flop",0Dh, 0Ah
            db      "Ce programme va sauver une image de la disquette dans le fichier disk.sav",0Dh, 0Ah
            db      "Appuyez sur une touche. Pour stopper, Echap",0Dh, 0Ah, "$"
 
enroute     db      "Sauvegarde en cours",0Dh, 0Ah, "$"
 
avort       db      "Programme arrêté par l'utilisateur...",0Dh, 0Ah, "$"
 
err_mess    db      "Programme stoppé sur erreur...",0Dh, 0Ah, "$"
fichier     db      "disk.sav", NULL               ; Fichier image
handle      dw      NULL
NoCylindre  db      0
bouffeur    db      TailleSecteur*NbreSecteurs DUP (32h)   ; Buffer pour N secteur
 
.CODE
 
debut:   mov ax, @data             ; initialisation du data segment
         mov ds, ax
         mov es, ax
 
         ; Message de bienvenue
         mov ah, 09h
         mov dx, offset bijor
         int 21h
 
         ; attend une touche
         mov ah, 00h
         int 16h
         ; vérifier si touche Echap
         cmp al, 1Bh
         je  Avorte
 
         mov ah, 09h
         mov dx, offset enroute
         int 21h
 
         ; création du fichier d'essai
         mov ah, INT21_CreateFile
         xor cx, cx                ; attribut normal
         mov dx, offset fichier    ; fichier image
         int 21h
         jc  FinPrematuree
         mov handle, ax
 
         ; initialisation du compteur de pistes (ou cylindres)
         mov NoCylindre, 0
 
deb_cylindre:
         ; lecture de N secteurs tête 0
         mov ah, INT13_ReadSectors
         mov al, NbreSecteurs; nbre secteurs à lire
         mov ch, NoCylindre ; cylindre (8 bits bas)
         mov cl, 1          ; premier secteur
         mov dh, 0          ; tête 0
         mov dl, 0          ; disquette A:
         mov bx, offset bouffeur
         int 13h
         jc  FinPrematuree
 
         ; écriture dans le fichier d'essai
         mov ah, INT21_WriteFile
         mov bx, handle
         mov cx, TailleSecteur*NbreSecteurs ; écriture N octets
         mov dx, offset bouffeur
         int 21h
         jc  FinPrematuree
 
         ; lecture de N secteurs tête 1
         mov ah, INT13_ReadSectors
         mov al, NbreSecteurs; nbre secteurs à lire
         mov ch, NoCylindre ; cylindre (8 bits bas)
         mov cl, 1          ; premier secteur
         mov dh, 1          ; tête 1
         mov dl, 0          ; disquette A:
         mov bx, offset bouffeur
         int 13h
         jc  FinPrematuree
 
         ; écriture dans le fichier d'essai
         mov ah, INT21_WriteFile
         mov bx, handle
         mov cx, TailleSecteur*NbreSecteurs ; écriture N octets
         mov dx, offset bouffeur
         int 21h
         jc  FinPrematuree
 
         ; incrément et test du cylindre
         inc NoCylindre
         cmp NoCylindre, NbreCylindres
         jne deb_cylindre
 
         ; fermeture fichier d'essai
         mov ah, INT21_CloseFile
         mov bx, handle
         int 21h
         jc  FinPrematuree
 
 
         mov AH, 4Ch             ;fin du programme
         int 21h                 ;et retour au DOS
 
Avorte:
         mov ah, 09h
         mov dx, offset avort
         int 21h
 
         mov AH, 4Ch             ;fin du programme
         int 21h                 ;et retour au DOS
 
FinPrematuree:
         mov ah, 09h             ; sortie chaine echec
         mov dx, offset err_mess
         int 21h
 
         mov AH, 4Ch             ;fin du programme
         int 21h                 ;et retour au DOS
 
         END debut

Le programme ainsi modifié se construit par TASM et par MASM et produit un fichier disk.sav correspondant à nos attentes.

Terminons cette phase par quelques précisions sur les chaînes de caractères dans le code source. Le nom du fichier disk.sav se termine par un caractère nul, c’est-à-dire un 0. C’est une chaîne à zéro final, comme en C. Elle est utilisée par les fonctions fichiers du DOS. Les autres sont en revanche destinées aux routines de sortie écran, fonction 09h de l’ int 21h du DOS. Le caractère de fin de chaîne est le  $ . La chaîne bijor est intéressante à cet égard : très longue, elle inclut des séquences 0Dh 0Ah , qui sont des passages à la ligne. Ce qui est passé (dans DX) à la routine de sortie écran, c’est une adresse dans le segment de données. Pour trouver le  $ final, il faut chercher deux lignes plus bas. Les deux  db supplémentaires permettent simplement de raccourcir les lignes de listing. Ils pourraient aussi bien être précédés d’un nom de variable. Cette syntaxe est acceptée aussi bien par TASM que par MASM.

Il reste maintenant à écrire le pendant de sav_flop.exe , mak_flop.exe , qui va utiliser disk.sav pour recréer la disquette originale. Le nom disk.sav n’est pas modifiable. Pour utiliser ces programmes pour motoriser une petite base de disquettes, il faudra soit continuer à les faire évoluer, soit les utiliser conjointement à d’autres programmes ou fichiers batch.

La symétrie des fonctions Read et Write, tant pour les fichiers que pour les secteurs, fait que la transformation du programme va être très simple. Nous gardons la même cinématique, le même processus de balayage. Voyons le pseudo-algorithme modifié :

  Ouverture fichier ;

  Compteur de cylindres à 0 ;

  (Début boucle) ;

  Lecture 18 x 512 octets dans fichier ;

  Écriture 18 secteurs tête 0 ;

  Lecture 18 x 512 octets dans fichier ;

  Écriture 18 secteurs tête 1 ;

  Incrémenter compteur de cylindres ;

  Si pas égal à 80, reboucler ;

  Fermeture fichier.

La création d’un nouveau projet est maintenant relativement aisée. Nous commençons, comme pour une nouvelle étape, dans F:\faire_boot\faire_boot_restaure\ , puis nous renommons les deux fichiers sources ; enfin nous enlevons les anciens fichiers du groupe Sources et y ajoutons les nouveaux. Nous ne touchons pas au groupe Batches.

Nous avons ajouté une sécurité à ce programme, puisqu’il va écrire, et donc détruire, une disquette. Nous demanderons à l’utilisateur de fournir une disquette formatée et, de plus, de la nommer PETRUS. Ce nom de volume peut se retrouver à plusieurs endroits, mais il semble que, quelle que soit la version de Windows, sa place soit au début du second secteur de la face 2 (tête 1), sur la piste 0. Le programme va tester ce nom avant de continuer son œuvre. Une version sans cette sécurité sera fournie sur le CD-Rom.

Le projet, dont le listing est donné un peu plus loin, s’exécutant sous MASM, nous avons tenté de l’assembler avec TASM. Une liste de messages d’erreurs apparaît dans la fenêtre de sortie.

Damned !
figure 4.13 Damned !

En double-cliquant sur le premier de ces messages, nous tombons sur l’instruction je Avorte . Réflexion faite, l’erreur était fatale, puisque la cible est hors de la zone que peut atteindre ce saut conditionnel (le déplacement est codé sur 1 octet, en signé). Mais alors, MASM, comment procède-t-il ? Continuons dans la naïveté, reconstruisons avec MASM et inspectons le fichier listing, mak_flop.lst . Nous y trouvons :

                         ; vérifier si touche Echap
 0012  3C 1B                     cmp al, 1Bh
 0014  75 03 E9 00CB                 je  Avorte

75 03 veut dire jne 3 octets plus loin, soit juste après le je Avorte . Et E9 00CB , c’est jump Avorte . C’est loin d’être idiot. Nous allons donc ajouter la directive JUMPS  à notre listing, pour dire à TASM d’effectuer la même chose que MASM. Sur le plan des sauts courts conditionnels s’entend.

Voici enfin le listing final :

; Date:   13/07/2002
; Auteur: Pierre Maurette
; Objet:  création d'une disquette à partir d'une
;         disquette formatée et d'un fichier image disk.sav
 
TITLE MAK_FLOP
 
INCLUDE mak_flop.inc
 
.MODEL small
.STACK 100
 
.DATA
bijor       db      "Bienvenue dans mak_flop",0Dh, 0Ah
            db      "Ce programme va fabriquer une disquette de boot à partir de l'image DISK.SAV",0Dh, 0Ah
            db      "Insérez une disquette formatée, nom de volume 'PETRUS' puis",0Dh, 0Ah
            db      "appuyez sur une touche. Pour stopper, Echap",0Dh, 0Ah, "$"
enroute     db      "Fabrication en cours",0Dh, 0Ah, "$"
avort       db      "Programme arrêté par l'utilisateur...",0Dh, 0Ah, "$"
DefFormat   db      "Cette disquette ne semble pas formatée...",0Dh, 0Ah, "$"
BadVolume   db      "Le nom de volume doit être 'PETRUS'...",0Dh, 0Ah, "$"
err_mess    db      "Programme stoppé sur erreur...",0Dh, 0Ah, "$"
fichier     db      "disk.sav", NULL         ; Fichier image
handle      dw      NULL
NoCylindre  db      0
bouffeur    db      TailleSecteur*NbreSecteurs DUP (32h)   ; Buffer pour N secteur
 
 
;JUMPS    ; Décommenter pour TASM
 
.CODE
 
debut:   mov ax, @data             ; initialisation du data segment
         mov ds, ax
         mov es, ax
 
         ; Message de bienvenue
         mov ah, 09h
         mov dx, offset bijor
         int 21h
 
         ; attend une touche
         mov ah, 00h
         int 16h
         ; vérifier si touche Echap
         cmp al, 1Bh
         je  Avorte
 
         ; message de démarrage
         mov ah, 09h
         mov dx, offset enroute
         int 21h
 
         ; vérification de la disquette
         ; lecture du secteur 2 face 1
         mov ah, INT13_ReadSectors
         mov al, 1          ; nbre secteurs à lire
         mov ch, 0          ; cylindre (8 bits bas)
         mov cl, 2          ; second secteur
         mov dh, 1          ; tête 1
         mov dl, 0          ; disquette A:
         mov bx, offset bouffeur
         int 13h
         jc FinNonFormat
 
         mov bx, offset bouffeur
         cmp byte ptr [bx], 'P'
         jne  MauvaisVolume
         inc bx
         cmp byte ptr [bx], 'E'
         jne  MauvaisVolume
         inc bx
         cmp byte ptr [bx], 'T'
         jne  MauvaisVolume
         inc bx
         cmp byte ptr [bx], 'R'
         jne  MauvaisVolume
         inc bx
         cmp byte ptr [bx], 'U'
         jne  MauvaisVolume
         inc bx
         cmp byte ptr [bx], 'S'
         jne  MauvaisVolume
 
         ; ouverture du fichier d'essai
         mov ah, INT21_OpenFile
         xor al, al                ; mode Read Only
         mov dx, offset fichier    ; fichier image
         int 21h
         jc  FinPrematuree
         mov handle, ax
 
         ; initialisation du compteur de pistes (ou cylindres)
         mov NoCylindre, 0
 
deb_cylindre:
         ; lecture dans le fichier d'essai
         mov ah, INT21_ReadFile
         mov bx, handle
         mov cx, TailleSecteur*NbreSecteurs ; écriture N octets
         mov dx, offset bouffeur
         int 21h
         jc  FinPrematuree
 
         ; écriture de N secteurs tête 0
         mov ah, INT13_WriteSectors
         mov al, NbreSecteurs; nbre secteurs à écrire
         mov ch, NoCylindre ; cylindre (8 bits bas)
         mov cl, 1          ; premier secteur
         mov dh, 0          ; tête 0
         mov dl, 0          ; disquette A:
         mov bx, offset bouffeur
         int 13h
         jc  FinPrematuree
 
         ; lecture dans le fichier d'essai
         mov ah, INT21_ReadFile
         mov bx, handle
         mov cx, TailleSecteur*NbreSecteurs ; écriture N octets
         mov dx, offset bouffeur
         int 21h
         jc  FinPrematuree
 
         ; écriture de N secteurs tête 1
         mov ah, INT13_WriteSectors
         mov al, NbreSecteurs; nbre secteurs à écrire
         mov ch, NoCylindre ; cylindre (8 bits bas)
         mov cl, 1          ; premier secteur
         mov dh, 1          ; tête 1
         mov dl, 0          ; disquette A:
         mov bx, offset bouffeur
         int 13h
         jc  FinPrematuree
 
 
         ; incrément et test du cylindre
         inc NoCylindre
         cmp NoCylindre, NbreCylindres
         jne deb_cylindre
 
         ; fermeture fichier d'essai
         mov ah, INT21_CloseFile
         mov bx, handle
         int 21h
         jc  FinPrematuree
 
 
         mov AH, 4Ch             ;fin du programme
         int 21h                 ;et retour au DOS
 
Avorte:
         mov ah, 09h             ; sortie chaine arret par utilisateur
         mov dx, offset avort
         int 21h
 
         mov AH, 4Ch             ;fin du programme
         int 21h                 ;et retour au DOS
 
FinPrematuree:
         mov ah, 09h             ; sortie chaine echec
         mov dx, offset err_mess
         int 21h
 
         mov AH, 4Ch             ;fin du programme
         int 21h                 ;et retour au DOS
 
FinNonFormat:
         mov ah, 09h             ; sortie chaine non formatée
         mov dx, offset DefFormat
         int 21h
 
         mov AH, 4Ch             ;fin du programme
         int 21h                 ;et retour au DOS
 
MauvaisVolume:
         mov ah, 09h             ; sortie chaine mauvais nom de volume
         mov dx, offset BadVolume
         int 21h
 
         mov AH, 4Ch             ;fin du programme
         int 21h                 ;et retour au DOS
         END debut

Tel quel, ce programme fonctionne correctement, même sous XP. En cas de problème de qualité de média, il va s’arrêter sur un message d’erreur. La disquette sera laissée en l’état et devra être reformatée, ou mieux, jetée. sav_flop va laisser sur le disque, en cas d’arrêt prématuré, un fichier disk.sav incomplet, qu’il faudra détruire.

C’est un programme 16 bits, il est impossible d’en réutiliser le cœur dans un programme écrit par exemple avec Delphi, ou en C++ 32 bits. Ce serait tentant pour celui qui voudrait archiver et gérer une grande quantité de disquettes. En fait, il faudrait en reprendre la structure générale et utiliser l’API DeviceIOControl pour les accès disques bas niveau.

4.7 Utilisation de l'EDI de Visual C++

L’environnement de développement intégré Visual Studio, et plus particulièrement Visual C++, est adaptable plus qu'adapté à la conduite de projets en assembleur 32 bits ; les manipulations sont en effet un peu rebutantes. Par exemple, l'extension  .asm n'est pas reconnue par Visual Studio, et aucune coloration syntaxique n'est proposée. Néanmoins, et en particulier si vous l'utilisez déjà, Visual Studio reste une bonne solution.

4.7.1 Compiler des modules assembleur 32 bits au sein d'un projet C/C++

C'est un fonctionnement normal. Nous allons exploiter les ressources proposées par Microsoft pour cette manipulation. Et une version en anglais de Visual Studio6 + SP5, sous Windows 98SE. Si vous souhaitez disposer de cet environnement pour l'assembleur, c'est que vous utilisez Visual Studio pour d'autres tâches : vous le connaissez donc suffisamment pour ne pas avoir besoin d'aide à ce sujet.

Pour les liens, et éventuellement les fichiers, reportez-vous comme toujours au CD-Rom.

Nous devons avoir installé sur Visual Studio6 le Service Pack SP4 au minimum. Il faut ensuite télécharger et installer le Processor Pack. C'est un fichier vcppx.exe , qu'il suffit de lancer pour que tout se passe très rapidement. Il installe ml.exe v6.15, met à jour une tripotée de fichiers et copie un fichier d'aide, procpack.chm , que vous trouverez dans le dossier VC98 , ainsi qu'un ppreadme.htm , dont nous allons nous servir pour cette prise en main. C'est l'exemple donné dans ce fichier que nous allons reconstruire pas à pas.

Lancez VC++ et créez deux nouveaux fichiers texte, que vous sauvez sous les noms CMain.c et MASMSub.asm , dans un dossier F:\CAPP

/* Filename: CMAIN.C */ 
 
#include <stdio.h>
 
#ifdef __cplusplus
extern "C" {
#endif
 
void MasmSub (char *, short *, long *);
 
#ifdef __cplusplus
}
#endif
 
char chararray[4] = "abc";
short shortarray[3]  = {1, 2, 3};
long longarray[3] = {32768, 32769, 32770};
 
void main( void )
{
   printf ("%s\n", chararray);
   printf ("%d %d %d\n", shortarray[0], shortarray[1], shortarray[2]);
   printf ("%ld %ld %ld\n", longarray[0], longarray[1], longarray[2]);
   MasmSub (chararray, shortarray, longarray);
   printf ("%s\n", chararray);
   printf ("%d %d %d\n", shortarray[0], shortarray[1], shortarray[2]);
   printf ("%ld %ld %ld\n", longarray[0], longarray[1], longarray[2]);
}
LEGENDE=Le fichier CMain.c

 

; Filename: MASMSUB.ASM
; Assemble options needed for ML: /c /Cx /coff
 
.386
.MODEL flat, C
.CODE
 
MasmSub PROC uses esi, arraychar:PTR, arrayshort:PTR, arraylong:PTR
   mov esi, arraychar ; Load ESI with the address of the char array.
   mov BYTE PTR [esi], "x"      ; Since a char is 1 byte long, each
   mov BYTE PTR [esi+1], "y"    ; successive element can be accessed
   mov BYTE PTR [esi+2], "z"    ; by adding 1 more to esi.
   mov esi, arrayshort; Load ESI with the address of the short array.
   add WORD PTR [esi], 7        ; Since a short is 2 bytes long, each
   add WORD PTR [esi+2], 7      ; successive element can be accessed
   add WORD PTR [esi+4], 7      ; by adding 2 more to esi.
   mov esi, arraylong ; Load ESI with the address of the long array.
   inc DWORD PTR [esi]          ; Since a long is 4 bytes long, each
   inc DWORD PTR [esi+4]        ; successive element can be accessed
   inc DWORD PTR [esi+8]        ; by adding 4 more to esi.
   ret
MasmSub ENDP
END
LEGENDE=Le fichier MASMSub.asm

 

Créez ensuite un nouveau projet, sur le modèle Application console Win32, que vous nommez CAPP . Si le dossier  CAPP déjà créé est dans la racine de F: , choisissez F: comme dossier du projet. Vous acceptez un projet vide.

Ajoutez au projet les deux fichiers CMain.c et MASMSub.asm . Vous pouvez déplacer ce dernier vers le dossier Sources Files de l'Explorateur de projet ou alors ajouter les deux fichiers par un clic droit sur ce dossier, puis un clic sur Add to folder .

Vous devez maintenant indiquer à l'environnement comment construire MASMSub.asm . En cliquant dessus du bouton droit, vous constatez qu'effectivement l’entrée Compile est grisée. Choisissez Settings . Dans la liste déroulante Settings For : , sélectionnez All Configurations .

Sous l'onglet General , cochez Allway use custom build steps  ; cela a peu d'importance, les extensions  .asm n'ayant pas d'outil associé par défaut.

Sous l'onglet Custom Build , en vous aidant des listes Directories et Files , tapez les lignes suivantes, respectivement dans les boîtes Commands  :

ml.exe -c -coff -Cx -Fo$(IntDir)\$(InputName).obj $(InputName).asm

et Outputs  :

$(IntDir)\$(InputName).obj

(Les tirets  - et les slaches  / sont équivalents dans les options de ml.exe ).

En cours de configuration
figure 4.14 En cours de configuration

Ces paramétrages sont valables pour les versions Debug et Release.

Vous pouvez maintenant ajouter l'option  -Z1 en sélectionnant Win32 Debug dans la liste Settings For .

La ligne de commande lance ml.exe avec l'option  -c , qui lui demande de ne pas invoquer le lieur, précaution nécessaire puisque l'objet sera lié avec celui issu de CMain.c .

Une fois cette configuration effectuée, le projet peut être construit et débogué normalement. Il est, bien entendu, possible de placer des points d'arrêt dans le source assembleur.

Étudions la procédure assembleur. .MODEL flat, C indique que les conventions d'appel sont de type C. La procédure reçoit trois pointeurs sur trois tableaux de types variés et y modifie les valeurs. Placez un point d'arrêt sur mov esi, arraychar , la première ligne de la procédure.

Halte au point d'arrêt
figure 4.15 Halte au point d'arrêt

Pas d'outil spécialisé pour observer la pile ; ce n’est pas grave, tapez esp dans une fenêtre d'observation mémoire. Voyons ce qui a été empil, et dans quel ordre : il est facile de vérifier que 00424A3C , 00424A34 et 00424A30 pointent vers les trois tableaux passés en paramètres. Ils ont été empilés dans cet ordre par CMain , qui a ensuite empilé, par un CALL , son adresse de retour, 004010A4 .

Ensuite, le compilateur a empilé EBP, de valeur 0065FDF8 , opération équilibrée par un  LEAVE en fin de procédure. EBP a ensuite reçu la valeur de ESP à l'entrée de la procédure, ce qui permet d'accéder aux données transmises. Ce qu'il faut bien voir, c'est que les procédures n'accèdent pas à ces données en les dépilant (par ses POP). Nous reviendrons en détail sur ces points. Enfin, ESI a été empilé à l'entrée de la procédure, à cause du uses esi dans l'en-tête de la procédure.

4.7.2 Assembler des programmes assembleur 32 bits avec VS

Les programmes en assembleur pur induisent généralement l'usage de librairies. Dans une volonté de présenter un large éventail de l'offre, nous allons tester l'ensemble MASM32, librement téléchargeable sur Internet. Son installation est une décompression dans un dossier MASM32 . Le produit est complet, avec tutoriaux et éditeur, le tout en anglais. Mais notre but consiste à tester Visual Studio, l'installation de MASM32 étant vue au chapitre suivant.

D'abord, configurez l'environnement pour qu'il trouve les outils et les librairies. Pour cela, activez Tools/Options/Directories , puis la liste déroulante Show directories for .

Paramétrage des dossiers
figure 4.16 Paramétrage des dossiers

Quand vous cliquez sur le rectangle de saisie pour introduire un nouveau dossier, un bouton apparaît, qui vous permet de parcourir le disque à sa recherche. Des icônes sont masquées par la liste déroulante ; parmi elles, deux flèches permettent de déplacer un dossier dans la liste. Cela a son importance, puisque ces dossiers seront scannés de haut en bas, quand un objet ou une commande sont recherchés. Nous pourrons le vérifier pour les exécutables, la version de  ml.exe indiquant alors qu'il s'agit de celle de MASM32 ou de celle de Visual Studio, celle par exemple installée par le Processor Pack. Dans le cas d'une application exemple MASM32, en cas de problème, il est bon de remonter les dossiers de MASM32.

Vous avez saisi les dossiers suivants :

C:\MASM32\M32LIB , C:\MASM32\M32LIB , C:\MASM32\INCLUDE et C:\MASM32\BIN .

Dans ..\\masm32\example3\dir , vous trouvez un tout petit exemple. Copiez ce dossier sur le disque de travail, puis purgez-le de mdir.exe et mdir.obj . Créez exactement comme précédemment un projet vide nommé mdir , mais en corrigeant le nom du dossier en dir avant de valider.

Procédez exactement aux mêmes modifications qu'au paragraphe précédent. Le fait que  .asm soit seul ne change en rien la façon dont il doit être construit. Il va toujours générer un  .obj qui sera lié dans le projet.

Une première tentative de compilation se solde par un échec, sur cette ligne :

include \masm32\include\windows.inc

Il en existe plusieurs de ce type, la correction est immédiate :

include windows.inc

L'exécutable est ensuite construit sans problème. Il n'y en a pas non plus pour placer des points d'arrêt dans le source assembleur. C'est donc une solution tout à fait exploitable. Nous avons testé quelques autres programmes des exemples de MASM32, dont certains comprenaient des fichiers de ressources.

En général, les compilateurs 32 bits, assembleurs compris, pouvant produire un fichier objet au format COFF pourront être utilisés avec bonheur dans cet environnement.

 

4.7.3 Assembler des programmes assembleur 16 bits avec VS

Sur la base de fichiers batch, comme nous en avons déjà fabriqué et étudié, il est possible d'arriver laborieusement à un résultat. Nous passons à cet effet par le menu Tools/Customize/Tools . Le lieur et le débogueur de Visual Studio ne nous seront d'aucune utilité. Il faudra donc, par exemple, un construit.bat ou alors un assemble.bat plus un lie.bat , et enfin un debogue.bat . Tout cela pour en arriver à un UltraEdit sans coloration syntaxique. Donc, autant choisir une des solutions proposées précédemment.

 

4.8 Les fichiers MAKE

Nous avons, dans ce chapitre, abordé la notion de projet et nous nous sommes intéressés aux fichiers batch, ou fichiers de traitement par lot, d’extension  .bat . Un projet est constitué d’un ensemble de fichiers et en génère d’autres. Sur ces fichiers, des opérations sont effectuées, généralement matérialisées par des programmes comme les compilateurs, compilateurs de ressources, assembleurs et autres lieurs.

Même pour un petit programme, le nombre de lignes à saisir à chaque reconstruction est vite rebutant. Un programme assembleur peut demander de plus nombreux fichiers que s’il était écrit dans un langage de haut niveau, sans que cela soit une règle absolue.

L’utilisation des utilitaires de maintenance  de projet va faciliter cette tâche. À ne pas confondre avec les progiciels de gestion de projets, qui eux travaillent sur les versions, les équipes de développement et généralement avec des langages très éloignés de l’assembleur. Dans ces progiciels, le code est souvent secondaire.

 

4.8.1 Les fichiers de type MAKE.BAT ou MAKEIT.BAT

Avant de revenir sur ces utilitaires, parlons encore de nos chers fichiers batch. Vous trouverez souvent, avec un code source, des fichiers nommés par exemple make_projet1.bat , voire make.bat  ou makeit.bat . Une utilité marginale de ces fichiers est de transmettre de façon élégante à un tiers la méthode et les conditions de construction, en même temps que les fichiers sources. Il pourra soit utiliser le fichier tel quel, si par hasard il fonctionne sur sa machine, soit le décortiquer et s’en inspirer pour construire le projet. Voyons simplement un exemple, un peu plus fourni que ceux vus dans ce chapitre :

@echo off
 
: ----------------------------------------------------
:Réinitialisation de la variable path
: ----------------------------------------------------
PATH = %SystemRoot%\system32
 
SET PATH=C:\MASM\BIN;C:\MASM\BINR;%PATH%
SET LIB=C:\MASM\LIB
SET INCLUDE=C:\MASM\INCLUDE
SET INIT=C:\MASM\INIT
SET HELPFILES=C:\MASM\HELP\*.HLP
SET ASMEX=C:\MASM\SAMPLES
SET TMP=D:\WINDOWS\TEMP
 
echo Début du traitement
 
: ----------------------------------------------------
:Nettoyage
: ----------------------------------------------------
if exist main.obj del main.obj
if exist sub.obj del sub.obj
if exist main.exe del main.exe
 
 
: ----------------------------------------------------
:Tentatives d'assemblage des .asm
: ----------------------------------------------------
ml /c /coff main.asm
if errorlevel 1 goto errmain
 
ml /c /coff sub.asm
if errorlevel 1 goto errsub
 
: ----------------------------------------------------
:Tentatives de compilation des ressources
: ----------------------------------------------------
rc /v rsrc.rc
if errorlevel 1 goto errrc
 
cvtres /machine:ix86 rsrc.res
if errorlevel 1 goto errcvtres
 
: ----------------------------------------------------
:Tentatives d'édition de liens
: ----------------------------------------------------
link /SUBSYSTEM:WINDOWS main.obj sub.obj rsrc.obj
if errorlevel 1 goto errlink
goto fin
 
: ----------------------------------------------------
:Messages d'erreur
: ----------------------------------------------------
:errmain
echo Erreur dans l'assemblage de Main
goto fin
 
:errsub
echo Erreur dans l'assemblage de Sub
goto fin
 
:errrc
echo Erreur dans compilation de ressources
goto fin
 
:errcvtres
echo Erreur dans conversion de ressources
goto fin
 
:errlink
echo Erreur dans édition de liens
goto fin
 
: ----------------------------------------------------
:Fin de traitement
: ----------------------------------------------------
 
:fin
pause

Les améliorations possibles sont nombreuses. Vous pouvez remplacer un nom de fichier par sa valeur passée en argument au fichier batch (très pratique dans UltraEdit par exemple). Faites un simple essai.bat  :

@echo off
echo %1
echo %2.asm
echo %3
pause

et invoquez-le par essai iroise ouessant casquets .

 

4.8.2 Les utilitaires NMAKE, NMAKER et MAKE

Les programmes MAKE sont make.exe chez Borland et nmake.exe chez Microsoft. nmaker.exe est une version 16 bits du précédent utilisable en environnement DOS.

Astuce

Trouver des fichiers d’aide en français

Pour MAKE, le fichier bcb6tool.hlp , installé avec C++ Builder 6 dans une version suffisante, en version d’essai ou complète, est un fichier d’aide sur divers outils en ligne de commande, dont MAKE. Très utile. Mieux, bcb5tool.hlp et make.exe sont dans le C++ 5.5 en ligne de commande, disponibles sur Internet et présents sur le CD-Rom. Malheureusement, si ces fichiers d'aide sont en français, le texte consacré à MAKE est partiellement en anglais !

NMAKE est présenté dans MSDN, qui joue le rôle d’aide pour Visual Studio, en particulier Visual Studio.NET. Voir également sur Internet.

En anglais, aucun problème en revanche. Si vous l’avez, essayez  QH dans MASM 6.

Très schématiquement, ces programmes lisent un fichier texte appelé un makefile  pour générer une ou plusieurs cibles (targets).

prog.asm est un source, qui permet de fabriquer la cible prog.obj . Cette cible devient source, avec peut-être d’autres  .obj , pour la cible finale prog.exe . Une caractéristique de ces programmes est de déterminer quelles cibles, intermédiaires ou finales, doivent être reconstruites, à partir d'informations de dépendance et de la date/heure de dernière modification des fichiers.

MAKE et NMAKE sont très proches dans leur comportement. Plus précisément, les makefiles sont pratiquement interchangeables. MAKE accepte l’option  /N qui améliore encore son interprétation de makefiles de type NMAKE.

Les lignes de commandes de MAKE et NMAKE sont en revanche différentes, pour en arriver globalement aux mêmes résultats. N’importe quels outils peuvent être mentionnés dans les makefiles, par exemple un compilateur Fortran. Nous pourrons donc à fortiori utiliser NMAKE avec TASM, et inversement MAKE avec MASM/ML, si nous le souhaitons.

Les programmes MAKE lisent le fichier makefile désigné par l’option  /F de la ligne de commande, si présente, sinon le fichier makefile (sans extension) du dossier courant ( makefile.mak également, mais uniquement chez Borland).

Si vous avez constitué deux installations MASM et TASM, choisissez un fichier  .asm qui se construit dans les deux cas, puis fabriquez les deux fichiers textes suivants :

NOM=mak_flop
$(NOM).exe: $(NOM).obj
        c:\masm\bin\link $(NOM).obj
$(NOM).obj: $(NOM).asm
        c:\masm\bin\ml /Zi /Fl /Fm $(NOM).asm

pour MASM, et :

NOM=mak_flop
$(NOM).exe: $(NOM).obj
        f:\tasm\bin\tlink $(NOM).obj
$(NOM).obj: $(NOM).asm
        f:\tasm\bin\tasm  $(NOM).asm

pour TASM. Sauvez-les dans le dossier sous deux noms qui seront successivement modifiés en makefile . Dans une fenêtre DOS ouverte dans ce dossier de travail, invoquez successivement MAKE, NMAKE et NMAKER. Pensez à exécuter clean.bat entre chaque essai (si vous omettez ce détail, rien ne sera reconstruit). Par changement du nom, testez également les deux makefiles. Vous constatez que l’utilisation croisée est tout à fait possible.

Remarque

Fin des expériences de compatibilité

Sur des projets aussi minimalistes, il est possible de changer d’assembleur avec le même fichier  .asm , et de MAKE avec le même makefile. Déjà, le fichier mak_flop.asm doit être modifié d’une ligne, s’il n’est pas fait appel à l’assemblage conditionnel. À vrai dire, une version 5 de TASM aurait été plus compatible, mais peu importe. Dès que les projets prennent de l’ampleur, cette compatibilité devient plus difficile à obtenir. Donc, écrivons les fichiers  .asm pour un assembleur donné, des makefiles pour un MAKE précis. Il reste vrai que nous pouvons utiliser n’importe quel MAKE avec n’importe quel assembleur.

Un makefile est pratiquement synonyme de projet. Les EDI (Environnements de Développement Intégrés) peuvent souvent ouvrir des makefiles en tant que projet, et plus souvent encore exporter le projet courant sous la forme d’un makefile.

Un MAKE peut très bien être utilisé pour l'installation de logiciels, ou d'ensembles de fichiers. Il saura réaliser toutes les opérations nécessaires sur les disques, décompresser ce qui doit l'être, voire compiler, assembler, lier des fichiers.

Les makefiles, qui jouent par rapport au MAKE le rôle de fichiers de script, sont, il faut le dire, d'une syntaxe et d'une philosophie un peu obscure au premier abord. Dans le cadre de développements de taille raisonnable, l'usage d'un makefile n'est jamais indispensable. Il existe toujours une solution correcte pour obtenir le même résultat. Ces deux faits expliquent que son usage soit rare. C'est également pour cette raison que nous allons nous y intéresser.

Nous nous fonderons sur MAKE, de Borland, pour les explications qui suivent. Nous dégrossirons simplement le sujet ; à vous de l'approfondir par la pratique.

Nous parlions de syntaxe et de philosophie. La première, assez inhabituelle, rend le texte un peu plus confus, et participe à masquer la seconde. Nous allons donc commencer notre découverte par des éléments de syntaxe.

Les macros

Des commentaires peuvent être placés dans le makefile : ils commencent par le caractère  # et courent jusqu'à la fin de la ligne.

Nous allons parler immédiatement des macros , la lecture des listings s'en trouvera facilitée.

Une macro désigne tout simplement le remplacement d'un mot par un autre, comme les EQU du fichier sav_flop.inc . L'idée de base est de ne définir ce mot qu'à un endroit, dans le cadre de la réutilisation du makefile. Reprenons l'exemple :

NOM=mak_flop définit NOM comme ayant pris la valeur mak_flop . Cette définition peut comporter des espaces, mais doit commencer en première colonne de la ligne.

NOM ne sera pas utilisé tel quel, mais sous la forme $(NOM) . Si nous voulons réutiliser ce makefile pour construire sav_flop.exe , il suffira de modifier la première ligne en NOM=sav_flop , sans toucher aux six occurrences suivantes du nom du fichier.

Mieux, il va être possible de passer ce nom en argument, en appliquant la syntaxe d'appel suivante, après avoir effacé la première ligne : make /D NOM=sav_flop . Le /D (ou  -D ) est optionnel, mais doit être en majuscule. Si le mot comporte des espaces ou tabulations, il faudra le mettre entre guillemets. Pas d'espace de part et d'autre du signe  = . L'effacement de la première ligne est important, puisque la définition du fichier annule celle de la ligne de commande.

Il est possible de modifier une occurrence de la macro, sa définition restant inchangée : $(NOM:sav=mak) devient mak_flop . Le terme de remplacement pourrait comporter une macro. Le remplacement peut intervenir plusieurs fois dans la macro, si la sous-chaîne y apparaît plusieurs fois.

EXTENSION = .exe
FICHIERS = sav_flop.obj mak_flop.obj

$(FICHIERS:.obj=$(EXTENSION)) donnera : sav_flop.exe mak_flop.exe .

Les variables d'environnement comme PATH sont des macros de fait, pour peu qu'elles ne soient pas redéfinies explicitement. La syntaxe d'appel est toujours $(PATH) .

Il existe enfin des macros définies par MAKE. Tout d'abord quelques variables d'environnement locales :

__MSDOS__ , à 1 si MAKE s’exécute sous DOS. Puis __MAKE__ , MAKE , MAKEFLAGS et MAKEDIR qui désignent le numéro de version, le nom, les options de la ligne de commande et enfin le dossier de MAKE.

Il existe enfin huit macros prédéfinies, combinables avec quatre modificateurs appliqués essentiellement à $< et $@ , dont la définition figure dans les deux tableaux suivants. Ils ne sont pas compréhensibles à ce stade, nous nous y reporterons ultérieurement.

 

Macros par défaut de nom de fichier

Macro

Dans une règle implicite

Dans une règle explicite

$*

chemin\fichier antécédent

chemin\fichier cible

$<

chemin\fichier antécédent + extension

chemin\fichier cible + extension

$:

chemin pour antécédents

chemin pour cible

$.

fichier antécédent + extension

fichier cible + extension

$&

fichier antécédent

fichier cible

$@

chemin\fichier cible + extension

chemin\fichier cible + extension

$**

chemin\fichier antécédent + extension

tous fichiers antécédents + extension

$?

chemin\fichier antécédent + extension

anciens fichiers antécédents

 

Modificateurs de macros par défaut

Modificateur

Syntaxe

Résultat

D

$(>D) ou $(@D)

lecteur\chemin

F

$(>F) ou $(@F)

fichier + extension

B

$(>B) ou $(@B)

fichier

R

$(>R) ou $(@R)

lecteur\chemin\fichier

 

Cibles et règles

À la base, le programme MAKE va rechercher dans le makefile un certain nombre de cellules, appelées règles , qui expliquent comment obtenir une cible, pour l'instant un fichier, à partir d'autres fichiers. Il existe entre ces autres fichiers et la cible une relation de dépendance.

Réglons un problème de vocabulaire, important pour la compréhension : la documentation en anglais utilise le qualificatif de dependent pour qualifier ces autres fichiers, terme qui est généralement traduit par dépendant. Or, en français, ce terme évoque nettement la relation inverse de celle que nous voudrions expliciter. Dans l'exemple suivant, mak_flop.exe est la source. mak_flop.obj n'est pas dépendant de cette source, mais est plutôt la cause de son état et doit être mis à jour avant d'appliquer la règle de la ligne suivante.

mak_flop.exe: mak_flop.obj             # ligne des dépendances
        c:\masm\bin\link mak_flop.obj  # ligne de commande

Nous avons trouvé sur Internet une seule traduction par un autre terme que dépendant, prérequis . C'est bien de cela qu'il s'agit, les éléments de la seconde partie de la ligne des dépendances sont prérequis pour élaborer la cible. Nous avons préféré utiliser le mot antécédent . Il évoque bien le rapport chronologique entre les divers éléments.

Une règle est donc constituée d'une ligne de dépendance, commençant directement en première colonne, et d'une ligne de commande, commençant par un ou plusieurs espaces. Une règle correspond à une cible et à éventuellement un ou plusieurs antécédents.

MAKE va utiliser makefile pour construire une ou plusieurs cibles. Si aucune cible n'est précisée dans la ligne de commande make [options] [cible[cible]..] , c'est la première cible rencontrée dans le makefile qui sera construite. Pour construire plusieurs cibles sans les inclure dans la ligne de commande, il faut proposer comme première cible une cible symbolique, sans ligne de commande. Par exemple :

savboot: sav_flop.exe mak_flop.exe

et ensuite, définir les règles pour sav_flop.exe et mak_flop.exe .

Le fonctionnement est le suivant : la ligne de dépendance de la cible à construire indique à MAKE les antécédents de la cible. Il recherche alors des règles de dépendance pour chacun de ces antécédents devenus cibles. Il bâtit ainsi un arbre de dépendances. Dans notre cas, il trouvera mak_flop.asm en bas de l'arbre, comme antécédent de mak_flop.obj . Il remontera ensuite l'arbre. À chaque règle, il comparera les dates de dernière modification des fichiers et appliquera la ligne de commande, si au moins un des antécédents est plus récent que la cible.

Il y a, dans l'exemple, un rapport intime entre ligne de dépendance et ligne de commande. Pour des applications de ce type, la ligne de dépendance peut apparaître superflue, MAKE étant capable de la déduire de la ligne de commande. Mais cette simplicité n'est pas toujours de mise.

Résumons donc : pour construire une cible, la ligne de commande indique à MAKE : "Je suis telle cible, c'est comme ça que tu dois procéder pour me reconstruire, si tu décides de le faire." Et la ligne de dépendance ajoute : "Voici les cibles dont je dépends, à toi de voir si elles sont bien construites avant de me construire."

Une ligne de commande est en gros toute commande du système d'exploitation, tout ce qui peut se saisir au clavier (sous DOS). Il est donc clair qu'elle peut être constituée de plusieurs lignes.

Il peut, pour une cible, exister plusieurs lignes de dépendances. Mais, dans ce cas, la ligne de commande ou le bloc qui en tient lieu doit être unique, à la suite d'une seule de ces règles.

Un petit exercice pour bien expliciter ces règles. Éditons makefile  :

NOM=mak_flop
 
$(NOM).exe: makefile
$(NOM).exe: $(NOM).obj
        ml   $(NOM).asm
        link $(NOM).obj
        dir
 
#$(NOM).obj: $(NOM).asm
#        ml   $(NOM).asm

Les deux dernières lignes sont inhibées puisqu'elles sont mises en commentaire. Lancez MAKE. Reconstruction. Vous voyez l'effet de la commande  dir . Relancez make . Pas de reconstruction. Lancez un clean , puis make . Reconstruction. make à nouveau. Pas de reconstruction. Effectuez une sauvegarde de makefile , sans réelle modification. Cela va changer sa date. Puis make  : reconstruction.

La liste des antécédents peut être précédée d’une liste de dossier où MAKE pourra chercher les antécédents. Syntaxe : {rep1; rep2; ...} . Les accolades doivent être saisies.

Il est possible de définir plusieurs règles pour une même cible :

cible :: ant1, ant2
      commande1
cible:: ant3
      commande2

Notez le double  : .

 

Règles explicites – règles implicites

La règle que nous venons de présenter est une règle explicite  : elle indique à MAKE quand et comment construire un fichier particulier, ici mak_flop.exe .

S'il existe des règles explicites, c'est qu'il doit bien en exister d' implicites . Une règle implicite indique à MAKE : "Si je ne t'ai pas donné de règle (explicite) pour un fichier particulier, mais qui possède cette extension (par exemple  .exe ), alors applique cette règle." Voici un exemple :

NOM=mak_flop
 
.asm.obj:
   ml $<
 
.obj.exe:
   link $<
   
$(NOM).obj:

La dernière ligne définit la cible, par une règle explicite sans ligne de commande. Les commandes des deux règles implicites seront appliquées. L'ordre des règles n'à pas d'importance. Il est judicieux de commencer par les macros, puis les règles implicites, et enfin la définition de la cible par une règle explicite.

Les programmes MAKE vont chercher des règles implicites par défaut dans un fichier particulier. tools.ini , que nous avons déjà évoqué, et builtins.mak chez Borland. Pour ignorer ces fichiers, indiquez l'option  /r . Voici un exemple de builtins.mak (celui de TASM), vous pouvez essayer de l'analyser à l'aide de la documentation au besoin :

#
# Borland C++ - (C) Copyright 1993 by Borland International
#
 
# default is to target 16BIT
# pass -DWIN32 to make to target 32BIT
 
!if !$d(WIN32)
CC       = bcc
RC       = brcc
AS       = tasm
!else
CC       = bcc32
RC       = brcc32
AS       = tasm32
!endif
 
.asm.obj:
      $(AS) $(AFLAGS) $&.asm
 
.c.exe:
      $(CC) $(CFLAGS) $&.c
 
.c.obj:
      $(CC) $(CFLAGS) /c $&.c
 
.cpp.exe:
      $(CC) $(CFLAGS) $&.cpp
 
.cpp.obj:
      $(CC) $(CPPFLAGS) /c $&.cpp
 
.rc.res:
      $(RC) $(RFLAGS) /r $&
 
.SUFFIXES: .exe .obj .asm .c .res .rc
 
!if !$d(BCEXAMPLEDIR)
BCEXAMPLEDIR = $(MAKEDIR)\..\EXAMPLES
!endif

Les macros XXXFLAGS seront fournies par la ligne de commande. La directive .SUFFIXES indique à MAKE dans quel ordre rechercher les fichiers pour l'application d'une règle. L'utilisation principale est le lever de doute dans la fabrication des  .obj . Si, dans un projet, nous avons toto.c et toto.asm , et si aucune règle explicite n'est proposée pour construire un toto.obj , c'est toto.asm qui sera utilisé.

Les directives de MAKE

Il suffit d'aller dans l'aide pour en obtenir la liste. Une partie de ces directives est conditionnelle, d'usage évident. Par exemple, le dernier paragraphe de builtins.mak signifie : si la macro BCEXAMPLEDIR n'est pas définie, alors la définir égale à $(MAKEDIR)\..\EXAMPLES .

Nous avons vu comment passer une macro à MAKE par l'option  /D . Sans lui fournir de valeur, la macro sera simplement définie. Nous passons en fait une valeur booléenne. Avec make /DDEBUG , vous définissez la macro DEBUG, qui pourra permettre de choisir entre plusieurs règles, par des séquences :

!if $d(DEBUG)
; lignes pour version debug
!else
; lignes pour version finale
!endif 

Pour les autres directives, le fichier d'aide sera suffisant, et cette partie est en français. Citons !message , !undef d'usage courant, .SUFFIXES déjà vu.

Conclusion

Nous voici largement assez avancé pour découvrir les subtilités restantes à partir d'exemples et de l'aide. Nous sommes, quoi qu'il en soit, capables de fabriquer des makefiles très efficaces. Si ce n'est pas absolument clair, il ne faut pas hésiter à continuer les petits essais.

Nous n'avons pas développé ce qui concerne la syntaxe des commandes plus que ce qui était nécessaire pour les exemples, Ce sujet est développé en deux pages bien faites, en français, dans l'aide ; sa lecture est plus que conseillée.

 

Envoyer un message à l'auteur

se connecter pour pouvoir écrire

Ce site est créé et maintenu (laborieusement) par Pierre Maurette
hébergé par 1 & 1