1.4. Comment ça fonctionne?

L'assembleur permet d'opérer directement sur le processeur, la mémoire ou les périphériques. Sa syntaxe est simple, pour ne pas dire rudimentaire, et toutes les opérations possibles ne sont faites qu'à l'aide de quelques instructions comme mov, push, pop etc. En règle générale, les instructions sont nommées assez explicitement pour que l'on comprenne plus ou moins intuitivement l'opération effectivement réalisée :

Tableau 1-2. Exemple d'instructions assembleur

InstructionOpération effectuée
mov Permet de « bouger » des données d'un emplacement à un autre (espace mémoire ou registre processeur)
pushSert à « pousser » des données sur la pile
popPermet de récupérer les données empilées via push
......

Que veut dire exactement « s'adresser directement au processeur » ? Les langages de plus haut niveau ne permettent-ils pas cela également ?

En fait, le compilateur ne peut pas passer directement du C au bytecode. Il est obligé d'implémenter un analyseur syntaxique complexe qui interprètera ce que le programmeur à voulu faire dans un jargon particulier (C, Pascal etc.).

Lorsque vous codez en assembleur, vous ne dites pas A = 0, mais vous demandez directement au processeur de mettre un registre à 0, par exemple. La variable A sera en fait le registre eax ou une de ses subdivisions (al, ah ou ax [1]) et sa mise à 0 pourra se faire de plusieurs manières :

Un compilateur C pourra par exemple traduire le code suivant :


	A = 0
	B = 0
      

en :


	xor eax,eax
	mov A,eax
	xor eax,eax
	mov B,eax
      

On ne peut pas dire que cela soit optimum... Si nous avions fait cela directement en assembleur, nous aurions évité de définir 2 variables en utilisant le plus possible les registres :


	xor eax,eax
	xor ebx,ebx
      

Comme vous l'aurez compris, tout se passe souvent dans le détail, mais au bout du compte, c'est ce qui fera la différence. gcc et les compilateurs modernes génèrent du code de plus en plus optimisé, mais cela restera toujours de la génération automatique et le bytecode se verra toujours pollué de choses inutiles.

On pourrait cependant se demander en quoi une instruction assembleur xor est plus proche de la machine qu'une instruction C. En fait, les instructions assembleur sont juste étudiées pour être un peu plus digestes que du code hexadécimal ou binaire ; mais elles y ont leur équivalent exact. Par exemple xor ax,ax se traduira directement par le code hexadécimal 31C0 qui lui-même représente le code machine 0011000111000000. Il faut donc bien comprendre qu'en codant en assembleur, on code en fait quasi directement en binaire, et rien ne vient interférer entre ce que l'on veut faire et la façon dont cela sera effectivement fait.

Notes

[1]

Nous ne sommes pas encore entré dans les détails des registres, mais anticipons un peu en disant seulement qu'un registre peut contenir des données de différentes tailles : al et ah contiennent des données sur 8 bits et constituent à eux deux le registre ax qui lui contient des données sur 16 bits. Au-dessus nous avons le registre eax qui permet de stocker des données sur 32 bits (il n'existe pas de découpage en 2x16 bits comme pour ax).