lalahop

VirtualBox : configurations réseau possibles

Pour mes expériences diverses, j'adore utiliser des machines virtuelles. Je suis fan de VirtualBox OSE (et bientôt de KVM/Qemu 😉 ). Ce billet se propose de résumer les différentes configurations réseau possibles avec VirtualBox.

Avant tout, je vous conseille la lecture de la documentation de VirtualBox qui a le mérite d'être bien fournie sur le sujet : Chapter 6. Virtual networking.

Réseau interne : permet de créer un réseau virtuel, limité aux machines virtuelles qui y ont été explicitement ajoutées. Sous GNU/Linux, la sécurité est encore plus grande puisque seules les machines virtuelles crées par le même utilisateur (la comparaison est faite sur l'UID) peuvent être reliées entre elles. Les machines virtuelles peuvent communiquer entre elles sans restrictions mais elles ne peuvent pas communiquer avec l'hôte. Ce mode permet de tester des configurations réseau sans dommages sur le vrai réseau. On peut aussi imaginer un petit laboratoire permettant d'étudier les vers (disséquer sans risque, voir la propagation sur un réseau ...).

NAT : je me sers de ce mode quand je souhaite accéder seulement à Internet ou à d'autres services fournis sur mon réseau (exemple : un serveur web). L'hôte et l'invité partage la même adresse IP : la machine virtuelle est donc transparente sur le réseau : il n'y a pas de nouvelle machine. Par défaut, il est impossible de joindre l'invité depuis l'hôte. Cependant, on peut utiliser le port forwarding afin d'accéder à un ou plusieurs ports de l'invité. Voir la doc concernant le port forwarding. Cette idée du port forwarding m'a été donnée, pour la première fois, lors de la lecture du blog de Gauthier Garnier.

Cependant, le port forwarding n'est pas toujours la meilleure solution lors de simulation réseau. Pour cela, il existe un autre mode :

Accès par pont : VirtualBox bypass la pile réseau de l'hôte et accède directement à la carte réseau choisie. Vu du réseau, une nouvelle machine apparait. L'invité à accès au reste du LAN, à internet (comme avec le mode NAT quoi). Mais en plus, l'hôte (et les autres machines du LAN) peut avoir accès à la machine virtuelle sans port forwarding.

Je me sers de ce mode pour faire des expériences plus complexes entre l'invité et l'hôte et/ou mon LAN réel. Mais, parfois, je n'ai pas de routeur ni de commutateur connecté a mon hôte mais juste un modem. Dans ce cas précis, je n'arrive pas à faire communiquer mon invité et mon hôte en mode "accès par pont". Cela fonctionne en mode "NAT" mais je ne peux pas accéder comme je veux à l'invité via le réseau (comprendre : le port forwarding a des limites). Pour cela, il existe encore un autre mode :

Réseau privé hôte : Il s'agit d'un mix entre le mode "réseau interne" et le mode "accès par pont" : une ou plusieurs machines virtuelles peuvent communiquer entre elles et avec l'hôte sans limites. Néanmoins, les machines virtuelles n'ont pas accès au LAN réel ni à Internet. Cela peut néanmoins s'arranger :

Étape 1 : Transformer votre hôte en passerelle.

$ sudo -i
# echo 1 > /proc/sys/net/ipv4/ip_forward
# iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# exit

Si vous voulez conserver l'ip forwarding après un reboot de l'hôte, pensez à décommenter la ligne "net.ipv4.ip_forward=1" dans le fichier /etc/sysctl.conf. Pour remettre les actions par défaut dans la table NAT, pensez à la commande :

sudo iptables -t nat -P POSTROUTING ACCEPT

Etape 2 : Ajoutez une route par défaut à l'invité :

sudo route add -net default gw IPHote

N'oubliez pas de remplacer IPHote par l'adresse IP de l'interface créée par VirtualBox sur l'hôte.

Il ne vous reste plus qu'a configurer les DNS (/etc/resolv.conf) et c'est terminé.

Remarque : je n'ai traité que le cas où le système hôte et le système invité sont des Unix. Je n'ai pas testé les cas avec un Windows comme invité ou comme hôte mais voici quelques pistes vers lesquelles chercher si ça vous intéresse.

  • Si votre hôte est un Windows et votre invité est un Unix, il faudra suivre ceci (marche aussi pour les dernières versions de Windows d'après ce qu'on m'a dit) pour l'étape 1. L'étape 2 ne change pas.
  • Si votre hôte est un Unix et votre invité un Windows, seul l'étape 2 change. La ligne de commande doit être quelque chose comme :
    route add 0.0.0.0 mask 0.0.0.0 IPHote
  • Si votre système hôte et votre système invité sous tous les deux des Windows, il faudra suivre les deux points ci-dessous.

Réseau VDE : ce mode apparait dans la liste quand le logiciel VDE est installé sur l'hote. Virtual Distributed Ethernet (VDE) est un commutateur virtuel. Il permet de relier les machines virtuelles (et même l'hôte selon la manière dont il est configuré) entre elles et offre les fonctions supplémentaires d'un commutateur (VLAN, STP, etc.). Il permet de faire un réseau encore plus réaliste. Allez sur le wiki du projet VDE pour avoir plus d'informations.

Sinon, pour cloner un disque dur virtuel (.vdi) afin de l'utiliser dans une autre machine virtuelle (par exemple), c'est par là : http://www.mdl4.com/2010/05/how-to-copy-clone-a-virtualbox-vdi-in-ubuntu/

ÉDIT du 07/08/2011 à 0h45 : Et pour accéder aux dossiers partagés avec GNU/Linux comme système invité, il suffit de faire :

mount -t vboxsf sharename mountpoint

Où "sharename" est le nom du partage tel que vous l'avez défini lors de sa création. "mountpoint" doit bien éviter exister dans le système de fichiers. Exemple :

mount -t vboxsf docs /mnt/docs

Source :Documentation officielle de Virtualbox.
Fin de l'édit

Chez les Moldus

Récemment, j'ai dépanné des Moldus de l'informatique. J'ai du :

  • Faire reconnaitre une carte PCI Wifi achetée d'occasion par un Windows XP. Rien de bien sorcier ... sauf quand la carte en question n'a pas été vendue avec ses pilotes et que le nouveau propriétaire ignore sa marque/son modèle. Quand en plus la carte prend en charge uniquement le WEP, on se retrouve à expliquer pourquoi ce n'est pas une bonne idée de remplacer une clé USB Wifi qui gère le WPA2 par une carte PCI Wifi qui ne gère que le WEP et les conséquences que cela a sur la sécurité du réseau domestique. Mais, en étant un tout petit peu pédagogue, on s'en sort :).
  • Faire en sorte que le lecteur/graveur CD/DVD reprenne du service. Là aussi, rien de bien compliqué : le lecteur/graveur fonctionnait au boot de l'ordinateur et le gestionnaire des périphériques indiquait un code d'erreur 19 "Windows ne peut pas démarrer ce périphérique matériel car ses informations de configuration (dans le registre) sont incomplètes ou endommagées. (Code 19)". Il suffit ensuite de suivre l'aide de Microsoft et notamment la méthode n°1. Toutefois, je n'ai pas supprimé la clé mais toutes les valeurs s'y trouvant.
  • Faire (encore) un peu de pédagogie et expliquer pourquoi ce n'est pas une bonne idée de fusionner les deux partitions qui étaient crées à l'origine sur le disque dur mais, au contraire, s'en servir pour séparer documents et système d'exploitation.

