Comment programmer en très bas niveau ? (Hexadécimal)

Marre des compilateurs ? Marre de la grammaire ? Envie de parler directement à l'oreille de votre processeur ? Revenez aux sources ! Je vous présente dans l'arbre des connaissances inutiles, la programmation en très bas niveau !


Il existe un mythe parmi les plus endurcis des programmeurs, un Eldorado, où on programme avec des chiffres et des lettres... Des suites sans fin de A, de F, de 5 ou encore de 3 qui se suivent, et qui aboutissent finalement à un objectif commun, envoyer des instruction particulières à une machine statique pour qu'elle s'anime, et réalise un miracle que l'on nomme programme.
 
 Tout le monde a pensé aux pauvres pionniers de la programmation qui codaient avec des 0 et des 1, et certains d'entre vous se sont attaqués à l'assembleur, pour essayer de ressembler un peu plus à ces génies... Mais non, on ne va pas faire de l'obscure language bas niveau où on va potasser un gros pâté de théorie avant de pondre le moindre code...
 
 Ici, on va parler très bas niveau, et on va essayer d'alléger au maximum (au début) la partie théorie, et pratiquer un maximum cette chose qu'on appelle l'Hexadécimal...
 
 Allez petits zéros ! Explorons cette nouvelle branche de l'arbre des connaissances inutiles ! Je vous le dit : Vous aller coder en Hexadécimal !
 
 (Aligné sur la syntaxe du Site du Zéro)
 
 Configuration recommandée :
 

>>Partie 1 : Introduction au très bas niveau.<<

Un rapide petit topo le temps d'exposer la situation, et ainsi arriver toutes les clés en mains dans le feu de l'action ! 
 
 C'est là qu'on va parler le plus des entrailles de votre machine, comment elle marche et comment on peut lui parler. Pour certains, ils trouveront que c'est un peu mince... Mais j'évite un peu les pavés, on reparlera avec des termes techniques compliqués plus tard. 

Le très bas niveau, cet incompris.

Qu'est ce que c'est un niveau ? Hein ? Et qu'est ce que c'est un bas ? Et un très ? Stop les question ! 
 (enfin... c'est ce que je vais essayer de vous faire croire)

Echelle des niveaux, de bas en haut.

Comme je l'ai déjà dit, ce tutoriel aura pour but du vous introduire à la programmation très bas niveau. Enfin, normalement, ça n'existe pas, il n'y a que Haut et Bas niveau, mais j'ai fais quelques distinctions personnelles pour avoir une meilleur représentation.
 Bon, une petite échelle des niveaux :
 
 

  • Interpréteur de commande (type Ms-Dos ou Goto++) --> Très Haut Niveau = Les instructions sont interprétés en temps réel par un programme dont les instructions ne sont pas compréhensible directement par le processeur.
  • Programme en C/C++ et autres  (Type Word ou Jeu vidéo) --> Haut niveau = Les instructions ne sont pas compréhensible directement par le processeur, c'est à ce niveau qu'il y a distinction entre un programme qui tourne sous Linux et un autre qui tourne sur Windows.
  • Programme en Assembleur, ou Asm  (Noyau Windows ou Linux) --> Bas niveau = Les instructions sont directement compréhensible par le processeur après compilation (enfin, assemblage).
  •  Programme Brut (Tout, en fait) --> Très bas niveau = Plus de compilation, il n'existe que des 0 et des 1. On utilise presque plus ce niveau car il est très très abstrait...

 
 L'idée qu'il faut ressortir de cette échelle est que le bas niveau et le très bas niveau sont très proches du fonctionnement de votre processeur. Il en découle plusieurs choses, comme qu'il y a autant de langages bas/très bas niveau que de type de processeurs, ou encore que les boucles, les fonctions et les variables, tel qu'elles sont d'habitude en haut niveau n'existent plus en bas niveau. En très bas niveau, si on résume, on a :
 

  • Définir un des 2 types de variables
  • Appeler un protocole

 Comme quoi, c'est un peu de la programmation "light", ce qui fait que pour juste écrire un hello word à l'écran on va devoir déployer une usine à gaz. 
 
 
 Par contre, bien qu'il y a cet handicap, le très bas niveau est beaucoup beaucoup plus rapide à exécuter... 
 (En comparaison avec un language assez haut niveau comme le Visual Basic, il est 30000 fois plus rapide à exécuter ! (un ordre de grandeur))
 D'où vient cet différence ? C'est le trajet d'une bête instruction, qui parfois passe par 3 fonctions différentes, le système d'exploitation, son noyau et le DOS avant d'arriver jusqu'à votre processeur.

Quelques différences entre les différents niveaux.

Vous commencez à saisir ? Bon, voyons de plus près le trajet de l'instruction :
 

  • Très Haut Niveau = Fichier d'instruction à interpréter --> Programme --> Système d'exploitation -(DOS)-> Processeur
  •  Haut Niveau = Programme --> Système d'exploitation -(DOS)-> Processeur
  •  Bas Niveau = Programme -(DOS)-> Processeur
  •  Très bas niveau = Programme -(DOS)-> Processeur

 Ainsi, c'est là qu'on voit pourquoi le très bas niveau est beaucoup plus efficace que les langages de haut niveau, qui font des détours et qui perdent énormément en performance... Mais alors, pourquoi avoir créer le Haut niveau ?
 
 Pour saisir la véritable utilité de l'introduction du Haut niveau, voyons au niveau de la compilation...
 Qu'est-ce la compilation ? Dans un programme, il y a le "code", qui ressemble à ça :
 "If (tata == toto) {printf ("toto est chez lui\n" ; return} "
 
 Et le programme lui-même, qui a été compilé et convertit en instructions plus simple à comprendre (pour une machine, bien sûr  ), et ça ressemble à ça :
 "¾6ÊG#=›‡D̃ÀܤŸÌÒFIåÁRŒJËÄØj"
 
 Voici un tableau comparatif :
 

  • Très Haut Niveau = Pas de compilation. Les commandes sont directement lues par un programme.
     Code "set /P kuku=" et Programme "set /P kuku=".
  •  Haut Niveau  = Compilation obligatoire.
     Code "printf ("yukulélé" ); " et Programme "G#=›‡D".
  •  Bas Niveau = Compilation (ou assemblage) obligatoire.
     Code "push AX" et Programme "G#=›‡D".
  •  Très bas niveau = Pas de compilation.
     Code "G#=›‡D" et Programme "G#=›‡D".

 
 Vous comprenez pourquoi des esprits tordus ont inventés le Haut niveau ? D'ailleurs, si vous regardez bien, le Très Haut et le Très Bas ont une différence par rapport aux autres, il n'ont pas de compilation. 
 
 Et vous voyez le code en très bas niveau ? C'est ça qu'on va apprendre à maîtriser ! 
 Ne partez pas ! Les programmes brut du très bas niveau ont trois façon de se lire :
 

  •  En caractère ASCII : G#=›‡D
  •  En Hexadécimal : B901
  •  En Binaire : 10111010

 
 On va ici étudier qu'en Hexadécimal ( Ouuuuf !  ), le binaire étant peu intéressant en soi (et peu compréhensible  ).Et on va éviter les caractères ASCII, à moins que vous avez un clavier avec 256 touches et un cerveau mal-formé au point de vouloir essayer.

Mais, et la comptabilité entre les processeurs ?

Je vais vous re-dire un truc :
 Il y a autant de langages très bas niveau que de processeurs !
 
 Et encore ! Entre l'architecture (la façon dont on interprète), le fabricant (Intel, Athlon, Pentium...etc) et l'endianness, on peut avoir près de 50 langages différents ! 
 (Sur un ordinateur, car une calculette ou un GPS ne parle pas pareil.  )
 
 Ben oui, sauf que je suis bon et généreux, et on va étudier ici sur un processeur qui date de 30 ans, un vieux truc en 16 bit réel x86 en Little-Endian.
 
 En théorie, le votre ayant 30 ans d'avance, n'importe quel processeur un peu standardisé comprendra absolument toutes nos commandes. Dans le cas où vous avez un doute, voici la référence de ce tutoriel :
 
 Processeur Intel Core2 CPU T5500 cadencé à 1.66Ghz en 32 bit d'architecture x86 et un endianness en Little-endian.
 
 C'est une configuration de processeur très courante (x86 + 32bit + Little endian) et présent dans la plupart des ordinateurs.
 
 Si vous essayez sur un ordinateur étrange ou encore sur un téléphone portable, ça pourrait poser problème. Mais normalement, vous n'aurez aucun problème.
 
 (Enfin, c'est là la limite entre la théorie et la pratique  )Vous commencez à comprendre ? Là, on a juste survolé le fonctionnement global de votre processeur. Maintenant, on va approfondir en parlant du personnage principal, le vrai, l'original, He-xa-dé-ci-maaaaaaal ! 

L'Hexadécimal, le cauchemar de Darwin.

Attention ! Lisez attentivement ! On y passe rapidement et si vous n'avez pas comprit, vous serez perdus ! 
 
 Mais vous savez, quand on programme en haut ou en bas niveau, savoir compter en hexadécimal, c'est un peu de la culture générale. 

L'Hexadecimal, l'art de compter avec des lettres.

La Base de 16, l'Hexadécimal

 
 Donc, qu'est ce que l'hexadécimal ? C'est une base, nous, on compte en base de 10, le décimal :
 
 Chiffres : 0123456789
 
 Nombres : 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 
 Et l'hexadécimal, inventé par des esprits tordus, est une base de 16 :
 
 Chiffres : 0123456789ABCDEF
 
 Nombres : 0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11 12 13 14
 Ne partez pas, l'hexadécimal n'est pas méchant, il est juste différent.
 

Les Conversions 1.0 !

 Maintenant, voyons comment passer d'une base à l'autre :
 
 C'est très facile !
 

  • 1) Démarrer --> Tous les programmes --> Accessoires --> Calculatrice
  • 2) Maintenant que vous avez ouvert la calculatrice, --> Affichage --> Scientifique
  • 3) Entrez une valeur en décimal, et pour changer de base, cliquez sur "Hex", "Déc", "Oct" ou "Bin".

