Aller plus loin

Ce dernier chapitre nous donnera des idées pour améliorer notre émulateur, et nous donnera également quelques pistes à suivre sur le chemin de l’émulation console.

De nouvelles pistes

De nouvelles fonctionnalités

Maintenant que notre émulateur est à peu près fonctionnel, nous pouvons rajouter de nouvelles fonctionnalités.

Il nous manque pour commencer un système de pause et de redémarrage de jeu. Ils peuvent se faire assez facilement, par exemple en rajoutant des variables replay et start à notre structure s_emulator.

De même, nous ne pouvons pas chosir de lancer un jeu pour le moment. Une première méthode assez simple est de donner le chemin de la rom à charger au programme. Ainsi, avec ./emulator Breakout.ch8, on lancerait le jeu Breakout. Mais pourquoi ne pas avoir une interface pour choisir un jeu à lancer, qui s’ouvrirait par exemple suite à une combinaison de touches (Ctrl+O par exemple) ? Avec ça, on peut non seulement lancer un jeu, mais aussi changer de jeu.

Toujours au niveau de la configuration, on pourrait penser à un système pour changer les touches de notre émulateur, peut-être même sauvegarder sa configuration dans un fichier et pouvoir ensuite le recharger.

Et finalement, puisqu’on parle de sauvegarder, ce serait bien d’avoir un système de sauvegarde pour les jeux !

Améliorer l’émulateur

Certains jeux, Blinky par exemple, peuvent ne pas fonctionner sur notre émulateur. En fait, comme les informations utilisées pour faire notre émulateur ne sont pas unanimes, certains jeux utilisent sûrement des propriétés que nous n’avons pas implémentées. Pour Blinky par exemple, augmenter la vitesse de jeu semble suffire pour obtenir un bon rendu. Nous avons donc tout intérêt à développer un émulateur configurable.

Vers l'infini et au-delà

Avec tout ceci, nous n’avons touché qu’à une petite part du monde de l’émulation console. La Chip 8 étant une machine plutôt simple, plusieurs notions n’ont pas été abordées ; voyons donc quelques pistes que nous pouvons suivre pour aller plus loin.

De nouvelles notions

La recompilation dynamique

Certaines consoles sont cadencées à une fréquence tellement élevée qu’une simple approche où on interprète chaque instruction n’est pas suffisante. Il existe une méthode appelée recompilation dynamique (dynarec) qui permet de contourner cet obstacle. Cette méthode, bien qu’un peu compliquée, offre des performances inégalables !

Ça peut être une bonne expérience d’essayer d’écrire un émulateur pour la Chip 8 avec de la recompilation dynamique. De plus, des exemples sont trouvables sur Internet à ce sujet (voir par exemple ce dépôt Github qui vient avec un document explicatif). Notons que la recompilation dynamique rend le code plus compliqué, et l’émulateur devient notamment plus compliqué à déboguer.

Le son et les cycles d’horloge

La Chip 8 n’est pas vraiment le système adapté pour travailler sur les deux points que sont le son et les cycles d’horloge.

D’un côté, la gestion du son se limitait à faire un bip. Rien de très excitant, et surtout rien de vraiment compliqué. L’émulation du son n’est pas toujours aussi facile ; gardons en tête que dans la plupart des cas, il faudra générer le son grâce aux instructions du jeu, sans faire appel à une ressource externe (c’est par exemple ce qu’il faut faire avec la MegaChip 8).

De l’autre côté, dans notre boucle principale, tous les opcodes ont le même temps d’exécution. Déjà, notre gestion du temps ne prend pas en compte le temps mis pour faire les instructions de la boucle. Mais en fait, il y a même plus important. Certaines consoles introduisent la notion de cycle d’horloge. Il se peut alors que certaines instructions prennent plus de temps que d’autres, et c’est à nous de trouver une méthode pour gérer cela de manière optimale !

Le débogage

La Chip 8 étant simple, il n’y a pas vraiment besoin de débogueur pour trouver les opcodes qui ne sont pas bien implémentés par notre émulateur. Sur une machine plus compliquée, ça peut grandement nous faciliter la tâche, surtout que le nombre d’instructions sera alors beaucoup plus grand.