Mais surtout, et c'est le point principal de ce billet : comment définir une musique (au format MP3), transférée via Bluetooh, en tant que sonnerie sur un Samsung Galaxy S. Par défaut, on peut choisir la sonnerie parmi une liste restreinte. L'appareil est évidemment équipé avec un Android "untouched"/ "unrooted", si j'ose dire.

Comme à chaque fois que je ne sais pas, je me retourne vers le nain Ternet et son ami Google. J'apprend que, contrairement à ce qu'on m'avait dit, il ne faut pas un ordinateur pour transférer la sonnerie sur le mobile. Je sélectionne un tutoriel qui explique tout cela parmi tans d'autres : Mettre un MP3 en sonnerie/alarme/notification.

Il suffit de créer les dossiers depuis l'application de gestion des fichiers du téléphone puis de déplacer la musique voulue du répertoire "bluetooh" vers le répertoire créé. Il faut néanmoins adapter la dernière partie de ce tutoriel puisque les menus ne sont pas identiques entre un HTC et un Samsung. De plus, les sonneries peuvent faire plus de 30 secondes sans problème.

Les Simpson : saison 21

Un petit billet rapide sur la famille jaune car je viens de me faire l'intégralité de cette série en français (merci à la Belgique plus rapide que Canal+ ;)) et j'avais envie de partager ça avec vous.

Si vous êtes fan des Simpson, je ne peux que vous conseiller de regarder cette 21éme saison. Si vous n'êtes pas fan des Simpson, je ne peux que vous conseiller de tenter de regarder cette 21éme saison ;).

Je pense qu'on a là une saison qui remonte le niveau des saison 19 et, surtout, 20 qui était en dessous du niveau habituel des Simpson notamment sur le plan "critique de la société".

C'est également une saison dans laquelle la France apparait plus que dans les autres saisons : Paris/Sarkozy/Carla dans l'épisode 5 "Le diable s'habille en nada", Paris et Tintin/Haddock (big up aux Belges) dans l'épisode 10 "il était une fois à Springfield", une référence a l'alcool dans l'épisode 13 "la couleur jaune", etc. . Certains me feront remarquer que la France était déjà à l'honneur dans le film sortit dans les salles il y a maintenant 3 ans et demi mais pas avec la même intensité.

L'humour, les gags récurrents et les références (culturelles, politiques, etc.) que l'on s'amuse à découvrir sont encore une fois au rendez-vous. Je n'en dirais pas plus afin de ne pas gâcher le plaisir précédemment évoqué.

Comme d'habitude, les histoires alternent entre des thèmes sérieux (la surveillance généralisée de la société, le racisme, les origines d'une famille, les énergies renouvelables pour ne citer qu'eux) et des thèmes plus futiles (non, toutes les blondes ne sont pas idiotes !, les policiers ne sont pas des gens isolés de la société, etc.).

Bien évidemment, cette saison comporte son lot d'épisodes décevants parmi lesquels je rangerai au minimum les épisodes 3 "l'insurgée" , 7 "les apprenties sorcières" et 16 "la plus grande histoire jamais ratée" (qu'est ce qu'ils nous font ch*er avec la religion sérieux !). Mais évidemment, ce classement est purement subjectif.

Pour finir, je vous conseille de regarder l'épisode 20 "l'œil sur la ville" qui parle de la surveillance généralisée des sociétés humaines et de ses dérives.

Parmi les dérives évoquées dans cet épisode, on trouve :

  • l'abandon d'un tas de libertés individuelles au nom de la sécurité (évoqué par Lisa durant la réunion).
  • l'abus de pouvoir. Flanders en fait clairement en jugeant les faits non plus selon la loi mais selon ses croyances (cf : le couple qui s'embrasse). Il en fait aussi en réagissant a n'importe quoi ("ne laissez pas votre arrosage automatique sous la pluie", choix de la mauvaise fourchette pour manger, etc.).
  • utilisation de mauvais arguments pour justifier une efficacité qui reste à démontrer (cf : la réponse faite à Carl).
  • création de zones de non-droit qui sont, de ce fait, bien plus dangereuses.

Cet épisode est donc un très bon point de départ pour réfléchir à la question d'une surveillance généralisée de nos sociétés. Pour ma part, j'ai déjà mené cette réflexion.

Quelques commandes GNU/Linux

Table des matières

Un mois que je n'ai rien posté ... Un mois de folie pendant lequel je n'ai eu que de rares moments à moi. Il est temps de se mettre à jour. Et en parlant de mise à jour : j'ai mis à jour ce blog, sans accrocs (ou en tout cas ils ne sont pas encore visibles).