Pour les curieux qui n'ont pas peur des conversion, voici des liens complémentaires qui pourront vous l'apprendre mieux que moi :
 
 Passer du décimal vers une autre base (Wikipédia)
 Passer d'une base quelconque à une autre (Wikipédia)
 Un tutoriel de Dentuk plein de bon sens (Site du Zéro !)

L'éditeur Hexadécimal, ami pour la vie

Téléchargement et utilisation de EditHexa.

 On va travailler en Hexadécimal, donc, il va vous falloir un éditeur hexadécimal :
 
 Le must : EditHexa 7.8 !
 
 Bon, voyons comment s'en servir :
 
 1. Vous ouvrez le logiciel, normalement, vous avez un écran blanc avec pleins de gros boutons en haut. Cliquez sur la feuille cornée pour faire un nouveau fichier.
 2. Placez le sur le bureau, appelez-le "Testeur.com" et mettez lui une taille de 256 (on prend de la marge...).
 3. Faites deux clic sur un "00", voilà, maintenant, vous pouvez écrire en Hexadécimal !
 
 C'est bon ! Vous savez tout !
 

Votre premier TP !

 On va faire un petit TP ! Placez vous sur le premier octet (case), et écrivez cecin:
 
 BA0F01B409CD21B408CD21B44CCD214875676820212054752061732072657573736973202124
 
 Bon, avant que vous mettiez deux heures à tout copier , voici une astuce :
 
 1. Allez dans "outils avancé"
 2. Ajouter une séquence
 3. "Offset en hexadécimal = 0", puis "Emplacement = écraser le fichier original"
 4. Collez votre séquence dans le champ central, puis validez !
 
 Normalement, vous aurez toute votre séquence commençant tout en haut à droite et fessant 2 lignes et demi. Allez dans "outils avancés", enregistrer, et écrasez l'original.
 
 Allez sur votre bureau et exécutez "Testeur.com". (Appuyez sur une touche pour le terminer.
 
 Maintenant, regardez à droite, sur votre fichier mais en caractères normaux... MAGIE !  Que voyez vous ?
 
 Allez, un peu d'aide :
 
 º´ Í!´Í!´LÍ!Hugh ! Tu as reussis !$
 
 Ahah ! Vous voyez, votre chaîne de caractère a été conservée ! 
 
 Ainsi, outil pratique, lorsque je vous donnerais des corrigés d'exercices, vous pourrez directement insérer votre code. 
 
 D'ailleurs, saisissez aussi l'utilité du parallélisme entre l'Hexadécimal à droite et le texte à gauche :
 On pourra directement écrire notre chaîne de caractère ! 
 (Au lieu de faire une conversion laborieuse pour chaque caractèreRetenez surtout une chose :
 Gardez sous la main votre fidèle calculatrice Windows !

>>Partie 2 : Bâptême du feu : Le Hello Word<<

Souquez ferme moussaillon ! Voici la tempête qui s'annonce !
 
 Tout à coup, surgissant des eaux tel un kraken des temps anciens, le terrible Hello Word vous attaque !
 (Heureusement que Sbirematqui arrive à la fin ! )
 
 Plus sérieusement, voici une introduction digne de ce nom :
 L'exercice du "Hello Word" est l'exercice de base par excellence !  
Il consiste à afficher une chaîne de caractère à l'écran, un
 "Hello Word". Le but de cette partie sera d'introduire suffisamment de bases pour arriver à vous mener à réussir cet exercice, voir même en faire un jeu très très sommaire.
 Il est normalement très simple pour des programmes haut niveau, mais là, on est pas en haut, on est en bas.   
(Et c'est très très dur, en bas...  wink

Les registres - Première épreuve...

Bon, on attaque l'entrée, les registres seront tout le temps présent dans vos programmes, normal, c'est l'essentiel même du bas niveau.
 
 Préparez vous ! Cette journée sera une journée avec un soleil rouge !

Les registres, variables de base...

Bon, déjà, qu'est ce que une variable ?
 Prenons un exemple :
 
 Tous les matins, je mange 100g de céréales.
 
 On pourrait dire :
 
 Prenons un nombre noté Ma (pour masse)puis définissons Ma à 100g.
 
 Tous les matins, je mange Ma de céréales.
 
 Quelle utilité ? Vous allez voir :
 
 Tous les matins, si il fait beau je mangerais 100g de céréales, si il fait mauvais je mangerais 120g de céréales.
 
 Ou sinon :
 
 Prenons un nombre noté Ma (pour masse) puis définissons Ma à 100g.
 
 Si il fait mauvais, j'ajoute 20g à Ma.
 
 Tous les matins, je mange Ma de céréales.
 
 Voilà une bonne idée de ce que c'est une variable : Une représentation d'une valeur qui peut changer. Dans notre petit monde du Bas niveau, il existe plusieurs types de variable :
 
 -Les registres, prédéfinis par 2 lettres (enfin, là, on ne travaille qu'avec 2 lettres) seulement et très rapide pour l'ordinateur à utiliser. Il sont stockés sur des petites mémoires directement dans le processeur. Ils sont assez étroits, ils ne contient que 16bits, c'est à dire un nombre hexadécimal à 4 chiffres, donc de 0 à 65536 dans notre bon vieux système décimal.
 
 -Les adresses, c'est tout simplement une adresse d'un point précis dans la mémoire vive. Ce point fait 2 nombre de 4 chiffres en hexadécimal (Nous travaillons ici en 16 bit, des adresses de type ????x????, il existe actuellement des processeurs plus évolués tournant en 32 ou 64bit), donc de 0x0 à 65536x65536, et la mémoire est plus lente à accéder... L'intérêt ? Très simple ! Si on connaît le point A et B, on peut savoir toutes les coordonnées des points entre A et B ! Donc beaucoup plus volumineux qu'un simple registre...
 
 On ne détaillera ici, pour notre hello word, que les plus essentiels, les registres dits "généraux" :
 
 

  • AX = L'Accumulateur, il sert à manipuler des nombres et autres. Il sert aussi d'entrée et de sortie pour une commande spéchiale. Il est très utilisé, souvenez-vous en !
  • CX = Le Compteur, il compte le nombre de saut, sert surtout aux boucles. Enfin, vous pouvez vous en servir pour stocker quelque chose, mais c'est pas idéal.
  • DX = Les Données, indique une adresse ou stock un chiffre, il stocke des données.
  • BX = La Base, pour l'instant, on s'en fout, il nous sert à rien (pour l'instant... ).

 
 Bon, voilà les généraux... C'est un peu vague, mais pour l'instant, ça suffira. Juste une dernière chose, chaque registre général est composé de deux parties, la partie haute et la partie basse, de chacune de 2 chiffres hexadécimal. Ainsi, pour de petits chiffres, on pourra stocker deux chiffres. Pour accéder à ces mini-registres (ou sous-registre), on va remplacer le X par un H (Hight, partie haute) ou par un L (Low, partie basse). Ainsi, on aura 4 registres généraux, et 8 sous-registres :
 
 AX, BX, CX, DX
 
 AH, AL, BH, BL, CH, CL, DH, DL
 
 Bon, trêve de plaisanteries, passons à la pratique des registres !

Définir un registre - Les valeurs

Aïe ! Et là, c'est le drame ! Nous allons commencer à voir des instructions en Hexadécimal, et là ça se corse...
 
 Pour définir un registre à une valeur précise, voici comment ça se goupille :
 
 La commande de définition d'un registre général ou d'un sous registre général est la suivante : le B
 
 Puis, associé au B, on a le registre/sous-registre qu'on veut définir, sauf qu'ils sont sous forme Hexadécimale, voici la correspondance :
 
 0 = AL
 
 1 = CL
 
 2 = DL
 
 3 = BL
 
 4 = AH
 
 5 = CH
 
 6 = DH
 
 7 = BH
 
 8 = AX
 
 9 = CX
 
 A = DX
 
 B = BX
 
 C'est pas compliqué ! Ils sont dans l'ordre ! Bon, maintenant qu'on sait comment choisir son registre et utiliser la fonction, voyons comment entrer la valeur...
 Pour cela, prenons un exemple, définissons le registre DH à B4 (Bah oui, toutes les valeurs sont en hexa, donc, munissez vous de la calculette windows en mode scientifique !). On commence par B, puis on accole le 6, et on met la valeur :
 
 B6 B4
 
 Voilà ! Pas compliqué !
 Maintenant, passons à la vitesse supérieur en définissant AX à 01FA (ne pas oublier que les X sont des registres "entiers", qui font 2 paquets de 2 chiffre). On prend le B, on met le 8, et on a :
 
 B8 FA 01
 
 AHHHH ! (ou ALLLL !) Quel est ce sort ? Pour 01FA (ou 506 en décimal), on a FA01 ! Normal, car il ne faut pas oublier comment votre ordinateur l'exécute votre programme (son endianness) :
 
 B8 FA 01 --> Il lit la commande qui lui dit "définis AX avec les chiffres qui vont suivre)
 
 B8 FA 01 --> Il lit la première partie, et là, il met AL à FA.
 
 B8 FA 01  --> Il lit la seconde partie, et là, il met AH à 01.
 
 Encore sous le choc de la terrible vérité, vous vacillez. Définir un registre entier est comme définir ses deux petits sous-registres ! (Mais ça prend moins de place)

Définir un registre - Un autre registre

Attention, idée sadique qui germe dans ma tête :
 
 Maintenant qu'on sait définir un registre à une valeur fixe, pourquoi pas le définir à une variable, à un autre registre ?
 
 Avant de se lancer, voyons ce que nous allons faire :
Voici ce qu'on a fait juqu'à maintenant :
 1 >> AX, alors AX = 1
 Et voici ce qu'on va apprendre ici :
 1 >> AX puis AX >> DX, alors on aura AX = 1 et DX = AX (donc 1)
 Ce sera toujours une commande, et elle se consitue ici de deux parties :
 
 -De "89" , qui dit à l'ordinateur "prend le registre qui arrive (AX dans notre exemple) et met le dans le suivant (DX dans notre exemple)"
 
 -Et de ??, représentant nos deux registres AX et DX, dont la syntaxe est plus compliquée...
 
 Déjà, à cause de l'endianness, la définition d'un registre par un autre registre est "à l'envers" (pour définir DX à AX, on écrit "prend AX et met le dans DX"wink, mais en plus, les correspondance entre la commande hexa et les registres ne sont pas les mêmes... (c'est là que ça devient sadique )
 
 Dans notre ??, le DEUXIÈME chiffre indique le registre qu'on va définir (DX), et le premier le registre qui contient la valeur (AX).
 
 Le premier chiffre (le registre qui contient la valeur, AX) a cette syntaxe :
 
 C pour le registre AX ou CX
 
 D pour le registre DX ou BX
 
 A mais ! C'est pas du jeu ! C'est quoi ce "ou" ?
 
 Vous allez voir avec la syntaxe du deuxième chiffre (le registre qu'on définit, DX) :
 
 0 = AX pour les registres AX et DX
 
 1 = CX pour les registres AX et DX
 
 2 = DX pour les registres AX et DX
 
 3 = BX pour les registres AX et DX
 
 Et :
 
 8 = AX  pour les registres CX et BX
 
 9 = CX pour les registres CX et BX
 
 A = DX pour les registres CX et BX
 
 B =BX pour les registres CX et BX
 
 (C'est un peu complexe, mais je vais donner plus tard un exemple.)
 Et pourquoi ça ? 
 Tout simplement, on compacte et on économise la place, au lieu de faire 256 commandes.
 
 Récapitulons ce que nous venons de voir :
 
 89 C[0-3] (C pour AX ou CX, puisqu'on a 0-3)
 |
 |->On définis un registre à la valeur de AX.
 
 
 89 C[8-B] (C pour AX ou CX, puisqu'on a 8-B)
 |
 |->On définis un registre à la valeur de CX
 
 
 89 D[0-3] (D pour DX ou BX, puisqu'on a 0-3)
 |
 |->On définis un registre à la valeur de DX.
 
 
 89 D[8-B] (D pour DX ou BX, puisqu'on a 8-B)
 |
 |->On définis un registre à la valeur de BX.
 
 Vache hein ?
 
 Pour conclure, voici notre exemple du début en code hexa :
 89 C2
 Traduit en français :
 "Tu prend AX et tu le met dans DX"

Ajouter quelque chose à un registre.

Maintenant, (et oui ! Toujours dans la manipulation des registres) on va voir comment ajouter une valeur à un registre...
Ici, on va plus faire un :
 1 >> AX ou un AX >> DX
 Mais on va additionner une valeur ou un registre :
 AX + 1 >> AX ou DX + AX >> DX
 En gros, on ajouteras quelque chose.
 
 On va aller vite, ce n'est pas d'une complexité absolue ( ).
 

Ajouter une valeur, petite ou grande :

 Syntaxe pour les petites valeurs (pas très important, on retiendra surtout les grandes), de 00 à FF : 83 + C + Registre + Valeur
 
 
 0 = AX
 
 1 = CX
 
 2 = DX
 
 3 = BX
 
 Ce qui fait, que si on veut ajouter 5 au registre CX, on aura : 83 C1 05
 
 Alors, pour ajouter 12 à DX, on fait ? 83 C2 0C (Oui, c'était une question piège )
 
 Maintenant, voyons la syntaxe pour les grandes valeurs, de 0000 à FFFF (Retenez là, on pourra aussi l'utiliser pour les petites)
 On a : 81 + C + Registre + Valeur
 
 (Rappel : La valeur est toujours inversée, donc on aura pour B049 --> 49 B0)
 
 C'est là qu'il y a une exception ! Et oui, regardez la liste suivant où on ajoute 0C1F à tous les registres :
 
 AX --> 05 1F 0C
 
 CX --> 81 C1 1F 0C
 
 DX --> 81 C2 1F 0C
 
 BX --> 81 C3 1F 0C
 
 Ahh ! Oui, pour ajouter une valeur à AX, opération très très courante, on a simplifié pour économiser de la place en remplaçant le 81 C0 par un petit 05 .
 

Ajouter un registre :

 
 Et enfin, pour ajouter un registre à un autre :
 
 Syntaxe : 01 + Code registre
 
 Le code registre est le même que pour définir un registre dans un autre (moins sadique là, non ? ), donc, on a :
 
 01 C[0-3]
 |
 |->Ajoute à un registre la valeur de AX
 
 
 01 C[8-B]
 |
 |->Ajoute à un registre la valeur de CX
 
 
 01 D[0-3]
 |
 |->Ajoute à un registre la valeur de DX
 
 
 01 D[8-B]
 |
 |->Ajoute à un registre la valeur de BX

Un TP sur les registres !

Pour détendre l'atmosphère, on va faire un petit TP qui ne sert strictement à rien.
 
 Donc, écrivez-moi un programme qui définis AX à B0A4, puis on définis tous les registres généraux sur AX. Ajoutez à CX la valeur 1001 et à DX la valeur 51. Ensuite, vous allez ajouter à DX le registre BX, puis à CX le registre DX et enfin à AX le registre CX. Pour finir, vous allez définir DX à la valeur AX.
 
 Question bonus : Quel sera la valeur de DX en fin de programme ?
 
 J'explique d'une autre façon :
 
 AX << B0A4
 CX << AX
 DX << AX
 BX << AX
 
 CX << CX + 1001
 DX << DX + 0051
 
 DX << DX + BX
 CX << CX + DX
 AX << AX + CX
 
 DX << AX
 
 C'est peut-être plus claire pour certains d'entre vous...
 
 Réfléchissez bien ! Ne trichez pas ! Voici la correction, mais ne cédez pas à la facilité !
 

Spoiler (Sélectionnez le texte dans le cadre pointillé pour le faire apparaître)

Je viens de dire de ne pas céder à la facilité !
 Au travail !

 Voici la correction, si t'a bien travaillé :
 

Spoiler (Sélectionnez le texte dans le cadre pointillé pour le faire apparaître)

La solution avec les espaces :
 
 B8 A4 B0 89 C1 89 C2 89 C3 81 C1 01 10 83 C2 51 01 DA 01 D1 01 C8 89 C2
 
 Et celle sans espace, copier-collable (mais pour ce TP, ça ne sert à rien ) :
 
 B8A4B089C189C289C381C1011083C25101DA01D101C889C2
 
 Cette solution marche, est immuable et a été même assemblée en assembleur. Si vous êtes arrivés à ce résultat, je dis bravo ! Vous avez tout compris !
 
 Question bonus pour vérifier si vous savez compter en Hexa :
 
 B04A + B04A + B04A + B04A + 1001 + 51 = 2D2E2
 
 Remarque : Cette valeur n'aurait jamais pu être stockée dans DX, limité à FFFF, ou 65536. La, on atteint 185058 . On aurait eu besoin d'utiliser les registres 32bits, non vus dans ce chapitre.

Premier contact avec l'Hexadécimal, ça choque, hein ? Rassurez vous, le temps de prendre une douche froide, et on repars !

La suite plus loin...

Laissez un commentaire

29 Commentaires

  • Et bé dis donc !

    Quelques erreurs cependant :

    Sbirematqui a écrit :

    Et l'hexadécimal, inventé par des esprits tordus, est une base de 12 :

    Hérétique, l'héxadécimal c'est une base 16 razz

    Sbirematqui a écrit :

    Les adresses, c'est tout simplement une adresse d'un point précis dans la mémoire vive. Ce point fait 2 nombres de 4 chiffres en hexadécimal, donc de 0x0 à 65536x65536

    Il y a 30 de cela ! sourire Aujourd'hui une adresse sur un système 32bit fait 8 chiffres héxa (soit 32 bit) et 16 chiffres sur un système 64bit.

    D'ailleurs il y a d'autres choses factuelles que tu as dites qui dépendent en fait beaucoup de l'architecture matérielle (i.e. du processeur) comme le coup du 01FA/FA01 qui dépend de l'endianess du processeur (est-ce qu'il lit de gauche à droite ou de droite à gauche) ou encore les registres qui sur un proc 64 ne doit plus faire 2 et 2 mais 4 et 4 (là j'avoue que je ne suis pas sûr de moi mais je le verrais bien comme ça).

    Il y a aussi les langages de programmation interprétés qui se situent entre ton très haut niveau et haut niveau.

    Bel effort quand même, c'est indéniable.

  • Premier contact avec l'Hexadécimal, ça choque, hein ? Rassurez vous, le temps de prendre une douche froide, et on repars !

    [Partie 1/2]La mémoire vive - découverte

    Bon, dans la catégorie variables, après les registre, je nomme la mémoire vive !

    Durant cette première partie, nous allons étudier tout d'abord ce que c'est la mémoire, puis les nouveaux registres liés à la mémoire... Et oui ! Quand il en a plus, il y en a encore !

    L'adresse, l'offset et le segment !

    Bon, déjà, posons un principe : La mémoire a été faite par des sadiques pour des masochistes, ça tombe bien, je suis un poil sadique, et vous un poil masochiste...

    Donc, qu'est ce que c'est la mémoire d'un ordinateur... C'est des cases, des millions de petites cases, dans lesquelles il y a 8 petits boutons, les octets. Ainsi, si on regarde avec une loupe dans une case parmi d'autre, on verra un joli "11010100" qui s'approche plus de l'art abstrait en grande quantité. Remarquons qu'avec 8 petits boutons, on peut, en base 2, coder un chiffre allant de 0 à 255. Comme cela était très peu compréhensible, on s'est mis à nommer les "11010100" par des nombres, en base de 16, qui allaient eux aussi de 0 à 255... Vous devinez ? L'hexadécimal ! Donc, le fameux "11010100" donne un simple "D4".

    Ainsi, maintenant, si on regarde dans la mémoire avec une loupe, on verra :

    ...11010100 01101010 10101101 11010110 11111111 01010010...

    Mais on l'écrira :

    ...D4 6A AD D6 FF 52...

    Question maintenant :

    Pour trouver une information dans la mémoire... Comment on fait ?


    Bah ! C'est simple dira l'idiot du village ! On dit le numéro de la case (ou de l'octet) !

    ...D4 6A AD D6 FF 52 ...

    ...25 26 27 28 29 30 ...

    Ah ! Que le monde est bien fait ! Sauf que...

    Comment on fait pour stocker ce nombre ?

    Ben... Dans les registres (de nouveaux registres ) ! Et là, c'est le drame :

    -Les registres sont en hexadécimal...

    -Les registres font de 0000 à FFFF, autrement dit... de 0 à 65535 !

    Le premier point ne nous dérange pas trop, mais le deuxième... Comment fait t'on pour désigner là 948560ème case (octet) ? Autrement dit la E7950ème case (octet) ? On est limité à 65535 (ou FFFF), nan ?

    Oui mon petit zéro...
    L'heure est grave : C'est ainsi qu'on utilisera, pas un, ni trois, mais bien DEUX registres pour stocker l'adresse ! Ainsi, on aura en premier le segment "FFFF" , puis, on aura l'offset "FFFF" ! C'est là que la magie opère... on aura :

    FFFFxFFFF

    Les plus loquaces d'entre vous diront : "Mais alors ! Ça veut dire qu'on a 65536^65536 = 4294967296 cases !"

    Oui, si les segments étaient patients et qu'ils attendaient que le précédant finisse avant de commencer ! En réalité, ce sont de mauvais garçons et ils débutent à chaque fois 16 octets après le début du précédent ! Là, je pense qu'un schéma complet s'impose...

    Attention ! Il n'y a qu'une case (on dira octet à l'avenir) par colonne ! Ainsi, sur ce schéma, on ne voit seulement 34 octets ! De 0 à 33, donc 34... Les numéros indiquent l'offset et chaque ligne représente un segment !

    Remarquez qu'ici, j'ai commencé au segment 3, donc, pour la première adresse, il y a comme synonyme :

    0003x0001

    0002x0010...etc

    De même pour les autres adresse proposés.

    Ce que votre programme en fait !

    Bon, vous avez une image de la mémoire vive dans votre tête, avec des segments qui contiennent chacun 65536 (de 0 à 65535) octets indiqués par l'offset (par rapport au début du segment), on va voir comment votre programme l'utilise normalement...

    Déjà, il y a deux types de programmes : Les COM, et les EXE.

    Nous, dans notre Hello Word, on va utiliser un COM. Voici comment il se goupille :

    On exécute notre COM, on a le DOS qui lui attribue un segment entier (alors que l'EXE à le droit à 4 segments). Puis, le DOS génère un "PSP" (nan, pas la console), qui se chargera des formalités pour pouvoir faire fonctionner votre COM. Ces formalités prennent de la place, 256 octets (100 en hexadécimal), ainsi, on aura diverses informations stockées au début. Ensuite, pour pouvoir lire directement votre programme, il le copie à la suite du PSP, c'est à dire après l'offset 100 (en Hexadécimal). Enfin, il définit les registres de segments et d'offsets à des valeurs par défaut pour un COM.

    Dans votre COM, on utilisera à jamais qu'un unique segment, à tout faire, qui sera composé ainsi :

    [-->PSP<--][-->COM<--][>Données ][->Pile<--] Contenu

    [0000-0100][0100-????][????-FFFF][FFFF-????] Offset

    [0000-0256][0256-????][???-65536][65536-???] (en décimal)

    Bon, maintenant qu'on a éveillé votre curiosité avec la PILE (Plus Importante Literie Épique), nous allons voir ce que c'est cette chose.

    La Pile vue de profil...

    La pile sert à entasser des données, comme des crêpes.
    Imaginons des crêpes de couleur :

    Rouge ou Vert ou Bleu

    On va entasser des crêpes dans cet ordre : Rouge puis Vert puis Bleu puis Rouge puis Rouge puis Vert

    Vert

    Rouge

    Rouge

    Bleu

    Vert

    Rouge

    Et quand on va dépiler nos crêpes, elles arriveront dans cet ordre : Vert puis Rouge puis Rouge puis Bleu puis Vert puis Rouge

    La pile, c'est pareil, on part avec un registre à FFFE (il y a un 0000 en bas de la pile), et quand on empile des données (en partant de la fin du segment pour avoir un maximum de place), on diminue de 2 le registre (FFFE - 2 = FFFC) et on définit l'offset FFFC et FFFD à notre valeur (donc de 2 octet, pour avoir les "4DC8" (4) chiffres hexadécimaux).

    Enfin, quand on dépile, on sort la donnée qui est "sur" la pile et on incrémente le registre de la pile de 2. Ainsi, on pourra stocker pleins de petites adresses et données dans la pile, qui nous servira à sauvegarder des valeurs de registre... etc

    De nouveaux registres dédiées à la mémoire !

    Pour stocker une information, on utilisera la mémoire... Pour l'adresse de l'endroit où on a stocké cette information, on utilise des nouveaux registres... Bon, ici, on va apprendre qui sont ces nouveaux, et comment les manipuler.

    Voici les présentation :
    Les registres de segment :


    • CS = Le segment du code, il indique où est le segment qui contient le code, plus précisément le segment où se trouve la prochaine instruction à éxécuter. Dans un COM, tous les registres de segment sont identiques.
    • DS = Le segment des données, il indique où est le segment qui contient les données de votre programme. Dans un COM, tous les registres de segment sont identiques.
    • ES = Le segment extra, pour manipuler n'importe quel segment en dehors de notre programme. Par contre, attention aux violations de segments appartenant à d'autre programmes...
    • SS = Le segment de la pile (stack), il indique où est le segment qui contient la pile. Dans un COM, tous les registres de segment sont identiques.


    Ces registres ne sont pas des registres d'utilisation courante comme les généraux. On y touchera très très peu, sauf dans certains cas et il faudra savoir exactement ce qu'on fait. Si on le fait au hasard, on peut même planter tout votre ordinateur (et vous attendrez la fin de sa batterie ou une coupure de courant )

    Les registres d'offset :


    • IP = L'offset de la prochaine instruction à exécuter. Associé à CS, il permet de savoir où est la prochaine instruction, aidant grandement notre processeur.
    • SP = L'offset du haut de la pile, il pointe sur l'adresse de la dernière donnée empilée. Associé à SS, il permet de savoir exactement où est le haut de pile.
    • BP = Il est un peu particulier, comme son cousin, BX. On l'utilisera plus tard.
    • SI = Un index, très utile au processeur quand il veut lire une chaîne de caractère sans faire une fastidieuse boucle. Il peut servir à autre chose.
    • DI = Un index, très utile au processeur quand il veut lire une chaîne de caractère sans faire une fastidieuse boucle. Il peut servir à autre chose.


    Il y en a 5, c'est lourd...
    Mais vous allez voir, dans la pratique, ça rentre tout seul.

    A suivreSur ce, nous pouvons attaquer la deuxième partie où nous allons entrer dans le vif du sujet... La manipulation de la mémoire...

    [Partie 2/2]La mémoire vive - Manipulation

    Cette fois-ci, on a finis la théorie sur la mémoire ! On se lance dans la manipulation !

    Dans ce chapitre, on va tout d'abord apprendre à se servir des registres de mémoire. Ensuite, on aura les clés en main, et on s'attaquera à la manipulation directe de la mémoire, puis à celle de la pile.

    Manipulons les registres de mémoire

    Les registres de segment :

     Dis père Sbirematqui, pourquoi tant de registre de mémoire ?
     
     C'est simple, on en a déjà parlé, c'est pour stocker les deux nombres indiquant la position dans la mémoire :
     Le Segment et l'Offset
     
     Ah non ! Pas le segment ! Il est compliqué, il commence pas où on veut !
     
     Attend petit enfant j'ai une surprise pour toi !
     
    Une surprise ? 
     
     Tu te rappelle quand on a dit qu'on avait tous nos segments étaient au même endroit dans un fichier COM ? Ben, on va travailler sur des COM, on aura donc jamais à manipuler ces registres ! (Tant qu'on travaillera dans un COM...)
     
    Youuuupiiiii ! 
     
     Pas trop de réjouissances quand même !
     
     On va quand même voir comment récupérer la valeur de ces registres... c'est à dire, définir un registre général à un registre de segment !
     
     Donc, c'est à peu près le même principe que pour les registres généraux, sauf que comme tous nos registres de segment on la même valeur (ils désignent tous notre segment à tout faire), on aura pas de syntaxe biscornue !
     Ainsi, on ne travaillera qu'avec CS dans cette partie.
     
     D'ailleurs, voici la syntaxe pour définir un registre à CS :
     8C + C + Registre
     
     Et là, je ressort les équivalences :
     8 = AX
     
     9 = CX
     
     A = DX
     
     B = BX
     
     Une batterie d'exemples :
     Mettre CS dans AX : 8C C8
     Mettre CS dans CX : 8C C9
     Mettre CS dans DX : 8C CA
     Mettre CS dans BX : 8C CB
     
     Voilà, vous savez tout ! Pour le moment...
     

    Les registres d'offset

     Bon, là, ça se corse !

    Définir un registre d'offset à une valeur

     On va faire dans l'ordre... Les segments dits "standards", ils sont 4 : (on exclu IP, il est... particulier )
     SP, BP, SI et DI
     
     Vous vous rappelez comment on définit un registre général ?
     Citation : Chapitre sur les registresLa commande de définition d'un registre général ou d'un sous registre général est la suivante : le B
     
     Puis, associé au B, on a le registre/sous-registre qu'on veut définir, sauf qu'ils sont sous forme Hexadécimale, voici la correspondance :
     
     0 = AL
     
     1 = CL
     
     2 = DL
     
     [. . .]
     
     9 = CX
     
     A = DX
     
     B = BX
     
     Ben, on va compléter la liste :
     [. . .]
     
     9 = CX
     
     A = DX
     
     B = BX
     
     C = SP
     
     D = BP
     
     E = SI
     
     F = DI
     
     Très dur, hein ?
     
     Des exemples :
     On définir SP à 7845 = BC 45 78
     On définir DI à 1001 = BF 01 10
     

    Définir un registre d'offset à un autre registre

     
     Je vous laisse deviner... on va compléter la partie "à un autre registre" !
     
     Citation : Chapitre sur les registres - COMPLETE
     Le premier chiffre (le registre qui contient la valeur, AX) a cette syntaxe :
     
     C pour le registre AX ou CX
     
     D pour le registre DX ou BX
     
     E pour le registre SP ou BP
     
     F pour le registre SI ou DI
     
     A mais ! C'est pas du jeu ! C'est quoi ce "ou" ?
     
     Vous allez voir avec la syntaxe du deuxième chiffre (le registre qu'on définit, DX) :
     
     0 = AX pour les registres AX et DX et SP et BP
     
     1 = CX pour les registres AX et DX et SP et BP
     
     2 = DX pour les registres AX et DX et SP et BP
     
     3 = BX pour les registres AX et DX et SP et BP
     
     4 = SP pour les registres AX et DX et SP et BP
     
     5 = BP pour les registres AX et DX et SP et BP
     
     6 = SI pour les registres AX et DX et SP et BP
     
     7 = DI pour les registres AX et DX et SP et BP
     
     
     Et :
     
     8 = AX pour les registres CX et BX et SI et DI
     
     9 = CX pour les registres CX et BX et SI et DI
     
     A = DX pour les registres CX et BX et SI et DI
     
     B = BX pour les registres CX et BX et SI et DI
     
     C = SP pour les registres CX et BX et SI et DI
     
     D = BP pour les registres CX et BX et SI et DI
     
     E = SI pour les registres CX et BX et SI et DI
     
     F = DI pour les registres CX et BX et SI et DI
     
     Ça commence à faire un peu long... Nan ? Mais au moins, ça a le mérite d'être clair !
     
     Je vous fait un petit récapitulatif :
     
    89 C[0-7] (C pour AX ou CX, puisqu'on a 0-7)
     |
     |->On définis un registre à la valeur de AX.
     
     
     89 C[8-F] (C pour AX ou CX, puisqu'on a 8-F)
     |
     |->On définis un registre à la valeur de CX
     
     
     89 D[0-7] (D pour DX ou BX, puisqu'on a 0-7)
     |
     |->On définis un registre à la valeur de DX.
     
     
     89 D[8-F] (D pour DX ou BX, puisqu'on a 8-F)
     |
     |->On définis un registre à la valeur de BX.
     
     --------------------------------------------
     
     89 E[0-7] (E pour SP ou BP, puisqu'on a 0-7)
     |
     |->On définis un registre à la valeur de SP.
     
     89 E[8-F] (E pour SP ou BP, puisqu'on a 8-F)
     |
     |->On définis un registre à la valeur de BP.
     
     
     89 F[0-7] (F pour SI ou DI, puisqu'on a 0-7)
     |
     |->On définis un registre à la valeur de SI
     
     
     89 F[8-F] (F pour SI ou DI, puisqu'on a 8-F)
     |
     |->On définis un registre à la valeur de DI.
     
    On ne verra pas comment ajouter un registre d'offset, puisque la seule chose qui change est le 89 de la commande. Vous avez le code registre, le plus important !
     

    Le cas du registre IP

     
     Pour finir, attaquons le cas IP... Il est particulier celui-là, puisqu'il désigne la prochaine instruction à exécuter... Ce rôle fait de lui l'axe de l'exécution d'un programme, et comme il est incrémenté à chaque instruction, il est même très difficile d'y accéder (et on évite les utilisations malveillantes )
     
     Donc, on pourra :
     -Le définir à une valeur
     -Le définir à un registre
     -Le récupérer dans un registre
     
     Bon programme, hein ?
     
     Sauf que, il existe des moyens détournés de faire chacune de ces actions...
     
     Nous allons ici voir comment la récupérer dans un registre. 
     
     Il y a un enchaînement de commandes pour tirer les vers du nez de IP :
     E8 00 00 66 58
     
     Le E8 00 00 réalise un saut en avant sur le code de 00 00 offset (qu'il est c** ), et pour pouvoir revenir en arrière... Il stocke IP (l'offset de la prochaine instruction) !
     Et où est-ce qu'il le stocke ? Dans la pile !
     Le 66 58 dépile la valeur de IP et la met dans le registre correspondant à 8, AX* !
     
     Ne vous occupez pas des détails, pour l'instant, retenez que la fonction E8 00 00 66 58 renvoie l'offset de 66. Donc, pour avoir l'offset final, il faut faire :
     0x0 > E8
     0x1 > 00
     0x2 > 00
     0x3 > 66
     0x4 > 58
     0x5 > 05
     0x6 > 05
     0x7 > 00
     0x8 >
     
     Donc : E8 00 00 66 58 05 05 00
     On obtient l'adresse de 66, et on lui ajoute 5, pour avoir l'IP de la prochaine instruction, en 8
     
    * Votre processeur est en 32 bits (en théorie), donc, le registre IP est sur 32 bits : 0000 0008
     Ainsi, il est peu fiable de mettre un nombre trop grand dans un "petit" registre AX en 16 bits, donc, le code cité ici dépile IP dans la version 32 bits de AX -> EAX
     Ainsi, il définira tout le registre, et définira du même coup la partie basse, AX.
     
     Ce détail est à titre informatif, pour pas que vous vous lancez dans des essais foireux.

    Manipulons la mémoire elle-même !

    Ce que nous allons voir maintenant n'a pas de réelle importance sur le hello word, mais comme on est dans le chapitre "mémoire", on va quand même le travailler !
     

    Rappel :

     
     Pour désigner un octet (une "case"wink, on a besoin de deux nombres :
     

    • Le segment
    • L'offset

     
     Le segment désigne dans quelle partie de la mémoire on se trouve, des sortes de balises posées tous les 16 octets, et l'offset est le décalage en avant par rapport à cette balise.
     
     On le note :
     SSSSrazzPPP
     (En hexadécimal, bien sûr)
     

    Définir un octet en mémoire :

     
     Pour le segment, c'est réglé d'avance, on a nos registres de segment... Mais pour l'offset, il change souvent donc on doit le définir soit manuellement, soit à partir d'un registre d'offset (BP, SI, DI et autres).
     

    A l'aide d'une adresse fixe :

     
     Votre adresse se composera ainsi :
     DSperplexe???
     (On utilisera le registre DS puisqu'on est en COM)
     
     Le début de votre code sera :
     DS:0100
     (Puisqu'on a le PSP au début, de l'offset 0 à 255)
     
     Et la fin de votre segment :
     DS:FFFF
     
     La syntaxe de la commande est la suivante :
     C7 06 + Offset + Valeur
     (Le C7 06 est le "DS:", puis il vient l'offset PPPP, et enfin la valeur qu'on veut lui définir.)
     
     
     Donc, le code suivant met la valeur 45C8 à l'offset 04F5 :
     C7 06 F5 04 C8 45
     
     C'est si simple ?
     Oui, et quand on voudra récupérer la valeur de l'octet d'offset 04F5, on aura un zoli 45C8 à nous attendre !
     
     
     Maintenant, voyons comment mettre un registre en mémoire...
     
     Là, plusieurs facteurs entrent en jeu, et on a un code registre différent ( ), de plus, il faut prendre en compte le segment... Donc, on ne va voir que les 4 généraux, et quand vous allez savoir vous servir de la pile, il n'y aura plus de problèmes...
     
     

    • Syntaxe pour AX (remarquez l'exception, comme pour l'addition) : A3 + Offset
       
       Un exemple, on définis l'octet 12E1 à AX :
       A3 E1 12
       >>Retenez en priorité AX !<<
       -----
    • Syntaxe pour CX : 89 0E + Offset
       
       Un exemple, on définis l'octet 7D33 à CX :
       89 0E 33 7D
       ----
    • Syntaxe pour DX : 89 16 + Offset
       
       Un exemple, on définis l'octet 3741 à DX :
       89 16 41 37
       ----
    • Syntaxe pour BX : 89 1E + Offset
       
       Un exemple, on définis l'octet ABCD à BX :
       89 1E CD AB
       ----

    C'est certes plus compliqué, mais dès que vous saurez utiliser la pile, vous pourrez faire toutes les opérations avec tous les registres en passant par AX...
     

    A l'aide d'un des registres :

     
     Bon, on utilisera généralement que 2 registres d'offset pour l'adressage d'un octet : SI et DI
     Donc, on va voir en priorité ces deux gugus, puis on va voir BP, mais sans détailler le réel fond de sa commande...
     
    Il y a plusieurs façon de définir à partir d'un registre d'offset, mais je vais vous présenter ici la plus simple, avec une variante pour qu'elle puisse s'appliquer à BP.
     
     Donc, la syntaxe pour SI et DI :
     C7 + registre + Valeur
     
     Le registre est définit ainsi, avec DS associé :
     
    SI = 04
     DI = 05
     
     Un exemple d'utilisation (Est-ce nécessaire ?) :
     Définir DSconfusedI à la valeur 5ECA
     C7 04 CA 5E
     
     Donc, si SI vaut 0024, on définira l'octet d'offset 0024 à 5ECA. L'avantage ? C'est simple, on ne peut pas toujours se baser sur une adresse constante, de type ????, et l'utilisation d'adresse variables, type SI ou DI, nous permet, par exemple, d'additionner une liste de nombre stockée en mémoire, de faire des tableaux...etc
     
     Ensuite, on peut aussi utiliser BP, mais lui est un peu particulier, puisque, comme IP ou SP, il est un pointeur. On peut le définir ainsi (même si on ne s'en servira pas tout de suite...) :
     3E C7 46 00 + Valeur
     
     On y reviendra plus tard, pour l'instant, c'est juste pour la culture générale.
     
    Mais, pour définir un octet désigné par un registre d'offset à une valeur contenue dans un registre général ?
     
     J'y viens !
     
     La syntaxe est un poil plus complexe :
     89 + "petit" code registre
     
     (Le "petit code registre" est le petit-frère du grand code registre qu'on a vu précédemment, dans la partie des registres, avec les définitions et additions... )
     
     Et voici un tableau pour le petit code registre :
     

     
     Bon, j'espère que celui-ci est un peu plus clair, et je vais quand même faire 2/3 exemples :
     89 15
     (On définit l'octet DSsourireI à DX)
     
     89 0C
     (On définit l'octet DSconfusedI à CX)
     
     89 14
     (On définit l'octet DSconfusedI à DX)
     
    Vous venez d'achever les connaissances les plus baveuses à savoir, maintenant, on tombe dans un domaine clair avec la suite du chapitre et du tutoriel.
     Il vous est fortement conseillé de re-survoler tout depuis le début de la partie 1, pour ensuite passer à notre petit 3, bien plus amusant, la pile !

    Empilons et Dépilons au Marteau-Pilon !

     Cette partie n'est pas terminée...
     

    ---------

     Bon, après, on va attaquer un long marathon où on va voir entre autres les conditions, les boucles, et toutes les choses tordues qui vous feront suer des litres de jus de cervelle ! C'est un début sur la longue quête de la programmation en Hexadécimal...
     
     Ce tutoriel est assez difficile à concevoir, de part l'équilibre que j'essaye de garder entre trop et pas assez de théorie, très barbante quand on passe en bas/très bas niveau.
     
     To be continues...

  • Windows et GNU/Linux (ou autre système d'exploitations) sont en langage de haut niveau, seul quelques parties sont en assembleur.

    Heureusement d'ailleurs, sinon les capacités de portages (mise en place de compatibilité avec d'autre architectures processeur) serait difficile.

    Et programmer en binaire, je ne suis pas sûr que cela se fasse, ce serait comme prendre une hache pour supprimer les pages qui ne t'intéressent pas d'un livre.

  • Une grande partie d'un système exploitation est souvent codée en C/C++, c'est vrai, mais il reste une base fixe du système d'exploitation qui gère le stricte nécessaire, la répartition de la mémoire entre les processus, la gestion de la multi-instanciabilité...etc

    Sinon, programmer en binaire... Je pense que je vais oublier cette partie, car, elle est potentiellement inutile. (Encore plus qu'avec de l'Hexa... razz)

  • Wouaaa, les souvenirs d'IUT, ça me revient tout d'un coup ! *swoosh*

    Nous avions travaillé sur un processeur 8 bit dont nous avions (presque) conçu nous-même l'architecture matérielle, il fallait ensuite écrire des programmes pour effectuer certaines tâches basiques comme des calculs mathématiques simples.

    Mais j'ai tout oublié, et je ne tiens pas spécialement à m'en souvenir. razz Néanmoins, je salue l'effort pour écrire ce magnifique article smile

  • Wikipédia Base arithmétique.

    L'article Wikipédia oublie une base très importante: la base 1 ou base unaire, 3 = 1 + 1 + 1 + 0

    Chaque base engendre une structure de données en informatique.

    L'incrémentation engendre l'opération élémentaire d'insertion dans les structures de données correspondantes :

    Base unaire: la liste, 3 éléments = 1 élément + (1 élément + (1 élément + 0 élément)).

    Base binaire: l'arbre binaire, 3 éléments = (1 élément) + 1 élément + (1 élément)

    La décrémentation engendre l'opération élémentaire de retrait dans les structures de données correspondantes.

    L'addition engendre les opérations complexes dans les structure de données correspondantes :

    Addition unaire: la concaténation de deux listes, la longueur de la somme est la somme des longueurs

    Addition binaire: la fusion de deux arbres binomiaux

    Les structures de données modernes les plus performantes sont conçues à partir de systèmes de numération exotiques.

    Pour les intéressés voir La thèse de Chris Okasaki qui couvre en détail ce genre de questions.

    Zergy a écrit :

    Et programmer en binaire, je ne suis pas sûr que cela se fasse, ce serait comme prendre une hache pour supprimer les pages qui ne t'intéressent pas d'un livre.

    Ça n'est que l'alphabet, si tu n'apprends pas à lire ça ne sert à rien, encore une fois Chris Okasaki explique très bien où ça te mène.

  • Mais non quoi... je suis en pseudo-vacances, et vous, vous me ressortez mes cours... Je vous détesteeeeeeeeeeeee ! sourire

  • SpiceGuid a écrit :

    Pour les intéressés voir La thèse de Chris Okasaki qui couvre en détail ce genre de questions.

    J'ai été voir et j'ai commencé à lire mais j'y ai un peu un problème de taille, l'auteur fait constamment référence à des codes (ex: [...] is called ephemeral [DSST89] ou encore [...]less efficient than imperative languages in some cases [BAG92, Pip96]). SpiceGuid est-ce que tu saurais me dire à quoi se rapporte ces codes ?

  • Ce sont des notes bibliographiques.

    Bibliography

    [DSST89] James R. Driscoll, Neil Sarnak, Daniel D. K. Sleator, and Robert E. Tarjan. Making data structures persistent. Journal of Computer and System Sciences, 38(1Lullab frown86–124, February 1989. (pp. 2, 12, 20, 37, 128)

    Ces notes bibliographiques n'ont aucun intérêt pour le lecteur. Elles servent au jury qui décerne le diplôme, afin de différencier ce qui est un authentique travail de recherche. Ne pas mettre de notes bibliographiques peut faire planer un soupçon de plagiat. Il n'est pas nécessaire de lire ce qui est référencé en bibliographie, ça n'est d'ailleurs pas possible car nombre de ces publications sont soit payantes soit introuvables car compte-rendus de conférences ou autres filières papier à diffusion restreinte.

    Par contre une thèse elle doit être gratuite et accessible à tous, elle peut se lire indépendamment. Dans les faits la plupart des thèses exigent une très bonne culture générale. Celle de Okasaki est difficile mais elle explique bien pourquoi il est indispensable de savoir coder les opérations classiques dans les bases unaires et binaires. La fin de la thèse utilise des systèmes de numération exotiques plus diverses techniques avancées (modules paramétrés récursifs, nested-data-types, structural-boostrapping) pour concevoir des structures de données où toutes les opérations s'effectuent en temps constant. Malheureusement ces structures de données sont impossibles à implanter dans les langages usuels, même en Scala, F# ou autres gadgeteries innovantes "wink .

     

  • Quel c**. Je me doutais que c'était des références bibliographiques mais j'ai même pas pris le temps de vérifier en fin de document.

    Merci pour ta réponse en tout cas smile

  • Réécriture partielle du tutoriel, suppression de la partie "binaire". ajout d'une introduction à la manipulation de la mémoire, définissant et expliquant son fonctionnement.

  • Ajout de deux paragraphes de préscision dans l'introduction.

    Ajout d'un demi-paragraphe indigeste sur les registres "de mémoire".

  • Mise à jour sur la syntaxe du Site du Zéro, en attente de validation.

    Bilan :

    3/4 nouveaux chapitres + réécriture

  • Bonjour à tous !

    Désolé, je suis obligé de déterrer ce message. Après 3 longues années de cryogénisation, beaucoup de jeune motivé et curieux s'intéresse encore et toujours aux entrailles de la machine. Pendant que des vieux sournois complexifie et innove. Les nouvelles technologiques ont l'avantage d'apporter des nouvelles façon de communiquer, de formuler les informations mais jamais d'expliquer le fonctionnement fondamental de ces technologiques. Pour des questions de droit intellectuel.

    Bref, je suis confronté à un problème : comment programmer en hexadécimal ?

    Dans ce cours on peut lire le fonctionnement des registres, le codage en hexadécimal. Mais qui de vous peut-il créer un programme avec ces explications minimalistes ? Comment on commence ? Doit-on deviner les opcodes ?

    Pour m'aider à comprendre, j'aimerai (si c'est possible) que quelqu'un m'explique les 80 octets du premier algorithme :

    BA0F01B4
    09CD21B4
    08CD21B4
    4CCD2148
    75676820
    21205475
    20617320
    72657573
    73697320
    21240000

    Autrement je remercie Sbirematqui pour son tutoriel (bien qu'il soit simplifié) et un grand merci pour la personne qui me dissèquera l'algorithme. sourire

  • yowsag a écrit :

    Doit-on deviner les opcodes ?

    Heureusement non.

    Il sont fournis dans la documentation utilisateur des cpus.

    Par exemple Motorola M68000 Family Programmer's Reference Manual.

    Ou encore Intel® 64 and IA-32 Architectures Software Developer's Manual.

  • Merci SpiceGuid !

    Je suppose qu'il doit y avoir des codes, des notions à apprendre pour lire ce genre de documentation. C'est ma question.

    Quelles commandes sont nécessaires pour commencer un programme ?
    Tu ne peux pas taper MOV AX,4 comme ça. Il faut bien une entête.

    Comment afficher un message dans la console ? Un hello world par exemple ?

    Comment récupérer définir un registre ou une adresse mémoire ?

    Je n'ai malheureusement trouvé aucun tutoriel sur internet. On nous répète toujours les mêmes choses avec les registres. Et comme à mon habitude j'arrive derrière et je ne comprends rien. Pas faute d'avoir essayer.

    Merci, j'ai besoin de vos aides ! smile

    • Quel famille de cpu ?

      Une console MS-DOS command.com ?

      Une console cmd.exe Win32 ?

      Une console Linux ? Mac OS X ?

      C'est à chaque fois complètement différent.

      Pour des généralités sur la programmation Win32 en ASM intel 32 bits je te recommande le tutoriel de Noteworthy. Après il y a un forum francophone spécialisé où tu pourras poser des questions plus spécifiques. Si, après recherche, après demande sur un forum spécialisé, tu ne trouves toujours pas de réponse à un problème, reviens demander ici, il y aura forcément quelqu'un pour te répondre.

  • Bien que pas terminé tu peux aller lire mes écrits qui se rapporte à ce sujet.

  • Ce tutoriel n'est vraiment pas très utile à une personne désirant comprendre les entrailles de sa machine, son écriture m'a par contre aidé à l'époque.

    Il s'agit ici de bouts de code issus de programmes rédigés en Flat Assembler (FASM), que je conseille à tous curieux ou débutants, et assemblés pour mon Intel. On n'a pas vraiment besoin d'en-têtes ou autre choses tant que le système d'exploitation prend en charge une machine virtuelle pour les programmes 16 bits **hardcore** codés avec les pieds, comme Windows XP.

    Ils peuvent aussi être écrits sur le premier secteur d'un périphérique externe (une disquette pour faire simple) de façon à faire booter l'ordinateur dessus. D'ailleurs, j'ai quelques expériences ridicules sur le sujet, notamment le petit Mariow qui réinvente le pétrole. J'avais codé d'autres thèmes 8-bits, mais les disquettes et les sources ont été perdues.

    Bref, écoute SpiceGuid et Aka, ça vaut mieux que m'écouter moi, simple bidouilleur. razz

  • Si ! Je vois une entête au programme.
    Par supposition, la dernière commande doit correspondre à l'affichage d'un message. Mais les trois premières ? Je remarque même un séparateur : B4 (ou 21 j'hésite). A quoi sert-il ? Il délimite les commandes entre elles ?

    COMMANDES
    BA0F01B4
    09CD21B4
    08CD21B4
    4CCD21

    LETTRES
    48756768
    20212054
    75206173
    20726575
    73736973
    202124

    Oui, ton tutoriel c'était de la bidouille. Il aurait fallu que tu te concentres sur l'algorithme affichant "Hugh ! Tu as reussis !". Plus intéressent surtout par rapport au thème principal "Comment programmer en très bas niveau ? (Hexadécimal)". On s'attend quand même à écrire un algorithme correcte. Par exemple calculer N+2 et afficher le résultat.

    Pour information, je tourne avec Intel Core I5 x64 Windows 8.

    Merci beaucoup ! smile

    • ASM

      Le Intel Core I5 x64 c'est juste un Intel 80486 survitaminé.

      Au niveau programmation ça ne fait pas de différence, c'est 100% rétro-compatible.

      Par conséquent il te faut commencer par apprendre l'ASM Intel 80486.

      APIs

      Quant à Windows 8, au niveau programmation j'ai du mal à définir la bestiole.

      En gros c'est un hybride de transition de Win32 (obsolète) vers... on ne sait pas très bien quoi.

      Mon opinion : apprendre Win32 est une perte de temps parce que obsolète.

      Alors que les APIs de GTK et Qt sont là pour durer dans l'évolution.

      Prends une liste de logiciels gratuits comme :

      • PhotoFiltre
      • Paint.Net
      • Apophysis
      • Hexapad
      • Sokoban YASC
      • Winbrique
      • Cube Explorer

      Lequel est le moins portable ?

      Spoiler (Sélectionnez le texte dans le cadre pointillé pour le faire apparaître)

      Paint.Net tourne sous .Net du coup il ne fonctionne pas sous Windows 98, ni sous Windows XP si on n'a pas installé la machine virtuelle .Net

      Tous les autres programmes tournent sous Windows 98, Windows XP et supérieur.

      Lequel consomme le plus de mémoire ?

      Spoiler (Sélectionnez le texte dans le cadre pointillé pour le faire apparaître)

      Cube explorer doit explorer / mémoriser les 43252003274489856 combinaisons d'un Rubik's cube.

      Ça fait de lui un excellent candidat pour la palme de la consommation mémoire.

      Et pourtant il se peut se contenter de 128Mo.

      Ce qui n'est pas le cas de Paint.Net qui doit traîner son boulet sa machine virtuelle.

      Quel est le point commun de tous ces logiciels ?

      Spoiler (Sélectionnez le texte dans le cadre pointillé pour le faire apparaître)

      Tous sauf Paint.Net sont programmés en Delphi

      Bas niveau

      Pour finir tu as une fausse idée de ce qu'est le bas niveau.

      On peut très bien faire des programmes avec seulement 3 'instructions'.

      Comme c'est le cas de ce programme en 2 dimensions sous licence EUPL :

      Le jeu des tours de Hanoï en λ-calcul

      Ce programme ne comporte que 3 'instructions' :

      • rond
      • carré avec 1 coin arrondi
      • carré contenant des points
  • Il n'y a pas d'en-tête pour celui-ci, c'est du COM moche et dégueulasse, ou je me trompe. : )

    Utilise FASM, c'est comme utiliser les opcodes directement, mais t'as un programme qui te fait la traduction en mots. Taper de l'hexa directement n'est utile que pour décomposer le fonctionnement d'un "push", "call" ou "int", c'est à dire pas ce que tu veux faire.

    Tu veux programmer en bas biveau, au plus bas possible, fait du FASM.

  • (Ah, ça fait du bien de lire ça le matin avant d'aller en cours..!)

  • À Épitech on ne te demande pas d'être passionné ou de réfléchir :

    1. Tu payes.

    2. Hop, dans la piscine, tu nages ou tu coules.

  • Salut Romain, il faudrait que l'auteur original de ce post réponde, or il passe ici très irrégulièrement. Je lui ai laissé un message sur ses trois comptes Skype, j'espère qu'il en verra au moins un !

  • Hoy ! Romain, je te laisse regarder ce qu'il y a sur l'Internet, mais pour moi, ce petit (faux) tutoriel de l'époque a surtout été un moyen de comprendre ce qui se tramait dans le processeur.

    Malheureusement, il n'y a pas beaucoup de cours bien faits sur la toile pour apprendre à manger du bas niveau, du coup, le meilleur moyen, c'est d'utiliser l'outil google pour "Apprendre l'assembleur", se casser les dents dessus, y revenir, et surtout travailler à faire du C, puis à observer comment il est traduit en assembleur avec un débugger ou un objdump.

    C'est pas agréable, tout est peu lisible, mais il n'y a pas de méthode facile et efficace, vu qu'au final c'est presque une "intuition" qu'il faut se construire. N'empêche, en quatre ans, je suis passé du tutoriel sur l'assembleur que je lisais sur mon téléphone-post-it dans le bus à une compréhension plutôt fine du bouzin, mais une incapacité totale de l'enseigner. razz

    Pour répondre précisément à ta question, il s'agit d'opcodes, c'est propre à ton processeur, et le "pourquoi 89 ?" n'a pas vraiment de raison. Un jour, dans une pièce, un ingénieur a du choisir quel numéro allait désigner telle opération, il a choisit 89 pour d'obscures raisons. C'est juste un nombre, qui pour le processeur, permettra de faire appel à tel circuit et exécuter telle commande. (si on gribouille schématiquement, c'est exactement ça)

    En fait, le tutoriel est très mal fait, parce que pour être kikoo, je faisais ça avec des opcodes en Hexa au lieu de traiter l'assembleur, aka mettre des lettres au lieu de numéros sur les opérations (comme ça, on peut lire ce que c'est). C'était un mauvais choix.

    Par contre, si tu souhaites continuer dans l'assembleur, un très très bon choix est le FLAT ASM (FASM) utilisé ici, qui n'est pas le plus courant (tu croiseras plus souvent du Microsoft ASM (MASM) ou du NASM), mais le FASM reste très formateur. deg

    Bref, je suis pas sûr d'avoir aidé, mais bonne chance pour ta quête initiatique ! Dis-toi pour te motiver que l'on ne comprend rien au début, puis ensuite on ne comprend rien, puis encore on ne comprend rien, et un jour quelqu'un te pose une question bête, et là tu te rends compte qu'à force de ne rien comprendre, tu as réussi à comprendre bien plus de choses que ce que tu auras jamais espéré !

    Avec un cerveau, si tu décides que tu vas apprendre l'assembleur et comprendre le bas niveau, tu le feras, et jamais sous-estime toi ! DoubleAccentCirconflexe

  • (Ah, c'est par cet article que je suis arrivé sur Aerie's Guard smile . À croire que c'est Sbirematqui qui attire le plus de monde sur le refuge DoubleAccentCirconflexe )

    Pour télécharger FASM : http://flatassembler.net/download.php

    Tu décompresses dans un dossier, et pour écrire/compiler du code, tu utilises FASMW.EXE

    Certains des tutoriaux que l'on trouve sur Internet sont écrits pour le DOS, c'est le cas de celui de Benoît-M sur developpez.com.

    Dans ce cas, il faut utiliser DOSBox, un émulateur qui permet de tourner de vieux programmes DOS : http://sourceforge.net/projects/dosbox/files/latest/download.

    De nombreux tutos pour DOSBox sont dispo sur Internet mais un petit résumé n'est pas de refus :
    mount c {Chemin absolu du dossier}
    puis c:
    pour accéder à un dossier dans DOSBox.

    Puis tu tapes le nom de ton programme.
    Exemple:
    mount c "E:\Prog\ASM" c: HELLO.COM

    Personnellement je compile l'ASM puis je regarde le fichier COM avec EditHexa.
    C'est comme ça que j'ai compris comment marche l'instruction JMP.

    Et j'ai fait un "Plus ou Moins" sommaire, aussi :

    Copiable/Collable dans EditHexa :

    B42CCD2189D380E364B409BA4801CD21515350B401CD212C30B200B90A0000C2E2FCCD212C3000C2585B5938DAB40974107C07BA7801CD21E2D1BA6A01CD21EBCABA8701CD21CD20456E7472657A20756E206E6F6D62726520656E7472652031206574203130303A20240A432765737420706C7573210A240A4327657374206D6F696E73210A240A427261766F202124
    

    Code FASM (avec l'hexadécimal en commentaire) :

    org 100h
    use16
    
    mov ah, 2ch                         ; B4 2C
    int 21h                             ; CD 21
    mov bx, dx                          ; 89 D3
    and bl, 100                         ; 80 E3 64
                                        ;
    mov ah, 09h                         ; B4 09
                                        ;
    main:                               ;
            mov dx, question            ; BA 48 01
            int 21h                     ; CD 21
                                        ;
            push cx                     ; 51 // Ces lignes sont inutiles 
            push bx                     ; 53 // mais j'ai eu la flemme
            push ax                     ; 50 // de les enlever...
                    mov ah, 01h         ; B4 01
                    int 21h             ; CD 21
                    sub al, 30h         ; 2C 30
                    mov dl, 0           ; B2 00
                    mov cx, 10          ; B9 0A 00
                    mult:               ;
                        add dl, al      ; 00 C2
                    loop mult           ; E2 FC
                    int 21h             ; CD 21
                    sub al, 30h         ; 2C 30
                    add dl, al          ; 00 C2
            pop ax                      ; 58 // Idem que
            pop bx                      ; 5B // plus haut
            pop cx                      ; 59 // :)
                                        ;
            cmp dl, bl                  ; 38 DA
            mov ah,09h                  ; B4 09
            je  fin                     ; 74 10
            jl  plusgrand               ; 7C 07
                                        ;
                    mov dx, moinsmsg    ; BA 78 01
                    int 21h             ; CD 21
                    loop main           ; E2 D1
                                        ;
            plusgrand:                  ;
                    mov dx,plusmsg      ; BA 6A 01
                    int 21h             ; CD 21
                                        ;
    jmp main                            ; EB CA
    fin:                                ;
            mov dx, bravomsg            ; BA 87 01
            int 21h                     ; CD 21
                                        ;
            int 20h                     ; CD 20
    
    question db "Entrez un nombre entre 1 et 100: ",24h
    plusmsg  db 0Ah,"C'est plus!",0Ah,24h
    moinsmsg db 0Ah,"C'est moins!",0Ah,24h
    bravomsg db 0Ah,"Bravo !",24h   
    

    (C'est quoi la différence entre les balises code et script en fait ? perplexe Mis à part que la balise code faut la merde à chaque fois que je l'utilise ... ouf )

    Bonne chance !

  • (La balise code sert pour identifier du code au sein d'un paragraphe. La balise script sert à afficher un bloc de code avec plusieurs lignes et possède des options de coloration syntaxique)

Laissez un commentaire

Vous devez être connecté pour commenter sur le Refuge. Identifiez-vous maintenant ou inscrivez-vous !


Marre des pubs ? Inscrivez-vous !