dimanche 30 août 2009

- Introduction -

Bienvenue !

assembleur est le langage le plus basique accessible au programmeur, c'est ce que nous expliquerons lors de la première partie. Il reste, malgré son aspect faussement vieillot, utilisé tous les jours, même si l'on ne trouve
plus de programme entièrement écrit en assembleur. Il permet un échange direct avec le processeur, ce qui dégage déjà certaines motivations à son apprentissage. En effet, ce langage permet de découvrir le processeur ainsi que son fonctionnement. L'assembleur permet de revenir aux bases du fonctionnement de l'ordinateur. Ainsi, le programmeur est seulement confronté à des fonctions basiques telles que celles qui permettent l'affichage d'un pixel ou encore l'écriture d'un caractère ce qui révèle de réels défis et ce qui explique l'intérêt pour ce langage. De plus l'exécution de programme en assembleur est très rapide.

Pour commencer ce tutoriel vous n'avez pas besoin de connaissances particulières en programmation.


Ce cours est composé des parties suivantes :
  • I - Notions de base
  • II- Vers la programmation


Si vous avez des questions au cours du tuto,
voici mon email : anthony.putters@gmail.com pour les poser. Bonne apprentissage.

samedi 29 août 2009

Partie I - Chapitre 1 : Qu'est-ce que l'assembleur ?

Un langage informatique n'est, en pratique, qu'une suite d'instructions que le programmeur demande d'accomplir à une machine. Il existe différents types de langages informatiques. Nous pouvons distinguer trois grandes catégories de langages :

  • le langage machine ;
  • les langages assembleurs ;
  • les langages de haut niveau.
Dans cette partie nous situerons l'assembleur à travers les différents types de langages existants. Nous en ferons ensuite une rapide présentation.

Sommaire du chapitre :

  • A - Les catégories de langages
  • B - L'assembleur 80x86

A - Les catégories de langages


1 - Langage machine


Le langage machine est celui qui est directement compréhensible par le processeur. Ce dernier ne comprend que les 0 et les 1. Les opérations simples deviennent d'énormes suites de 0 et de 1 incompréhensibles.
Voici ci-dessous le code permettant de calculer 2 + 2. La première ligne permet de mettre dans AX, un des registres du processeur, la valeur 2. Nous verrons en détail par la suite ce que c'est, considérons pour l'instant qu'il s'agit d'une variable qui prend dans ce cas la valeur 2. Ensuite la deuxième ligne permet d'y ajouter 2, représenté par 00000010 en binaire (que nous apprendrons à manipuler par la suite).

Code Machine
1
2
10111000 00000010 00000000
00000101 00000010 00000000


Vous conviendrez rapidement que ce langage est fastidieux et qu'il présente de nombreux risques d'erreurs. Les programmeurs possédaient une liste à laquelle ils se référaient pour ajouter au code les instructions voulues, en reportant le code binaire de cette dernière.


2 - Langages assembleurs



C'est en 1950 qu'est arrivée l'idée de remplacer ces instructions en binaire par des mots mnémotechniques plus faciles à manier, du type :

Code Assembleur
1
MOV AX, 2

Permettant simplement de déplacer 2 dans le registre AX. MOV remplace ici : 10111000, beaucoup plus simple à utiliser. Par contre ce type de programme, non compréhensible directement par le processeur exige une étape de traduction en langage machine par un interpréteur pour être compris par le processeur. Il faut savoir qu'il existe différents types de processeurs qui impliquent différents langages machine, donc différents langages assembleurs.

Celui que nous allons étudier est celui qui est lié au processeur 80x86, qui est apparu en 1978. Ce langage est aussi compréhensible par les versions plus récentes de processeur, tels que les processeurs 80186 et 80188.


3 - Langages de haut niveau