Maintenant, je souhaite revenir sur des commandes disponibles sous GNU/Linux qui m'ont été utiles ces derniers temps. Ce ne sont pas des commandes inédites, elles sont facilement trouvables sur le web mais les mettre une fois de plus ne fait pas de mal et me cela me permettra de les retrouver facilement si besoin est.

Monter/démonter une iso

Pas besoin d'un logiciel tiers comme Deamon Tools, un simple montage suffit.

sudo mkdir /media/iso
sudo mount -o loop /chemin/vers/iso /media/iso

Pour démonter l'iso après usage :

sudo umount /media/iso

Et si on ne compte pas se resservir du dossier /media/iso pour remonter une autre iso :

sudo rmdir /media/iso

Copier le contenu d'un CD/DVD (non protégé) dans un fichier iso

Pour un CD :

dd if=/dev/cdrom of=/chemin/vers/iso/a/creer.iso

Pour un DVD :

dd if=/dev/dvd of=/chemin/vers/iso/a/creer.iso

ÉDIT 27/02/2011 18h45 :
Pour les DVD du commerce, qui sont protégés, dd ne suffit pas. Voir : Copier un DVD du commerce (protégé).

Avant de retirer une disquette

Après avoir écrit sur une disquette, pensez à la démonter avant de la retirer du lecteur. Si vous ne le faites pas, vous n'êtes pas sûr que l'écriture a bien eu lieu et vous pouvez donc perdre des données.

umount /dev/floppy

Créer une archive au format 7z

Il faut avoir installé le paquet p7zip-full. Le paquet p7zip ne permet pas de créer une archive, juste de la décompresser.

7z a -mx=9 nom_de_archive.7z dossier_a_compresser/

Le paramètre "x" permet de définir le taux de compression sur une échelle de 0 à 9. 0 désignant aucune compression et 9 désignant la compression maximale.

Décompresser une archive au format 7z en ligne de commande

Au cas où vous n'auriez pas trouvé depuis tout ce temps 😉 :

7z x archive.7z

Effacer un DVD RW

J'ai désinstallé k3b et Brasero depuis longtemps à cause de leur instabilité. Xfburn me dit que le DVD est déjà vierge. La commande growisofs ne m'apporte pas plus satisfaction. Finalement, la commande qui a résolu mon problème :

cdrdao blank --speed 2

Obtenir des informations sur une vidéo

Si vous voulez connaitre des informations de base (container, codecs, nombres de flux, résolution, aspect, fps, etc.) sur une vidéo, vous pouvez utiliser ffmpeg de la manière suivante :

ffmpeg -i video.mkv

Vous pouvez aussi utiliser mplayer, et ça, je ne le savais pas avant d'être tombé dessus au détour d'une recherche.

Pour avoir un peu plus d'informations (bit rate du/des flux, paramètres d'encodage, informations très détaillées sur les flux, etc. ), je reste fan de mediainfo. Même si je trouve la CLI bien plus lisible, sachez qu'une GUI est disponible (mediainfo-gui). Voici un ppa pour installer facilement mediainfo/mediainfo-gui avec apt-get.

Réaliser des traitements par lot sur des images

Pour cela, il faut utiliser ImageMagick. Voici 1 ligne de commandes qui permet de convertir les images jpeg du répertoire courant (et de ses sous-dossiers) en images png :

find ./ -name "*.jpg" -exec bash -c "convert -quality 100 {} {}.png && rename 's/\.jpg.png$/\.png/' {}.png && rm {}" \;

Find trouve les fichiers qui ont l'extension .jpg. Pour chaque fichier trouvé, find lance la commande qui se trouve après "-exec" et avant "\;".

Bash permet de lancer un nouveau shell. L'option -c permet de passer les commandes à exécuter, en tant que paramètre, sous la forme d'une chaine de caractères.

Convert {} {}.png créer une image png a partir de l'image jpg. Cette nouvelle image porte le nom de l'ancienne suivie de ".png". L'option -quality 100 permet de spécifier le taux de compression sur une échelle allant de 0 à 100 où 100 indique le plus fort taux de compression.

Rename 's/\.jpg.png$/\.png/' {}.png change l'extension .jpg.png du fichier en .png.

rm {} supprime l'image original (celle au format jpeg).

ÉDIT 03/07/2011 23h55 :
La commande ci-dessus peut aussi s'écrire :

find ./ -name "*.jpg" -exec convert -quality 100 {} {}.png \; -exec rename 's/\.jpg.png$/\.png/' {}.png \; -exec rm {} \;

A vous d'adapter ces lignes pour faire d'autres transformations par lot sur vos images grâce à ImageMagick.

Générer un mot de passe aléatoire en ligne de commande

Je ne souhaite pas utiliser un logiciel supplémentaire tel pwgen que je connais déjà. Tout est dit sur ce site : Générer des mots de passe aléatoires sous Linux - Tux-planet.

Découper un fichier

La méthode classique

7z a -mx=0 -v500m archive.7z fichier_à_découper

"-mx = 0" permet de définir un taux de compression minimale. A vous d'adapter ce taux en fonction du fichier que vous voulez découper : si vous découpez une vidéo déjà compressée grâce à théora/vorbis ou VP8/vorbis (;)), alors, la compresser ne servira à rien (le temps de compression/décompression sera disproportionné par rapport au gain obtenu en terme de place). A l'inverse, si vous découper une archive tar contenant des fichiers php/html, alors vous avez tout intérêt à compresser les fichiers pendant le découpage.

"-v" permet de définir la taille de chaque morceau du fichier. "b" permet de définir la taille du morceau en octets, "k" en kilos, "m" en mégas, "g" en gigas.

Pour recomposer le fichier, là encore, c'est du classique, on demande la décompression du premier morceau et 7z s'occupe du reste :

7z x archive.7z.001

La méthode split

split -d -b500m fichier_a_découper nom_des_morceaux.

