I. De l'assembleur▲
Relu par ClaudeLELOUP.
Aujourd'hui, il existe une multitude de langages de programmation aux caractéristiques fort différentes. Des langages-objets, interprétés, portables, etc. se rencontrent quotidiennement. A tel point qu'il faut faire une totale confiance au compilateur pour traduire cela dans la langue de la machine. Car bien évidemment, un ordinateur ne comprend ni le C, ni le C++, encore moins le java, le CamL et autres PHP, Python et consorts. Aussi le curieux peut légitimement se poser cette question : que se passe-t-il vraiment dans le ventre de la machine ? Je joue en ce moment avec le langage de programmation le plus proche de la machine, celui à l'origine de tout : le langage assembleur. Je vous propose de jouer avec moi.
I-1. Du langage machine▲
Un ordinateur ne comprend rien, à l'origine. Que se passe-t-il à la mise sous tension ? L'ordinateur vient brutalement à la vie. Et sa vie, à l'ordinateur, consiste à lire un livre. A la mise sous tension, il ouvre la première page de son livre et lit le premier mot. Et lire, pour un ordinateur, consiste à se modifier, à changer son état. Or donc, il lit son premier mot, et s'en trouve changé : son mot à lire n'est plus le premier, il devient le deuxième. Il a certaines de ses variables modifiées en fonction du mot lu. Un mot du livre de l'ordinateur, c'est quoi ? C'est un ensemble de fils électriques, sous tension ou pas. Vu qu'on ne s'amuse pas à ajouter ou enlever des fils à tout bout de champ, il s'ensuit que tous ces mots ont le même nombre de lettres, c'est-à -dire de fils. Nous avons ordonné ces fils en leur donnant un numéro. Et on dit que 1 est un fil électrique sous tension, et 0 un fil électrique sans tension. Un mot de l'ordinateur est donc représenté par la suite ordonnée des 0 et des 1 représentant l'état de tension de chaque fil électrique. Et une suite ordonnée de chiffres, ça donne un nombre. 86 est un mot du livre de l'ordinateur. D'un strict point de vue formel, chaque type de processeur doit avoir son propre dictionnaire. 86 n'a a priori aucune chance de signifier la même chose pour un processeur de marque AMD et pour un autre de marque Intel. 86 peut faire une addition de deux variables chez l'AMD, et afficher à l'écran la biographie des rois de France sur l'Intel. Certains se sont alors mis d'accord sur (on a aussi la version : certains ont imposé) un dictionnaire commun. Ce dictionnaire commun, c'est (une partie) de la norme Compatible PC. 86 veut dire (à peu près) la même chose chez tous les ordinateurs compatibles PC. Ca implique quelques bricoles, comme :
- n'importe qui peut écrire un livre pour n'importe quel ordinateur ;
- on n'est pas obligé de réécrire toutes les bibliothèques pour tous les ordinateurs ;
- on ne peut pas mettre n'importe quelle fonction dans un processeur.
Bien. L'informatique a avancé. Chaque nombre est une fonction à peu près partagée par un sous-groupe d'ordinateurs. Extraordinaire. Et si, je dis ça je ne dis rien, mais si, comme ça, histoire que moi je comprenne l'histoire qu'il y a dans le livre de l'ordinateur, on remplaçait, discrètement, tous ces nombres par les noms des fonctions qui leur correspondent ? Dites ? Et de ce jour, le désassembleur était né : traduire en mots plus humains les mots-nombres de l'ordinateur. Et si on faisait l'opération inverse ? Et si j'écrivais les noms des fonctions, et qu'après, on les traduisait en langue ordinateur ? Hou là , très compliqué, c'est de l'analyse de texte, on ne sait pas faire. Ce qu'on peut faire, par contre, c'est associer chaque mot ordinateur avec un tout petit mot d'anglais abrégé. Là , on devrait s'en sortir. C'est de ce modeste objectif que naquit l'assembleur. L'assembleur, c'est le premier langage de programmation différent du langage de la machine.
I-2. De l'assembleur par rapport au langage machine▲
L'assembleur est donc une traduction du langage machine. Et comme toutes les traductions depuis la tour de Babel, elle n'a aucune raison d'être la même suivant les traducteurs. Et, de fait, l'assembleur diffère selon les programmes de traduction. Ceci implique deux trois petits soucis, notamment le fait que pour que l'ordinateur fasse exactement la même chose, il faut lui demander parfois différemment. C'est ennuyeux. Aussi aime-t-on, comme les chefs d'Etat, garder le même traducteur, qui traduira toujours de la même façon. Ces traducteurs traduisent une langue constituée de mots d'anglais abrégés en une langue constituée de nombres. Ces deux langues sont définies et existent indépendamment du traducteur. Celle que nous voulons traduire, celle constituée de petits mots d'anglais abrégés, est un dictionnaire de mnémoniques. Ces mnémoniques font aussi partie de la norme Compatible PC. La différence entre les traducteurs tient à la façon de les arranger entre eux. Des traducteurs, il en existe quantité. Il faut en choisir un. Affaire de goût. Mais si on souhaite coller au plus près de la machine, et tout produire soi-même parce qu'on fait mi-moyen confiance aux autres compilos, ou parce qu'on veut bien comprendre ce qui se passe dans la machine, il faut en choisir un qui ne fait qu'assembler (i.e. traduire), sans autres fioritures. Personnellement, j'ai choisi NASM. Donc la suite se passera avec NASM. NASM peut, si j'ai tout bien compris, produire des fichiers exécutables pour Windows et Linux.
I-3. Des fichiers exécutables▲
Justement, qu'est-ce qu'un fichier exécutable ? Sous le prétexte un peu subtil d'organiser tout le schmilblick qu'on peut croiser dans le disque dur d'un ordinateur (données d'Excel, vidéo du petit dernier bavant sur mamie, code de calcul de la bombe atomique), de fourbes ingénieurs en informatique (dont certains étaient même ingénieurs de recherche !) ont décidé, comme ça, qu'on allait avoir des fichiers en lieu et place d'une bouillie d'octets, et que le seul livre que l'ordinateur ouvrirait jamais s'appellerait "le système d'exploitation". Ces gens-là n'ont aucune poésie. Le système d'exploitation sait quels sont les fichiers disponibles sur l'ordinateur, et quels sont ceux qui sont des programmes. Il peut aussi ajouter des chapitres dans son propre livre, c'est-à -dire faire exécuter d'autres programmes à l'ordinateur. Comme tout est fichier (en gros) pour le système d'exploitation, cela signifie que certains fichiers sont des chapitres à ajouter à son livre : les fichiers exécutables. Afin que le système d'exploitation sache quelques bricoles quant à ces chapitres à ajouter, ces fichiers exécutables ont un en-tête donnant des informations aussi futiles que le nombre de pages du chapitre à ajouter, par exemple. Le traducteur ne sait pas écrire cet en-tête, à part pour un seul type de fichiers exécutables : le format COM, disponible sous DOS. Pour les gens qui n'ont pas eu la chance de naître avant Windows 95, DOS est le système d'exploitation sur lequel est construit Windows. Il est toujours accessible, bien que de plus en plus caché dans les tréfonds de l'ordinateur sous Windows. Donc, pour partir du plus simple possible, j'utilise NASM pour générer des fichiers COM.
I-4. Des outils à utiliser▲
- NASM, donc.
- Un éditeur de texte.
Personnellement, j'ai une tendresse particulière pour celui-ci, qui fait de la coloration syntaxique pour maints langages : Notepad++.
- Une fenêtre de commande DOS.
Menu "Démarrer" ou "Windows", "Exécuter un programme", "cmd".
- DOSBox.
Il se trouve que les versions les plus récentes de Windows (pardonnez-moi d'être sur un vieux tromblon) interdisent tout un tas de trucs, notamment tout ce qu'on utilise pour commencer l'assembleur. Pour passer outre, exécutez donc les fichiers générés sur DOSBoxLa page de téléchargement de DOSBox.
I-5. Se jeter à l'eau du haut d'une falaise venteuse▲
Je n'y tiens plus, écrivons notre chapitre du livre de l'ordinateur. Ouvrons notre éditeur préféré, créons un nouveau fichier texte nommé comme on veut (personnellement il s'appelle "Carabistouille.txt") et tapons, avec la force de conviction que donne le bon droit :
org
0x0100
; Adresse de début .COM
;Ecriture de la chaîne hello dans la console
mov
dx
, hello
mov
ah
, 0x9
int
0x21
ret
hello: db
'Bonjour papi.'
, 10
, 13
, '$'
Une fois sauvegardé, on lance une console, on se met dans le bon répertoire, et on commande :
nasm -
o [un nom].com [nom complet du fichier texte]
Puis, en étant fou :
[un nom].com
Et boum. Ca formate le disque dur. Non, je blague. On a eu "Bonjour papi." affiché dans la console, une ligne vide, puis le retour de l'invite de commande.
Ceci est le plus petit programme qui fasse quelque chose : afficher une chaîne de caractères. Regardez la taille du fichier [un nom].com : 24 octets. On ne peut pas beaucoup plus petit : la chaîne de caractères comporte 16 caractères (Bonjour papi. + caractère 10 + caractère 13 + $), il ne reste que 8 octets de programme à proprement parler.
Maintenant, étudions ce que nous avons écrit ligne par ligne :
org
0x0100
; Adresse de début .COM
Tout de suite, dès le départ, cette ligne n'est pas vraiment une ligne de code : c'est un message envoyé à NASM. org veut dire que tout ce qu'on va écrire n'est pas vraiment le chapitre : on laisse de la place pour l'en-tête du système d'exploitation. Un peu comme si on laissait de la place au début du chapitre pour la préface. 0x0100 est, aussi curieux que cela puisse paraître, un nombre. Il se décompose comme suit :
- 0x indique qu'il s'agit d'un nombre écrit en hexadécimal ;
- 0100 est le nombre en hexadécimal.
En notation décimale, ce nombre est 256, soit la taille de l'en-tête. D'ailleurs, on peut tout à fait remplacer "org 0x0100" par "org 256", ça marche pareil. Ce qui nous apprend qu'on peut écrire un nombre en décimal en indiquant simplement le nombre, et l'écrire en hexadécimal en le préfixant par "0x". 0 = zéro, je précise.
Après, on a un point-virgule. Ceci indique à NASM que le reste de la ligne est du commentaire, du gloubi-boulga dont il n'a que faire mais qui peut nous aider, nous autres pauvres êtres biologiques. On notera également que visiblement, le nombre d'espaces entre les choses d'une ligne n'a pas d'importance. Toujours ça de pris.
;Ecriture de la chaîne hello dans la console
Commentaire, donc.
mov
dx
, hello
MOV, première instruction processeur. C'est L'instruction processeur, la grande, celle qui est utilisée à tout bout de champ. Elle déplace un nombre d'une case à une autre. Ici, on utilise la syntaxe Intel : destination, source. Si vous êtes passé par de l'assembleur Motorola ou AT&T, c'est l'inverse. On met donc le nombre contenu dans la case "hello" dans la case "dx". Trépidant. Alors, oui, les cases ont des noms. Ou pas. Disons que certaines cases particulières ont des noms, et qu'on peut affecter un nom aux autres, mais sinon, elles ont des numéros. Le nom "hello" correspond en fait à un numéro : le 264. Vous pouvez remplacer "hello" par 264, ça marche tout aussi bien. Pourquoi 264 ? C'est la taille du fichier exécutable moins la longueur de la chaîne de caractères plus la taille de l'en-tête : 24-16+256. Autrement dit, le numéro de l'octet de début de la chaîne une fois le programme chargé en mémoire. DX n'a pas de numéro, parce que c'est une case dans le processeur. Elle a un nom. C'est un registre. Cette instruction met donc dans le registre DX l'adresse du départ de la chaîne de caractères.
mov
ah
, 0x9
On met dans le registre AH la valeur 9, codée en hexadécimal. On peut la coder directement en décimal, ça marche aussi.
int
0x21
INT, deuxième instruction processeur. On demande au processeur d'appeler un petit programme stocké ailleurs, qui porte le numéro 0x21, mais bien connu du processeur. Il fait partie d'une catégorie de programmes qu'on appelle interruptions, d'où le mnémonique. C'est un programme du système d'exploitation, donc ça ne marche que sous DOS. C'est SPE-CI-FIQUE à DOS. Ce programme fait des choses différentes selon la valeur stockée dans le registre AH. Si AH vaut 9, alors ce programme affiche la chaîne de caractères dont l'adresse du début est stockée dans DX. Voilà pourquoi on a mis l'adresse de notre chaîne de caractères dans DX, et 9 dans AH. Mais c'est bien gentil d'écrire la chaîne dont on a le début, mais la fin, elle est où ? Hé bien, la fonction 0x9 de l'interruption 0x21 s'arrête quand elle rencontre le caractère "$". C'est pour ça que notre chaîne se termine par un $ qu'on ne voit pas à l'écran.
ret
Fin du programme. C'est tout. Fin d'un programme COM.
hello
:
db
'Bonjour papi.'
, 10
, 13
, '$'
hello: marque une adresse. Cette case mémoire porte le nom "hello", dorénavant. Utile à NASM, pas au processeur. C'est pour nous éviter de nous balader avec des numéros de cases mémoire un peu partout. "hello" est quand même plus clair que 264, non ? db indique qu'il s'agit de Data Bytes, d'octets de données. Pas d'autres choses : d'octets. Ensuite vient "Bonjour papi.", une virgule pour dire qu'on continue, le caractère numéro 10 (caractère ASCII = octet), le 13, et le $ terminal. Les guillemets servent à demander à NASM de traduire le caractère ASCII en sa valeur numérique. "B" est plus lisible que 66. Comme les caractères 10 et 13 ne sont pas affichables (il s'agit des caractères affichant un saut de ligne), on a indiqué directement leur valeur numérique.
Voilà , je crois qu'on a tout.