Ces langages possèdent tout d'abord un avantage particulier, en effet ils sont compatibles avec de nombreux ordinateurs, par le biais d'interpréteurs adaptés aux différents processeurs, contrairement aux langages assembleurs, de portabilité très restreinte. Ils sont de plus en plus faciles à utiliser et à comprendre. Nombre de ces langages proposent des fonctions très simples comme : printf("..."); en C ou encore : PRINT "..." en Basic permettant simplement d'afficher une chaîne de caractères. Une telle instruction est représentée en assembleur par :

Code Assembleur : Afficher une chaine de caractère
1
2
3
4
5
6

msg    DB "..."     ; Définit la chaîne de caractères à afficher

MOV DX, offset msg
; Précise l'emplacement de la chaîne de caractères
MOV AH, 9            ; Appelle la 9ème sous-fonction
INT 21h              ; De l'interruption 21h(hexadécimal) du DOS

Ne vous inquiétez pas il est normal de ne pas comprendre, nous verrons tout cela plus tard.

Ce code se traduit en langage machine par :

Code Machine : Afficher une chaine de caractère
1
2
3
4
5

11101011 00000100
00101110 00101110 00101110 00100100
10111010 00000010 00000000
10110100 00001001
11001101 00100001

Ceci représente simplement une traduction littérale du code en assembleur ci-dessus, compréhensible par le processeur. Vous constaterez donc que les langages évolués constituent un véritable "racourci" par rapport aux langages assembleurs.


B - L'assembleur 80x86



Ce langage est constitué de différents éléments faisant sa puissance :

  • un groupe d'instructions ;
  • différentes interruptions ;
  • et les registres du processeur.

1 - Les instructions


Le processeur comprend une petite série d'instructions simples, à elles seules incapables de former un programme complet :

  • Calculs basiques, additions, soustractions, multiplications, divisions, incrémentations(+1), décrémentations(-1)...
  • Comparaisons entre deux nombres
  • Manipulations de caractères, chaînes de caractères
  • Et autres...
Il est clair que ces quelques instructions ne pourraient pas combler tous les besoins d'un programme. C'est pour cela que le processeur met à notre disposition des interruptions et des registres.


2 - Les interruptions


L'ordinateur met à notre service les interruptions. Ce sont des fonctions telles qu'afficher un pixel, un caractère, envoyer des données à un des périphériques (USB, imprimante...). Il y'a deux groupes d'interruptions :

Les interruptions sont définies par des nombres hexadécimaux, que nous verrons plus tard. Le "h" à la fin des numéros repaire un nombre hexadécimal.