Il y a bien un "." à la fin de la commande.
"-d" permet de dire que le compteur de fichiers sera numérique au lieu d'alphabétique, "-b" permet de définir la taille des morceaux . A ce sujet, je vous copie un morceau du man :

SIZE may be (or may be an integer optionally followed by) one of following: KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.

Pour recomposer le fichier :
Sous UNIX :

cat nom_des_morceaux.* > fichier_final

Sous Windows :

type nom_des_morceaux.* > fichier_final

Split ou 7z ?

En utilisant un taux de compression 0 avec 7z, split et 7z sont à égalité lors du temps mis pour découper un même fichier, à plus ou moins une seconde d'écart. Ce qui fera la différence entre l'usage de l'un ou de l'autre, c'est de savoir si vous avez besoin de compresser le fichier, comme évoqué plus haut.

Voici le blog qui m'a inspiré pour split : Le blog du Gentil Démon.

Détacher un programme d'une console

J'utilisais la commande nohup jusqu'à ce que je lise ceci : [resolu] Détacher une appli de la console après l'avoir lancée sur ubuntu-fr.org.

Après, cela dépend de l'usage : si faire un commande & exit sur un shell local fonctionne très bien si l'on ne souhaite pas réutiliser le shell pour une autre commande, faire cette commande sur un shell distant (via SSH par exemple) provoquera la fermeture du shell. Dans ce cas, mieux vaut un nohup commande &.

Quand vous avez un problème d'affichage (KDE)

Quand des artefacts graphiques apparaissent sur votre écran ou quand KDE semble figé à l'ouverture de votre session, il suffit d'ouvrir un terminal (ctrl + alt + f1 par exemple) et de balancer un :

sudo service kdm restart

Vous retournerez sur l'écran de connexion et tout vos programmes graphiques seront perdus mais les problèmes graphiques cesseront.

Supprimer un dépôt

Ubuntu-Tweak et ppa-purge permettent de nettoyer les dépôts utilisés. Mais parfois, ils échouent.

Dans ce cas, il suffit de rechercher un(des) fichier(s) portant le nom du dépôt que vous voulez supprimer, dans le dossier /etc/apt/sources.list.d puis de supprimer le fichier concerné. Il faut ensuite chercher à savoir si le dépôt a une clé GPG. Pour cela, on utilise la commande apt-key list, en root. Si le dépôt a une clé, on la supprime : apt-key del clé en root. Enfin, on met à jour la liste des paquets avec apt-get update.

Par exemple, pour supprimer le dépôt medibuntu à la main :

~$ ls /etc/apt/sources.list.d/medibuntu*
medibuntu.list
medibuntu.list.save
~$ sudo rm /etc/apt/sources.list.d/medibuntu.list /etc/apt/sources.list.d/medibuntu.list.save
~$ sudo apt-key list

[...]
pub   1024D/0C5A2783 2006-11-23
uid                  Medibuntu Packaging Team <admin@lists.medibuntu.org>
uid                  The Medibuntu Team <medibuntu@sos-sts.com>
sub   2048g/16C7105A 2006-11-23

~$ sudo apt-key del 0C5A2783
OK
~$ sudo apt-get update

Assembleur 8086+ : exercice 4

Table des matières

Comme d'habitude, je ne détaillerai rien concernant la compilation/l'édition des liens encore cette fois-ci. Reportez-vous au premier billet pour plus d'informations.

Explications préalables

Avant de commencer ce nouvel exercice, je souhaite revenir sur deux ou trois trucs qui m'ont été demandés.

Pourquoi mov ax, 4C00h pour fermer le programme ?

Il y a deux questions ici.

  1. D'une part, on ne peut pas utiliser une autre instruction pour terminer le programme. L'interruption 20h se destine aux programmes COM et la fonction 00h de l'interruption 21h a un usage incertain sur les programmes EXE. Comme d'habitude, je ne l'invente pas
  2. D'autre part, pourquoi mettre le nombre 4C00 en hexadécimal dans le registre AX ? L'interruption 21h s'attend à avoir le "nom" de la fonction à appeler (4Ch) dans AH et la fonction 4Ch attend le code retour à retourner dans AH. On retourne 0 (donc 00h) quand on veut indiquer que l'exécution du programme s'est bien déroulée (équivalent à un exit(0) en C). Mov ax, 4C00h revient à faire mov ah, 4Ch mov al, 00h si vous préférez. Il en va de même pour la saisie d'une chaîne de caractères : mov ax, 0C0Ah permet d'appeler la fonction qui a la charge de vider le buffer d'entrée puis d'appeler la fonction de saisie voulue (ici on choisi la 0Ah).

Pourquoi j'utilise des accents dans mes chaînes de caractères sous GNU/Linux mais pas sous Windows ?

Car cela serait plus compliqué pour vous.

Sous GNU/Linux, les éditeurs ont souvent le jeu de caractères (charset dans la langue de Shakespeare) "UTF-8" d'actif par défaut (en tout cas, je n'en ai pas croisé un qui ne le soit pas). Le shell est aussi dans ce jeu de caractères. Il n'y a donc pas de problèmes pour l'affichage des caractères accentués.

Sous Windows, il est possible de faire afficher des caractères accentués dans l'invite de commande, contrairement à l'idée reçue très répandue mais il faut choisir le bon charset dans votre éditeur : le charset "OEM 850". Comme tous les éditeurs ne le propose pas (Notepad++ does), je propose du code sans accents afin de ne pas embrouiller ceux qui ne veulent pas s'embêter avec ça. Tester et approuver sous XP.

Tes codes sont pas optimisés, tu utilises n'importe quel registre pour n'importe quoi !

J'ai jamais prétendu le contraire 😛 .

