Outils pour utilisateurs

Outils du site


nsi:premiere:architecture:langage_assembleur

Langage Assembleur

Bien que certains standards se soient mis en place, le langage assembleur est le langage d'un processeur particulier et donc, suivant les technologies mis en œuvre dans un processeur donné, l'assembleur sera différent.

Le langage proposé dans ce cours n'est qu'un exemple correspondant à un processeur fictif que j'ai inventé pour les besoins du cours. Mais il est suffisamment proche des cas réels pour vous être utile.

Compilation / Interprétation

  • Un langage de programmation comme Python, JS, C, … est un langage pour le programmeur humain.
  • La machine parle un langage binaire peu compréhensible pour l'humain.

L'Assembleur est le langage de la machine.

Dans les tous premiers temps de l'informatique, on écrivait directement les programmes dans le langage de la machine. C'était pénible et cela ne permettait pas de faire des programmes très complexes.

À mesure de l'évolution de la technologie et des théories sur les langages de nouveaux langages plus évolués, plus faciles à comprendre pour l'humain sont apparus.

C est un langage qui reste assez difficile parce qu'il nécessite une bonne compréhension de ce qui se passe dans la machine. Python est au contraire très proche de l'humain et donc beaucoup plus facile à apprendre.

Mais la machine continue à parler son langage. Donc il faut traduire.

  • Certains langages – Python, JS, PHP, … – sont interprétés. Ils refont la traduction à chaque exécution.
  • Certains langages – C, Rust, Go, … – sont compilés. La traduction est faite une fois. On exécute ensuite la version traduite.

Vous pouvez comparer cela au travail de traducteur / interprète.

  • l'interprète est comme le journaliste sportif qui interroge un sportif en fin d'épreuve et traduit ce que dit le sportif en même temps que le sportif parle, en temps réel.
  • le traducteur est celui qui écrit une version traduite d'un roman. Le traducteur peut prendre son temps pour bien choisir ses mots et il n'a à faire le travail qu'une fois.

L'Assembleur c'est mieux ?

Le travail de traduction peut ajouter des erreurs, il peut faire perdre quelque chose… Autrefois, les programmes écrits directement en Assembleur étaient toujours plus performants que ceux compilés/interprétés. On a donc longtemps continué à faire des choses en assembleur.

Aujourd'hui, les compilateurs et interpréteurs sont devenus beaucoup plus performants. Il n'est plus utile de programmer en Assembleur.

Apprendre l'Assembleur sert seulement à mieux comprendre comment ça marche.

Quelques différences

Cas interprété

  • Le programme interprète – par exemple python.exe – doit être présent sur la machine qui exécute le programme.
  • Le concepteur du programme n'a pas à se soucier du type de machine qui exécutera le programme du moment que celle ci possède une version de l'interprète.
  • Le concepteur du programme est obligé de fournir le code du programme. On est open source.
  • La traduction se faisant à la volée, elle est moins optimale.

Cas compilé

  • Le programme compilé fonctionne seul. On n'a besoin de rien d'autre pour l'exécuter.
  • Le programme a été compilé pour un type de machine. Un programme compilé pour un PC 64 bits ne fonctionnera pas sur un PC 32 bits. Il faut produire diverses versions.
  • Le concepteur n'a pas à fournir son code. Il donne seulement l'exécutable (version compilé) qui est quasiment illisible (puisque dans le langage de la machine)
  • Le compilateur peut produire un exécutable mieux optimisé.

Assembleur et architecture du microprocesseur

L'assembleur est très proche du matériel. Il faut donc connaître au moins un peu l'organisation d'un processeur pour comprendre ce que peut faire l'assembleur.

 Structure de Von Neumann

La plupart des processeurs adoptent la structure dite de Von Neumann.

  • L'unité de séquencement est le chef d'orchestre, il dirige les autres ;
  • l'unité arithmétique et logique (UAL) fait les calculs (+, *, etc.) ;
  • la mémoire contient toutes les données utiles : résultats de calculs mais aussi le programme lui-même ;
  • entrées / sorties car le processeur doit servir à quelque chose, il agit sur l'extérieur.

Registre

On utilisera souvent le mot registre. On peut considérer que c'est une case mémoire. Elle contient une donnée, un mot binaire.

Un registre est une mémoire très rapide utilisée au cœur du microprocesseur. Un microprocesseur contient généralement une mémoire interne constituée de registres. On trouve d'autres registres comme le registre qui mémorise la ligne d programme à laquelle on se trouve ou bien les registres utilisés par l'unité de calcul.