En fait, nous pouvons même voir des outils pour le débogage dans certains projets d’émulateur de Chip 8. Et finalement n’est-ce pas là une bonne idée de rajouter des outils de débogage à l’émulateur Chip 8 avant de passer à une autre console. Cela donnera alors de l’expérience pour une autre console !

De nouvelles consoles

Pour aller plus loin, nous pouvons également nous frotter à d’autres consoles. Un bon projet pour continuer est la SuperChip 8 qui est en quelque sorte le descendant de la Chip 8. Ces deux systèmes ont donc beaucoup d’instructions en commun, et il sera facile de mettre à jour notre émulateur. Et on peut même avancer encore avec la Chip 8 puisque la SuperChip8 a aussi un descendant, la MegaChip8 (descendant qui a un écran en couleur).

Les extensions de la Chip 8

En fait, la Chip8 a vraiment plusieurs extensions. Ce dépôt Github en recense quelques-unes, mais avec la SuperChip 8 et la MegaChip 8, nous avons déjà beaucoup à faire (et beaucoup voudront sûrement passer à une autre console plus conventionnelle après tout ça).

Deux fois huit ?

Ça fait seize, et la Chip 16 est un système conçu spécialement pour les débutants en émulation. Il est en couleur, a plus d’instructions que la Chip 8, et surtout, toutes les informations nécessaires sont disponibles. Voici un dépôt Github avec tout le nécessaire.


Bien sûr, il est également possible de se lancer directement sur une « vraie » console. Mais attention, s’attaquer à une console de dernière génération est très certainement une erreur. La Game Boy est une bonne console pour commencer, et sera déjà un très bon défi !

Vers la compilation ?

Le domaine de l’émulation peut facilement être relié à celui de la compilation. En fait, on pourrait se demander comment écrire un programme pour la Chip 8. Il faudrait écrire chaque opcode à la main. Ce serait long… Et chiant. Pour pallier cela, pourquoi ne pas avoir un langage dans lequel écrire le programme, et ensuite le compiler.

Il est plus simple de commencer par l’écriture d’un assembleur. Nous pouvons remarquer qu’un nom a été associé à chaque instruction dans ce chapitre. Ceci nous permet alors d’avoir un assembleur, et la traduction en opcode est directe.

ld V3, 0        ; met V3 à 0
rnd V2, 16      ; nombre au hasard (inférieur à 16) est mis dans V2
:wait_input
kwait V1        ; attend appui sur une touche et met la valeur dans V1
add V3, 1       ; incrémente V3
skreq V1, V2    ; si V1 et V2 sont égaux, on saute l'instruction suivante.
jmp wait_input  ; on saute au label wait_input (ne sera pas fait si V1 == V2)
ld V4 0
ldst V4
kwait V1

Ce programme tire un nombre au hasard (inférieur à 16), et demande à l’utilisateur une saisie (qui correspond à un nombre entre 0 et 16). S’il n’a pas le bon nombre, on retourne à l’étiquette wait_input, et on redemande une saisie. Quand il aura trouvé le bon nombre, on mettra le compteur sonore à V4 (qu’on a préalablement mis à 0), ce qui permettra de jouer un bruit, puis on attend une saisie de l’utilisateur.

En clair, c’est un jeu où l’utilisateur doit trouver un nombre entre 0 et 16. Un son est joué lorsqu’il l’a trouvé.

La traduction du code en opcode est directe ; pour plus de lisibilité, nous gardons les retours à la ligne, mais il faut garder en tête que ce que nous voulons à la fin est une suite de bits correspondants aux instructions.

6300
C20F
F10A
7301
5120
1002 
6400
F10A

Nous pouvons alors nous amuser à écrire un assembleur pour la Chip 8 (merci à @nohar pour cette idée) ! Ceci demandera (si tout est fait à la main) des connaissances en analyse lexicale et syntaxique ; c’est tout un nouveau monde qui apparaît ici.


Ce chapitre nous aura donc fourni beaucoup de choses pour poursuivre sur le chemin de l’émulation. Verrons-nous bientôt apparaître des dizaines de projets d’émulation sur le forum ?