Concernant l'optimisation, je ne suis pas convaincu par tous les conseils qu'on trouve sur le net et dans le doute, j'évite de les utiliser. Exemple : pour vider un registre, il est de coutume de faire un xor registre, registre (remplacez registre par le registre à vider). Je fais un mov registre,0. Si on regarde la liste des instructions que je vous ai donné dans le premier billet, on voit que sur le 8086 le mov registre, immédiat coûte plus de cycle d'horloge que le xor registre, registre (4 contre 3). Mais sur les générations suivantes, on tombe à égalité à 2 cycles. Donc je me dis qu'aujourd'hui ça doit être pareil voir mieux. Après, je dois aussi être complet et vous informer que certaines instructions prennent moins de code machine que d'autres et que donc le poids des exécutables générés peuvent variés. Exemple : un xor eax, eax a un code machine plus petit qu'un mov eax, 0. Cela peut avoir son importance dans certains types de codes (ex.: exploit).

Concernant l'utilisation des registres, je ne peux être que d'accord. Mais étant donné que je souhaite montrer progressivement les possibilités, j'évite de parler de la pile dès le premier programme 😉 .

Ceci étant dit, on va pouvoir commencer un nouvel exercice.

Exercice 4 : énoncé

  1. Transformer une lettre minuscule saisie par l'utilisateur en une lettre majuscule. Si le caractère saisi est bien une lettre minuscule, on la transforme et on l'affiche, sinon on affiche un message d'erreur.
  2. Transformer une chaîne de caractères minuscules saisie par l'utilisateur en une chaîne de caractères majuscules. On affichera la chaîne transformée. Si une lettre n'est pas une minuscule, on l'affichera sans tenter de la transformer. Pareil si la chaîne entière ne contient aucune minuscule.
  3. Faire saisir une chaîne à l'utilisateur et n'afficher que les voyelles de cette chaîne. S'il n'y a aucune voyelle dans la chaîne, on affichera un message.
  4. Déclarer un nombre inférieur à 65535 dans le segment des données puis faire afficher ce nombre en hexadécimal.

Note : Comme d'habitude, cet énoncé n’est pas de moi : je l’ai repris à T.M. / E.R. et je l’ai modifié.

Exercice 4 : aides

Il faut :

  • Regarder la table ASCII et remarquer qu'une minuscule est comprise entre 61h et 7A (soit entre 97 et 122 en décimal). Les majuscules vont de 41h à 5Ah, soit 20h en moins que les minuscules leur correspondant. A partir de là, la question 1 est résolue : il ne vous reste plus qu'a utiliser l'instruction de comparaison et les sauts pour réagir en fonction du caractère saisi 😉 .
  • Pour la question 2, on réutilise la question 1 à l'intérieur d'une boucle qui parcourt la chaine saisie. Si le caractère est une minuscule, on le remplace dans la chaîne par son équivalent majuscule. Sinon non. C'est aussi simple que cela.
  • Pour la question 3, il suffit de parcourir la chaîne. Si le caractère est compris dans l'ensemble {a, e, i, o, u, y, A, E, I, O, U, Y}, on l'affiche, sinon non. Je suis sûr que certains d'entre vous sont déjà en train de compter le nombre de tests à faire. Ce n'est pas la bonne méthode, je pense. Il suffit, pour chaque caractère de la chaîne saisie, de boucler sur un tableau qui contient les voyelles et de tester si le caractère saisi correspond à la voyelle en cours.
  • Pour savoir si le caractère saisi par l'utilisateur n'est pas une minuscule, ou pour savoir si il n'y a pas de voyelle dans la chaîne et afficher un message en conséquence, il suffit de se faire une sorte de booléen en mémoire ou dans un registre. Si j'ai affiché une voyelle, je met le booléen à vrai, par exemple.
  • Pour afficher un nombre <= 65535 (donc 16 bits) en hexadécimal, il faudrait pouvoir isoler 4 groupes de 4 bits et afficher la correspondance hexadécimale de chaque groupe. On utilisera un masque (and 000Fh) afin de récupérer uniquement les 4 bits les plus à droite dans un registre donné et de mettre à 0 les autres bits (car AND 0,1 = 0 😉 ). Ensuite, il devient simple d’afficher le caractère hexadécimal qui correspond à chacun des groupes isolés à l’aide d'une boucle et d’un tableau associatif qui, à un index numérique (0, 1, …, 15), associe le caractère hexadécimal équivalent a ce nombre ("0", "1", …., "F".). Dis comme ça, vous comprenez qu'on va toujours récupérer les mêmes 4 bits et donc afficher un nombre faux. Il faut donc décaler les bits du registre de 4 en 4 (12, 8, 4, 0) vers la droite avant de récupérer les 4 bits les plus faibles avec le masque et d'afficher le caractère hexadécimal qui correspond au groupe.

Vous avez toutes les cartes en main.

 DOSSEG
.MODEL SMALL
 
.STACK
 

.DATA
	; 1)
	message db "Transformation d'une lettre miniscule saisie au clavier en lettre majuscule : $"
	message2 db 13,10,"-> Saisie d'une lettre minuscule : $"

	message3 db 13,10,'-> Caractere saisi : $'
	message4 db 13,10,'-> Caractere transforme : $'

	messageException db "Le caractere saisi n'est pas une lettre minuscule $"
 
	; 2)
	message5 db 13,10,10,"Transformation d'une chaine minuscule saisie au clavier en une chaine majuscule : $"

	message6 db 13,10,"-> Saisie d'une chaine minuscule (15 caracteres max) : $"
	message7 db 13,10,'-> Chaine saisie : $'

	message8 db 13,10,'-> Chaine transforme : $'
 
	; 3)
	message9 db 13,10,10,"Afficher que les voyelles d'une chaine : $"

	message10 db 13,10,"-> Saisie d'une chaine (15 caracteres max) : $"
	message11 db 13,10,'-> Chaine saisie : $'

	message12 db 13,10,'-> Chaine transforme : $'
	messageException2 db "Il n'y a pas de voyelle dans cette chaine. $"

	voyelles db 'aeiouyAEIOUY'
 
	; 4)
	message13 db 13,10,10,'Afficher un nombre <= 65535 initialise dans la zone DATA (ici : 1984) en hexadecimal : $'

	nombre dw 1984
	TAB_Hexa db "0123456789ABCDEF"
 
	; Saisies
	caractereSaisi db 4 dup(2)

	chaineSaisie db 18 dup(16)
 
