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:

Tiles (motifs) utilisés durant une partie 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: