Emulateur Gameboy Flash
9/07/10
Comment retranscrire la magie d’un jeu tel que Super Marioland en flash ? En considérant le nombre de planques et le gameplay inimitable et old school, la magie du pixel art… Autant écrire un émulateur Gameboy, et charger la ROM !
Plus une sorte de défi Geek qu’un truc utile, ou même innovant (après quelques recherches on trouve même des émulateurs GB en JS), voila de quoi bien occuper quelques journées. Le résultat:
Tester l’émulateur Gameboy Flash
Je profite de cette expérience low-level pour faire quelques commentaires sur la programmation, bon ok le code de l’émulateur est plutôt sale et pourrait être optimisé (ce qui me prendrais beaucoup de temps pour juste refaire un travail déja existant), mais l’approche d’un processeur (équivalent au Zilog 80 pour les connaisseurs) tel que celui là et des techniques un peu roublardes de l’ASM pousse à se poser des questions.
Questions d’espace
Au final, le fichier SWF (Flash) obtenu pèse … aux alentours de 13K. Et la ROM de mario en fait 64K (seulement).
Un jeu, simple certes mais quand même aussi complet soit t-il et qui rentre au final sur moins de 100K, je ne sais pas ce que vous en pensez, mais je trouve ça plutôt impressionant. Imaginez que l’on essaie de faire la même chose entierement en flash, je vous garantie que vous dépasserez cette taille très très vite!
Notons que le processeur est un processeur 8bits et que les adresses sont codées sur 16Bits, ce qui ne fait que 64K adressable. Parmis les adresses accessibles, seulement 32K de la ROM sont visibles, il est nécéssaire pour les développeurs de switcher manuellement les morceaux de la ROM qu’il sera possible d’acceder à un moment donnée!
Les graphismes
Bien entendu, les développeurs de jeux Gameboy ne s’amusaient pas à écrire la tête de Mario pixel après pixel. Pour cela, ils utilisent des tiles. En gros, ce sont des motifs qui sont chargés dans une zone spéciale de la mémoire vidéo (videoram) et qui sont par la suite réutilisés dans le jeu. Voici une palette de Tiles extraite de Mario:
Une fois ces tiles chargés, le jeu écrit un mappage de ces derniers dans une autre mémoire pour constituer une image avec. L’image crée ainsi est plus grande que celle de l’écran de la Gameboy, le jeu écrit aussi des valeurs (SCROLLX et SCROLLY) dans des registres spéciaux de manière à spécifier quelle partie doit être dessinée sur l’écran (ce qui permet d’anticiper les déplacement du joueur, en « scrollant l’image »).
Le hic, c’est que ces coordonnées X et Y peuvent être modifiées à tout moment, y compris pendant le tracé des lignes sur l’écran LCD. Prenons par exemple Mario, les informations en haut (Vies, score, temps etc.) restent fixe alors que le décor bouge lorsque vous vous déplacez, le jeu profite en fait d’une interruption spéciale se déclenchant exactement à la dernière ligne du bloc d »informations en haut pour changer les valeurs de SCROLLX et de SCROLLY. Roublards !
Un petit exemple
Bien Geek mais y’en a à qui ça fera sans doute plaisir, voici un morceau d’assembleur extrait du jeu Tetris que j’ai commenté:
000002CD: LD A,(FF80h) ; Registre ou le jeu stocke
; l'état des bouttons appuyés
000002CF: AND 0Fh ; "Et" logique, pour qu'il ne
; reste que les 4 derniers bits
000002D1: CP 0Fh ; On le compare à %00001111
; c'est à dire les 4 bits à 1
; (Ici, A+B+START+SELECT)
000002D3: JP Z,021Bh ; Si c'est égal, on saute en
; 021B, qui est ici l'adresse
; de réinitialisation du jeu
Maintenant, vous savez pourquoi quand vous appuyez sur A+B+START+SELECT sur GB, le jeu est réinitialisé!
La difficulté du débuggage
L’écriture d’un émulateur prend du temps et le débuggage peut par moment s’avérer extrement difficile. J’ai remarqué que la plupart de mes erreurs étaient dues à des OPCodes (instructions processeur) mal implémentées, faussant par la suite le comportement du jeu.
Au delà de ces instructions, il faut également vérifier que les registres gérés en temps normal par le processeur sont à peu près cohérents, ainsi en oubliant de mettre à jour un de ces registres incrémentés plusieurs milliers de fois par seconde (et servant en général de générateur pseudo-aléatoire), je me suis retrouvé face à un jeu de Tetris me servant toujours la même pièce !
Heureusement, j’ai pu profiter de plusieurs ressources:
- Une documentation du CPU de la Gameboy, plutôt écrite pour les développeurs à la base, mais assez complète.
- GBME, un emulateur qui s’auto-proclame « Buggy » et « incomplet », mais plutôt excellent au niveau des outils de Débuggage (Breakpoints, Memory dump etc.)
- Un émulateur dont le code source (C#) a été diffusé
- Virtual Gameboy Tools, une suite d’outils dont un simple décompilateur (mais pratique pour aller jeter un coup d’oeil à l’ASM)
Tetris ASCII
2/06/10
Pour les ennuyés du TTY voici un petit Tetris en mode console écrit en C.
Télécharger l’archive (Tetris-1.0.tgz)
La largeur et la hauteur sont modifiables directement (cf main.c). Vous pouvez également regarder le code source qui est normalement relativement propre et modifier les figures ou les niveaux !
Touches:
- Q: Gauche:
- S: Bas (acceleration)
- D: Droite
- [ESPACE]: Tourner
Le confort de Ruby
9/02/10
J’ai eu l’occasion de tester Ruby, un langage de programmation assez intéréssant, voici un petit résumé des concepts sympatiques de Ruby.
Une syntaxe légère
La syntaxe du langage est en effet particulièrement sobre et légère, tout en gardant une expression riche.
# Pas de ; ou d'accolads à tour de bras
class Greeter
def initialize
# Les attributs préfixés de "@"
@name = "Jacques"
end
# Parametres optionels
def say_hi (who = "World")
puts "Hello #{who} je suis #{@name} !"
end
end
# Simple instance et appel de fonction
g = Greeter.new
g.say_hi "Paul"
On ne peut que reconnaitre la legereté d’écriture, notament:
- L’absence de points virgules
- L’absence des accolades (le mot clé end servant de séparateur)
- La possibilité d’omettre les parentheeses lors d’un appel de fonction, meme en lui passant des arguments
- Les variables non préfixées comme dans php (avec son $ eternel)
Un langage multi-paradigmes
Ruby offre un grand nombre de possibilités en couvrant plusieurs paradigmes de programmation:
#Tout est objet dans Ruby
-3.abs #3
# Les tableaux nativement gérés
[1,2,4,6].include?(2) #true
# Les opérateurs surchargeables
[1,2]+[3,4] #[1, 2, 3 ,4]
# Ecriture de classes rapide
class Essay
def test
puts "Hi"
end
end
# Instanciation a la volée
Essay.new.test
# Omission du return, la derniere expression
# evaluee est retournée
def foisdeux (x)
2*x
end
# Egalement possibilité d'utiliser des fonctions
# anonymes (lambda-calcul)
[1,2,3,4,5].select {|x| x%2==0} #[2, 4]
lambda {puts "Hello"}.call #Hello
# Possibilité d'étendre les classes existantes
class String
def startwith (s)
self[0,s.length]==s
end
end
"tototiti".startwith("erk") #false
"tototiti".startwith("tot") #true
Il y a beaucoup de calcul « anonymes » (fonctions écrites à la volées et a durée de vie éphémère)
En résumé le langage est assez convivial, surtout très esthétique je dirais (pas forcément extrêmement évident pour un premier langage par exemple, mais très compréhensible à la lecture). Les itérations à tour de bras et les fonctions « à la volée » permettent une bonne clarté et une bonne lisibilité.
Maitrisez la taille de vos images
25/01/10
Voici une solution générique au problème souvent rencontré de la gestion de la taille des images. En effet, et comme vous le savez, redimensionner un objet <img> a tendance à faire charger à l’utilisateur une image beaucoup trop grosse pour ce qu’il en verra, de plus les navigateurs n’ont pas toujours de modules d’anti-aliasing. La solution bien connue est de générer, coté serveur, des images de la bonne dimension (qu’il s’agisse de miniature ou pas).
J’ai écrit cette classe pour essayer de me débarasser de ce problème ennuyeux, voici comment elle fonctionne.
Chargement d’une image
Tout d’abord, il faut instancier l’image, puis lui dire quel fichier charger
$i = new Image;
$i->fromFile("image.jpg");
Le paramètre peut aussi être passé directement dans le constructeur, afin de gagner de la place:
$i = new Image("image.jpg");
Appliquer une redimension
Il existe ensuite plusieurs façon de redimensionner l’image, voici des exemples pour mieux comprendre.

$i->;resize(300,200), réduit éventuellement l'image mais ne l'agrandit jamais (pratique pour les miniatures)

$i->scaleResize(300,200), redimensionne l'image en gardant le ratio de maniere a la faire tenir dans la place attribuée

$i->forceResize(300,200), force l'image a faire exactement la taille donnée, sans que le ratio ne compte (peut déformer)

$i->cropResize(150,100), fonctionne comme resize mais coupe l'espace blanc autour de l'image (revient à limiter la largeur et la hauteur)
Chaîne d’opération
Notons que tout l’interêt du code que je présente ici est que les opérations ne sont pas appliquées. En effet apres une ouverture (fromFile) et une redimension (resize…) la classe n’a strictement rien fait d’autre que d’enregistrer tout ce qui lui est demandé. Il est alors possible de la forcer à effectuer les opérations et à sauvegarder l’image avec la fonction save() (voir plus bas). Mais l’option la plus interessante est de lui demander d’utiliser son système de cache de manière à tout gérer automatiquement.
En appellant la méthode cacheFile(), la classe commencera à calculer un hash de tout ce qui lui est demandé (les opérations, le fichier d’origine, le format demandé etc.) et générera alors un nom de fichier de cache. Si ce fichier existe, elle ne fera strictement rien d’autre que de retourner le nom de ce fichier. Si il n’existe pas, elle appellera save(), la fonction permettant de générer l’image en la sortant dans le fichier de cache.
Les fonctions jpeg(), gif() et png() sont des raccourcis de cacheFile().
Exemples et raccourcis
Voici un exemple d’execution détaillé:
//Créé l'instance
$i = new Image;
//Charge l'image "image.jpg"
$i->fromFile("image.jpg");
//Utilise une redimension non-agrandissante dans une boite 300x200
$i->resize(300, 200);
//Applique les modifications ou alors récupere le cache correspondant
// $file contiendra le nom du fichier cache obtenu
$file = $i->jpeg();
Et la version raccourcie, grace à la définition de la fonction Image() et des return $this; Imaginez que vous écriviez une gallerie photo dans laquelle vous souhaitez mettre des miniatures en 120×120:
<img src="<? echo Image($file)->resize(120,120)->jpeg(); ?>" alt="" />
L’interêt est de mêler étroitement la simplicité d’écriture et les performances. Lorsque le fichier de cache existe déja, l’utilisation de la classe Image ne nécessite que le calcul d’un hash et quelques vérifications. Une fois le dossier de cache correctement configuré, cette ligne suffira a maitriser le rendu des images.
Autre utilisation, rendu dans un fichier
Nous voudrons parfois pouvoir utiliser ce système non pas à l’affichage, mais à un instant précis du code. Pour cela vous pouvez utiliser la fonction save($fichier, $format= »jpg », $qualite=80).
//Ouverture de l'image $i = new Image($file_name); //Utilise une redimension non-agrandissante dans une boite 120x120 $i->resize(120, 120); //Sauvegarde le résultat dans le répertoire des miniatures (thumb) $i->save($thumb_dir.$file_name, "jpg");
Pour conclure voici les sources de la classe en question:
Voir le code source de la classe Image
Fonctionnalités obscures de PHP
21/01/10
Un petit article pour parler des fonctionalités « obscures » de PHP. J’ai trouvé en effet mal documenté certains features qui me semblent intéréssant du coté des interfaces. Je vous laisse regarder la doc si vous n’avez pas envie de lire l’article en cliquant ici.
Des pseudos-tableaux
Il est en effet possible d’utiliser la syntaxe suivante (utiliser des objets comme des arrays):
$m = new Machin; $m["test"] = 12
Ca peut parraitre bizarre, mais c’est parfois pratique. Voici une petite description de l’utilisation de ce feature:
class Machin implements ArrayAccess {
//Est-ce que isset($obj[$var] ?
public function offsetExists($var) {
return true;
}
//Quelle est la valeur de $obj[$var] ?
public function offsetGet($var) {
return 42;
}
//Définition de la valeur $obj[$var]
public function offsetSet($var, $val) {
echo "$var = $val!\n";
}
//Appel à unset($var[$obj])
public function offsetUnset($var) {
echo "removing $var\n";
}
}
$m = new Machin;
if (isset($m["toto"])) { //true
echo $m["toto"]."\n"; //42
$m["truc"] = "bidule"; //truc = bidule!
unset($m["blork"]); //removing blork
}
Il est par exemple plus facile de créer un système de session customizées, enfin, laissez votre imagination tourner je pense que vous aurez bien plus d’idées que moi, sachant que chaque acces au tableau provoque un appel de fonction et peut donc être dynamique (pourquoi pas requêter un SGBD). Il est aussi possible de « mapper » un serveur memcache par exemple, créer une classe qui permettra d’utiliser le serveur de cache comme un simple tableau.
Des objets itérables
Vous qui utilisez des templates php à tour de bras vous devez apprécier fortement l’instruction foreach. Je vous laisse donc découvrir ce que vous pouvez faire en implémentant Iterator:
- function rewind() : retourne à la premiere position de l’itération
- function current() : retourne l’élément courant
- function key() : retourne la clé courante
- function next() : passe à l’élément suivant
- function valide() : retourne vrai si l’élément est valide (permet d’arrêter l’itération)
Il est alors possible de faire:
$trucs = new Trucs;
foreach ($trucs as $t) {
//Faire quelque chose avec $t
}
Ce qui peut s’avérer pratique lorsque la sémantique de votre objet est justement d’être un containers. Pour plus de détails je vous laisse consulter la documentation.
Appels de méthodes enchaînés
Ce n’est pas vraiment quelque chose d’obscur ou de caché mais plus une astuce. Lorsque vous souhaitez appeller plusieurs fonctions sur le même objet, et que ces fonctions ne retournent rien, rien ne coute d’ajouter un return $this; à la fin de la fonction en question, ce qui vous permet d’enchainer les appels sur la même ligne. Un petit exemple:
class Query {
private $sql;
public function __construct() {
$this->sql = "SELECT * FROM ";
}
public function from($table) {
$this->sql.= "`$table` ";
return $this;
}
public function where($where) {
$this->sql.= "WHERE $where ";
return $this;
}
public function exec() {
echo "Requete: ".$this->sql."\n";
}
}
$q = new Query;
$q->from("users")->where("id=12")->exec();
//Requete: SELECT * FROM `users` WHERE id=12
L’exemple est très caricatural, mais on voit vite les avantages d’un tels procedé. Cette syntaxe est largement utilsiée par certaines libs comme Doctrine.
Les fonctions magiques
Chaque classes définit ses propres méthodes, mais peut aussi définir des méthodes dynamiquement. Lorsqu’une méthode n’existe pas, php appelle la fonction __call() si elle existe. un petit exemple:
class User {
public function __call($func, $params) {
if (substr($func, 0, 2) == "by") {
$name = trim(strtolower(substr($func, 2)));
$param = $params[0];
echo "SELECT * FROM users WHERE `$name`=\"".$params[0]."\"";
}
}
}
$u = new User;
$u->byName("toto");
//SELECT * FROM users WHERE `name`="toto"
Encore très simplifié, mais je pense que le concept transparait bien, les fonctions peuvent être parfois plus parlantes que des noms d’arguments.
A noter qu’il existe d’autres fonctions magiques, comme celles permettant d’acceder à des pseudos-propriétés ($u->name par exemple) de manière dynamique (avec un appel de fonction). Là aussi, jetez un oeil au manuel.
Le mécanisme de réflexion
Un autre aspect sur lequele je ne vais pas m’éterniser, le mécanisme de réflexion. Comme tous les langages de haut niveau, PHP peut s’analyser lui même, il est ainsi possible de savoir dynamiquement si une fonction existe, si une classe implémente telle ou telle interface, dans quel fichier elle est définie ou encore le noms et les valeurs des propriétés statiques.
Voila je m’arrête ici, j’éspère vous avoir donné quelques idées pour vos projets. La souplesse du langage permet de faire beaucoup de choses assez sympas qui rendent le code (celui qui utilise vos classes bizarres!) plus lisible et facile à maintenir, donc à vos claviers.
Les bases de données
21/01/10
Depuis de nombreuses années plusieurs concepts progressent concernant les bases de données, on peut cependant remarquer que, premièrement, le modèle relationnel est le plus largement utilisé, et ensuite que nous continuons d’écrire des requêtes SQL dans nos codes sources, mélangeant ainsi deux langages.
Resterons nous perpetuellement bloqué sur le modèle relationnel et ses jointures, ou trouverons nous un jour une façon plus simple d’appréhender un modèle de donnée complexe ?
Seul l’avenir répondra à cette question, mais pas forcément un avenir si proche qu’on le pense, en effet une thèse écrite en 1993 fait déja état des SGBD Orientés Objets et propose un concept de langage persistant, mais pourtant les bases relationnelles ne trouvent toujours pas de sérieux concurents. En attendant, voici quelques remarques et commentaires pratique sur l’utilisation des bases de données.
Modulabilité
Le premier problème posé est que nous utilisons d’un coté un langage (PHP, Java, C++ …) et d’un autre un SGBD (MySQL, PostgresSQL, Oracle…). Le traitement des données est donc doublement dépendant des technologies utilisées, car les requêtes sont spécifiques et diffèrent d’un SGBD à un autre. Les solutions d’interopérabilité sont assez intéréssantes, mais trouvent leurs limites. Il s’agit en général d’utiliser des classes spéciales du langage utilisé qui serviront en grande partie à la communication avec le serveur (intermédiaires).
Cette approche est appellée mapping objet-relationnel, autrement dit l’illusion d’utiliser une base de données orientée objet alors que tout est relationnel. Ces systèmes se nomment « ORM » (Object-relational mapping), on en trouve en PHP comme Propel, Doctrine ou pdoMap. Ainsi il est par exemple possible d’écrire:
$u = new User; $u->prenom = "Jacques"; $u->age = 32; $u->insert();
(L’ORM Est une classe abstraite de laquelle hérite User). Et l’ORM s’occupe automatiquement de générer les requêtes SQL correctes. Autre exemple:
$u = new User; $u->get(35); $u->age++; $u->update();
Et pour les requêtes un peu compliquées, les ORM vont jusqu’à créer des langages, comme le JDOQL (Java) ou le DQL (PHP), qui servent à requêter non pas les bases de données mais les classes (ce qui engendre naturellement des requêtes vers la base, mais de manière opaque). Ces requêtes sont généralement plus courtes et plus expressives que le langage SQL.
On remarque ainsi plusieurs avantages:
- La syntaxe du langage peut s’appliquer aux champs de la base (comme le « age++ »), la lisibilité du code ainsi obtenu peut être très bonne
- Les fonctions SQL « CRUD » (Create, Retrieve, Update & Delete) se font très facilement, plus une seule ligne de code SQL à écrire
- La syntaxe est en théorie la plus indépendante du SGBD possible, le concept « utopique » étant que le changement d’SGBD ne nécéssiterait que du bricolage dans l’ORM et non pas dans le code PHP
- Les mêmes ORM sont parfois présents dans plusieurs frameworks, on retrouvera Propel dans Symfony par exemple
- L’ORM peut servir d’intermédiaire pour établir des mécanismes de Cache
Mais aussi des inconvénients:
- La gestion des bases de données complexes (ayant des multiples jointures) est assez peu aisée à manipuler
- La lourdeur d’execution en prendra forcément un coup, ce type de classes faisant souvent appel à des accesseurs magiques ou à des éléments du modèle objet
Ce système permet aussi de profiter d’avoir des classes pour chaque table afin d’écrire des méthodes « custom ». Par exemple:
$u->addFriend($v);
Le découpage « un objet par table » prend généralement un sens assez pertinent.
Performances
Les performances sont aussi très importantes dans la communication avec un SGBD. Des politiques de mise en cache peuvent être adoptées. L’idée est assez simple, chaque requête du style « récupérer un enregistrement par sa clé » fait d’abord appel à une fonction du langage qui vérifie si le cache ne contient pas cet enregistrement.
Avantage immédiat, les enregistrements très récement et très souvent accédés restent « sous le coude ». C’est un peu comme si vous travailliez dans une grande bibliothèque, vous iriez chercher les trois livres dont vous avez besoin et vous les poseriez sur la table à coté de vous.
Ce mécanisme a été implémenté dans PHP par le biais d’APC (Alternative PHP Cache), qui permet de stocker ou de récupérer des éléments par leur index. Il existe aussi memcached, un serveur entièrement dédié à la mise en cache d’informations. Il est notament massivement utilisé par Facebook, le concept étant simple, on lui accorde une certaine quantité de mémoire et il se débrouille pour associer nos clés à nos données.
Autre détails, penser simplement à indexer la base de données sur les champs sur lesquels faire des recherches ou des jointures. Il faut parfois faire preuve d’un peu d’astuce pour parvenir à optimiser la base de manière à éviter les requêtes trop lourdes. Créer un Index « à chaud » peut être une opération très lourde, mais le gain par la suite sera forcément très intéréssant.
Nouveau problème posé par ces mécanismes: la scalabilité. Imaginez que votre site web fonctionne bien et que vous vouliez supporter un plus gros traffic, le cache ne sera pas forcément très pratique, car il doit être global (d’ou l’interêt de memcache par rapport à APC par exemple)
Scalabilité
Voici une proposition de découpage possible assez arbitraire:
- Un serveur executant le SGBD (prendre une grosse machine, car cette partie n’est pas facile à découper)
- N Frontaux HTTP derrière un Rétro-proxy ou un load balancer
- Eventuellement des frontaux dédiés aux fichiers statiques
- Un ou plusieurs serveurs de cache (machines avec beaucoup de RAM, un processeur raisonable et un disque dur minimaliste)
La plus grande difficulté repose sur la répartition de la machine hébérgeant la base de données. Nous pouvons nous rassurer en nous disant que même des très grosses bases peuvent facilement tenir le coup pour peu qu’elles soient bien indexées, et que le réseau n’est pas un facteur très contraignant quant à la rapidité (en LAN les performances sont plutôt bonnes entre les frontaux.
Maintenabilité
Un des concepts aussi employé dans les grosses architectures est l’éloignement de la base de données des serveurs applicatifs. Cet éloignement se fait par l’ajout de services, généralement conçus pour marcher via XML, et permet de garder la maitrise de l’ensemble des requêtes effectuées dans la base.
Ainsi les administrateurs de bases de données (DBA) entretiendront leur base et créeront des services associés à chaque type de requête qu’il sera possible de rencontrer. Ainsi les applications ne communiqueront jamais directement avec le SGBD mais avec des interfaces XML.
En conclusion, même si les bases relationelles restent franchement de la partie, un ensemble considérable d’outils et de couches qui viennent se greffer autour de ces derniers change l’environnement dans lequel les développeurs doivent évoluer.