.CODE
	mov ax, @DATA

	mov ds, ax
 
	; 1) Transformation d'une lettre minuscule saisie en majuscule
	mov ah, 09h

	mov dx, offset message
	int 21h
 
	mov ah, 09h

	mov dx, offset message2
	int 21h
 
	mov ax, 0C0Ah

	mov dx, offset caractereSaisi
	int 21h
 
	mov ah, 09h

	mov dx, offset message3
	int 21h
 
	mov ah, 02h

	mov dl, [caractereSaisi+2]
	int 21h
 
	mov ah, 09h

	mov dx, offset message4
	int 21h
 
	mov dl, [caractereSaisi+2]

	cmp dl, 61h
	jb invalide
 
	cmp dl, 7Ah

	ja invalide
 
	sub dl, 20h
	mov ah, 02h

	int 21h
	jmp finQ1
 
	invalide: 
	mov ah, 09h

	mov dx, offset messageException
	int 21h
 
	finQ1 :
 

	; 2) Transformation d'une chaine saisie en majuscule
	mov ah, 09h
	mov dx, offset message5
	int 21h

 
	mov ah, 09h
	mov dx, offset message6
	int 21h

 
	mov ax, 0C0Ah
	mov dx, offset chaineSaisie
	int 21h

 
	mov ah, 09h
	mov dx, offset message7
	int 21h	

 
	mov bx, 0
	mov bl, [chaineSaisie + 1]

	mov [chaineSaisie + 2 + bx], '$'
 
	mov ah, 09h

	mov dx, offset chaineSaisie+2
	int 21h	
 
	mov ah, 09h

	mov dx, offset message8
	int 21h	
 
	sub bx, 1	
	bcl:

		cmp bx, 0
		jl finbcl
 
		cmp [chaineSaisie+2+bx], 61h

		jb finP
 
		cmp [chaineSaisie+2+bx], 7Ah

		ja finP
 
		sub [chaineSaisie+2+bx], 20h

 
		finP:
		sub bx, 1
		jmp bcl
	finbcl :

 
	mov ah, 09h
	mov dx, offset chaineSaisie+2

	int 21h	
 
	; 3) Afficher que les voyelles d'une chaine
	mov ah, 09h

	mov dx, offset message9
	int 21h
 
	mov ah, 09h

	mov dx, offset message10
	int 21h
 
	mov ax, 0C0Ah

	mov dx, offset chaineSaisie
	int 21h
 
	mov ah, 09h

	mov dx, offset message11
	int 21h	
 
	mov bx, 0

	mov bl, [chaineSaisie + 1]
	mov [chaineSaisie + 2 + bx], '$'

	sub bx, 1
 
	mov ah, 09h

	mov dx, offset chaineSaisie+2
	int 21h	
 
	mov ah, 09h

	mov dx, offset message12
	int 21h	
 
	mov cx, 0

	mov dh, bl
	mov bx, 0
	mov ah, 02h

	bcl2:
		cmp bl, dh
		ja finbcl2
 

			mov si, 11
			bcl3:
				cmp si, 0

				jl finbcl3
 
				mov cl, [voyelles+si]
				cmp [chaineSaisie + 2 + bx], cl

				jne finP3
 
				mov ch, 1
				mov dl, cl

				int 21h
 
				finP3:
				sub si, 1

				jmp bcl3
 
			finbcl3:
 
		add bl, 1

		jmp bcl2
	finbcl2 :
 
	cmp ch, 1
	je fin3

 
	mov ah, 09h
	mov dx, offset messageException2
	int 21h

 
	fin3 :
 
	; 4) Afficher un nombre en hexadecimal
	mov ah, 09h

	mov dx, offset message13
	int 21h
 
	mov ah, 02h

	mov cl, 12
	bcl4 :		
		cmp cl, 0

		jl fin_bcl4
 
		mov bx, [nombre]
		shr bx, cl

		and bx, 000Fh
 
		mov dl, [TAB_Hexa + bx] 
		int 21h

 
		sub cl, 4
		jmp bcl4			 
	fin_bcl4 :
 
	; FIN

	mov ax, 4C00h
	int 21h
END

Exercice 4 : sous Windows, avec Nasm et le linker Val

segment .data
; 1)
	message db "Transformation d'une lettre miniscule saisie au clavier en lettre majuscule : $"
	message2 db 13,10,"-> Saisie d'une lettre minuscule : $"

	message3 db 13,10,'-> Caractere saisi : $'
	message4 db 13,10,'-> Caractere transforme : $'

	messageException db "Le caractere saisi n'est pas une lettre minuscule $"
 
	; 2)
	message5 db 13,10,10,"Transformation d'une chaine minuscule saisie au clavier en une chaine majuscule : $"

	message6 db 13,10,"-> Saisie d'une chaine minuscule (15 caracteres max) : $"
	message7 db 13,10,'-> Chaine saisie : $'

	message8 db 13,10,'-> Chaine transforme : $'
 
	; 3)
	message9 db 13,10,10,"Afficher que les voyelles d'une chaine : $"

	message10 db 13,10,"-> Saisie d'une chaine (15 caracteres max) : $"
	message11 db 13,10,'-> Chaine saisie : $'

	message12 db 13,10,'-> Chaine transforme : $'
	messageException2 db "Il n'y a pas de voyelle dans cette chaine. $"

	voyelles db 'aeiouyAEIOUY'
 
	; 4)
	message13 db 13,10,10,'Afficher un nombre <= 65535 initialise dans la zone DATA (ici : 1984) en hexadecimal : $'

	nombre dw 1984
	TAB_Hexa db "0123456789ABCDEF"
 
	; Saisies
	caractereSaisi times 4 db 2

	chaineSaisie times 18 db 16
 
segment stack stack

	resb 64
	stackstop:
 