Interruptions
Groupe
01h à 09h Interruptions matérielles générées par les différentes cartes d'extensions et circuits auxiliaires d'un PC
10h à 20h Interruptions du BIOS : un ensemble de fonctions, contenues dans la mémoire morte (ROM) de la carte mère servant à faire des opérations basiques (écrire un caractère à l'écran, lire un secteur sur un disque, etc...)
21h à 27h Interruptions que le DOS, présent dans les PC, met à la disposition des programmes lors de leurs fonctionnement (appelé DOS API : Interface de Programmation d'Application). DOS met à notre disposition de nombreuses interruptions.
Les autres Après on trouve des interruptions plus spécialisées comme par exemple l'interruption 33h qui contient les Fonctions Gestionnaires de souris ...

Nous verrons le fonctionnement de ces interruptions plus en détail par la suite.

3 - Les registres


Le processeur contient de nombreux registres. Ce sont en quelque sorte des cases pouvant contenir un nombre. Ils ont plusieurs utilités. Ils peuvent servir à faire des opérations simplement, mais aussi à informer sur le déroulement d'un programme (emplacement du code, instructions en cours...), d'un calcul (retenu, plus grand que...). Voici les différents types de registres (nous les verrons plus en détail par la suite) :

TypeExemples
Rôles
Les registres
généraux
AX, BX,
CX, DX
Ils servent pour faire des opérations(AX, BX, DX), définir un nombre de boucles (CX) le cas échéant. Ils servent aussi à indiquer les attributs pour les interruptions
Registres de segment et d'offset
CS, DS,
ES, FS,
GS, SS...
Ils indiquent l'emplacement du code, de la pile, des données... A l'aide du segment sur lesquels ils se trouvent et des offsets (ce que nous expliquerons plus tard)
Les registres

FLAG
AF, CF, DR, IF,

OF, PF, SF...
Ils indiquent les informations suite à un calcul,

ou une comparaison

vendredi 28 août 2009

Partie I - Chapitre 2 : Utilisation d'un émulateur 80x86 et Généralités de syntaxe

Maintenant que nous avons situé et découvert le langage assembleur, nous allons étudier la syntaxe de l'assembleur qui n'est pas vraiment compliquée comme vous pourrez le voir. La mise en forme du programme reste relativement libre. Tout est à peu près permis tant qu'il n'y a qu'une seule instruction par ligne. Nous allons donc voir les principales caractéristiques syntaxiques de l'assembleur. Ensuite nous découvrirons le programme qui nous permettra de créer des fichiers exécutables à partir du code, ainsi que la fonction émulateur (que nous définirons) de ce logiciel.

Sommaire du chapitre :

  • Émulateur 80x86
  • Forme et syntaxe de l'assembleur


A - Émulateur 80x86




Comme nous l'avons vu précédemment l'assembleur exige une transposition en chiffres binaires par un interpréteur pour être compris par le processeur. Il en existe plusieurs dont certains très connus tels que : NASM, MASM. Nous allons plutôt nous pencher sur un émulateur.


Mais qu'est-ce qu'un émulateur ?

Un émulateur (asm) est un logiciel qui recrée les actions et les réactions d'un ordinateur lors de l'exécution d'un programme, dans notre cas, écrit en assembleur.
L'émulateur dégage certains avantages. Il permet une correction beaucoup plus facile. Il donne un aperçu en temps réel et indique la valeur des différents registres au cours de l'exécution...
Il s'agit d'un instrument très pratique.
Nous allons utiliser Emu8086.


Pourquoi ce choix ?

- Tout d'abord il est très simple d'utilisation et très pratique. En effet il comporte notamment une coloration syntaxique très claire.

- Il présente de nombreux exemples qui illustrent certains points tels que les fonctions de l'interruption 21h sur les fichiers, avec : ouverture, lecture, écriture, fermeture.

- Il possède de nombreux outils comme un convertisseur très évolué.

- Il y a beaucoup d'options lors de l'émulation :
  • instruction par instruction ;
  • évolution des variables et des registres au cours du programme.

1 - Première approche


Tout d'abord, vous devez télécharger le fichier "emulator-demo.zip" à l'adresse suivante :

Télécharger


Enregistrez le fichier sur votre ordinateur.

Vous devez ensuite extraire le dossier à l'aide de WinRar, si vous l'avez, sinon vous pouvez faire appel à un logiciel gratuit qui fonctionne très bien.
- UltimateZip : Télécharger


Une fois le dossier extrait, nous trouvons trois fichiers. Nous allons exécuter : "setup.exe".
Là, je vous laisse entre les mains du processus d'installation...

...Une fois le logiciel installé, lorsque vous le lancez, cette page apparaît :

Image utilisateur


Nous ne sommes pas encore sur le point de nous en servir, car l'assembleur demande beaucoup de bases et de concepts avant de pouvoir faire de simples programmes. Cependant, vous pourrez toujours essayer les petits exemples que nous verrons, en les tapant directement, puis en lançant l'émulation, sans se référer à la mise en forme particulière du programme en assembleur.

Revenons à notre page de départ. Lorsque vous voudrez écrire un programme, ou un simple bout de code, il vous suffira de cliquer sur :

Image utilisateur


C'est ensuite cette page qui s'affiche :

Image utilisateur


Cette page sert à définir les caractéristiques du fichier que vous allez écrire.

Effectivement, il existe différents types de fichiers écrits en assembleur. Nous verrons les deux principaux dans la troisième partie : *.exe et *.com . Nous laisserons les deux autres de côtés. Pour tous nos exemples, les deux types de fichiers pourront être utilisés. Le plus utilisé restera du moins le *.exe .

Vous pourrez néanmoins aussi choisir :

Image utilisateur


Qui vous fournira un espace de travail vierge, si ce n'est mieux.

L'option que vous aurez choisie vous fera déboucher sur la page principale.

2 - Les fonctions de cet émulateur


La barre d'outils illustrée ci-dessous consistera en votre poste de travail principal.

Image utilisateur


Les fonctions sont relativement explicites et simples d'utilisation. Je vais ici simplement détailler les fonctions qui concernent l'assemblage.

>/tr>
Image utilisateur
Permet de compiler le code en créant un fichier exécutable que vous pouvez enregistrer à l'endroit de votre choix.

Image utilisateur
Produit une émulation du code que vous avez tapé avec une représentation de l'écran de l'ordinateur dans une fenêtre et le code analysé dans une autre.

3 - L'émulation


Lorsque vous cliquez sur "emulate", c'est cette fenêtre qui s'ouvre :

Image utilisateur





Image utilisateurPermet de charger une nouvelle source *.asm pour l'émuler.

Image utilisateurPermet de charger la source à nouveau pour recommencer l'émulation.

Image utilisateurPermet, lorsqu'elle est accessible, d'aller à l'instruction précédente lors de l'émulation instruction par instruction.


Image utilisateur
Permet d'exécuter l'instruction en cours, et de passer à la suivante.

Image utilisateurPermet d'effectuer une émulation de la totalité du code, ce sera la fonction la plus utilisée.

Image utilisateurPermet d'affecter un temps d'attente entre l'exécution de chaque fonction.

La partie à gauche nommée registers indique l'évolution des registres au cours de l'exécution.

Ensuite nous trouvons les différents boutons situés en bas à droite :

Image utilisateurFait apparaître la fenêtre représentant l'écran lors de l'émulation qui apparaît déjà (par défaut).

Image utilisateurFait apparaître une page contenant la source, elle aussi déjà présente par défaut.

Image utilisateurFait revenir à l'état initial, avant l'exécution du code.

Image utilisateurAffiche la fenêtre suivante :
Image utilisateur

Qui rassemble toutes les variables définies dans le programme ainsi que leur valeur actuelle.

Image utilisateurFait apparaître une fenêtre représentant les valeurs actuellement sur la pile.

Image utilisateurFait apparaître une fenêtre indiquant la valeur actuelle des registres FLAG.

Nous laisserons de côté pour le moment :

Image utilisateur

Image utilisateur


B - Forme et syntaxe de l'assembleur


Majuscules

Il est très important de savoir que l'assembleur NE FAIT PAS ATTENTION A LA CASE..

En effet :

TotO = tOTo = TOTO = toto.

Exemple :

Code Assembler : Minuscules
1
2
3
4
5
6
7
8

jmp debut 
msg db "Hello, World$"
    
début :

mov dx, offset msg
mov ah, 9
int 21h

Ce petit programme qui permet d'afficher "Hello, World" ("$" : permettant repairer la fin de la chaîne de caractères), est le même que celui-ci :

Code Assembler : Majuscules
1
2
3
4
5
6
7
8

JMP debut 
msg DB "Hello, World$"
    
début :

MOV DX, OFFSET msg
MOV AH, 9
INT 21h

Je conseillerais d'écrire toutes les instructions en majuscule et d'écrire toutes les variables en minuscule comme sur le deuxième exemple. Cela permet de mieux se repérer.


Les commentaires

Les commentaires sont TRES IMPORTANTS, ils indiquent des explications sur les suites d'instructions concernées. Vous comprendrez vite que les programmes en assembleur deviennent rapidement très longs et qu'il est dur de s'y repérer. C'est pour cela qu'il est très important de commenter les étapes du code. Vous remarquerez que les relectures et les corrections deviennent beaucoup plus faciles. Je reconnais très bien que les commentaires sont fastidieux mais ils sont très importants.

Un point-virgule annonce le début d'un commentaire.

Exemple :

Code Assembler : Commentaires
1
2

Instruction ; Commentaire 1
Instruction ; Commentaire 2

Voici l'exemple d'un code complètement commenté :

Code Assembler
1
2
3
4
5
6
7
8

JMP debut          ; Aller à 'début'
 
msg    db "..."    ; Définit la chaîne de caractères à afficher
 
debut:
MOV DX, offset msg1 ; précise l'emplacement de la chaîne de caractères
MOV AH, 9           ; Appelle la 9ème sous - fonction
INT 21h             ; De l'interruption 21h(hexadécimal) du DOS


Le retour à la ligne

En assembleur il ne peut y avoir qu'une seule instruction par ligne :

Exemple :

Code Assembler
1
2

MOV AX, 5  ; Déplacer 5 dans AX 
MOV CX, AX ; Déplacer AX dans CX

Nous ne pouvons pas écrire :

Code Assembler
1
MOV AX, 5    MOV CX, AX

De plus dans un code nous pouvons sauter autant de lignes que nous le souhaitons. Je vous conseille donc de bien espacer les différentes parties de code qui fonctionnent ensemble. Par exemple voici un code pour tracer un pixel :

Code Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

; Partie sauvegarde des valeurs des registres :
PUSH CX  ; Sauvegarde le contenu de CX
PUSH DX  ; Sauvegarde le contenu de DX
PUSH AX  ; Sauvegarde le contenu de AX
 
; Appel de la fonction impliquant la modification 
; des registres sauvegardés au préalable
MOV CX, [BP + 4] ; Récupère la valeur de X 
MOV DX, [BP + 4] ; Récupère la valeur de Y 
MOV AL, 15  ; Numéro de la couleur
MOV AH, 0ch ; Sous-fonction 0C en hexadécimal
INT 10h     ; Fonction 10 en héxadécimal
 
; Partie réstoration des registres :
POP AX  ; Restaure le contenu de AX
POP DX  ; Restaure le contenu de DX
POP CX  ; Restaure le contenu de CX

Enfin, il est TRÈS IMPORTANT d'aller à la ligne à la fin de la dernière instruction du programme pour qu'elle soit prise en compte par l'interpréteur.


Les guillemets et virgule ou point

  • Les guillemets


  • Dans ce langage vous devez savoir que l'apostrophe (') est identique aux guillemets (") donc "a" = 'a', après c'est une question de goût.

    Nous pouvons donc retrouver les deux cas :

    Code Assembler
    1

    msg DB 'Bonjour$'

    Ou :

    Code Assembler
    1

    msg DB "Bonjour$"

    Qui sont au final équivalents.


  • Point ou virgule


  • Il est important de savoir que de manière générale, l'ordinateur utilise le point pour présenter les nombres décimaux. On n'écrira donc pas 2,5 mais 2.5.

    La virgule sert à séparer deux opérandes dans une instruction.


    Espacement

    Au même titre que les retours à la ligne, vous pouvez écrire autant d'espaces que vous voulez. Que se soit devant l'instruction, après ou encore entre les opérandes.

    Exemple :

    Code Assembler : Espacement
    1
    2

    MOV    AX,    2     ; Déplace 2 dans AX
    ADD    AX,    2     ; Ajoute 2 à AX

    est équivalent à :

    Code Assembler : Espacement
    1
    2

    MOV AX, 2 ; Déplace 2 dans AX
    ADD AX, 2 ; Ajoute 2 à AX

    Par contre il est IMPOSSIBLE de coller les instructions et les opérandes. On ne peut donc pas rencontrer un code du type :

    Code Assembler
    1
    MOVCX,AX

    Mais évidemment :

    Code Assembler
    1
    MOV CX,AX