UAL

L'Unité Arithmétique et Logique fait les calculs. Ce sont des calculs simples mais ils sont à la base de tout ce que fera le microprocesseur. Ces un composant essentiel qui peut faire une grande différence d'un microprocesseur à un autre :

  • certains microprocesseurs disposent des circuits pour faire des opérations compliquées comme des cosinus, racines carrées, des opérations sur les flottants…
  • d'autres ne savent faire que des PLUS et des MOINS avec des entiers.
  • dans certains cas, l'UAL dispose d'une trentaine de registres pour faire ses calculs,
  • dans d'autres cas il ne dispose que du registre contenant le résultat, le registre de travail.

Dans notre cas, l'UAL n'aura qu'un registre de travail. On appelle parfois ce registre W pour Work.

Exemple

MOV #4
ADD #8
STR @x

Chaque ligne est une instruction simple que le processeur est capable d'exécuter. Dans cet exemple, voici ce que fait le programme :

  • charger la valeur 4 dans le registre de travail,
  • additionner 8 au contenu du registre de travail,
  • écrire le contenu du registre de travail dans la mémoire étiqueté x – STR signifie STORE, c'est à dire stocker.

Premiers enseignements

Les instructions sont peu variées et elles correspondent aux différents organes du processeur.

Voici le genre de chose qu'une instruction peut faire :

  • copier une donnée d'une case mémoire à une autre,
  • exécuter un calcul avec l'UAL,
  • lire en entrée / écrire en sortie,
  • changer le flux d'instruction : instructions de saut.

Flux d'instructions

Détaillons le dernier item qui est le moins évident. Un programme s'exécute une ligne après l'autre, dans l'ordre. L'unité de séquencement dispose donc d'un compteur de ligne – on dit aussi pointeur de ligne – qui après chaque instruction se contente de passer à la ligne suivante.

Mais parfois on a besoin de recommencer un bloc d'instructions (boucle) ou bien de ne pas exécuter un bloc d'instruction (alternative comme if). Dans ces cas là, on ne veut pas simplement passer à la ligne suivante. On veut passer (on dit souvent sauter = jump) à une certaine ligne du programme. Certaines instructions sont là pour cela.

Voyons un exemple avec un branchement.

    MOV @x    // contenu de x dans travail
    CMP #0    // on va comparer à 0
    BGE L     // si plus grand ou égal, sauter à ligne étiquetée "L"
    MULT #-1  // multiplier travail par -1
    STR @x    // stocker travail dans x
L   HALT      // fin

Vous noterez que l'on a ajouté une marge pour permettre l'ajout d'une étiquette en tête de ligne. On pourrait aussi numéroter les lignes mais l'usage est plutôt de mettre une étiquette pour indiquer les lignes où l'on est susceptible de sauter.

Vous pouvez constater que dans ce programme, suivant la valeur de x, on ne fait pas la même chose. On pourrait le traduire en Python :

if x < 0:
    x *= -1

Le code assembleur est toujours très long comparé à l'équivalent dans un langage évolué.

Littéral et adresse

Dans le morceau de programme, vous pouvez voir #4 et #8. Dans les deux cas on parle de littéral. Quand on écrit #4, il faut comprendre : le nombre 4.

On aurait pu voir @4. Le symbole @ indique que l'on parle d'une adresse.

  • ADD @4 : on additionne au registre de travail le contenu de la mémoire à l'adresse 4.
  • ADD #4 : on additionne au registre de travail le nombre 4.

Souvent, on ne veut pas spécifier une adresse explicitement. Il est donc rare que l'on note quelque chose comme @4. On écrit plutôt quelque chose comme @x et on laisse le système décider dans quel case mémoire il placera x.

Programme et données

Il faut comprendre que la mémoire contiendra à la fois le programme et les données du programme. Reprenons le programme précédent.

    MOV @x
    CMP #0
    BGE L
    MULT #-1
    STR @x
L   HALT

Ce programme comporte 6 lignes et utilise une variable x. Dans le microprocesseur, les 6 lignes de programme occuperont les lignes 0 à 5. On pourra placer x en ligne 6. Si on veut explicitement placer x à la suite du programme, on peut écrire :

    MOV @x
    CMP #0
    BGE L
    MULT #-1
    STR @x
L   HALT
x   DATA

ce qui permet d'indiquer que la ligne mémoire après la ligne contenant HALT devra contenir la mémoire x.

nsi/premiere/architecture/langage_assembleur.txt · Dernière modification : de goupillwiki