segment .code
..start:

	mov ax, data
	mov ds, ax
 

	mov ax, stack
	mov ss, ax
	mov sp, stackstop

 
	; 1) Transformation d'une lettre minuscule saisie en majuscule
	mov ah, 09h
	mov dx, message
	int 21h

 
	mov ah, 09h
	mov dx, message2
	int 21h

 
	mov ax, 0C0Ah
	mov dx, caractereSaisi
	int 21h

 
	mov ah, 09h
	mov dx, message3
	int 21h

 
	mov ah, 02h
	mov dl, [caractereSaisi+2]

	int 21h
 
	mov ah, 09h
	mov dx, message4
	int 21h

 
	mov dl, [caractereSaisi+2]
	cmp dl, 61h

	jb invalide
 
	cmp dl, 7Ah
	ja invalide

 
	sub dl, 20h
	mov ah, 02h

	int 21h
	jmp finQ1
 
	invalide: 
	mov ah, 09h

	mov dx, messageException
	int 21h
 
	finQ1 :
 

	; 2) Transformation d'une chaine saisie en majuscule
	mov ah, 09h
	mov dx, message5
	int 21h

 
	mov ah, 09h
	mov dx, message6
	int 21h

 
	mov ax, 0C0Ah
	mov dx, chaineSaisie
	int 21h

 
	mov ah, 09h
	mov dx, message7
	int 21h	

 
	mov bx, 0
	mov bl, [chaineSaisie + 1]

	mov byte [chaineSaisie + 2 + bx], '$'

 
	mov ah, 09h
	mov dx, chaineSaisie+2

	int 21h	
 
	mov ah, 09h
	mov dx, message8
	int 21h	

 
	sub bx, 1	
	bcl:
		cmp bx, 0

		jl finbcl
 
		cmp byte [chaineSaisie+2+bx], 61h

		jb finP
 
		cmp byte [chaineSaisie+2+bx], 7Ah

		ja finP
 
		sub byte [chaineSaisie+2+bx], 20h

 
		finP:
		sub bx, 1
		jmp bcl
	finbcl :

 
	mov ah, 09h
	mov dx, chaineSaisie+2

	int 21h	
 
	; 3) Afficher que les voyelles d'une chaine
	mov ah, 09h

	mov dx, message9
	int 21h
 
	mov ah, 09h

	mov dx, message10
	int 21h
 
	mov ax, 0C0Ah

	mov dx, chaineSaisie
	int 21h
 
	mov ah, 09h

	mov dx, message11
	int 21h	
 
	mov bx, 0

	mov bl, [chaineSaisie + 1]
	mov byte [chaineSaisie + 2 + bx], '$'

	sub bx, 1
 
	mov ah, 09h

	mov dx, chaineSaisie+2
	int 21h	
 
	mov ah, 09h

	mov dx, message12
	int 21h	
 
	mov cx, 0

	mov dh, bl
	mov bx, 0
	mov ah, 02h

	bcl2:
		cmp bl, dh
		ja finbcl2
 

			mov si, 11
			bcl3:
				cmp si, 0

				jl finbcl3
 
				mov cl, [voyelles+si]
				cmp [chaineSaisie + 2 + bx], cl

				jne finP3
 
				mov ch, 1
				mov dl, cl

				int 21h
 
				finP3:
				sub si, 1

				jmp bcl3
 
			finbcl3:
 
		add bl, 1

		jmp bcl2
	finbcl2 :
 
	cmp ch, 1
	je fin3

 
	mov ah, 09h
	mov dx, messageException2
	int 21h

 
	fin3 :
 
	; 4) Afficher un nombre en hexadecimal
	mov ah, 09h

	mov dx, message13
	int 21h
 
	mov ah, 02h

	mov cl, 12
	bcl4 :		
		cmp cl, 0

		jl fin_bcl4
 
		mov bx, [nombre]
		shr bx, cl

		and bx, 000Fh
 
		mov dl, [TAB_Hexa + bx] 
		int 21h

 
		sub cl, 4
		jmp bcl4			 
	fin_bcl4 :
 
	; FIN

	mov ax, 4C00h
	int 21h

Exercice 4 : sous GNU/Linux, avec Nasm et ld

section .data
	; 1)
	message db "Transformation d'une lettre miniscule saisie au clavier en lettre majuscule : "
	lenMessage equ $-message

 
	message2 db 10,"-> Saisie d'une lettre minuscule : "
	lenMessage2 equ $-message2

 
	message3 db 10,'-> Caractère saisi : '
	lenMessage3 equ $-message3

 
	message4 db 10,'-> Caractère transformé : '
	lenMessage4 equ $-message4

 
	messageException db "Le caractère saisi n'est pas une lettre minuscule "
	lenMessageException equ $-messageException
 
	; 2)

	message5 db 10,10,"Transformation d'une chaine minuscule saisie au clavier en une chaine majuscule : "
	lenMessage5 equ $-message5

 
	message6 db 10,"-> Saisie d'une chaine minuscule (15 caractères max) : "
	lenMessage6 equ $-message6

 
	message7 db 10,'-> Chaine saisie : '
	lenMessage7 equ $-message7

 
	message8 db 10,'-> Chaine transformée : '
	lenMessage8 equ $-message8

 
	; 3)
	message9 db 10,10,"Afficher que les voyelles d'une chaine : "
	lenMessage9 equ $-message9

 
	message10 db 10,"-> Saisie d'une chaine (15 caractères max) : "
	lenMessage10 equ $-message10

 
	message11 db 10,"-> Chaine saisie : "
	lenMessage11 equ $-message11

 
	message12 db 10,"-> Chaine transformée : "
	lenMessage12 equ $-message12

 
	messageException2 db "Il n'y a pas de voyelle dans cette chaine. "
	lenMessageException2 equ $-messageException2
 
	voyelles db 'aeiouyAEIOUY'

 
	; 4)
	message13 db 10,10,'Afficher un nombre <= 65535 initialisé dans la zone DATA (ici : 1984) en hexadécimal : '
	lenMessage13 equ $-message13

 
	nombre dw 1984
	hexa db '0123456789ABCDEF'
 
	; FIN

	fin db 10,'FIN',10
	lenFin equ $-fin 

 
 
