A l'époque, ma source d'informations était la fameuse "bible PC", à l'époque, il n'y avait pas Internet.
J'ai bossé au départ sur Amiga (CPU 68000) avant de passer sur PC.
Ma plus grosse difficulté à été l'inversion source destination :
sur x86:
mov ax,1 ; chargement valeur 1 dans registre ax (syntaxe Intel instruction destination,source)
sur 68000
move.w %1,d0 chargement valeur 1 en 16bits dans registre d0 (syntaxe AT&T instruction source, destination)
Pas facile à comprendre au début le mode segment:offset du x86 quand on vient du 68000. Sur 68000, je n'ai pas utilisé la pagination, la MMU étant à part du CPU. (intégré dans les x86 depuis le 286 je crois)
Dans mon exemple on peut voir que les mnémoniques d'un CPU à l'autre ne sont pas très différents, les registres changent par contre.
La plus grosse différence maintenant se situe au niveau des CPUs RISC/CISC. Il est à noter que la plupart des ordis d’aujourd’hui utilisent des x86. Par contre tout ce qui est smartphone/tablettes sont en CPU ARM (donc en RISC).
Il faut comprendre que l'assembleur a été inventé pour simplifier la relecture et modification de code machine. Ensuite sont venus les langages tels que FORTRAN, COBOL, BASIC, C, etc ... plus simple à lire et modifier que de l'assembleur, et surtout portable.
Auparavant un programme en assembleur était plus rapide que l'équivalent écrit en C, mais maintenant avec la puissance des compilateurs, ce n'est plus le cas. Et le compilateur optimisera bien mieux qu'un humain.
Si je reprends l'exemple d'Obsidian :
ou
Dans l'absolu, ces deux codes font la même chose : ajouté 1 au contenu du registre eax, par contre l'un est plus efficace que l'autre car prendra moins de temps à être traité par le CPU. Ce genre de cas est géré automatiquement par un compilateur C (ou autre langage) moderne. Et ce cas n'est pas forcément valable avec un CPU ARM, encore une fois, on s'en fous, on laisse le compilateur gérer.
Avant d'apprendre l'assembleur dans le milieu scolaire, j'ai appris la logique combinatoire et la logique séquentielle. On part de l'éléctronique avec ses portes logiques, pour arriver à un CPU. En parallèle, une bonne compréhension de l'algorithmie depuis un langage haut niveau permet de remplacer un code C du genre :
1 2 3 4 5 6 7
|
int compteur=0;
for (int boucle=0;boucle<2;++boucle)
{
++compteur;
} |
en code assembleur :
1 2 3 4 5 6 7 8 9 10 11 12
|
xor eax,eax
mov [compteur],eax
mov ecx,2
boucle:
inc eax
dec ecx
cmp ecx,0
jne boucle
mov [compteur],eax
compteur: dd 0 |
Dans cet exemple, le nombre de boucle étant fixe, le compilateur aurait fait ce qu'on appele un déroulage de boucle, il aurait recopié le code de la boucle autant de fois que le nombre de passes à faire. Le code est plus long mais plus rapide, car il n'y a pas de test à faire. Dans mon code assembleur, le nombre de boucle se situe dans le registre ecx, j'aurais pu utiliser le mnémonique loop qui décrémente ecx et fait un saut conditionnel, mais je crois qu'il est plus rapide de ne pas s'en servir. Mon code assembleur doit aussi être optimisable au niveau du cmp
Pour info : mes codes ne sont notés qu'à titre indicatif et de mémoire, donc à vérifier
Partager