<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Linux Attitude&#187; Code</title>
	<atom:link href="http://linux-attitude.fr/tag/code/feed" rel="self" type="application/rss+xml" />
	<link>http://linux-attitude.fr</link>
	<description>Le libre est un état d&#039;esprit</description>
	<lastBuildDate>Mon, 06 Feb 2012 08:01:04 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.3</generator>
		<item>
		<title>Le xml n&#8217;est pas pour les humains</title>
		<link>http://linux-attitude.fr/post/le-xml-nest-pas-pour-les-humain?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=le-xml-nest-pas-pour-les-humain</link>
		<comments>http://linux-attitude.fr/post/le-xml-nest-pas-pour-les-humain#comments</comments>
		<pubDate>Mon, 17 May 2010 17:12:36 +0000</pubDate>
		<dc:creator>peck</dc:creator>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Scripting]]></category>

		<guid isPermaLink="false">http://linux-attitude.fr/?p=884</guid>
		<description><![CDATA[Niveau&#160;: &#160;&#160;&#160;&#160;&#160; Résumé&#160;: Aujourd'hui un thème un peu polémique mais pas trop quand même. Le XML est un descendant du SGML. C'est un format structuré de données comme l'est l'ASN1 ou le LDIF. Tous ces formats ont été inventés dans une seule optique&#160;: être facile à parser par une machine. Et pourtant quand on voit [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Niveau</strong>&nbsp;: <span class="s">&nbsp;</span><span class="s">&nbsp;</span><span class="e">&nbsp;</span><span class="e">&nbsp;</span><span class="e">&nbsp;</span><br />
<strong>Résumé</strong>&nbsp;:</p>


<p>Aujourd'hui un thème un peu polémique mais pas trop quand même.</p>


<p>Le <a href="http://fr.wikipedia.org/wiki/Extensible_Markup_Language" hreflang="fr">XML</a> est un descendant du <a href="http://fr.wikipedia.org/wiki/Standard_Generalized_Markup_Language" hreflang="fr">SGML</a>. C'est un format structuré de données comme l'est <a href="http://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One" hreflang="en">l'ASN1</a> ou le <a href="http://fr.wikipedia.org/wiki/LDAP_Data_Interchange_Format">LDIF</a>.</p>


<p>Tous ces formats ont été inventés dans une seule optique&nbsp;: être facile à parser par une machine. Et pourtant quand on voit les subtilités du XML et les difficultés à faire un parseur, on peut avoir des doutes... Mais de ce fait ils sont peu lisibles.</p>


<p>Ne vous méprenez pas, le XML a tout à fait sa place dans les échange de données, dans la communication entre machines. Le format est bien défini et fonctionne quel que soit le type de machine. Il est d'ailleurs très utilisé car on trouve des parseurs partout, dans presque tous les langages, et dans presque tous les frameworks.</p>


<p>Mais à force d'en user on en abuse.</p>


<p>Non le XML n'est PAS fait pour les humains&nbsp;! Le xml n'est pas à sa place dans les fichiers de configuration, à moins que vous n'ayez pour objectif que l'utilisateur ne touche jamais à ces fichiers et que vous ayez le temps de développer une interface entre l'utilisateur et la modification (texte&nbsp;? graphique&nbsp;? sonore ?).</p>


<p>Le XML est pratique car il permet au développeur d'éviter l'usage de protocoles binaires incompréhensibles et mal parsés entre deux applications. Il est pratique car un autre développeur peut lire ce protocole sans l'avoir appris. Mais vous remarquerez que je ne parle que de développeurs, pas d'utilisateurs.</p>


<p>Alors par pitié arrêtez le XML dans les fichiers destinés aux HUMAINS. Il existe bien d'autres formats ayant des parseurs et tout aussi lisibles&nbsp;:</p>
<ul>
<li>le <a href="http://fr.wikipedia.org/wiki/Fichier_INI" hreflang="fr">format INI</a></li>
<li>le <a href="http://fr.wikipedia.org/wiki/Yaml" hreflang="fr">format YAML</a></li>
<li>le <a href="http://fr.wikipedia.org/wiki/Json">format JSON</a></li>
</ul>

<p>Mieux, il existe <a href="http://onlamp.com/pub/a/onlamp/2006/02/16/introducing-lua.html" hreflang="en">LUA</a> qui vous permet à la fois d'avoir un fichier de configuration simple et lisible, mais qui en plus autorise l'utilisateur à faire des choses que vous n'auriez pas prévu, comme des configuration conditionnelles&nbsp;!</p>
	Tags:<a href="http://linux-attitude.fr/tag/code" title="Code" rel="tag">Code</a>, <a href="http://linux-attitude.fr/tag/scripting" title="Scripting" rel="tag">Scripting</a><br />
]]></content:encoded>
			<wfw:commentRss>http://linux-attitude.fr/post/le-xml-nest-pas-pour-les-humain/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Un interpréteur fantôme</title>
		<link>http://linux-attitude.fr/post/un-interpreteur-fantome?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=un-interpreteur-fantome</link>
		<comments>http://linux-attitude.fr/post/un-interpreteur-fantome#comments</comments>
		<pubDate>Mon, 03 May 2010 16:32:31 +0000</pubDate>
		<dc:creator>peck</dc:creator>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Curiosité]]></category>

		<guid isPermaLink="false">http://linux-attitude.fr/?p=413</guid>
		<description><![CDATA[Niveau&#160;: &#160;&#160;&#160;&#160;&#160; Résumé&#160;: Maintenant que nous savons nous servir d'un interpréteur postscript, nous allons pouvoir développer. Postscript est un langage complet au sens de Turing. C'est à dire qu'on peut tout faire avec, même faire décoller une fusée. Mais bon, avant d'en arriver là, on va essayer de faire quelque chose d'utile. Pour vous présenter [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Niveau</strong>&nbsp;: <span class="s">&nbsp;</span><span class="s">&nbsp;</span><span class="s">&nbsp;</span><span class="s">&nbsp;</span><span class="e">&nbsp;</span><br />
<strong>Résumé</strong>&nbsp;:</p>


<p>Maintenant que nous savons nous servir d'un interpréteur postscript, nous allons pouvoir développer. Postscript est un langage complet au sens de Turing. C'est à dire qu'on peut tout faire avec, même faire décoller une fusée. Mais bon, avant d'en arriver là, on va essayer de faire quelque chose d'utile.</p>


<p>Pour vous présenter le langage, je vous propose un exemple basé sur la fourmi de Langton. C'est une fourmi qui a ceci de particulier qu'elle ne sait que tourner. Son univers ressemble beaucoup à celui du jeu de la vie, c'est-à-dire une grille infinie de cases noires ou blanches. Lorsque la fourmi est sur une case noire, elle tourne à droite, lorsqu'elle est sur une case blanche, elle tourne à gauche. De plus elle inverse la couleur de la case sur laquelle elle se trouve.</p>


<p>Le concept est assez simple, et lorsqu'on lance la fourmi sur une grille blanche, son comportement a l'air aléatoire. Et pourtant au bout d'un moment elle fabrique ce qui ressemble à une autoroute.</p>


<p>Tout ça pour dire que le postscript c'est bien. C'est un langage à pile, qui fonctionne en notation inversée. Les possesseurs de <a href="http://fr.wikipedia.org/wiki/HP-48" hreflang="fr">HP48</a> et amateurs de RPL y retrouveront leurs petits (ou de vieux souvenirs). Les développeurs  <a href="http://fr.wikipedia.org/wiki/Forth_(langage)" hreflang="fr">forth</a> (faite sous savoir si vous êtes dans la salle) apprécieront aussi probablement.</p>


<h3>La base</h3>

<h4>Les commentaires</h4>

<p>Toute ligne commençant par un % est un commentaire.
Exemple&nbsp;:</p>
<pre>
% Rien
</pre>


<h4>Les commandes</h4>

<p>Les commandes sont des mots simples. Étant donné la notation inversée, la commande se trouve après ses arguments. Par exemple une addition&nbsp;:</p>
<pre>
5 9 add
</pre>


<p>A cette notation est associée une pile. 5 pose 5 sur la pile, 9 pose 9 sur la pile, add retire les 2 derniers éléments (donc 9 et 5) puis additionne et repose le résultat sur la pile. D'où la représentation de la pile&nbsp;:</p>
<pre>
    %avant la ligne de commande
5   % 5 
5 9 % 9
    %pendant le add (invisible)
14  % après le add 
</pre>


<p>Les commandes peuvent être séparées par des espaces ou des retours à la ligne.
<span id="more-413"></span></p>


<h4>Les variables</h4>

<p>Les variables sont définie avec la commande def. Un nom de variable se pose sur la pile avec le préfixe /. Donc pour définir la variable pi</p>
<pre>
/pi 3.1415 def
</pre>


<p>Une variable se lance ou se pose sur la pile avec son nom dans préfixe. Donc pour poser pi sur la pile&nbsp;:</p>
<pre>
pi
</pre>


<h4>Les fonctions</h4>

<p>Une fonction est tout simplement un ensemble d'instruction placées entre { }. Une fonction toute seule n'a que peu d'intérêt, mais une fonction peut être mise dans une variable avec la commande def ce qui permet de la réutiliser autant de fois que voulu.</p>


<p>Exemple une fonction qui incrémente un élément de la pile de 1&nbsp;:</p>
<pre>
/incremente { 1 add } def
</pre>


<h4>Manipulation de la pile</h4>

<p>La pile étant un élément central, il existe des opérateurs pour la manipuler. Le principaux sont&nbsp;:</p>
<ul>
<li>pop&nbsp;: retire un élément de la pile</li>
<li>dup&nbsp;: duplique un élément de la pile</li>
<li>exch&nbsp;: échange les 2 derniers éléments de la pile</li>
<li>pstack&nbsp;: affiche le contenu de la pile (utile pour le debug)</li>
<li>clear&nbsp;: vide la pile</li>
</ul>


<h4>L'affichage</h4>

<p>L'affichage étant le but ultime de postscript, vous comprendrez qu'il y a beaucoup de primitives d'affichage, essentiellement vectoriel.</p>


<p>Regardez bien ça vous rappellera le <a href="http://fr.wikipedia.org/wiki/Logo_%28langage%29" hreflang="fr">logo</a>. Dessinons un carré&nbsp;:</p>
<pre>
newpath
0 0 moveto
10 0 rlineto
10 10 rlineto
0 10 rlineto
0 0 rlineto
stroke
</pre>


<p>Petite astuce si vous testez les commandes, chaque dessin doit se terminer par un showpage (ici par exemple, juste après le stroke), cela affiche le contenu du buffer sur une page et passe à la feuille suivante.</p>


<p>Quelques exemples de commande&nbsp;:</p>
<ul>
<li>setlinewidth (un paramètre&nbsp;: la largeur de trait)</li>
<li>closepath (ferme le dessin)</li>
<li>fill (remplit un chemin fermé)</li>
</ul>

<h4>Contrôle de flux</h4>

<p>Tous comme n'importe quel langage, le postscript permet des boucles et des if sous la forme "booléen méthode if". Par exemple&nbsp;:</p>
<pre>
% incrémente le dernier élément de la pile s'il est inférieur à 10
dup 10 lt { 1 add } if
</pre>


<p>Pour une boucle for, même topo. Exemple une somme de 1 à 10&nbsp;:</p>
<pre>
% valeur initiale
0 
% début incrément fin méthode for
1 1 10 { add } for
</pre>


<p>Voilà, je n'ai fait que survoler le langage, vous trouverez plus de détail dans ce livre <a href="http://www-cdf.fnal.gov/offline/PostScript/BLUEBOOK.PDF" hreflang="en">référence</a>.</p>


<h3>Un exemple</h3>

<p>Pour continuer, j'ai écrit un petit exemple commenté <a href="http://linux-attitude.fr/public/Code/fourmi.ps">disponible en téléchargement</a>. Vous pouvez l'ouvrir avec un éditeur de texte ou utilise ps2pdf pour le rendre affichable. Cet exemple génère 75 pages visibles en PDF, chaque page correspond à 150 itérations du mouvement de la fourmi.</p>


<p>Lisez-le vous verrez que finalement le postscript c'est pas trop compliqué quand c'est pas écrit par une machine sur une seule ligne.</p>

<p></p><p>Si vous avez aimé, il y a aussi : </p><ol><li><a href='http://linux-attitude.fr/post/postscript-est-un-langage-de-script' rel='bookmark' title='Permanent Link: Postscript est un langage &#8230; de script'>Postscript est un langage &#8230; de script</a></li>
<li><a href='http://linux-attitude.fr/post/torture-de-cerveau' rel='bookmark' title='Permanent Link: Torture de cerveau'>Torture de cerveau</a></li>
<li><a href='http://linux-attitude.fr/post/calculatrice-en-ligne-de-commande' rel='bookmark' title='Permanent Link: Calculatrice en ligne de commande'>Calculatrice en ligne de commande</a></li>
</ol>
	Tags:<a href="http://linux-attitude.fr/tag/code" title="Code" rel="tag">Code</a>, <a href="http://linux-attitude.fr/tag/curiosite" title="Curiosité" rel="tag">Curiosité</a><br />
]]></content:encoded>
			<wfw:commentRss>http://linux-attitude.fr/post/un-interpreteur-fantome/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Perles</title>
		<link>http://linux-attitude.fr/post/perles?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=perles</link>
		<comments>http://linux-attitude.fr/post/perles#comments</comments>
		<pubDate>Mon, 08 Dec 2008 23:03:00 +0000</pubDate>
		<dc:creator>peck</dc:creator>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[Scripting]]></category>

		<guid isPermaLink="false">http://linux-attitude.fr/post/perles</guid>
		<description><![CDATA[Niveau&#160;: &#160;&#160;&#160;&#160;&#160; Résumé&#160;: perl Quelques trucs et astuces de perlistes en vrac. Pour des vrais astuces de pro régulières, n'hésitez pas à visiter le site des moines de perl. Initialiser un tableau de chaînes sans avec une syntaxe légère, très lisible quand on n'a que des mots, le séparateur étant les caractères blancs, incluant le [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Niveau</strong>&nbsp;: <span class="s">&nbsp;</span><span class="s">&nbsp;</span><span class="s">&nbsp;</span><span class="e">&nbsp;</span><span class="e">&nbsp;</span><br />
<strong>Résumé</strong>&nbsp;: perl</p>


<p>Quelques trucs et astuces de perlistes en vrac. Pour des vrais astuces de pro régulières, n'hésitez pas à visiter le site des <a href="http://www.perlmonks.org/" hreflang="en">moines</a> de perl.</p>


<p>Initialiser un tableau de chaînes sans avec une syntaxe légère, très lisible quand on n'a que des mots, le séparateur étant les caractères blancs, incluant le retour à la ligne&nbsp;:</p>
<pre>
@tableau = qw( mot1 mot2 mot3 );
</pre>


<p>Passer du mode script de quelques lignes au mode développement long. Cela vous oblige à déclarer et initialiser toutes vos variables entre autre&nbsp;:</p>
<pre>
use strict;
</pre>


<p>Le debug facile&nbsp;:</p>
<pre>
use Data::Dumper;
# variable peut être n'importe quoi, toutes les sous-sous parties sont écrites :
print Dumper($variable,\@variable);
print Dumper(\%variable);
</pre>


<p>L'aide en ligne facile, ça fait tout de suite classe dans un script (<a href="http://perldoc.perl.org/perlpod.html" hreflang="en">format</a>)&nbsp;:</p>
<pre>
use Pod::Usage;
# imprime le contenu de SYNOPSIS et termine le programme avec un code d'erreur 1
pod2usage(1);
# attention, pod est chatouilleux sur les retours à la ligne avant et après les =

=head1 SYNOPSIS

commande  [-o|--option=&lt;valeur&gt;]

=cut
</pre>


<p>La page de manuel facile (pas seulement le SYNOPSIS comme précédemment)&nbsp;:</p>
<pre>
# où fichier.pl est le code précédent
$ pod2man fichier.pl &gt; fichier.1
$ man ./fichier.1
</pre>


<p>Et le dernier pour la route, le parsing d'options facile, très classe, surtout en combinaison avec pod2usage (<a href="http://perldoc.perl.org/Getopt/Long.html#Summary-of-Option-Specifications" hreflang="en">format</a>)&nbsp;:</p>
<pre>
use Getopt::Long;
my $options = {
        option =&gt; 'defaut'
};
GetOptions( $options, qw(
        option|o=s
));
</pre>

<p></p><p>Si vous avez aimé, il y a aussi : </p><ol><li><a href='http://linux-attitude.fr/post/un-seul-langage-pour-les-scripts' rel='bookmark' title='Permanent Link: Un seul langage pour les scripts'>Un seul langage pour les scripts</a></li>
<li><a href='http://linux-attitude.fr/post/expression-reguliere' rel='bookmark' title='Permanent Link: Expression régulière'>Expression régulière</a></li>
<li><a href='http://linux-attitude.fr/post/le-perl-completement-inutile' rel='bookmark' title='Permanent Link: Le perl complètement inutile'>Le perl complètement inutile</a></li>
</ol>
	Tags:<a href="http://linux-attitude.fr/tag/code" title="Code" rel="tag">Code</a>, <a href="http://linux-attitude.fr/tag/perl" title="Perl" rel="tag">Perl</a>, <a href="http://linux-attitude.fr/tag/scripting" title="Scripting" rel="tag">Scripting</a><br />
]]></content:encoded>
			<wfw:commentRss>http://linux-attitude.fr/post/perles/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Vectorisation</title>
		<link>http://linux-attitude.fr/post/vectorisation?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=vectorisation</link>
		<comments>http://linux-attitude.fr/post/vectorisation#comments</comments>
		<pubDate>Thu, 13 Nov 2008 23:44:00 +0000</pubDate>
		<dc:creator>peck</dc:creator>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[planet-libre]]></category>

		<guid isPermaLink="false">http://linux-attitude.fr/post/vectorisation</guid>
		<description><![CDATA[Niveau&#160;: &#160;&#160;&#160;&#160;&#160; Résumé&#160;: SSE Aujourd'hui l'avenir (et un peu le passé aussi). Vous souvenez-vous des cray&#160;? Ces machines surpuissantes qui avaient au moins la puissance de calcul d'une TI92. Comment faisaient-ils pour avoir une telle puissance&#160;? Il faisaient des calculs identiques en parallèle et multipliaient ainsi par n le nombre d'opérations. C'est ce qu'on appelle [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Niveau</strong>&nbsp;: <span class="s">&nbsp;</span><span class="s">&nbsp;</span><span class="s">&nbsp;</span><span class="s">&nbsp;</span><span class="e">&nbsp;</span><br />
<strong>Résumé</strong>&nbsp;: SSE</p>


<p>Aujourd'hui l'avenir (et un peu le passé aussi). Vous souvenez-vous des cray&nbsp;? Ces machines surpuissantes qui avaient au moins la puissance de calcul d'une TI92. Comment faisaient-ils pour avoir une telle puissance&nbsp;? Il faisaient des calculs identiques en parallèle et multipliaient ainsi par n le nombre d'opérations. C'est ce qu'on appelle une <a href="http://en.wikipedia.org/wiki/Vector_processor" hreflang="en">architecture vectorielle</a>.</p>


<p>Intel dans sa grande bonté a commencé à mettre en place une telle architecture dans ses processeurs. Tout d'abord avec les instructions <a href="http://fr.wikipedia.org/wiki/MMX_(processeur)" hreflang="fr">MMX</a>. Ces instructions utilisent les registres servant au calcul en virgule flottante (quelques transistors de gagnés), lesquels font 64 bits. On peut ainsi exécuter des instructions identiques simultanément sur 4 fois 1 octets ou sur 2 fois 2 octets. Ce qui est bien, mais pas top.</p>


<p>Le <a href="http://fr.wikipedia.org/wiki/Streaming_SIMD_Extensions" hreflang="fr">SSE</a> est une amélioration du concept. Tout d'abord on utilise de nouveaux registres (ce qui libère les calculs en virgule flottante) et on leur donne une taille de 128bits. Du coup on peut manipuler deux fois plus d'entiers et cela devient intéressant pour les flottants. On peut caser 4 flottants simple précision et 2 flottants double précision dans 128 bits. Le SSE s'arrête aux 4 flottants et le SSE2 ajoute quasiment toutes les autres combinaisons possibles (flottants et entiers).</p>



<h3>Version normale</h3>

<p>Comme nous sommes joueurs, nous allons jouer à améliorer notre code de <a href="http://linux-attitude.fr/post/Le-retour-de-la-vengeance-de-la-bidouille">calcul de Mandelbrot</a>. Mais cette fois, nous allons le laisser tourner sous linux.
Comme ne sommes plus dans notre monde, mais sur un vrai système, nous allons devoir faire quelques modifications. Nous allons continuer à écrire directement en mémoire, donc on garde nos fonctions d'écriture de pixels, mais cette fois dans une mémoire allouée avec malloc. Ajoutons le support de l'écriture d'un des format d'image les plus simples, le BMP, pour stocker notre image dans un fichier et pouvoir admirer le résultat. Pour cela on prend la première <a href="http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html" hreflang="en">documentation</a> venue.</p>
<pre>
/* Ecriture de l'image dans un fichier bitmap (on écrit l'en-tête kivabien puis les données) */
void writebitmap(char* filename, char*bitmap, int width, int height);
</pre>


<p>Et voilà, cela nous donne une première version du code quasiment identique à la précédente et donc non optimisée pour le SSE (disponible à la fin de l'article).</p>


<p><span id="more-25"></span></p>


<h3>Version SSE</h3>

<p>Commençons à travailler en découvrant les instructions du sse. Celles-ci sont rendues disponibles en C à travers les en-têtes qui vont bien. Ouf, cela nous évitera de coder directement en assembleur&nbsp;:</p>
<pre>
/* mm=mmx   xmm=sse  emm=sse2   pmm=sse3 (rien que de très logique ...) */
#include &lt;emmintrin.h&gt;
</pre>


<p>Il est possible de lire ces en-têtes pour savoir quelles sont les instructions et les types disponibles. C'est un peu long et assez peu instructif lorsqu'on ne connait pas la base du sse, mais ils peuvent servir de référence quand on sait ce qu'on cherche. Ils se trouvent dans /usr/lib/gcc/x86_64-linux-gnu/4.1.2/include/ (à adapter à votre cas). Les types et fonctions du sse sont codifiés.</p>


<p>Les opérations sont construites comme suit _mm_XXXX_YY&nbsp;:</p>
<ul>
<li>_mm est un préfixe</li>
<li>XXXX est le nom de l'instruction en assembleur (à quelques variations près)</li>
<li>YY est le type des opérandes manipulés&nbsp;:
<ul>
<li>sd&nbsp;: double seul (1 double qui n'occupe pas tout le registre)</li>
<li>pd&nbsp;: double packé (2 doubles dans un registre)</li>
<li>ss&nbsp;: flottant seul (1 flottant qui n'occupe pas tout le registre)</li>
<li>ps&nbsp;: flottant packé (4 flottants dans un registre)</li>
<li>...</li>
</ul></li>
</ul>

<p>Les types de données sont aussi définis selon un certain schéma&nbsp;:</p>
<ul>
<li>__m128&nbsp;: registre de 128 bits</li>
<li>__m128i&nbsp;: registre de 128 bits destiné au calcul entier</li>
<li>__m128d&nbsp;: registre de 128 bits destiné au calcul flottant</li>
<li>__v4sf&nbsp;: vecteur de 4 simple flotants (4 * 32 bits)</li>
<li>__v2di&nbsp;: vecteur de 2 entiers doubles (2 * 64 bits)</li>
<li>...</li>
</ul>

<p>Notez que d'un point de vue binaire, ils sont tous identiques et font 128bits.</p>




<p>Maintenant nous allons modifier le code pour qu'il calcule les points 4 par 4. On va donc changer nos 2 fonctions de calcul en&nbsp;:</p>
<pre>
/* conversion de coordonnées 4 points par 4 */
void convert_i2d4(int x, int y, __v4sf *ppx, __v4sf *ppy)
/* une itération du calcul pour 4 points */
__v4sf iteration4(__v4sf cx, __v4sf cy, __v4sf *ppx, __v4sf *ppy)
</pre>


<p>On utilise un des types précédents&nbsp;: __v4sf. C'est un <strong>v</strong>ecteur de 128 bits contenant <strong>4</strong> <strong>f</strong>lottants <strong>s</strong>imple précision.
Mais comme nous n'utilisons plus des types standards du C, nous devrons utiliser des fonctions à la place des opérateurs +, -, / et *, ce qui rend le code un peu plus lourd à lire et à écrire, par exemple&nbsp;:</p>
<pre>
        /* t = p^2 + C */
        tx = _mm_add_ps( _mm_sub_ps(_mm_mul_ps( *ppx, *ppx),  _mm_mul_ps(*ppy, *ppy)) , cx);
        ty = _mm_add_ps( _mm_mul_ps(two, _mm_mul_ps(*ppx, *ppy)), cy);
</pre>


<p>On y ajoute quelques constantes globales du fait que les constantes n'existent pas pour les type vectoriels&nbsp;:</p>
<pre>
__v4sf two = _mm_set_ps(2,2,2,2);
</pre>



<p>Et enfin, on doit adapter un peu notre algorithme pour qu'il ne s'arrête que lorsque les 4 points ont tous dépassés le seuil. Pour que la bonne couleur soit bien enregistrée séparément pour chaque point, on va faire la comparaison sur les 4 points grâce à _mm_cmple_ps qui renvoit 0 ou -1 sous forme de vecteur d'entier.
Il nous suffit d'additionner les résultats pour récupérer la couleur de chaque point telle que nous l'avions définie plus tôt.</p>
<pre>
    /* calcul */
    radius = iteration4(cx, cy, &amp;px, &amp;py);
    /* on compare au rayon max, la valeur de retour est constituées de 4 entiers valant 0 ou -1 */
    jj = (__m128i)_mm_cmple_ps(radius, eight);
    /* et on additionne tous les -1 */
    ii = _mm_add_epi32(ii, jj);
    /* un int fait 32 bits sur les i386 et les x86-64 ... */
    ptr = (int*)&amp;jj;
    /* il y a un moment où ce n'est plus la peine de calculer */
    if(ptr[0] == 0 &amp;&amp; ptr[1] == 0 &amp;&amp; ptr[2] == 0 &amp;&amp; ptr[3] == 0)
        break;
 </pre>


<p>Le code complet est disponible à la fin de l'article.</p>


<h3>Résultat</h3>

<p>Mais cela vaut-il le coup&nbsp;? Oui, et nous allons le montrer. Tout d'abord sur un processeur 32 bits. Nous allons comparer plusieurs combinaisons. Avec sse, sans sse, avec différents niveaux d'optimisation, avec notre code spécifique, et sans.</p>


<center>
<table border="1">
<caption><b>exécution sur un i386 (en secondes)</b></caption>
<tr>
<td  valign="bottom"  align="left"><b>Optimization</b></td>
<td colspan="2" rowspan="1" align="center"><b>-</b></td>
<td colspan="2" rowspan="1" align="center"><b>-O1</b></td>
<td colspan="2" rowspan="1" align="center"><b>-O2</b></td>
<td colspan="2" rowspan="1" align="center"><b>-O3</b></td>
</tr>
<tr>
<td align="left" ><b>Gcc parameter</b></td>
<td align="center"><b>-</b></td>
<td align="center"><b> -msse2</b></td>
<td align="center"><b>-</b></td>
<td align="center"><b> -msse2</b></td>
<td align="center"><b>-</b></td>
<td align="center"><b> -msse2</b></td>
<td align="center"><b>-</b></td>
<td align="center"><b> -msse2</b></td>
</tr>
<tr>
<td align="left" ><b>normal code</b></td>
<td align="center" >6.20</td>
<td align="center" >6.40</td>
<td align="center" >3.4</td>
<td align="center" >3.5</td>
<td align="center" >2.95</td>
<td align="center" >2.65</td>
<td align="center" >2.60</td>
<td align="center" >2.35</td>
</tr>
<tr>
<td align="left" ><b>SSE code</b></td>
<td align="center" >-</td>
<td align="center" >4.75</td>
<td align="center" >-</td>
<td align="center" >0.91</td>
<td align="center" >-</td>
<td align="center" >0.55</td>
<td align="center" >-</td>
<td align="center" >0.50</td>
</tr>
</table>
</center>



<p>Les résultats sont assez parlants. Que peut-on en déduire&nbsp;? Que le sse ne semble pas très performant en l'absence d'optimisation (nosse.c et colonnes sans  optimisation ou avec -O1).</p>


<p>Ensuite, que même sans utiliser le sse, gcc est lui-même capable de nous faire gagner beaucoup en performances avec ses optimisations. Mais gardez à l'esprit que cela est surtout valable parce qu'on utilise beaucoup de calculs en virgule flottantes et que nos fonctions prennent de gros arguments.</p>


<p>Enfin, que la modification de notre algorithme pour profiter du sse nous a permis de faire mieux que x4 sur du code déjà optimisé par gcc. En effet, gcc optimise ce qu'il peut, mais il lui est très difficile de changer un algorithme. Il est prévu que de nouvelles versions de gcc sachent <a href="http://gcc.gnu.org/projects/tree-ssa/vectorization.html" hreflang="en">vectoriser automatiquement</a> des calculs, mais il faudra tout de même faire attention à la façon dont on code.</p>

<center>
<table border="1">
<caption><b>exécution sur un x86-64 (en secondes)</b></caption>
<tr>
<td align="left"><b>Optimization</b></td>
<td colspan="2" rowspan="1" align="center" ><b>-</b></td>
<td colspan="2" rowspan="1" align="center" ><b> -O1</b></td>
<td colspan="2" rowspan="1" align="center" ><b> -O2</b></td>
<td colspan="2" rowspan="1" align="center" ><b> -O3</b></td>
</tr>
<tr>
<td align="left" ><b>Gcc parameter</b></td>
<td align="center" ><b>-</b></td>
<td align="center" ><b> -msse2</b></td>
<td align="center" ><b>-</b></td>
<td align="center" ><b> -msse2</b></td>
<td align="center" ><b>-</b></td>
<td align="center" ><b> -msse2</b></td>
<td align="center" ><b>-</b></td>
<td align="center" ><b> -msse2</b></td>
</tr>
<tr>
<td align="left" ><b>normal code</b></td>
<td align="center" >2.10</td>
<td align="center" >2.10</td>
<td align="center" >1.05</td>
<td align="center" >1.05</td>
<td align="center" >0.76</td>
<td align="center" >0.76</td>
<td align="center" >0.72</td>
<td align="center" >0.72</td>
</tr>
<tr>
<td align="left" ><b>SSE code</b></td>
<td align="center" >2.10</td>
<td align="center" >2.10</td>
<td align="center" >0.30</td>
<td align="center" >0.30</td>
<td align="center" >0.30</td>
<td align="center" >0.30</td>
<td align="center" >0.30</td>
<td align="center" >0.30</td>
</tr>
</table>
</center>



<p>Et enfin regardons le même code sur un processeur 64 bits. Tout d'abord on remarque que ma machine 64 bits est plus puissante que la précédente :-) Ensuite, on constate qu'il n'y a pas de différence entre l'option sse et sont absence. Et heureusement puisque tous les processeurs 64 bits ont le sse par défaut et le compilateur ne se gène pas pour l'ajouter lui-même. On remarque que comme pour le code 32 bits, il y a un facteur 3 entre l'option -O3 et l'absence d'optimisation pour le code normal.</p>


<p>Par contre, les différents niveaux d'optimisation au delà de -O1 n'influent plus sur la vitesse d'exécution. Je n'ai pas d'explications à cela.</p>


<p>Et enfin on constate que le sse a réduit la différence de puissance entre les 2 machines.</p>



<p>Pour ceux qui se demanderaient si on mesure les bonnes choses, un test écrivant tous les pixels en noir a été fait, et il ne prend pas  plus de 0.03s sur ces plate-formes (toujours moins de 10%). Ce qui veut dire que nos comparaisons sont pertinentes (et qu'il n'est pas nécessaire d'optimiser cette partie).</p>


<p>Vous voulez tout savoir sur le SSE&nbsp;? Arstechnica a publié un très bon <a href="http://arstechnica.com/articles/paedia/cpu/simd.ars" hreflang="en">article</a> sur le sujet. Sinon, vous avez toujours la liste détaillée des <a href="http://www.intel80386.com/simd/mmx2-doc.html" hreflang="en">instructions</a>.</p>


<p><strong>PS :</strong> Intel trouvant que la vectorisation est une bonne chose et voyant que tout cela marche bien a prévu d'ajouter les instructions <a href="http://software.intel.com/sites/avx/" hreflang="en">AVX</a> dans ses prochains processeurs. En gros, c'est la même chose que le SSE mais sur 256 bits cette fois.</p>


<p><strong>PJ</strong>&nbsp;:</p>
<ul>
<li><a href="http://linux-attitude.fr/public/Code/mandel3/mandel-nosse.c">mandel-nosse.c</a></li>
<li><a href="http://linux-attitude.fr/public/Code/mandel3/mandel-sse.c">mandel-sse.c</a></li>
</ul>

<p></p><p>Si vous avez aimé, il y a aussi : </p><ol><li><a href='http://linux-attitude.fr/post/le-retour-de-la-bidouille' rel='bookmark' title='Permanent Link: Le retour de la bidouille'>Le retour de la bidouille</a></li>
<li><a href='http://linux-attitude.fr/post/profiling-et-developpement' rel='bookmark' title='Permanent Link: Profiling et développement'>Profiling et développement</a></li>
<li><a href='http://linux-attitude.fr/post/le-retour-de-la-vengeance-de-la-bidouille' rel='bookmark' title='Permanent Link: Le retour de la vengeance de la bidouille'>Le retour de la vengeance de la bidouille</a></li>
</ol>
	Tags:<a href="http://linux-attitude.fr/tag/c" title="C" rel="tag">C</a>, <a href="http://linux-attitude.fr/tag/code" title="Code" rel="tag">Code</a>, <a href="http://linux-attitude.fr/tag/planet-libre" title="planet-libre" rel="tag">planet-libre</a><br />
]]></content:encoded>
			<wfw:commentRss>http://linux-attitude.fr/post/vectorisation/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Le retour de la vengeance de la bidouille</title>
		<link>http://linux-attitude.fr/post/le-retour-de-la-vengeance-de-la-bidouille?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=le-retour-de-la-vengeance-de-la-bidouille</link>
		<comments>http://linux-attitude.fr/post/le-retour-de-la-vengeance-de-la-bidouille#comments</comments>
		<pubDate>Wed, 05 Nov 2008 18:28:00 +0000</pubDate>
		<dc:creator>peck</dc:creator>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Noyau]]></category>
		<category><![CDATA[planet-libre]]></category>

		<guid isPermaLink="false">http://linux-attitude.fr/post/le-retour-de-la-vengeance-de-la-bidouille</guid>
		<description><![CDATA[Niveau&#160;: &#160;&#160;&#160;&#160;&#160; Résumé&#160;: grub&#160;; noyau&#160;; multiboot&#160;; mandelbrot Suite à de précédentes bidouilles, on veut faire mieux. Multiboot Donc grub peut booter plusieurs formats, l'un d'entre eux est le multiboot, un format de fichier bootable défini par les développeurs de grub espérant qu'il soit adopté par différentes distributions. Il souffre d'un certain nombre de faiblesses et [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Niveau</strong>&nbsp;: <span class="s">&nbsp;</span><span class="s">&nbsp;</span><span class="s">&nbsp;</span><span class="s">&nbsp;</span><span class="e">&nbsp;</span><br />
<strong>Résumé</strong>&nbsp;: grub&nbsp;; noyau&nbsp;; multiboot&nbsp;; mandelbrot</p>


<p>Suite à de précédentes <a href="http://linux-attitude.fr/post/Le-retour-de-la-bidouille">bidouilles</a>, on veut faire mieux.</p>


<h3>Multiboot</h3>

<p>Donc grub peut booter plusieurs formats, l'un d'entre eux est le multiboot, un format de fichier bootable défini par les développeurs de grub espérant qu'il soit adopté par différentes distributions. Il souffre d'un certain nombre de faiblesses et n'est pas encore vraiment un standard. D'autant que grub lui-même ne supporte pas encore complètement la <a href="http://www.gnu.org/software/grub/manual/multiboot/multiboot.html" hreflang="en">spécification</a> pourtant très courte. Si vous ne voulez pas lire toute la spécification, allez directement au chapitre 4 où on trouve un code d'exemple prêt à compiler pour vous lancer dans le développement.</p>


<p>Vous trouverez un autre exemple chez quelqu'un qui s'est amusé à coder un <a href="http://www.erikyyy.de/invaders/" hreflang="en">space invaders</a> bootable directement à partir de la spécification multiboot.</p>


<p>Alors c'est parti, reprenons notre projet précédent.
Faites attention, le processeur est dans un état particulier lorsqu'on vous laisse la main (lisez le chapitre 3.2), à vous de vous en accommoder.</p>



<h3>Mode graphique</h3>

<p>Tout d'abord on voudrait changer le mode graphique, et si possible pour mieux que la première fois. Pour cela, on va utiliser le standard <a href="http://en.wikipedia.org/wiki/VESA_BIOS_Extensions" hreflang="en">vesa 2.0</a> qui standardise des modes avec une plus grande définition et qui ajoute de nouvelles fonctions au bios pour manipuler la carte.</p>


<p>Choisissons le mode 0x118 (1024x768x24) avec 16 millions de couleurs. Plus besoin de gérer un palette puisqu'on écrit directement les couleurs RGB à utiliser pour le pixel. On aura tout de même une fonction de palette puisqu'on n'utilise qu'un petit nombre de couleurs placées sur une échelle.</p>


<p>Le problème c'est que vesa n'a pas prévu qu'on puisse appeler les fonctions de la carte en mode protégé sans passer au moins une fois par une interruption en mode réel. Multiboot est bien gentil, il a prévu le cas, malheureusement, grub n'implémente pas les fonctions qu'il devrait pour le faire pour nous. Un <a href="http://www.smk.co.za/software/vbe-grub/" hreflang="en">patch</a> est disponible pour ceux qui voudrait quand même cette fonctionnalité.</p>


<p><span id="more-23"></span></p>


<p>Il nous faudra donc faire nous-même ces interruptions. Pour survivre au mode réel nous devrons être sous le premier Mo de RAM, or grub nous place juste au dessus (en 0x100000). Notre premier bout de code aura donc pour but de nous déplacer en dessous. Pour cela on commence par prendre l'en-tête multiboot (beaucoup plus simple que pour les zImage) et on écrit le code pour se déplacer en 0x1000 (choix arbitraire)&nbsp;:</p>
<pre>
        /* on se copie ailleurs */
        mov     $KERNEL_SIZE,%ecx /* en octets */
        mov     $KERNEL_ORIG,%esi /* source juste après ce secteur */
        mov     $KERNEL_DEST,%edi /* destination sous les 1Mo */
        rep movsw

        /* et on va dans notre nouveau noyau */
        ljmp    $0x8,$KERNEL_DEST
</pre>


<p>Bien, maintenant, il faut passer du mode protégé au mode réel et inversement pour pouvoir faire des interruptions. Choisissons comme toujours de ne pas réinventer la roue et repiquons le code de grub qui dispose déjà de ces fonctions en assembleur. Elles s'appellent real_to_prot et prot_to_real. Pour ceux qui voudraient comprendre comment faire, c'est <a href="http://www.sudleyplace.com/pmtorm.html" hreflang="en">partout</a> sur le net. Comme le <a href="http://www.internals.com/articles/protmode/protmode.htm" hreflang="en">mode protégé</a> a besoin de descripteurs de segment, on n'oublie pas de les définir aussi (lire la fin du fichier asm.S) .</p>


<p>Du coup on peut utiliser 2 fonctions vesa bien utiles dans notre code, set_mode pour mettre en place notre mode graphique, et get_modeinfo pour savoir comment l'utiliser. En effet, on voudrait, comme avant pouvoir accéder directement à la mémoire vidéo pour y mettre nos pixels. La fonction get_modeinfo est là pour ça, elle utilise la fonction <a href="http://www.ctyme.com/intr/rb-0274.htm" hreflang="en">0x4F01</a> du bios. On lui passe une adresse dans laquelle elle va écrire une structure de données nous informant sur le mode choisi. On choisit l'adresse de cette structure au pif puisqu'on n'a pas de gestion de la mémoire et qu'on a toute la ram pour nous. On prend 8000:0000.</p>


<p>La structure renvoyée contient l'adresse du mapping de la mémoire vidéo en 28h, ainsi que ses dimensions. Cela nos permettrait de changer le mode dans notre code facilement (notez les conversions de type pour lire le bon nombre d'octets)&nbsp;:</p>
<pre>
        /* on récupère les infos sur le mode qui nous intéresse */
        get_modeinfo(0x118);
        /* conversion de l'adresse du format [segment:offset] au format à plat */
        data = (MODEINFO_SEG &lt;&lt; 4)+MODEINFO_OFF;
        /* on joue sur les converstions pour la lecture du bon nombre d'octets en ram */
        video = (unsigned char*) *(unsigned int*)(data + 0x28);
        width = (int) *(unsigned short*)(data + 0x12);
        height = (int) *(unsigned short*)(data + 0x14);
</pre>



<h3>Code</h3>


<p>On repart du code de l'article précédent. On y apporte quelques modifications techniques liées au changement de mode. Par exemple l'écriture d'un pixel se résume à l'écriture des 3 couleurs au bon endroit en ram&nbsp;:</p>
<pre>
        int pos = bpp*(x + y*width);
        /* et on ecrit directement en mémoire vidéo */
        video[pos] = r;
        video[pos+1] = g;
        video[pos+2] = b;
</pre>


<p>Pour ceux qui veulent aller plus loin dans la compréhension du mode protégé, ça aurait pu aussi être l'occasion de mettre en place un segment séparé pour écrire en mémoire vidéo avec la commande <a href="http://www.jamesmolloy.co.uk/tutorial_html/4.-The%20GDT%20and%20IDT.html" hreflang="en">lgdt</a>.</p>


<p>On peut imaginer y ajouter des tâches, par exemple une sorte de thread pour gérer le clavier. Mais la ça commence à devenir complexe et il vaudrait mieux lire des OS existants pour bien comprendre tout ça. Par exemple il existe <a href="http://www.minix3.org/" hreflang="en">minix</a> qui a été fait dans un but pédagogique.</p>


<h3>Compilation</h3>

<p>Une autre chose change, c'est la compilation. Puisque le format multiboot est plus simple, nous n'avons plus besoin du script setup.ld. Par contre, puisque notre binaire contient deux morceaux (souvenez vous de la copie sous les 1Mo en début d'article), on devra modifier notre compilation.</p>


<p>Ce qui se résume en 2 partie, une pour le code de copie fait pour tourner en 0x100000&nbsp;:</p>
<pre>
LDFLAGS1=&quot;-nostdlib -Wl,-N -Wl,-Ttext -Wl,100000 -m32&quot;
gcc $LDFLAGS1 -o boot.exec boot.o
objcopy -O binary boot.exec boot
</pre>


<p>Et une partie contenant tout le reste du code, qu'on va placer en 0x1000&nbsp;:</p>
<pre>
LDFLAGS2=&quot;-nostdlib -Wl,-N -Wl,-Ttext -Wl,1000 -m32&quot;
gcc $LDFLAGS2 -o kernel.exec start.o asm.o kernel.o debug.o
objcopy -O binary kernel.exec kernel
</pre>


<p>Et enfin une dernière copie qui va concaténer les 2 binaires&nbsp;:</p>
<pre>
# on force la taille pour éviter les problèmes avec le bootloader
dd if=/dev/zero of=mandel2 bs=256 count=32 2&gt;/dev/null 
# la premiere partie
dd if=boot bs=256 count=1 of=mandel2 conv=notrunc 2&gt;/dev/null
# la 2e partie a une position fixée dans le code de la première (boot.S + header.h)
dd if=kernel bs=256 seek=1 of=mandel2 conv=notrunc 2&gt;/dev/null
</pre>


<h3>Run</h3>


<p>Encore une fois, il suffit de préciser le fichier à grub pour qu'il le boote&nbsp;:</p>
<pre>
kernel         (hd0,0)/boot/mandel
</pre>


<p>Vous trouverez les sources en pièce jointe de cet article, et en complément une image disque contenant les 2 projets avec un grub installé qui fonctionne directement sur <a href="http://bellard.org/qemu/" hreflang="en">qemu</a>.</p>
<pre>
$ qemu image.dd
</pre>


<p>Bravo à vous&nbsp;!</p>


<p><img src="http://linux-attitude.fr/public/Schemas/mandel2.png" alt="mandel2.png" style="display:block; margin:0 auto;" title="mandel2.png, oct 2008" /></p>


<p><strong>PJ</strong>&nbsp;:</p>
<ul>
<li><a href="http://linux-attitude.fr/public/Code/mandel2/asm.S">asm.S</a></li>
<li><a href="http://linux-attitude.fr/public/Code/mandel2/boot.S">boot.S</a></li>
<li><a href="http://linux-attitude.fr/public/Code/mandel2/compile.sh">compile.sh</a></li>
<li><a href="http://linux-attitude.fr/public/Code/mandel2/debug.c">debug.c</a></li>
<li><a href="http://linux-attitude.fr/public/Code/mandel2/header.h">header.h</a></li>
<li><a href="http://linux-attitude.fr/public/Code/mandel2/kernel.c">kernel.c</a></li>
<li><a href="http://linux-attitude.fr/public/Code/mandel2/start.S">start.S</a></li>
<li><a href="http://linux-attitude.fr/public/Code/mandel.dd">mandel.dd</a></li>
</ul>

<p></p><p>Si vous avez aimé, il y a aussi : </p><ol><li><a href='http://linux-attitude.fr/post/le-retour-de-la-bidouille' rel='bookmark' title='Permanent Link: Le retour de la bidouille'>Le retour de la bidouille</a></li>
<li><a href='http://linux-attitude.fr/post/en-vrac-25' rel='bookmark' title='Permanent Link: En vrac (25)'>En vrac (25)</a></li>
<li><a href='http://linux-attitude.fr/post/en-vrac-18' rel='bookmark' title='Permanent Link: En vrac (18)'>En vrac (18)</a></li>
</ol>
	Tags:<a href="http://linux-attitude.fr/tag/c" title="C" rel="tag">C</a>, <a href="http://linux-attitude.fr/tag/code" title="Code" rel="tag">Code</a>, <a href="http://linux-attitude.fr/tag/noyau" title="Noyau" rel="tag">Noyau</a>, <a href="http://linux-attitude.fr/tag/planet-libre" title="planet-libre" rel="tag">planet-libre</a><br />
]]></content:encoded>
			<wfw:commentRss>http://linux-attitude.fr/post/le-retour-de-la-vengeance-de-la-bidouille/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Le retour de la bidouille</title>
		<link>http://linux-attitude.fr/post/le-retour-de-la-bidouille?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=le-retour-de-la-bidouille</link>
		<comments>http://linux-attitude.fr/post/le-retour-de-la-bidouille#comments</comments>
		<pubDate>Mon, 03 Nov 2008 18:51:00 +0000</pubDate>
		<dc:creator>peck</dc:creator>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Noyau]]></category>
		<category><![CDATA[planet-libre]]></category>

		<guid isPermaLink="false">http://linux-attitude.fr/post/le-retour-de-la-bidouille</guid>
		<description><![CDATA[Niveau&#160;: &#160;&#160;&#160;&#160;&#160; Résumé&#160;: grub&#160;; noyau&#160;; mandelbrot Vous souvenez-vous du bon vieux temps&#160;? Je vous parle du temps de l'autoexec.bat et des driver de quelques ko, le temps où on écrivait directement en mémoire vidéo, le temps de l'assembleur, du mode réel et des interruptions. Hé bien cette époque bien que révolue nous a laissé des [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Niveau</strong>&nbsp;: <span class="s">&nbsp;</span><span class="s">&nbsp;</span><span class="s">&nbsp;</span><span class="s">&nbsp;</span><span class="e">&nbsp;</span><br />
<strong>Résumé</strong>&nbsp;: grub&nbsp;; noyau&nbsp;; mandelbrot</p>


<p>Vous souvenez-vous du bon vieux temps&nbsp;? Je vous parle du temps de l'autoexec.bat et des driver de quelques ko, le temps où on écrivait directement en mémoire vidéo, le temps de l'assembleur, du mode réel et des interruptions.</p>


<p>Hé bien cette époque bien que révolue nous a laissé des traces. Il est toujours possible de coder sans noyau, de faire son propre noyau, de se passer de système d'exploitation ou de booter directement sur une application en quelques secondes.</p>


<h3>Boot loader</h3>


<p>Choisissons un bootloader, <a href="http://linux-attitude.fr/post/Chargeeeez">grub</a> par exemple, nous allons lui demander de lancer notre programme. Avec grub il existe plusieurs méthodes pour charger un os, entre autres&nbsp;:</p>

<ul>
<li><strong>Chainloader</strong> charge 512 octets, met le processeur en mode en mode réel, tel qu'il serait après le passage du bios. Pratique pour partir de zéro, mais 512 octet c'est un peu petit et nous n'avons pas vraiment envie de gérer le chargement de fichier sur le disque en plus.</li>
</ul>
<ul>
<li><strong>Linux</strong> charge un noyau au format zimage ou bzimage et lui passe le contrôle en mode réel 16 bits. Il est possible de charger des fichiers de plusieurs méga avec le format bzimage.</li>
</ul>
<ul>
<li><strong>Multiboot</strong> charge un noyau au format multiboot et lui passe le contrôle en mode protégé 32 bits après un certain nombre d'initialisations. On utilisera cette méthode une prochaine fois.</li>
</ul>


<h3>zImage</h3>


<p>On commence par une solution simple à mettre en place, un exécutable au format zImage. Le format est décrit dans la documentation des sources du noyau linux&nbsp;:  <a href="http://lxr.linux.no/linux+v2.6.26.6/Documentation/i386/boot.txt" hreflang="en">/usr/src/linux-2.X.XX/Documentation/i386/boot.txt</a>. C'est dans ce format que nous allons développer un binaire bootable directement.</p>


<p>Le bootloader (grub) lit l'en-tête du fichier zImage puis pose la première section (attention, taille limitée à quelques dizaines de ko) à l'adresse 0x90000, Ensuite, il lit le reste du fichier et le place en 0x1000 (pour une zImage, limitée à 512ko) ou en 0x100000 (pour une bzImage). Ensuite, dans le cas de linux, le noyau s'amuse à redéplacer tout ça selon un processus plus ou moins compliqué <a href="http://tldp.org/HOWTO/Linux-Init-HOWTO-2.html" hreflang="en">décrit ici</a> et <a href="http://www.tldp.org/HOWTO/Linux-i386-Boot-Code-HOWTO/index.html" hreflang="en">ici</a>. Attention, ne prenez pas ces documents pour argent comptant, ils sont un peu dépassés. En pratique nous n'irons pas si loin puisque nous resterons en mode réel et que 512ko nous seront largement suffisant (640Ko ça devrait suffire pour tout le monde !).</p>


<p>Nous allons donc faire simple et récupérer le code de boot de linux pour lancer notre petit projet. Le fichier qui contient le code de lancement ainsi que les en-têtes qui vont bien s'appelle <a href="http://lxr.linux.no/linux+v2.6.26.6/arch/x86/boot/header.S">header.S</a>.</p>


<p>Attention, il est écrit en assembleur GNU (gas), celui-ci diffère beaucoup de l'assembleur intel. Le point le plus important est que les arguments sont inversés (source puis destination).
<strong> header.S </strong>
Apportons quelques modifications au fichier pour qu'il corresponde à nos besoins&nbsp;:</p>


<p><span id="more-22"></span></p>

<ul>
<li>suppression des include</li>
<li>changement de la version de format pour se simplifier la vie</li>
</ul>
<pre>
-               .word   0x0209          # header version number (&gt;= 0x0105)
+               .word   0x0207          # header version number (&gt;= 0x0105)
</pre>

<ul>
<li>pas de payload (il faudra tout faire dans 4 secteurs (2Ko)</li>
</ul>
<pre>
-payload_offset:                .long input_data
-payload_length:                .long input_data_end-input_data
+payload_offset:                .long 0
+payload_length:                .long 0
</pre>

<ul>
<li>suppression des appels à puts pour éviter les dépendances inutiles</li>
</ul>
<pre>
-       calll   puts
</pre>

<ul>
<li>ajout des #define correspondant à nos besoins (surtout pour que ça compile)</li>
</ul>
<pre>
+#define DEF_SYSSEG       0x1000                                  
+#define DEF_SYSSIZE  0x200
+#define ASK_VGA          0xfffd          /* ask for it at bootup */
+#define STACK_SIZE      512     /* Minimum number of bytes for stack */
+#define CONFIG_PHYSICAL_ALIGN 0x200000
+#define COMMAND_LINE_SIZE 2048
</pre>


<p>Ce fichier se termine par un appel à la fonction cmain qu'on codera en C, bien plus sympa à écrire que l'assembleur.</p>



<h3>Fractint</h3>


<p>Comme nous voulons avoir des résultats rapidement, nous allons choisir un projet simple, dessiner l'<a href="http://fr.wikipedia.org/wiki/Ensemble_de_Mandelbrot" hreflang="fr">ensemble de Mandelbrot</a> en vga. Résumons l'algorithme en 3 étapes&nbsp;: initialiser le mode graphique, afficher la fractale et attendre.</p>


<p>On utilise gcc (du projet gnu pour ceux qui suivent ;-), mais son compilateur C ne génère que de l'assembleur 32 bits. Donc la ruse de sioux est de préfixer le code par la directive suivante pour que l'assembleur le transforme code compatible 16 bits (nécessaire puisque nous sommes en mode réel)&nbsp;:</p>
<pre>
asm(&quot;.code16gcc&quot;);
</pre>


<p>Pour le mode graphique, choisissons le mode vga 0x13 (320x200x8), il est facile à utiliser car chaque pixel est mappé sur une adresse en RAM et donc adressable directement en mode réel à l'adresse 0xA0000 (A000:0000 pour les habitués de l'assembleur Intel 16 bits).
Les vieux savent bien que la carte graphique se gère avec <a href="http://www.ctyme.com/intr/int-10.htm" hreflang="en">l'interruption 10h</a>, fonction 0 pour choisir le mode vidéo.</p>


<p>C'est l'occasion de regarder comment on incruste de l'assembleur dans du code C avec gcc&nbsp;:</p>
<pre>
/* Je vous fais la version longue */
/* Le code assembleur paramétré (d'où les %%) */        asm (&quot;pushl %%ebp; pushw %%ds; int $0x10; popw %%ds; popl %%ebp&quot;
/* L'affectation des valeurs de retour  */                      :
/* L'affectation des paramètres */                              : &quot;a&quot; (mode)
/* La liste des registres à sauvegarder */                      : &quot;ebx&quot;, &quot;ecx&quot;, &quot;edx&quot;, &quot;esi&quot;, &quot;edi&quot;);
</pre>

<p>On trouve en ligne les détails pour faire du <a href="http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html" hreflang="en">code assembleur en gcc</a>. Remarquez que si vous n'avez aucun '_:_' (pas de paramètre) les <strong>%%</strong> doivent être remplacés par des <strong>%</strong>.</p>


<p>Maintenant, la fonction pour modifier la palette de couleurs (8 bits, 256 couleurs, une palette) avec des valeurs RGB sur 6 bits&nbsp;:</p>
<pre>
/* la technique d'origine pour modifier la palette vga */
/* on modifie directement des registres de la carte graphique */
        asm( &quot;outb %%al,%%dx&quot; : : &quot;a&quot; (idx), &quot;d&quot; (0x03c8) );
        asm( &quot;outb %%al,%%dx&quot; : : &quot;a&quot; (r), &quot;d&quot; (0x03c9) );
        asm( &quot;outb %%al,%%dx&quot; : : &quot;a&quot; (g), &quot;d&quot; (0x03c9) );
        asm( &quot;outb %%al,%%dx&quot; : : &quot;a&quot; (b), &quot;d&quot; (0x03c9) );
</pre>


<p>Et la fonction pour modifier un pixel&nbsp;:</p>
<pre>
/* calcul de la position du pixel en mémoire vidéo */
        int pos = 320*y+x;
/* Technique à la mano car gas ne supporte pas l'adressage 16bits avec des segments
 * - on passe par ds qu'on doit sauvegarder
 * - la mémoire vidéo du mode vga 13h commence en A000:0000
 */
        asm (&quot;push %%ds; movw %%ax,%%ds; movb %%cl,(%%bx); pop %%ds&quot;
                : : &quot;a&quot; (0xA000), &quot;b&quot;(pos), &quot;c&quot; (color));
</pre>


<p>Le reste est un algorithme basique de calcul de Mandelbrot. Pour rappeler quelques souvenirs&nbsp;:</p>
<ul>
<li>c = (x,y)</li>
<li>p0 = (0,0)</li>
<li>p(n+1) = p(n)^2 + c</li>
<li>plus p diverge vite, plus on est loin de l'ensemble (-&gt; choix de la couleur)</li>
</ul>

<h3>Compilation</h3>


<p>Il faut maintenant compiler tout ça pour en faire un binaire au format zImage.</p>


<p>On compile avec gcc en faisant bien attention à ne pas inclure la libc (et à spécifier une architecture 32 bits pour les amd64)&nbsp;:</p>
<pre>
CFLAGS=&quot;-fno-builtin -nostdinc -O2 -I. -Wall -m32&quot;
gcc $CFLAGS -c header.S
gcc $CFLAGS -c kernel.c
</pre>


<p>Ensuite, il faut faire l'édition de lien (on fait toujours attention à la libc), nous avons besoin d'un script (setup.ld) pour maîtriser complètement le binaire sortant. Il est repiqué de linux, avec une instruction supplémentaire à la fin pour aligner la taille du binaire à celle précisée dans l'en-tête&nbsp;:</p>
<pre>
LDFLAGS=&quot;-nostdlib -Wl,-N&quot;
gcc $LDFLAGS -o mandel.exec -T setup.ld header.o kernel.o
</pre>


<p>Et on extrait le binaire final de notre binaire au format <a href="http://en.wikipedia.org/wiki/Executable_and_Linkable_Format" hreflang="en">elf</a>&nbsp;:</p>
<pre>
# extraction du binaire
objcopy -O binary mandel mandel.exec
</pre>


<h3>Grub, qemu</h3>


<p>Il nous faut maintenant tester notre œuvre. Plutôt que de devoir rebooter notre vraie machine, nous allons utiliser une machine virtuelle complète (qemu, vmware, virtualbox ...), dans laquelle on installe grub.</p>


<p>Et dans votre menu (ou dans le shell grub)&nbsp;:</p>
<pre>
kernel (hd0,0)/boot/mandel
</pre>


<p>C'est jôliii&nbsp;!
<img src="http://linux-attitude.fr/public/Schemas/mandel1.png" alt="mandel1.png" style="display:block; margin:0 auto;" title="mandel1.png, oct 2008" /></p>


<p>Pour ceux qui veulent tester, il y a en pièces jointes, les sources ainsi qu'une image disque toute prête pour <a href="http://bellard.org/qemu/" hreflang="en">qemu</a> contenant les sources, le binaire compilé, ainsi qu'un grub préinstallé avec un menu (et le contenu du prochain article avec, mais chut). Pour ceux qui n'ont pas tout suivi, essayez de lire les sources, ca peut être instructif.</p>
<pre>
$ qemu mandel.dd
</pre>


<p>Dans le prochain numéro, nous ferons la même chose en plus grand, plus beau et en mode protégé.</p>


<p><strong>PJ</strong>&nbsp;:</p>
<ul>
<li><a href="http://linux-attitude.fr/public/Code/mandel1/compile.sh">compile.sh</a></li>
<li><a href="http://linux-attitude.fr/public/Code/mandel1/header.S">header.S</a></li>
<li><a href="http://linux-attitude.fr/public/Code/mandel1/kernel.c">kernel.c</a></li>
<li><a href="http://linux-attitude.fr/public/Code/mandel1/setup.ld">setup.ld</a></li>
<li><a href="http://linux-attitude.fr/public/Code/mandel.dd">mandel.dd</a></li>
</ul>

<p></p><p>Si vous avez aimé, il y a aussi : </p><ol><li><a href='http://linux-attitude.fr/post/le-retour-de-la-vengeance-de-la-bidouille' rel='bookmark' title='Permanent Link: Le retour de la vengeance de la bidouille'>Le retour de la vengeance de la bidouille</a></li>
<li><a href='http://linux-attitude.fr/post/vectorisation' rel='bookmark' title='Permanent Link: Vectorisation'>Vectorisation</a></li>
<li><a href='http://linux-attitude.fr/post/en-vrac-25' rel='bookmark' title='Permanent Link: En vrac (25)'>En vrac (25)</a></li>
</ol>
	Tags:<a href="http://linux-attitude.fr/tag/code" title="Code" rel="tag">Code</a>, <a href="http://linux-attitude.fr/tag/noyau" title="Noyau" rel="tag">Noyau</a>, <a href="http://linux-attitude.fr/tag/planet-libre" title="planet-libre" rel="tag">planet-libre</a><br />
]]></content:encoded>
			<wfw:commentRss>http://linux-attitude.fr/post/le-retour-de-la-bidouille/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using disk: basic
Page Caching using disk: enhanced (User agent is rejected)
Database Caching 19/82 queries in 0.117 seconds using apc
Object Caching 1589/1665 objects using apc
Content Delivery Network via N/A

Served from: linux-attitude.fr @ 2012-02-08 23:44:39 -->