section .bss
	caractereSaisi resb 2
	chaineSaisie resb 16

	lenSaisie resd 1
	buffer resb 1
	noVoyelle resb 1

	buffertwo resb 32
 
 
section .text
global _start
 
_start:

	; 1) Transformation d'une lettre minuscule saisie en majuscule
	mov eax, 4
	mov ebx, 1

	mov ecx, message
	mov edx, lenMessage
	int 80h

 
	mov eax, 4
	mov ebx, 1

	mov ecx, message2
	mov edx, lenMessage2
	int 80h

 
	mov eax, 3
	mov ebx, 0

	mov ecx, caractereSaisi
	mov edx, 2
	int 80h

 
	mov eax, 4
	mov ebx, 1

	mov ecx, message3
	mov edx, lenMessage3
	int 80h

 
	mov eax, 4
	mov ebx, 1

	mov ecx, caractereSaisi
	mov edx, 1
	int 80h

 
	mov eax, 4
	mov ebx, 1

	mov ecx, message4
	mov edx, lenMessage4
	int 80h

 
	mov dl, [caractereSaisi]
	cmp dl, 61h

	jb invalide
 
	cmp dl, 7Ah
	ja invalide

 
	sub dl, 20h
	mov [buffer], dl

 
	mov eax, 4
	mov ebx, 1

	mov ecx, buffer
	mov edx, 1
	int 80h	
	jmp finQ1

 
	invalide: 
	mov eax, 4
	mov ebx, 1

	mov ecx, messageException
	mov edx, lenMessageException
	int 80h

 
	finQ1 :
 
	; 2) Transformation d'une chaine saisie en majuscule
	mov eax, 4

	mov ebx, 1
	mov ecx, message5
	mov edx, lenMessage5
	int 80h

 
	mov eax, 4
	mov ebx, 1

	mov ecx, message6
	mov edx, lenMessage6
	int 80h

 
	mov eax, 3
	mov ebx, 0

	mov ecx, chaineSaisie
	mov edx, 16
	int 80h

 
	mov [lenSaisie], eax
 
	mov eax, 4

	mov ebx, 1
	mov ecx, message7
	mov edx, lenMessage7
	int 80h

 
	mov eax, 4
	mov ebx, 1

	mov ecx, chaineSaisie
	mov edx, [lenSaisie]
	int 80h

 
	mov eax, 4
	mov ebx, 1

	mov ecx, message8
	mov edx, lenMessage8
	int 80h

 
	; On enleve 2 car :
	; -1 car la taille de la chaine ne correspond pas à ses indices :
	; 	une chaîne d'une taille 7 va de 0 à 6
	; -1 car on ne veut pas traiter le retour à la ligne
	mov ebx, [lenSaisie]

	sub ebx, 2
	bcl:
		cmp ebx, 0

		jl finbcl
 
		cmp byte [chaineSaisie+ebx], 61h

		jb finP
 
		cmp byte [chaineSaisie+ebx], 7Ah

		ja finP
 
		sub byte [chaineSaisie+ebx], 20h

 
		finP:
		sub ebx, 1
		jmp bcl
	finbcl :

 
	mov eax, 4
	mov ebx, 1

	mov ecx, chaineSaisie
	mov edx, [lenSaisie]
	int 80h

 
 
	; 3) Afficher que les voyelles d'une chaine
	mov eax, 4
	mov ebx, 1

	mov ecx, message9
	mov edx, lenMessage9
	int 80h

 
	mov eax, 4
	mov ebx, 1

	mov ecx, message10
	mov edx, lenMessage10
	int 80h

 
	mov eax, 3
	mov ebx, 0

	mov ecx, chaineSaisie
	mov edx, 16
	int 80h

 
	mov [lenSaisie], eax
 
	mov eax, 4

	mov ebx, 1
	mov ecx, message11
	mov edx, lenMessage11
	int 80h

 
	mov eax, 4
	mov ebx, 1

	mov ecx, chaineSaisie
	mov edx, [lenSaisie]
	int 80h

 
	mov eax, 4
	mov ebx, 1

	mov ecx, message12
	mov edx, lenMessage12
	int 80h

 
	mov byte [noVoyelle], 1
	mov esi, 0

	mov edi, 0
	mov ebx, 1
	mov edx, 1

 
	sub dword [lenSaisie], 2
	bcl2:
		cmp edi, [lenSaisie]

		ja finbcl2
 
			mov esi, 11
			bcl3:

				cmp esi, 0
				jl finbcl3
 
				mov cl, [voyelles+esi]

				cmp [chaineSaisie+edi], cl
				jne finP3
 
				mov eax, 4

				mov byte [buffer], cl
				mov ecx, buffer
				int 80h

 
				mov byte [noVoyelle], 0
 
				finP3:
				sub esi, 1

				jmp bcl3
			finbcl3:
 
		add edi, 1
		jmp bcl2
	finbcl2 :

 
	cmp byte [noVoyelle], 0
	je fin3
 
	mov eax, 4

	mov ebx, 1
	mov ecx, messageException2
	mov edx, lenMessageException2
	int 80h

 
	fin3 :	
 
	; 4) Afficher un nombre en hexadecimal
	mov eax, 4

	mov ebx, 1
	mov ecx, message13
	mov edx, lenMessage13
	int 80h

 
	mov cl, 12
	bcl4:		
		cmp cl, 0

		jl fin_bcl4
 
		mov byte [buffer], cl
		mov dx, 0

		mov dx, [nombre]
		shr dx, cl
		and dx, 000Fh

 
		mov eax, 4
		mov ebx, 1

		mov ecx, hexa
		add ecx, edx
		mov edx, 1

		int 80h
 
		mov cl, [buffer]
		sub cl, 4

		jmp bcl4			 
	fin_bcl4:
 
	; FIN
	mov eax, 4

	mov ebx, 1
	mov ecx, fin
	mov edx, lenFin
	int 80h

 
	mov eax, 1
	mov ebx, 0

	int 80h